diff --git a/modules/boe-module-scorm/pom.xml b/modules/boe-module-scorm/pom.xml
new file mode 100644
index 00000000..a17d0493
--- /dev/null
+++ b/modules/boe-module-scorm/pom.xml
@@ -0,0 +1,127 @@
+
+
+ 4.0.0
+
+
+ com.xboe
+ module
+ 1.0.0
+
+
+ xboe-module-scorm
+ xboe-module-scorm
+ jar
+ scorm课件的处理
+
+ 1.8
+
+
+
+ com.xboe
+ xboe-core
+
+
+ com.xboe
+ xboe-module-course
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+ commons-validator
+ commons-validator
+ 1.6
+
+
+ commons-io
+ commons-io
+ 2.6
+
+
+ dom4j
+ dom4j
+ 1.6.1
+
+
+ net.sourceforge.cardme
+ cardme
+ 0.4.0
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.apache.commons
+ commons-text
+
+
+ commons-codec
+ commons-codec
+
+
+ org.hibernate
+ hibernate-core
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-web
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework
+ spring-aop
+
+
+ org.springframework.data
+ spring-data-jpa
+
+
+ org.springframework
+ spring-aspects
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
diff --git a/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/SCORM.java b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/SCORM.java
new file mode 100644
index 00000000..a1056c47
--- /dev/null
+++ b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/SCORM.java
@@ -0,0 +1,269 @@
+package com.xboe.module.scorm;
+
+import java.math.BigDecimal;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.xboe.module.scorm.cam.load.SCORMPackageManager;
+import com.xboe.module.scorm.cam.model.ContentPackage;
+import com.xboe.module.scorm.cam.model.DeliveryContent;
+import com.xboe.module.scorm.common.CommonUtils;
+import com.xboe.module.scorm.common.ID;
+import com.xboe.module.scorm.rte.api.LearnerAttempt;
+import com.xboe.module.scorm.rte.api.SCORMRuntimeManager;
+import com.xboe.module.scorm.rte.model.Objectives.Instance;
+import com.xboe.module.scorm.rte.model.RuntimeData;
+import com.xboe.module.scorm.sn.api.AttemptManager;
+import com.xboe.module.scorm.sn.api.ProcessResult;
+import com.xboe.module.scorm.sn.api.SCORMSeqNavManager;
+import com.xboe.module.scorm.sn.api.event.EventType;
+import com.xboe.module.scorm.sn.api.event.NavigationEvent;
+import com.xboe.module.scorm.sn.model.definition.ObjectiveDescription;
+import com.xboe.module.scorm.sn.model.tracking.AttemptProgressInformation;
+import com.xboe.module.scorm.sn.model.tracking.ObjectiveProgressInformation;
+import com.xboe.module.scorm.sn.model.tree.Activity;
+
+@Slf4j
+public class SCORM {
+
+ private static SCORM instance;
+
+ private SCORMPackageManager packageManager;
+
+ private SCORMRuntimeManager runtimeManager;
+
+ private SCORMSeqNavManager snManager;
+
+ private SCORM() {
+ packageManager = SCORMPackageManager.getInstance();
+ runtimeManager = SCORMRuntimeManager.getInstance();
+ snManager = SCORMSeqNavManager.getInstance();
+ }
+
+ public static SCORM getInstance() {
+ if (instance == null) {
+ synchronized (SCORM.class) {
+ if (instance == null) {
+ instance = new SCORM();
+ }
+ }
+ }
+ return instance;
+ }
+
+ public SCORMResult process(NavigationEvent event, ID activityTreeID) {
+ if (event == null || activityTreeID == null) {
+ return new SCORMResult("Illegal Arguments");
+ }
+ ProcessResult processResult = snManager.process(activityTreeID, event);
+ if (!processResult.isSuccess()) {
+ return new SCORMResult(processResult.getErrorMsg());
+ }
+ if (processResult.getDeliveryActivity() == null) {
+ return new SCORMResult();
+ }
+ Activity deliveryActivity = processResult.getDeliveryActivity();
+ ContentPackage contentPackage = packageManager.launch(activityTreeID.getLmsContentPackageID());
+ if (contentPackage == null) {
+ return new SCORMResult(CommonUtils.format("launch content package \"{}\" failed",
+ activityTreeID.getLmsContentPackageID()));
+ }
+ DeliveryContent deliveryContent = contentPackage.getDeliveryContent(deliveryActivity.getId().getIdentifier());
+ if (deliveryContent == null) {
+ return new SCORMResult(CommonUtils.format("obtain delivery content \"{}\" failed",
+ deliveryActivity.getId().getIdentifier()));
+ }
+// if (!mapTrackingInfoToRuntimeData(deliveryActivity)) {
+// return new SCORMResult("sync Tracking Information and Run-time Data failed");
+// }
+ return new SCORMResult(deliveryContent, deliveryActivity);
+ }
+
+ public boolean mapTrackingInfoToRuntimeData(Activity activity) {
+ LearnerAttempt learnerAttempt = runtimeManager.getLearnerAttempt(activity.getId());
+ if (learnerAttempt == null) {
+ log.error("Learner Attempt \"{}\" not exist", activity.getId());
+ return false;
+ }
+ RuntimeData runtimeData = learnerAttempt.getRuntimeData();
+ for (Instance instance : runtimeData.getCmi().getObjectives().getInstances()) {
+ ObjectiveDescription objectiveDescription = activity.getSequencingDefinition()
+ .findObjectiveDescriptionByID(instance.getId().getValue());
+ ObjectiveProgressInformation information = objectiveDescription.getObjectiveProgressInformation();
+ if (information.isObjectiveProgressStatus()) {
+ instance.getSuccessStatus().setValue(information.isObjectiveSatisfiedStatus() ? "passed" : "failed");
+ }
+ if (information.isObjectiveMeasureStatus()) {
+ instance.getScore().getScaled().setValue(information.getObjectiveNormalizedMeasure()
+ .getValue().setScale(7, BigDecimal.ROUND_HALF_UP));
+ }
+ }
+ AttemptProgressInformation attemptProgressInformation = activity.getAttemptProgressInformation();
+ if (attemptProgressInformation.isAttemptProgressStatus()) {
+ runtimeData.getCmi().getCompletionStatus().getCompletionStatus().setValue(attemptProgressInformation
+ .isAttemptCompletionStatus() ? "completed" : "incomplete");
+ }
+ if (attemptProgressInformation.isAttemptCompletionAmountStatus()) {
+ runtimeData.getCmi().getProgressMeasure().getProgressMeasure().setValue(attemptProgressInformation
+ .getAttemptCompletionAmount().getValue().setScale(7, BigDecimal.ROUND_HALF_UP));
+ }
+ return true;
+ }
+
+ public void mapRuntimeDataToTrackingInfo(Activity activity) {
+ if (activity == null) {
+ return;
+ }
+ log.info("mapRuntimeDataToTrackingInfo: {}", activity.getId());
+ LearnerAttempt learnerAttempt = runtimeManager.getLearnerAttempt(activity.getId());
+ if (learnerAttempt == null) {
+ log.error("mapRuntimeDataToTrackingInfo: Learner Attempt \"{}\" not exist", activity.getId());
+ return;
+ }
+ ID attemptManagerID = activity.getBelongActivityTreeID();
+ AttemptManager attemptManager = snManager.findAttemptManagerBy(attemptManagerID);
+ if (attemptManager == null) {
+ log.error("mapRuntimeDataToTrackingInfo: Attempt Manager \"{}\" not exist", attemptManagerID);
+ return;
+ }
+ if (attemptManager.getLastProcessedEventType() == EventType.Abandon
+ || attemptManager.getLastProcessedEventType() == EventType.AbandonAll) {
+ return;
+ }
+ RuntimeData runtimeData = learnerAttempt.getRuntimeData();
+ AttemptProgressInformation attemptProgressInformation = activity.getAttemptProgressInformation();
+ for (Instance instance : runtimeData.getCmi().getObjectives().getInstances()) {
+ ObjectiveDescription objectiveDescription = activity.getSequencingDefinition()
+ .findObjectiveDescriptionByID(instance.getId().getValue());
+
+ if (objectiveDescription == null) {
+ continue;
+ }
+
+ ObjectiveProgressInformation information = objectiveDescription.getObjectiveProgressInformation();
+
+ if (instance.getSuccessStatus().getValue().equals("unknown")) {
+ information.setObjectiveProgressStatus(false);
+ information.setObjectiveSatisfiedStatus(false);
+ } else {
+ information.setObjectiveProgressStatus(true);
+ information.setObjectiveSatisfiedStatus("passed".equals(instance.getSuccessStatus().getValue()));
+ }
+
+ if (instance.getScore().getScaled().getValue() == null) {
+ information.setObjectiveMeasureStatus(false);
+ information.setObjectiveNormalizedMeasure(0.0);
+ } else {
+ information.setObjectiveMeasureStatus(true);
+ information.setObjectiveNormalizedMeasure(instance.getScore().getScaled().getValue().doubleValue());
+ }
+
+ if (instance.getCompletionStatus().getValue().equals("unknown")) {
+ information.setObjectiveCompletionProgressStatus(false);
+ information.setObjectiveCompletionStatus(false);
+ } else {
+ information.setObjectiveCompletionProgressStatus(true);
+ information.setObjectiveCompletionStatus("completed".equals(instance.getCompletionStatus().getValue()));
+ }
+
+ if (instance.getProgressMeasure().getValue() == null) {
+ information.setObjectiveCompletionAmountStatus(false);
+ information.setObjectiveCompletionAmount(0.0);
+ } else {
+ information.setObjectiveCompletionAmountStatus(false);
+ information.setObjectiveCompletionAmount(instance.getProgressMeasure().getValue().doubleValue());
+ }
+
+ if (objectiveDescription.isObjectiveContributesToRollup()) { // primary
+ if (instance.getCompletionStatus().getValue().equals("unknown")) {
+ attemptProgressInformation.setAttemptProgressStatus(false);
+ attemptProgressInformation.setAttemptCompletionStatus(false);
+ } else {
+ attemptProgressInformation.setAttemptProgressStatus(true);
+ attemptProgressInformation.setAttemptCompletionStatus(
+ "completed".equals(instance.getCompletionStatus().getValue()));
+ }
+
+ if (instance.getProgressMeasure().getValue() == null) {
+ attemptProgressInformation.setAttemptCompletionAmountStatus(false);
+ attemptProgressInformation.getAttemptCompletionAmount().setValue(0.0);
+ } else {
+ attemptProgressInformation.setAttemptCompletionAmountStatus(true);
+ attemptProgressInformation.getAttemptCompletionAmount().setValue(
+ instance.getProgressMeasure().getValue().doubleValue());
+ }
+ }
+ }
+
+ if (runtimeData.getCmi().getCompletionStatus().getCompletionStatus().getValue().equals("unknown")) {
+ attemptProgressInformation.setAttemptProgressStatus(false);
+ attemptProgressInformation.setAttemptCompletionStatus(false);
+ } else {
+ attemptProgressInformation.setAttemptProgressStatus(true);
+ attemptProgressInformation.setAttemptCompletionStatus(
+ "completed".equals(runtimeData.getCmi().getCompletionStatus().getCompletionStatus().getValue()));
+ }
+ if (runtimeData.getCmi().getProgressMeasure().getProgressMeasure().getValue() == null) {
+ attemptProgressInformation.setAttemptCompletionAmountStatus(false);
+ attemptProgressInformation.getAttemptCompletionAmount().setValue(0.0);
+ } else {
+ attemptProgressInformation.setAttemptCompletionAmountStatus(true);
+ attemptProgressInformation.getAttemptCompletionAmount().setValue(
+ runtimeData.getCmi().getProgressMeasure().getProgressMeasure().getValue().doubleValue());
+ }
+
+ ObjectiveDescription objectiveDescription = activity.getSequencingDefinition().getPrimaryObjectiveDescription();
+
+ if (objectiveDescription == null) {
+ return;
+ }
+
+ ObjectiveProgressInformation information = objectiveDescription.getObjectiveProgressInformation();
+
+ if (runtimeData.getCmi().getSuccessStatus().getSuccessStatus().getValue().equals("unknown")) {
+ information.setObjectiveProgressStatus(false);
+ information.setObjectiveSatisfiedStatus(false);
+ } else {
+ information.setObjectiveProgressStatus(true);
+ information.setObjectiveSatisfiedStatus(
+ "passed".equals(runtimeData.getCmi().getSuccessStatus().getSuccessStatus().getValue()));
+ }
+
+ if (runtimeData.getCmi().getScore().getScaled().getValue() == null) {
+ information.setObjectiveMeasureStatus(false);
+ information.setObjectiveNormalizedMeasure(0.0);
+ } else {
+ information.setObjectiveMeasureStatus(true);
+ information.setObjectiveNormalizedMeasure(runtimeData.getCmi().getScore().getScaled().getValue().doubleValue());
+ }
+
+ if (runtimeData.getCmi().getCompletionStatus().getCompletionStatus().getValue().equals("unknown")) {
+ information.setObjectiveCompletionProgressStatus(false);
+ information.setObjectiveCompletionStatus(false);
+ } else {
+ information.setObjectiveCompletionProgressStatus(true);
+ information.setObjectiveCompletionStatus("completed".equals(runtimeData.getCmi().getCompletionStatus().getCompletionStatus().getValue()));
+ }
+
+ if (runtimeData.getCmi().getProgressMeasure().getProgressMeasure().getValue() == null) {
+ information.setObjectiveCompletionAmountStatus(false);
+ information.setObjectiveCompletionAmount(0.0);
+ } else {
+ information.setObjectiveCompletionAmountStatus(false);
+ information.setObjectiveCompletionAmount(runtimeData.getCmi().getProgressMeasure().getProgressMeasure().getValue().doubleValue());
+ }
+ }
+
+ public SCORMPackageManager getPackageManager() {
+ return packageManager;
+ }
+
+ public SCORMRuntimeManager getRuntimeManager() {
+ return runtimeManager;
+ }
+
+ public SCORMSeqNavManager getSnManager() {
+ return snManager;
+ }
+
+}
diff --git a/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/SCORMResult.java b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/SCORMResult.java
new file mode 100644
index 00000000..705da653
--- /dev/null
+++ b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/SCORMResult.java
@@ -0,0 +1,50 @@
+package com.xboe.module.scorm;
+
+import com.xboe.module.scorm.cam.model.DeliveryContent;
+import com.xboe.module.scorm.sn.model.tree.Activity;
+
+public class SCORMResult {
+
+ private final boolean success;
+
+ private final DeliveryContent deliveryContent;
+
+ private final Activity deliveryActivity;
+
+ private final String errorMsg;
+
+ public SCORMResult() {
+ this(true, null, null, "");
+ }
+
+ public SCORMResult(DeliveryContent deliveryContent, Activity deliveryActivity) {
+ this(true, deliveryContent, deliveryActivity, "");
+ }
+
+ public SCORMResult(String errorMsg) {
+ this(false, null, null, errorMsg);
+ }
+
+ public SCORMResult(boolean success, DeliveryContent deliveryContent, Activity deliveryActivity, String errorMsg) {
+ this.success = success;
+ this.deliveryContent = deliveryContent;
+ this.deliveryActivity = deliveryActivity;
+ this.errorMsg = errorMsg;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public DeliveryContent getDeliveryContent() {
+ return deliveryContent;
+ }
+
+ public Activity getDeliveryActivity() {
+ return deliveryActivity;
+ }
+
+ public String getErrorMsg() {
+ return errorMsg;
+ }
+}
diff --git a/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/Constants.java b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/Constants.java
new file mode 100644
index 00000000..c3011221
--- /dev/null
+++ b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/Constants.java
@@ -0,0 +1,7 @@
+package com.xboe.module.scorm.cam.load;
+
+public final class Constants {
+
+
+
+}
diff --git a/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/ContentPackageGenerator.java b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/ContentPackageGenerator.java
new file mode 100644
index 00000000..e83cb073
--- /dev/null
+++ b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/ContentPackageGenerator.java
@@ -0,0 +1,1658 @@
+package com.xboe.module.scorm.cam.load;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.dom4j.io.SAXReader;
+
+import lombok.extern.slf4j.Slf4j;
+
+
+import com.xboe.module.scorm.cam.model.AdlseqMapInfo;
+import com.xboe.module.scorm.cam.model.AdlseqObjective;
+import com.xboe.module.scorm.cam.model.AdlseqObjectives;
+import com.xboe.module.scorm.cam.model.Annotation;
+import com.xboe.module.scorm.cam.model.Classification;
+import com.xboe.module.scorm.cam.model.CompletionThreshold;
+import com.xboe.module.scorm.cam.model.ConditionRule;
+import com.xboe.module.scorm.cam.model.ConstrainedChoiceConsiderations;
+import com.xboe.module.scorm.cam.model.ContentPackage;
+import com.xboe.module.scorm.cam.model.Contribute;
+import com.xboe.module.scorm.cam.model.ControlMode;
+import com.xboe.module.scorm.cam.model.Data;
+import com.xboe.module.scorm.cam.model.DateTime;
+import com.xboe.module.scorm.cam.model.DeliveryControls;
+import com.xboe.module.scorm.cam.model.Dependency;
+import com.xboe.module.scorm.cam.model.Duration;
+import com.xboe.module.scorm.cam.model.Educational;
+import com.xboe.module.scorm.cam.model.General;
+import com.xboe.module.scorm.cam.model.HideLMSUI;
+import com.xboe.module.scorm.cam.model.Identifier;
+import com.xboe.module.scorm.cam.model.Item;
+import com.xboe.module.scorm.cam.model.LOM;
+import com.xboe.module.scorm.cam.model.LanguageString;
+import com.xboe.module.scorm.cam.model.LanguageStrings;
+import com.xboe.module.scorm.cam.model.LifeCycle;
+import com.xboe.module.scorm.cam.model.LimitConditions;
+import com.xboe.module.scorm.cam.model.Manifest;
+import com.xboe.module.scorm.cam.model.ManifestMetadata;
+import com.xboe.module.scorm.cam.model.MapInfo;
+import com.xboe.module.scorm.cam.model.MetaMetadata;
+import com.xboe.module.scorm.cam.model.Metadata;
+import com.xboe.module.scorm.cam.model.NavigationInterface;
+import com.xboe.module.scorm.cam.model.Objective;
+import com.xboe.module.scorm.cam.model.Objectives;
+import com.xboe.module.scorm.cam.model.OrComposite;
+import com.xboe.module.scorm.cam.model.Organization;
+import com.xboe.module.scorm.cam.model.Organizations;
+import com.xboe.module.scorm.cam.model.Presentation;
+import com.xboe.module.scorm.cam.model.RandomizationControls;
+import com.xboe.module.scorm.cam.model.Relation;
+import com.xboe.module.scorm.cam.model.RelationResource;
+import com.xboe.module.scorm.cam.model.Requirement;
+import com.xboe.module.scorm.cam.model.Resource;
+import com.xboe.module.scorm.cam.model.Resources;
+import com.xboe.module.scorm.cam.model.Rights;
+import com.xboe.module.scorm.cam.model.RollupCondition;
+import com.xboe.module.scorm.cam.model.RollupConditions;
+import com.xboe.module.scorm.cam.model.RollupConsiderations;
+import com.xboe.module.scorm.cam.model.RollupRule;
+import com.xboe.module.scorm.cam.model.RollupRules;
+import com.xboe.module.scorm.cam.model.RuleAction;
+import com.xboe.module.scorm.cam.model.RuleCondition;
+import com.xboe.module.scorm.cam.model.RuleConditions;
+import com.xboe.module.scorm.cam.model.Sequencing;
+import com.xboe.module.scorm.cam.model.SequencingCollection;
+import com.xboe.module.scorm.cam.model.SequencingRules;
+import com.xboe.module.scorm.cam.model.Taxon;
+import com.xboe.module.scorm.cam.model.TaxonPath;
+import com.xboe.module.scorm.cam.model.Technical;
+import com.xboe.module.scorm.cam.model.Vocabulary;
+import com.xboe.module.scorm.cam.model.datatype.AnyURI;
+import com.xboe.module.scorm.cam.model.datatype.Decimal;
+import com.xboe.module.scorm.cam.model.datatype.ID;
+import com.xboe.module.scorm.cam.model.datatype.IDRef;
+import com.xboe.module.scorm.cam.model.datatype.NonNegativeInteger;
+import com.xboe.module.scorm.cam.model.datatype.Token;
+import com.xboe.module.scorm.cam.model.datatype.VCard;
+import com.xboe.module.scorm.common.CommonUtils;
+
+@Slf4j
+public class ContentPackageGenerator {
+
+ private static final String NAMESPACE_IMSCP = "http://www.imsglobal.org/xsd/imscp_rootv1p1p2";
+ private static final String PREFIX_IMSCP = "imscp";
+
+ private static final String NAMESPACE_ADLCP = "http://www.adlnet.org/xsd/adlcp_rootv1p1p2";
+ private static final String PREFIX_ADLCP = "adlcp";
+
+ private static final String NAMESPACE_ADLSEQ = "http://www.adlnet.org/xsd/adlseq_rootv1p1p2";
+ private static final String PREFIX_ADLSEQ = "adlseq";
+
+ private static final String NAMESPACE_ADLNAV = "http://www.adlnet.org/xsd/adlnav_rootv1p1p2";
+ private static final String PREFIX_ADLNAV = "adlnav";
+
+ private static final String NAMESPACE_IMSSS = "http://www.imsglobal.org/xsd/imsss";
+ private static final String PREFIX_IMSSS = "imsss";
+
+ private static final String NAMESPACE_LOM = "http://ltsc.ieee.org/xsd/LOM";
+ private static final String PREFIX_LOM = "lom";
+
+ private static final String NAMESPACE_XML = "http://www.w3.org/2001/XMLSchema-instance";
+ private static final String PREFIX_XML = "xml";
+
+ private static final String MANIFEST_FILE_NAME = "imsmanifest.xml";
+
+ private static final boolean CHECK_NAMESPACE=false;//是否检查namespace
+
+ private QNameGenerator rootQNameGenerator;
+ private ContentPackage contentPackage;
+ private String scormPkgDir;
+
+ public ContentPackage generateContentPackageFromFile(String scormPkgDir) {
+ if (scormPkgDir == null) {
+ log.error("scorm package directory is null");
+ return contentPackage;
+ }
+
+ this.scormPkgDir = scormPkgDir + (scormPkgDir.endsWith("/") ? "" : "/");
+
+ File scormPkg = new File(scormPkgDir);
+ if (!scormPkg.exists() || !scormPkg.isDirectory()) {
+ log.error("Not found directory: {}", scormPkgDir);
+ return contentPackage;
+ }
+
+ File manifestXmlFile = new File(this.scormPkgDir + MANIFEST_FILE_NAME);
+ if (!manifestXmlFile.exists()) {
+ log.error("Not found {} in {}", MANIFEST_FILE_NAME, scormPkgDir);
+ return contentPackage;
+ }
+
+ Document manifestXml;
+ try {
+ SAXReader reader = new SAXReader();
+ manifestXml = reader.read(manifestXmlFile);
+
+ } catch (DocumentException e) {
+ log.error("read {} exception: {}", MANIFEST_FILE_NAME, CommonUtils.stringifyError(e));
+ return contentPackage;
+ }
+ if (manifestXml == null) {
+ log.error("parse {} error", MANIFEST_FILE_NAME);
+ return contentPackage;
+ }
+
+ contentPackage = new ContentPackage(scormPkgDir);
+ Element manifestNode = manifestXml.getRootElement();
+
+ // #1: 确定本次解析的名称空间
+ rootQNameGenerator = parseNamespace(manifestNode);
+
+ Element orgs=manifestNode.element("organizations");
+// System.out.println(rootQNameGenerator.toString());
+// System.out.println(rootQNameGenerator.imscp("organizations"));
+ //System.out.println(orgs.asXML());
+
+// if (rootQNameGenerator == null) {
+// log.error("inner error: parse namespace failed");
+// return contentPackage;
+// }
+
+ // #2: manifest
+ parseManifest(manifestNode);
+
+ // #3: manifest.metadata
+ //parseManifestMetadata(manifestNode.element(rootQNameGenerator.imscp("metadata")));
+ parseManifestMetadata(manifestNode.element("metadata"));
+
+ // #4: manifest.organizations
+ //parseOrganizations(manifestNode.element(rootQNameGenerator.imscp("organizations")));
+ parseOrganizations(manifestNode.element("organizations"));
+
+ // #5: manifest.resources
+ //parseResources(manifestNode.element(rootQNameGenerator.imscp("resources")));
+ parseResources(manifestNode.element("resources"));
+
+ // #6: manifest.sequencingCollection
+ //parseSequencingCollection(manifestNode.element(rootQNameGenerator.imsss("sequencingCollection")));
+ parseSequencingCollection(manifestNode.element("sequencingCollection"));
+
+
+ // #7: generate dependency
+ generateDependency();
+
+ return contentPackage;
+ }
+
+ private void generateDependency() {
+ for (Organization organization : contentPackage.getManifest().getOrganizations().getOrganizationList()) {
+ for (Item item : organization.getItemList()) {
+ item.setParentOrganization(organization);
+ generateDependency(item);
+ }
+ }
+ }
+
+ private void generateDependency(Item item) {
+ for (Item childItem : item.getItemList()) {
+ childItem.setParentItem(item);
+ generateDependency(childItem);
+ }
+ }
+
+ private QNameGenerator parseNamespace(Element node) {
+ if (node == null) {
+ return null;
+ }
+ Map namespaceMap = new HashMap<>();
+ node.content().forEach(obj -> {
+ if (obj instanceof Namespace) {
+ Namespace namespace = (Namespace) obj;
+ namespaceMap.put(namespace.getURI(), namespace);
+ //System.out.println("namespace="+namespace.asXML());
+ }
+ });
+ return new QNameGenerator(namespaceMap);
+ }
+
+ private void parseManifest(Element manifestNode) {
+ Manifest manifest = new Manifest();
+ Namespace pns = manifestNode.getNamespace();
+ String id = manifestNode.attributeValue(rootQNameGenerator.imscp("identifier", pns));
+ manifest.setIdentifier(id == null ? null : new ID(id));
+ manifest.setVersion(manifestNode.attributeValue(
+ rootQNameGenerator.imscp("version", pns)));
+ String base = manifestNode.attributeValue(rootQNameGenerator.xml("base", pns));
+ manifest.setXmlBase(base == null ? null : new AnyURI(base));
+ contentPackage.setManifest(manifest);
+ }
+
+ private void parseManifestMetadata(Element manifestMetadataNode) {
+ if (manifestMetadataNode != null) {
+ ManifestMetadata manifestMetadata = new ManifestMetadata();
+ manifestMetadata.setSchema(manifestMetadataNode.elementTextTrim(rootQNameGenerator.imscp("schema")));
+ manifestMetadata.setSchemaVersion(manifestMetadataNode.elementText(rootQNameGenerator.imscp("schemaversion")));
+ manifestMetadata.setMetadata(parseMetadata(manifestMetadataNode));
+ contentPackage.getManifest().setMetadata(manifestMetadata);
+ }
+ }
+
+ private void parseOrganizations(Element organizationsNode) {
+ if (organizationsNode != null) {
+ Organizations organizations = new Organizations();
+ String doi = organizationsNode.attributeValue( rootQNameGenerator.imscp("default", organizationsNode.getNamespace()));
+
+// List list=(List)organizationsNode.elements(rootQNameGenerator.imscp("organization"));
+// for(Element ele : list) {
+// System.out.println(ele.asXML());
+// System.out.println("title="+ele.elementText("title"));
+// parseOrganization(organizations, ele);
+// }
+//
+
+ organizations.setDefaultOrganizationID(doi == null ? null : new IDRef(doi));
+ organizationsNode.elements(rootQNameGenerator.imscp("organization")).forEach(element ->
+ parseOrganization(organizations, (Element)element)
+ );
+ contentPackage.getManifest().setOrganizations(organizations);
+ }
+ }
+
+ private void parseSequencingCollection(Element sequencingCollectionNode) {
+ if (sequencingCollectionNode != null) {
+ SequencingCollection sequencingCollection = new SequencingCollection();
+ for (Object element : sequencingCollectionNode.elements("sequencing")) {
+ Sequencing sequencing = parseSequencing((Element) element);
+ if (sequencing != null) {
+ sequencingCollection.getSequencingList().add(sequencing);
+ }
+ }
+ contentPackage.getManifest().setSequencingCollection(sequencingCollection);
+ }
+ }
+
+ private void parseOrganization(Organizations organizations, Element organizationNode) {
+ if (organizationNode != null) {
+ Organization organization = new Organization();
+ Namespace pns = organizationNode.getNamespace();
+ String id = organizationNode.attributeValue(rootQNameGenerator.imscp("identifier", pns));
+ organization.setIdentifier(id == null ? null : new ID(id));
+ String structure = organizationNode.attributeValue(rootQNameGenerator.imscp("structure", pns));
+ if (structure != null) {
+ organization.setStructure(structure);
+ }
+ Boolean ogts = parseBoolean(organizationNode.attributeValue(
+ rootQNameGenerator.adlseq("objectivesGlobalToSystem", pns)));
+ if (ogts != null) {
+ organization.setObjectivesGlobalToSystem(ogts);
+ }
+ Boolean sdgts = parseBoolean(organizationNode.attributeValue(
+ rootQNameGenerator.adlcp("sharedDataGlobalToSystem", pns)));
+ if (sdgts != null) {
+ organization.setSharedDataGlobalToSystem(sdgts);
+ }
+ //String title=organizationNode.elementTextTrim(rootQNameGenerator.imscp("title"));
+ String title=organizationNode.elementTextTrim("title");
+ organization.setTitle(title);
+ organizationNode.elements(rootQNameGenerator.imscp("item"))
+ .forEach(element -> parseItem(organization, (Element) element));
+ parseCompletionThreshold(organization, organizationNode.element(
+ rootQNameGenerator.adlcp("completionThreshold")));
+ organization.setMetadata(parseMetadata(organizationNode.element(rootQNameGenerator.imscp("metadata"))));
+ organization.setSequencing(parseSequencing(organizationNode.element(
+ rootQNameGenerator.imsss("sequencing"))));
+ organizations.getOrganizationList().add(organization);
+ }
+ }
+
+ private void parseItem(Organization organization, Element itemNode) {
+ if (itemNode != null) {
+ Item item = parseItem(itemNode);
+ if (item != null) {
+ organization.getItemList().add(item);
+ }
+ }
+ }
+
+ private void parseItem(Item item, Element itemNode) {
+ if (itemNode != null) {
+ Item childItem = parseItem(itemNode);
+ if (childItem != null) {
+ item.getItemList().add(childItem);
+ }
+ }
+ }
+
+ private Item parseItem(Element itemNode) {
+ if (itemNode != null) {
+ Item item = new Item();
+ Namespace pns = itemNode.getNamespace();
+ String id = itemNode.attributeValue(rootQNameGenerator.imscp("identifier", pns));
+ item.setIdentifier(id == null ? null : new ID(id));
+ item.setIdentifierref(itemNode.attributeValue(rootQNameGenerator.imscp("identifierref", pns)));
+ Boolean isvisible = parseBoolean(itemNode.attributeValue(rootQNameGenerator.imscp("isvisible", pns)));
+ if (isvisible != null) {
+ item.setIsvisible(isvisible);
+ }
+ item.setParameters(itemNode.attributeValue(rootQNameGenerator.imscp("parameters", pns)));
+ item.setTitle(itemNode.elementTextTrim(rootQNameGenerator.imscp("title")));
+ itemNode.elements(rootQNameGenerator.imscp("item"))
+ .forEach(element -> parseItem(item, (Element) element));
+ item.setTimeLimitAction(itemNode.elementTextTrim(rootQNameGenerator.adlcp("timeLimitAction")));
+ item.setDataFromLMS(itemNode.elementTextTrim(rootQNameGenerator.adlcp("dataFromLMS")));
+ parseCompletionThreshold(item, itemNode.element(rootQNameGenerator.adlcp("completionThreshold")));
+ parseData(item, itemNode.element(rootQNameGenerator.adlcp("data")));
+ item.setMetadata(parseMetadata(itemNode.element(rootQNameGenerator.imscp("metadata"))));
+ item.setSequencing(parseSequencing(itemNode.element(rootQNameGenerator.imsss("sequencing"))));
+ parsePresentation(item, itemNode.element(rootQNameGenerator.adlnav("presentation")));
+ return item;
+ }
+ return null;
+ }
+
+ private void parsePresentation(Item item, Element presentationNode) {
+ if (presentationNode != null) {
+ Presentation presentation = new Presentation();
+ parseNavigationInterface(presentation, presentationNode.element(
+ rootQNameGenerator.adlnav("navigationInterface")));
+ item.setPresentation(presentation);
+ }
+ }
+
+ private void parseNavigationInterface(Presentation presentation, Element navigationInterfaceNode) {
+ if (navigationInterfaceNode != null) {
+ NavigationInterface navigationInterface = new NavigationInterface();
+ for (Object element : navigationInterfaceNode.elements(rootQNameGenerator.adlnav("hideLMSUI"))) {
+ parseHideLMSUI(navigationInterface, (Element) element);
+ }
+ presentation.setNavigationInterface(navigationInterface);
+ }
+ }
+
+ private void parseHideLMSUI(NavigationInterface navigationInterface, Element hideLMSUINode) {
+ if (hideLMSUINode != null) {
+ HideLMSUI hideLMSUI = new HideLMSUI();
+ hideLMSUI.setValue(hideLMSUINode.getTextTrim());
+ navigationInterface.getHideLMSUIList().add(hideLMSUI);
+ }
+ }
+
+ private void parseCompletionThreshold(Organization organization, Element completionThresholdNode) {
+ if (completionThresholdNode != null) {
+ organization.setCompletionThreshold(parseCompletionThreshold(completionThresholdNode));
+ }
+ }
+
+ private void parseCompletionThreshold(Item item, Element completionThresholdNode) {
+ if (completionThresholdNode != null) {
+ item.setCompletionThreshold(parseCompletionThreshold(completionThresholdNode));
+ }
+ }
+
+ private CompletionThreshold parseCompletionThreshold(Element completionThresholdNode) {
+ if (completionThresholdNode != null) {
+ CompletionThreshold completionThreshold = new CompletionThreshold();
+ Namespace pns = completionThresholdNode.getNamespace();
+ Boolean cbm = parseBoolean(completionThresholdNode.attributeValue(
+ rootQNameGenerator.adlcp("completedByMeasure", pns)));
+ if (cbm != null) {
+ completionThreshold.setCompletedByMeasure(cbm);
+ }
+ Decimal mpm = parseDecimal(completionThresholdNode.attributeValue(
+ rootQNameGenerator.adlcp("minProgressMeasure", pns)), 4);
+ if (mpm != null) {
+ completionThreshold.setMinProgressMeasure(mpm);
+ }
+ Decimal pw = parseDecimal(completionThresholdNode.attributeValue(
+ rootQNameGenerator.adlcp("progressWeight", pns)), 4);
+ if (pw != null) {
+ completionThreshold.setProgressWeight(pw);
+ }
+ return completionThreshold;
+ }
+ return null;
+ }
+
+ private void parseData(Item item, Element dataNode) {
+ if (dataNode != null) {
+ Data data = new Data();
+ dataNode.elements(rootQNameGenerator.adlcp("map"))
+ .forEach(element -> parseMap(data, (Element) element));
+ item.setData(data);
+ }
+ }
+
+ private void parseMap(Data data, Element mapNode) {
+ if (mapNode != null) {
+ com.xboe.module.scorm.cam.model.Map map = new com.xboe.module.scorm.cam.model.Map();
+ Namespace pns = mapNode.getNamespace();
+ String tid = mapNode.attributeValue(rootQNameGenerator.adlcp("targetID", pns));
+ map.setTargetID(tid == null ? null : new AnyURI(tid));
+ Boolean rsd = parseBoolean(mapNode.attributeValue(rootQNameGenerator.adlcp("readSharedData", pns)));
+ if (rsd != null) {
+ map.setReadSharedData(rsd);
+ }
+ Boolean wsd = parseBoolean(mapNode.attributeValue(rootQNameGenerator.adlcp("writeSharedData", pns)));
+ if (wsd != null) {
+ map.setWriteSharedData(wsd);
+ }
+ data.getMapList().add(map);
+ }
+ }
+
+ private void parseResources(Element resourcesNode) {
+ if (resourcesNode != null) {
+ Resources resources = new Resources();
+ String base = resourcesNode.attributeValue(
+ rootQNameGenerator.xml("base", resourcesNode.getNamespace()));
+ resources.setXmlBase(base == null ? null : new AnyURI(base));
+ resourcesNode.elements(rootQNameGenerator.imscp("resource"))
+ .forEach(element -> parseResource(resources, (Element) element));
+ contentPackage.getManifest().setResources(resources);
+ }
+ }
+
+ private void parseResource(Resources resources, Element resourceNode) {
+ if (resourceNode != null) {
+ Resource resource = new Resource();
+ Namespace pns = resourceNode.getNamespace();
+ String id = resourceNode.attributeValue(rootQNameGenerator.imscp("identifier", pns));
+ resource.setIdentifier(id == null ? null : new ID(id));
+ resource.setType(resourceNode.attributeValue(rootQNameGenerator.imscp("type", pns)));
+ resource.setHref(resourceNode.attributeValue(rootQNameGenerator.imscp("href", pns)));
+ String base = resourceNode.attributeValue(rootQNameGenerator.xml("base", pns));
+ resource.setXmlBase(base == null ? null : new AnyURI(base));
+ resource.setScormType(resourceNode.attributeValue(rootQNameGenerator.adlcp("scormType", pns)));
+ String qNameForFile = rootQNameGenerator.imscp("file");
+ String qNameForDependency = rootQNameGenerator.imscp("dependency");
+ for (Object obj : resourceNode.elements()) {
+ Element element = (Element) obj;
+ if (element.getQName().equals(qNameForFile)) {
+ parseFile(resource, element);
+ } else if (element.getQName().equals(qNameForDependency)) {
+ parseDependency(resource, element);
+ }
+ }
+ resource.setMetadata(parseMetadata(resourceNode.element(rootQNameGenerator.imscp("metadata"))));
+ resources.getResourceList().add(resource);
+ }
+ }
+
+ private void parseFile(Resource resource, Element fileNode) {
+ if (fileNode != null) {
+ com.xboe.module.scorm.cam.model.File file = new com.xboe.module.scorm.cam.model.File();
+ file.setHref(fileNode.attributeValue(rootQNameGenerator.imscp("href", fileNode.getNamespace())));
+ file.setMetadata(parseMetadata(fileNode.element(rootQNameGenerator.imscp("metadata"))));
+ resource.getFileList().add(file);
+ }
+ }
+
+ private void parseDependency(Resource resource, Element dependencyNode) {
+ if (dependencyNode != null) {
+ Dependency dependency = new Dependency();
+ dependency.setIdentifierref(dependencyNode.attributeValue(
+ rootQNameGenerator.imscp("identifierref", dependencyNode.getNamespace())));
+ resource.getDependencyList().add(dependency);
+ }
+ }
+
+ private Metadata parseMetadata(Element metadataNode) {
+ if (metadataNode != null) {
+ Metadata metadata = new Metadata();
+ // inner lom
+ for (Object element : metadataNode.elements(rootQNameGenerator.lom("lom"))) {
+ Element lomNode = (Element) element;
+ Map namespaceMap = new HashMap<>();
+ for (Object obj : lomNode.content()) {
+ if (obj instanceof Namespace) {
+ namespaceMap.put(((Namespace) obj).getURI(), (Namespace) obj);
+ }
+ }
+ // : map.size == 0
+ // : map.size > 0
+ QNameGenerator qNameGenerator = new QNameGenerator(rootQNameGenerator, namespaceMap);
+ LOM lom = parseLOM(lomNode, qNameGenerator);
+ metadata.getLomList().add(lom);
+ }
+ // location
+ for (Object obj : metadataNode.elements(rootQNameGenerator.adlcp("location"))) {
+ Element locationNode = (Element) obj;
+ parseLocation(metadata, locationNode);
+ }
+ return metadata;
+ }
+ return null;
+ }
+
+ private void parseLocation(Metadata metadata, Element locationNode) {
+ if (locationNode != null) {
+ String location = locationNode.getTextTrim();
+ if ("".equals(location)) {
+ return;
+ }
+ metadata.getLocationLomMap().put(location, null);
+ File lomXmlFile = new File(this.scormPkgDir + location);
+ if (!lomXmlFile.exists()) {
+ log.error("Not found {} in {}", location, this.scormPkgDir);
+ return;
+ }
+ Document lomXml;
+ try {
+ lomXml = new SAXReader().read(lomXmlFile);
+ } catch (DocumentException e) {
+ log.error("read {} exception: {}", location, CommonUtils.stringifyError(e));
+ return;
+ }
+ if (lomXml == null) {
+ return;
+ }
+ Element lomNode = lomXml.getRootElement();
+ QNameGenerator qNameGenerator = parseNamespace(lomNode);
+ LOM lom = parseLOM(lomNode, qNameGenerator);
+ if (lom != null) {
+ metadata.getLocationLomMap().put(location, lom);
+ }
+ }
+ }
+
+ private LOM parseLOM(Element lomNode, QNameGenerator qNameGenerator) {
+ if (lomNode != null && qNameGenerator != null) {
+ LOM lom = new LOM();
+ parseGeneral(lom, lomNode.element(qNameGenerator.lom("general")), qNameGenerator);
+ parseLifeCycle(lom, lomNode.element(qNameGenerator.lom("lifeCycle")), qNameGenerator);
+ parseMetaMetadata(lom, lomNode.element(qNameGenerator.lom("metaMetadata")), qNameGenerator);
+ parseTechnical(lom, lomNode.element(qNameGenerator.lom("technical")), qNameGenerator);
+ for (Object element : lomNode.elements(qNameGenerator.lom("educational"))) {
+ parseEducational(lom, (Element) element, qNameGenerator);
+ }
+ parseRights(lom, lomNode.element(qNameGenerator.lom("rights")), qNameGenerator);
+ for (Object element : lomNode.elements(qNameGenerator.lom("relation"))) {
+ parseRelation(lom, (Element) element, qNameGenerator);
+ }
+ for (Object element : lomNode.elements(qNameGenerator.lom("annotation"))) {
+ parseAnnotation(lom, (Element) element, qNameGenerator);
+ }
+ for (Object element : lomNode.elements(qNameGenerator.lom("classification"))) {
+ parseClassification(lom, (Element) element, qNameGenerator);
+ }
+ }
+ return null;
+ }
+
+ private void parseGeneral(LOM lom, Element generalNode, QNameGenerator qNameGenerator) {
+ if (generalNode != null) {
+ General general = new General();
+ for (Object element : generalNode.elements(qNameGenerator.lom("identifier"))) {
+ Identifier identifier = parseIdentifier((Element) element, qNameGenerator);
+ if (identifier != null) {
+ general.getIdentifierList().add(identifier);
+ }
+ }
+ general.setTitle(parseLanguageStrings(generalNode.element(
+ qNameGenerator.lom("title")), qNameGenerator));
+ for (Object element : generalNode.elements(qNameGenerator.lom("language"))) {
+ String language = ((Element) element).getTextTrim();
+ if (language != null) {
+ general.getLanguageList().add(language);
+ }
+ }
+ for (Object element : generalNode.elements(qNameGenerator.lom("description"))) {
+ LanguageStrings description = parseLanguageStrings((Element) element, qNameGenerator);
+ if (description != null) {
+ general.getDescriptionList().add(description);
+ }
+ }
+ for (Object element : generalNode.elements(qNameGenerator.lom("keyword"))) {
+ LanguageStrings keyword = parseLanguageStrings((Element) element, qNameGenerator);
+ if (keyword != null) {
+ general.getKeywordList().add(keyword);
+ }
+ }
+ for (Object element : generalNode.elements(qNameGenerator.lom("coverage"))) {
+ LanguageStrings coverage = parseLanguageStrings((Element) element, qNameGenerator);
+ if (coverage != null) {
+ general.getCoverageList().add(coverage);
+ }
+ }
+ general.setStructure(parseVocabulary(generalNode.element(
+ qNameGenerator.lom("structure")), qNameGenerator));
+ general.setAggregationLevel(parseVocabulary(generalNode.element(
+ qNameGenerator.lom("structure")), qNameGenerator));
+ lom.setGeneral(general);
+ }
+ }
+
+ private void parseLifeCycle(LOM lom, Element lifeCycleNode, QNameGenerator qNameGenerator) {
+ if (lifeCycleNode != null) {
+ LifeCycle lifeCycle = new LifeCycle();
+ lifeCycle.setVersion(parseLanguageStrings(lifeCycleNode.element(
+ qNameGenerator.lom("version")), qNameGenerator));
+ lifeCycle.setStatus(parseVocabulary(lifeCycleNode.element(
+ qNameGenerator.lom("status")), qNameGenerator));
+ for (Object element : lifeCycleNode.elements(qNameGenerator.lom("contribute"))) {
+ Contribute contribute = parseContribute((Element) element, qNameGenerator);
+ if (contribute != null) {
+ lifeCycle.getContributeList().add(contribute);
+ }
+ }
+ lom.setLifeCycle(lifeCycle);
+ }
+ }
+
+ private void parseMetaMetadata(LOM lom, Element metaMetadataNode, QNameGenerator qNameGenerator) {
+ if (metaMetadataNode != null) {
+ MetaMetadata metaMetadata = new MetaMetadata();
+ for (Object element : metaMetadataNode.elements(qNameGenerator.lom("identifier"))) {
+ Identifier identifier = parseIdentifier((Element) element, qNameGenerator);
+ if (identifier != null) {
+ metaMetadata.getIdentifierList().add(identifier);
+ }
+ }
+ for (Object element : metaMetadataNode.elements(qNameGenerator.lom("contribute"))) {
+ Contribute contribute = parseContribute((Element) element, qNameGenerator);
+ if (contribute != null) {
+ metaMetadata.getContributeList().add(contribute);
+ }
+ }
+ for (Object element : metaMetadataNode.elements(qNameGenerator.lom("metadataSchema"))) {
+ String schema = ((Element) element).getTextTrim();
+ if (schema != null) {
+ metaMetadata.getMetadataSchema().add(schema);
+ }
+ }
+ metaMetadata.setLanguage(metaMetadataNode.elementTextTrim(qNameGenerator.lom("language")));
+ lom.setMetaMetadata(metaMetadata);
+ }
+ }
+
+ private void parseTechnical(LOM lom, Element technicalNode, QNameGenerator qNameGenerator) {
+ if (technicalNode != null) {
+ Technical technical = new Technical();
+ for (Object element : technicalNode.elements(qNameGenerator.lom("format"))) {
+ String format = ((Element) element).getTextTrim();
+ if (format != null) {
+ technical.getFormatList().add(format);
+ }
+ }
+ technical.setSize(technicalNode.elementTextTrim(qNameGenerator.lom("size")));
+ for (Object element : technicalNode.elements(qNameGenerator.lom("location"))) {
+ String location = ((Element) element).getTextTrim();
+ if (location != null) {
+ technical.getLocationList().add(location);
+ }
+ }
+ for (Object element : technicalNode.elements(qNameGenerator.lom("requirement"))) {
+ Requirement requirement = parseRequirement((Element) element, qNameGenerator);
+ if (requirement != null) {
+ technical.getRequirementList().add(requirement);
+ }
+ }
+ technical.setInstallationRemarks(parseLanguageStrings(technicalNode.element(
+ qNameGenerator.lom("installationRemarks")), qNameGenerator));
+ technical.setOtherPlatformRequirements(parseLanguageStrings(technicalNode.element(
+ qNameGenerator.lom("otherPlatformRequirements")), qNameGenerator));
+ technical.setDuration(parseDuration(technicalNode.element(
+ qNameGenerator.lom("duration")), qNameGenerator));
+ lom.setTechnical(technical);
+ }
+ }
+
+ private void parseEducational(LOM lom, Element educationalNode, QNameGenerator qNameGenerator) {
+ if (educationalNode != null) {
+ Educational educational = new Educational();
+ educational.setInteractivityType(parseVocabulary(educationalNode.element(
+ qNameGenerator.lom("interactivityType")), qNameGenerator));
+ for (Object element : educationalNode.elements(qNameGenerator.lom("learningResourceType"))) {
+ Vocabulary lrt = parseVocabulary((Element) element, qNameGenerator);
+ if (lrt != null) {
+ educational.getLearningResourceTypeList().add(lrt);
+ }
+ }
+ educational.setInteractivityLevel(parseVocabulary(educationalNode.element(
+ qNameGenerator.lom("interactivityLevel")), qNameGenerator));
+ educational.setSemanticDensity(parseVocabulary(educationalNode.element(
+ qNameGenerator.lom("semanticDensity")), qNameGenerator));
+ for (Object element : educationalNode.elements(qNameGenerator.lom("intendedEndUserRole"))) {
+ Vocabulary ieur = parseVocabulary((Element) element, qNameGenerator);
+ if (ieur != null) {
+ educational.getIntendedEndUserRoleList().add(ieur);
+ }
+ }
+ for (Object element : educationalNode.elements(qNameGenerator.lom("context"))) {
+ Vocabulary context = parseVocabulary((Element) element, qNameGenerator);
+ if (context != null) {
+ educational.getContextList().add(context);
+ }
+ }
+ for (Object element : educationalNode.elements(qNameGenerator.lom("typicalAgeRange"))) {
+ LanguageStrings tar = parseLanguageStrings((Element) element, qNameGenerator);
+ if (tar != null) {
+ educational.getTypicalAgeRangeList().add(tar);
+ }
+ }
+ educational.setDifficulty(parseVocabulary(educationalNode.element(
+ qNameGenerator.lom("difficulty")), qNameGenerator));
+ educational.setTypicalLearningTime(parseDuration(educationalNode.element(
+ qNameGenerator.lom("typicalLearningTime")), qNameGenerator));
+ for (Object element : educationalNode.elements(qNameGenerator.lom("description"))) {
+ LanguageStrings description = parseLanguageStrings((Element) element, qNameGenerator);
+ if (description != null) {
+ educational.getDescriptionList().add(description);
+ }
+ }
+ for (Object element : educationalNode.elements(qNameGenerator.lom("language"))) {
+ String language = ((Element) element).getTextTrim();
+ if (language != null) {
+ educational.getLanguageList().add(language);
+ }
+ }
+ lom.getEducationalList().add(educational);
+ }
+ }
+
+ private void parseRights(LOM lom, Element rightsNode, QNameGenerator qNameGenerator) {
+ if (rightsNode != null) {
+ Rights rights = new Rights();
+ rights.setCost(parseVocabulary(rightsNode.element(qNameGenerator.lom("cost")), qNameGenerator));
+ rights.setCopyrightAndOtherRestrictions(parseVocabulary(rightsNode.element(
+ qNameGenerator.lom("copyrightAndOtherRestrictions")), qNameGenerator));
+ rights.setDescription(parseLanguageStrings(rightsNode.element(
+ qNameGenerator.lom("description")), qNameGenerator));
+ lom.setRights(rights);
+ }
+ }
+
+ private void parseRelation(LOM lom, Element relationNode, QNameGenerator qNameGenerator) {
+ if (relationNode != null) {
+ Relation relation = new Relation();
+ relation.setKind(parseVocabulary(relationNode.element(
+ qNameGenerator.lom("kind")), qNameGenerator));
+ relation.setResource(parseRelationResource(relationNode.element(
+ qNameGenerator.lom("resource")), qNameGenerator));
+ lom.getRelationList().add(relation);
+ }
+ }
+
+ private void parseAnnotation(LOM lom, Element annotationNode, QNameGenerator qNameGenerator) {
+ if (annotationNode != null) {
+ Annotation annotation = new Annotation();
+ String entity = annotationNode.elementTextTrim(qNameGenerator.lom("entity"));
+ annotation.setEntity(entity == null ? null : new VCard(entity));
+ annotation.setDate(parseDateTime(annotationNode.element(qNameGenerator.lom("date")), qNameGenerator));
+ annotation.setDescription(parseLanguageStrings(annotationNode.element(
+ qNameGenerator.lom("description")), qNameGenerator));
+ lom.getAnnotationList().add(annotation);
+ }
+ }
+
+ private void parseClassification(LOM lom, Element classificationNode, QNameGenerator qNameGenerator) {
+ if (classificationNode != null) {
+ Classification classification = new Classification();
+ classification.setPurpose(parseVocabulary(classificationNode.element(
+ qNameGenerator.lom("purpose")), qNameGenerator));
+ for (Object element : classificationNode.elements(qNameGenerator.lom("taxonPath"))) {
+ TaxonPath taxonPath = parseTaxonPath((Element) element, qNameGenerator);
+ if (taxonPath != null) {
+ classification.getTaxonPathList().add(taxonPath);
+ }
+ }
+ classification.setDescription(parseLanguageStrings(classificationNode.element(
+ qNameGenerator.lom("description")), qNameGenerator));
+ for (Object element : classificationNode.elements(qNameGenerator.lom("keyword"))) {
+ LanguageStrings keyword = parseLanguageStrings((Element) element, qNameGenerator);
+ if (keyword != null) {
+ classification.getKeywordList().add(keyword);
+ }
+ }
+ lom.getClassificationList().add(classification);
+ }
+ }
+
+ private Identifier parseIdentifier(Element identifierNode, QNameGenerator qNameGenerator) {
+ if (identifierNode != null) {
+ Identifier identifier = new Identifier();
+ identifier.setCatalog(identifierNode.elementTextTrim(qNameGenerator.lom("catalog")));
+ identifier.setEntry(identifierNode.elementTextTrim(qNameGenerator.lom("entry")));
+ return identifier;
+ }
+ return null;
+ }
+
+ private Contribute parseContribute(Element contributeNode, QNameGenerator qNameGenerator) {
+ if (contributeNode != null) {
+ Contribute contribute = new Contribute();
+ contribute.setRole(parseVocabulary(contributeNode.element(
+ qNameGenerator.lom("role")), qNameGenerator));
+ for (Object element : contributeNode.elements(qNameGenerator.lom("entity"))) {
+ String entity = ((Element) element).getTextTrim();
+ if (entity != null) {
+ contribute.getEntityList().add(new VCard(entity));
+ }
+ }
+ contribute.setDate(parseDateTime(contributeNode.element(
+ qNameGenerator.lom("date")), qNameGenerator));
+ return contribute;
+ }
+ return null;
+ }
+
+ private Requirement parseRequirement(Element requirementNode, QNameGenerator qNameGenerator) {
+ if (requirementNode != null) {
+ Requirement requirement = new Requirement();
+ for (Object element : requirementNode.elements(qNameGenerator.lom("orComposite"))) {
+ OrComposite orComposite = parseOrComposite((Element) element, qNameGenerator);
+ if (orComposite != null) {
+ requirement.getOrCompositeList().add(orComposite);
+ }
+ }
+ return requirement;
+ }
+ return null;
+ }
+
+ private OrComposite parseOrComposite(Element orCompositeNode, QNameGenerator qNameGenerator) {
+ if (orCompositeNode != null) {
+ OrComposite orComposite = new OrComposite();
+ orComposite.setType(parseVocabulary(orCompositeNode.element(
+ qNameGenerator.lom("type")), qNameGenerator));
+ orComposite.setName(parseVocabulary(orCompositeNode.element(
+ qNameGenerator.lom("name")), qNameGenerator));
+ orComposite.setMinimumVersion(orCompositeNode.elementTextTrim(
+ qNameGenerator.lom("minimumVersion")));
+ orComposite.setMaximumVersion(orCompositeNode.elementTextTrim(
+ qNameGenerator.lom("maximumVersion")));
+ return orComposite;
+ }
+ return null;
+ }
+
+ private RelationResource parseRelationResource(Element relationResourceNode, QNameGenerator qNameGenerator) {
+ if (relationResourceNode != null) {
+ RelationResource relationResource = new RelationResource();
+ for (Object element : relationResourceNode.elements(qNameGenerator.lom("identifier"))) {
+ Identifier identifier = parseIdentifier((Element) element, qNameGenerator);
+ if (identifier != null) {
+ relationResource.getIdentifierList().add(identifier);
+ }
+ }
+ for (Object element : relationResourceNode.elements(qNameGenerator.lom("description"))) {
+ LanguageStrings description = parseLanguageStrings((Element) element, qNameGenerator);
+ if (description != null) {
+ relationResource.getDescriptionList().add(description);
+ }
+ }
+ return relationResource;
+ }
+ return null;
+ }
+
+ private TaxonPath parseTaxonPath(Element taxonPathNode, QNameGenerator qNameGenerator) {
+ if (taxonPathNode != null) {
+ TaxonPath taxonPath = new TaxonPath();
+ taxonPath.setSource(parseLanguageStrings(taxonPathNode.element(
+ qNameGenerator.lom("taxonPath")), qNameGenerator));
+ for (Object element : taxonPathNode.elements(qNameGenerator.lom("taxon"))) {
+ Taxon taxon = parseTaxon((Element) element, qNameGenerator);
+ if (taxon != null) {
+ taxonPath.getTaxonList().add(taxon);
+ }
+ }
+ return taxonPath;
+ }
+ return null;
+ }
+
+ private Taxon parseTaxon(Element taxonNode, QNameGenerator qNameGenerator) {
+ if (taxonNode != null) {
+ Taxon taxon = new Taxon();
+ taxon.setId(taxonNode.elementTextTrim(qNameGenerator.lom("id")));
+ taxon.setEntry(parseLanguageStrings(taxonNode.element(qNameGenerator.lom("entry")), qNameGenerator));
+ return taxon;
+ }
+ return null;
+ }
+
+ private LanguageStrings parseLanguageStrings(Element pNode, QNameGenerator qNameGenerator) {
+ if (pNode != null) {
+ LanguageStrings languageStrings = new LanguageStrings();
+ for (Object element : pNode.elements("string")) {
+ Element stringNode = (Element) element;
+ String language = stringNode.attributeValue(qNameGenerator.lom("language", stringNode.getNamespace()));
+ String text = stringNode.getTextTrim();
+ languageStrings.getLanguageStringList().add(new LanguageString(language, text));
+ }
+ return languageStrings;
+ }
+ return null;
+ }
+
+ private Vocabulary parseVocabulary(Element vocabularyNode, QNameGenerator qNameGenerator) {
+ if (vocabularyNode != null) {
+ String source = vocabularyNode.elementTextTrim(qNameGenerator.lom("source"));
+ String value = vocabularyNode.elementTextTrim(qNameGenerator.lom("value"));
+ return new Vocabulary(source, value);
+ }
+ return null;
+ }
+
+ private DateTime parseDateTime(Element dateNode, QNameGenerator qNameGenerator) {
+ if (dateNode != null) {
+ DateTime dateTime = new DateTime();
+ dateTime.setDateTime(dateNode.elementTextTrim(qNameGenerator.lom("dateTime")));
+ dateTime.setDescription(parseLanguageStrings(dateNode.element(
+ qNameGenerator.lom("description")), qNameGenerator));
+ return dateTime;
+ }
+ return null;
+ }
+
+ private Duration parseDuration(Element durationNode, QNameGenerator qNameGenerator) {
+ if (durationNode != null) {
+ Duration duration = new Duration();
+ duration.setDuration(durationNode.elementTextTrim(qNameGenerator.lom("duration")));
+ duration.setDescription(parseLanguageStrings(durationNode.element(
+ qNameGenerator.lom("description")), qNameGenerator));
+ return duration;
+ }
+ return null;
+ }
+
+ private Sequencing parseSequencing(Element sequencingNode) {
+ if (sequencingNode != null) {
+ Sequencing sequencing = new Sequencing();
+ Namespace pns = sequencingNode.getNamespace();
+ String id = sequencingNode.attributeValue(rootQNameGenerator.imsss("ID", pns));
+ sequencing.setId(id == null ? null : new ID(id));
+ String idRef = sequencingNode.attributeValue(rootQNameGenerator.imsss("IDRef", pns));
+ sequencing.setIdRef(idRef == null ? null : new IDRef(idRef));
+ parseControlMode(sequencing, sequencingNode.element(rootQNameGenerator.imsss("controlMode")));
+ parseSequencingRules(sequencing, sequencingNode.element(rootQNameGenerator.imsss("sequencingRules")));
+ parseLimitConditions(sequencing, sequencingNode.element(rootQNameGenerator.imsss("limitConditions")));
+ parseAuxiliaryResource(sequencing, sequencingNode.element(rootQNameGenerator.imsss("auxiliaryResources")));
+ parseRollupRules(sequencing, sequencingNode.element(rootQNameGenerator.imsss("rollupRules")));
+ parseObjectives(sequencing, sequencingNode.element(rootQNameGenerator.imsss("objectives")));
+ parseRandomizationControls(sequencing, sequencingNode.element(rootQNameGenerator.imsss("randomizationControls")));
+ parseDeliveryControls(sequencing, sequencingNode.element(rootQNameGenerator.imsss("deliveryControls")));
+ parseConstrainedChoiceConsiderations(sequencing, sequencingNode.element(rootQNameGenerator.adlseq("constrainedChoiceConsiderations")));
+ parseRollupConsiderations(sequencing, sequencingNode.element(rootQNameGenerator.adlseq("rollupConsiderations")));
+ parseAdlseqObjectives(sequencing, sequencingNode.element(rootQNameGenerator.adlseq("objectives")));
+ return sequencing;
+ }
+ return null;
+ }
+
+ private void parseControlMode(Sequencing sequencing, Element controlModeNode) {
+ if (controlModeNode != null) {
+ ControlMode controlMode = new ControlMode();
+ Namespace pns = controlModeNode.getNamespace();
+ Boolean choice = parseBoolean(controlModeNode.attributeValue(
+ rootQNameGenerator.imsss("choice", pns)));
+ if (choice != null) {
+ controlMode.setChoice(choice);
+ }
+ Boolean choiceExit = parseBoolean(controlModeNode.attributeValue(
+ rootQNameGenerator.imsss("choiceExit", pns)));
+ if (choiceExit != null) {
+ controlMode.setChoiceExit(choiceExit);
+ }
+ Boolean flow = parseBoolean(controlModeNode.attributeValue(
+ rootQNameGenerator.imsss("flow", pns)));
+ if (flow != null) {
+ controlMode.setFlow(flow);
+ }
+ Boolean forwardOnly = parseBoolean(controlModeNode.attributeValue(
+ rootQNameGenerator.imsss("forwardOnly", pns)));
+ if (forwardOnly != null) {
+ controlMode.setForwardOnly(forwardOnly);
+ }
+ Boolean useCurrentAttemptObjectiveInfo = parseBoolean(controlModeNode.attributeValue(
+ rootQNameGenerator.imsss("useCurrentAttemptObjectiveInfo", pns)));
+ if (useCurrentAttemptObjectiveInfo != null) {
+ controlMode.setUseCurrentAttemptObjectiveInfo(useCurrentAttemptObjectiveInfo);
+ }
+ Boolean useCurrentAttemptProgressInfo = parseBoolean(controlModeNode.attributeValue(
+ rootQNameGenerator.imsss("useCurrentAttemptProgressInfo", pns)));
+ if (useCurrentAttemptProgressInfo != null) {
+ controlMode.setUseCurrentAttemptProgressInfo(useCurrentAttemptProgressInfo);
+ }
+ sequencing.setControlMode(controlMode);
+ }
+ }
+
+ private void parseSequencingRules(Sequencing sequencing, Element sequencingRulesNode) {
+ if (sequencingRulesNode != null) {
+ SequencingRules sequencingRules = new SequencingRules();
+ String pre = rootQNameGenerator.imsss("preConditionRule");
+ String exit = rootQNameGenerator.imsss("exitConditionRule");
+ String post = rootQNameGenerator.imsss("postConditionRule");
+ for (Object element : sequencingRulesNode.elements()) {
+ Element conditionRuleNode = (Element) element;
+ QName qn = conditionRuleNode.getQName();
+ if (qn.equals(pre)) {
+ ConditionRule conditionRule = parseConditionRule(conditionRuleNode);
+ if (conditionRule != null) {
+ sequencingRules.getPreConditionRuleList().add(conditionRule);
+ }
+ } else if (qn.equals(exit)) {
+ ConditionRule conditionRule = parseConditionRule(conditionRuleNode);
+ if (conditionRule != null) {
+ sequencingRules.getExitConditionRuleList().add(conditionRule);
+ }
+ } else if (qn.equals(post)) {
+ ConditionRule conditionRule = parseConditionRule(conditionRuleNode);
+ if (conditionRule != null) {
+ sequencingRules.getPostConditionRuleList().add(conditionRule);
+ }
+ }
+ }
+ sequencing.setSequencingRules(sequencingRules);
+ }
+ }
+
+ private void parseLimitConditions(Sequencing sequencing, Element limitConditionsNode) {
+ if (limitConditionsNode != null) {
+ LimitConditions limitConditions = new LimitConditions();
+ Namespace pns = limitConditionsNode.getNamespace();
+ limitConditions.setAttemptLimit(parseNonNegativeInteger(limitConditionsNode.attributeValue(
+ rootQNameGenerator.imsss("attemptLimit", pns))));
+ limitConditions.setAttemptAbsoluteDurationLimit(limitConditionsNode.attributeValue(
+ rootQNameGenerator.imsss("attemptAbsoluteDurationLimit", pns)));
+ sequencing.setLimitConditions(limitConditions);
+ }
+ }
+
+ private void parseAuxiliaryResource(Sequencing sequencing, Element auxiliaryResourcesNode) {
+ // don't implementation
+ }
+
+ private void parseRollupRules(Sequencing sequencing, Element rollupRulesNode) {
+ if (rollupRulesNode != null) {
+ RollupRules rollupRules = new RollupRules();
+ Namespace pns = rollupRulesNode.getNamespace();
+ Boolean ros = parseBoolean(rollupRulesNode.attributeValue(
+ rootQNameGenerator.imsss("rollupObjectiveSatisfied", pns)));
+ if (ros != null) {
+ rollupRules.setRollupObjectiveSatisfied(ros);
+ }
+ Boolean rpc = parseBoolean(rollupRulesNode.attributeValue(
+ rootQNameGenerator.imsss("rollupProgressCompletion", pns)));
+ if (rpc != null) {
+ rollupRules.setRollupProgressCompletion(rpc);
+ }
+ Decimal omw = parseDecimal(rollupRulesNode.attributeValue(
+ rootQNameGenerator.imsss("objectiveMeasureWeight", pns)), 4);
+ if (omw != null) {
+ rollupRules.setObjectiveMeasureWeight(omw);
+ }
+ for (Object element : rollupRulesNode.elements(rootQNameGenerator.imsss("rollupRule"))) {
+ parseRollupRule(rollupRules, (Element) element);
+ }
+ sequencing.setRollupRules(rollupRules);
+ }
+ }
+
+ private void parseObjectives(Sequencing sequencing, Element objectivesNode) {
+ if (objectivesNode != null) {
+ Objectives objectives = new Objectives();
+ objectives.setPrimaryObjective(parseObjective(objectivesNode.element(
+ rootQNameGenerator.imsss("primaryObjective"))));
+ for (Object element : objectivesNode.elements(rootQNameGenerator.imsss("objective"))) {
+ Objective objective = parseObjective((Element) element);
+ if (objective != null) {
+ objectives.getObjectiveList().add(objective);
+ }
+ }
+ sequencing.setObjectives(objectives);
+ }
+ }
+
+ private void parseRandomizationControls(Sequencing sequencing, Element randomizationControlsNode) {
+ if (randomizationControlsNode != null) {
+ RandomizationControls randomizationControls = new RandomizationControls();
+ Namespace pns = randomizationControlsNode.getNamespace();
+ String rt = randomizationControlsNode.attributeValue(
+ rootQNameGenerator.imsss("randomizationTiming", pns));
+ if (rt != null) {
+ randomizationControls.setRandomizationTiming(new Token(rt));
+ }
+ randomizationControls.setSelectCount(parseNonNegativeInteger(randomizationControlsNode.attributeValue(
+ rootQNameGenerator.imsss("selectCount", pns))));
+ Boolean rc = parseBoolean(randomizationControlsNode.attributeValue(
+ rootQNameGenerator.imsss("reorderChildren", pns)));
+ if (rc != null) {
+ randomizationControls.setReorderChildren(rc);
+ }
+ String st = randomizationControlsNode.attributeValue(
+ rootQNameGenerator.imsss("selectionTiming", pns));
+ if (st != null) {
+ randomizationControls.setSelectionTiming(new Token(st));
+ }
+ sequencing.setRandomizationControls(randomizationControls);
+ }
+ }
+
+ private void parseDeliveryControls(Sequencing sequencing, Element deliveryControlsNode) {
+ if (deliveryControlsNode != null) {
+ DeliveryControls deliveryControls = new DeliveryControls();
+ Namespace pns = deliveryControlsNode.getNamespace();
+ Boolean tracked = parseBoolean(deliveryControlsNode.attributeValue(
+ rootQNameGenerator.imsss("tracked", pns)));
+ if (tracked != null) {
+ deliveryControls.setTracked(tracked);
+ }
+ Boolean csbc = parseBoolean(deliveryControlsNode.attributeValue(
+ rootQNameGenerator.imsss("completionSetByContent", pns)));
+ if (csbc != null) {
+ deliveryControls.setCompletionSetByContent(csbc);
+ }
+ Boolean osbc = parseBoolean(deliveryControlsNode.attributeValue(
+ rootQNameGenerator.imsss("objectiveSetByContent", pns)));
+ if (osbc != null) {
+ deliveryControls.setObjectiveSetByContent(osbc);
+ }
+ sequencing.setDeliveryControls(deliveryControls);
+ }
+ }
+
+ private void parseConstrainedChoiceConsiderations(Sequencing sequencing, Element constrainedChoiceConsiderationsNode) {
+ if (constrainedChoiceConsiderationsNode != null) {
+ ConstrainedChoiceConsiderations constrainedChoiceConsiderations = new ConstrainedChoiceConsiderations();
+ Namespace pns = constrainedChoiceConsiderationsNode.getNamespace();
+ Boolean pa = parseBoolean(constrainedChoiceConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("preventActivation", pns)));
+ if (pa != null) {
+ constrainedChoiceConsiderations.setPreventActivation(pa);
+ }
+ Boolean cc = parseBoolean(constrainedChoiceConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("constrainChoice", pns)));
+ if (cc != null) {
+ constrainedChoiceConsiderations.setConstrainChoice(cc);
+ }
+ sequencing.setConstrainedChoiceConsiderations(constrainedChoiceConsiderations);
+ }
+ }
+
+ private void parseRollupConsiderations(Sequencing sequencing, Element rollupConsiderationsNode) {
+ if (rollupConsiderationsNode != null) {
+ RollupConsiderations rollupConsiderations = new RollupConsiderations();
+ Namespace pns = rollupConsiderationsNode.getNamespace();
+ String rfs = rollupConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("requiredForSatisfied", pns));
+ if (rfs != null) {
+ rollupConsiderations.setRequiredForSatisfied(new Token(rfs));
+ }
+ String rfns = rollupConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("requiredForNotSatisfied", pns));
+ if (rfns != null) {
+ rollupConsiderations.setRequiredForNotSatisfied(new Token(rfns));
+ }
+ String rfc = rollupConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("requiredForCompleted", pns));
+ if (rfc != null) {
+ rollupConsiderations.setRequiredForCompleted(new Token(rfc));
+ }
+ String rfi = rollupConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("requiredForIncomplete", pns));
+ if (rfi != null) {
+ rollupConsiderations.setRequiredForIncomplete(new Token(rfi));
+ }
+ Boolean msia = parseBoolean(rollupConsiderationsNode.attributeValue(
+ rootQNameGenerator.adlseq("measureSatisfactionIfActive", pns)));
+ if (msia != null) {
+ rollupConsiderations.setMeasureSatisfactionIfActive(msia);
+ }
+ sequencing.setRollupConsiderations(rollupConsiderations);
+ }
+ }
+
+ private void parseAdlseqObjectives(Sequencing sequencing, Element adlseqObjectivesNode) {
+ if (adlseqObjectivesNode != null) {
+ AdlseqObjectives adlseqObjectives = new AdlseqObjectives();
+ for (Object element : adlseqObjectivesNode.elements(rootQNameGenerator.adlseq("objective"))) {
+ parseAdlseqObjective(adlseqObjectives, (Element) element);
+ }
+ sequencing.setAdlseqObjectives(adlseqObjectives);
+ }
+ }
+
+ private ConditionRule parseConditionRule(Element conditionRuleNode) {
+ if (conditionRuleNode != null) {
+ ConditionRule conditionRule = new ConditionRule();
+ parseRuleConditions(conditionRule, conditionRuleNode.element(
+ rootQNameGenerator.imsss("ruleConditions")));
+ Element actionNode = conditionRuleNode.element(rootQNameGenerator.imsss("ruleAction"));
+ String action = actionNode == null ? null : actionNode.attributeValue(
+ rootQNameGenerator.imsss("action", conditionRuleNode.getNamespace()));
+ RuleAction ruleAction = action == null ? null : new RuleAction();
+ if (ruleAction != null) {
+ ruleAction.setAction(new Token(action));
+ }
+ conditionRule.setRuleAction(ruleAction);
+ return conditionRule;
+ }
+ return null;
+ }
+
+ private void parseRuleConditions(ConditionRule conditionRule, Element ruleConditionsNode) {
+ if (ruleConditionsNode != null) {
+ RuleConditions ruleConditions = new RuleConditions();
+ String cc = ruleConditionsNode.attributeValue(
+ rootQNameGenerator.imsss("conditionCombination", ruleConditionsNode.getNamespace()));
+ if (cc != null) {
+ ruleConditions.setConditionCombination(new Token(cc));
+ }
+ for (Object element : ruleConditionsNode.elements(rootQNameGenerator.imsss("ruleCondition"))) {
+ parseRuleCondition(ruleConditions, (Element) element);
+ }
+ conditionRule.setRuleConditions(ruleConditions);
+ }
+ }
+
+ private void parseRuleCondition(RuleConditions ruleConditions, Element ruleConditionNode) {
+ if (ruleConditionNode != null) {
+ RuleCondition ruleCondition = new RuleCondition();
+ Namespace pns = ruleConditionNode.getNamespace();
+ String condition = ruleConditionNode.attributeValue(rootQNameGenerator.imsss("condition", pns));
+ ruleCondition.setCondition(condition == null ? null : new Token(condition));
+ ruleCondition.setReferencedObjective(ruleConditionNode.attributeValue(
+ rootQNameGenerator.imsss("referencedObjective", pns)));
+ ruleCondition.setMeasureThreshold(parseDecimal(ruleConditionNode.attributeValue(
+ rootQNameGenerator.imsss("measureThreshold", pns)), 4));
+ String op = ruleConditionNode.attributeValue(rootQNameGenerator.imsss("operator", pns));
+ if (op != null) {
+ ruleCondition.setOperator(new Token(op));
+ }
+ ruleConditions.getRuleConditionList().add(ruleCondition);
+ }
+ }
+
+ private void parseRollupRule(RollupRules rollupRules, Element rollupRuleNode) {
+ if (rollupRuleNode != null) {
+ RollupRule rollupRule = new RollupRule();
+ Namespace pns = rollupRuleNode.getNamespace();
+ String cas = rollupRuleNode.attributeValue(rootQNameGenerator.imsss("childActivitySet", pns));
+ if (cas != null) {
+ rollupRule.setChildActivitySet(new Token(cas));
+ }
+ NonNegativeInteger mc = parseNonNegativeInteger(rollupRuleNode.attributeValue(
+ rootQNameGenerator.imsss("minimumCount", pns)));
+ if (mc != null) {
+ rollupRule.setMinimumCount(mc);
+ }
+ Decimal mp = parseDecimal(rollupRuleNode.attributeValue(
+ rootQNameGenerator.imsss("minimumPercent", pns)), 4);
+ if (mp != null) {
+ rollupRule.setMinimumPercent(mp);
+ }
+ parseRollupConditions(rollupRule, rollupRuleNode.element(rootQNameGenerator.imsss("rollupConditions")));
+ Element actionNode = rollupRuleNode.element(rootQNameGenerator.imsss("rollupAction"));
+ String action = actionNode == null ? null : actionNode.attributeValue(
+ rootQNameGenerator.imsss("action", pns));
+ if (action != null) {
+ rollupRule.setRollupAction(new Token(action));
+ }
+ rollupRules.getRollupRuleList().add(rollupRule);
+ }
+ }
+
+ private void parseRollupConditions(RollupRule rollupRule, Element rollupConditionsNode) {
+ if (rollupConditionsNode != null) {
+ RollupConditions rollupConditions = new RollupConditions();
+ String cc = rollupConditionsNode.attributeValue(
+ rootQNameGenerator.imsss("conditionCombination", rollupConditionsNode.getNamespace()));
+ if (cc != null) {
+ rollupConditions.setConditionCombination(new Token(cc));
+ }
+ parseRollupCondition(rollupConditions, rollupConditionsNode.element(
+ rootQNameGenerator.imsss("rollupCondition")));
+ rollupRule.setRollupConditions(rollupConditions);
+ }
+ }
+
+ private void parseRollupCondition(RollupConditions rollupConditions, Element rollupConditionNode) {
+ if (rollupConditionNode != null) {
+ RollupCondition rollupCondition = new RollupCondition();
+ Namespace pns = rollupConditionNode.getNamespace();
+ String condition = rollupConditionNode.attributeValue(rootQNameGenerator.imsss("condition", pns));
+ rollupCondition.setCondition(condition == null ? null : new Token(condition));
+ String op = rollupConditionNode.attributeValue(rootQNameGenerator.imsss("operator", pns));
+ if (op != null) {
+ rollupCondition.setOperator(new Token(op));
+ }
+ rollupConditions.getRollupConditionList().add(rollupCondition);
+ }
+ }
+
+ private Objective parseObjective(Element objectiveNode) {
+ if (objectiveNode != null) {
+ Objective objective = new Objective();
+ Namespace pns = objectiveNode.getNamespace();
+ Boolean sbm = parseBoolean(objectiveNode.attributeValue(
+ rootQNameGenerator.imsss("satisfiedByMeasure", pns)));
+ if (sbm != null) {
+ objective.setSatisfiedByMeasure(sbm);
+ }
+ String oid = objectiveNode.attributeValue(rootQNameGenerator.imsss("objectiveID", pns));
+ if (oid != null) {
+ objective.setObjectiveID(new AnyURI(oid));
+ }
+ Decimal mnm = parseDecimal(objectiveNode.elementTextTrim(
+ rootQNameGenerator.imsss("minNormalizedMeasure")), 4);
+ if (mnm != null) {
+ objective.setMinNormalizedMeasure(mnm);
+ }
+ for (Object element : objectiveNode.elements(rootQNameGenerator.imsss("mapInfo"))) {
+ parseMapInfo(objective, (Element) element);
+ }
+ return objective;
+ }
+ return null;
+ }
+
+ private void parseMapInfo(Objective objective, Element mapInfoNode) {
+ if (mapInfoNode != null) {
+ MapInfo mapInfo = new MapInfo();
+ Namespace pns = mapInfoNode.getNamespace();
+ String toid = mapInfoNode.attributeValue(rootQNameGenerator.imsss("targetObjectiveID", pns));
+ mapInfo.setTargetObjectiveID(toid == null ? null : new AnyURI(toid));
+ Boolean rss = parseBoolean(mapInfoNode.attributeValue(
+ rootQNameGenerator.imsss("readSatisfiedStatus", pns)));
+ if (rss != null) {
+ mapInfo.setReadSatisfiedStatus(rss);
+ }
+ Boolean rnm = parseBoolean(mapInfoNode.attributeValue(
+ rootQNameGenerator.imsss("readNormalizedMeasure", pns)));
+ if (rnm != null) {
+ mapInfo.setReadNormalizedMeasure(rnm);
+ }
+ Boolean wss = parseBoolean(mapInfoNode.attributeValue(
+ rootQNameGenerator.imsss("writeSatisfiedStatus", pns)));
+ if (wss != null) {
+ mapInfo.setWriteSatisfiedStatus(wss);
+ }
+ Boolean wnm = parseBoolean(mapInfoNode.attributeValue(
+ rootQNameGenerator.imsss("writeNormalizedMeasure", pns)));
+ if (wnm != null) {
+ mapInfo.setWriteNormalizedMeasure(wnm);
+ }
+ objective.getMapInfoList().add(mapInfo);
+ }
+ }
+
+ private void parseAdlseqObjective(AdlseqObjectives adlseqObjectives, Element adlseqObjectiveNode) {
+ if (adlseqObjectiveNode != null) {
+ AdlseqObjective adlseqObjective = new AdlseqObjective();
+ String oid = adlseqObjectiveNode.attributeValue(
+ rootQNameGenerator.adlseq("objectiveID", adlseqObjectiveNode.getNamespace()));
+ adlseqObjective.setObjectiveID(oid == null ? null : new AnyURI(oid));
+ for (Object element : adlseqObjectiveNode.elements(rootQNameGenerator.adlseq("mapInfo"))) {
+ parseAdlSeqMapInfo(adlseqObjective, (Element) element);
+ }
+ adlseqObjectives.getObjectiveList().add(adlseqObjective);
+ }
+ }
+
+ private void parseAdlSeqMapInfo(AdlseqObjective adlseqObjective, Element adlseqMapInfoNode) {
+ if (adlseqMapInfoNode != null) {
+ AdlseqMapInfo adlseqMapInfo = new AdlseqMapInfo();
+ Namespace pns = adlseqMapInfoNode.getNamespace();
+ String toid = adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("targetObjectiveID", pns));
+ adlseqMapInfo.setTargetObjectiveID(toid == null ? null : new AnyURI(toid));
+ Boolean readRawScore = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("readRawScore", pns)));
+ if (readRawScore != null) {
+ adlseqMapInfo.setReadRawScore(readRawScore);
+ }
+ Boolean readMinScore = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("readMinScore", pns)));
+ if (readMinScore != null) {
+ adlseqMapInfo.setReadMinScore(readMinScore);
+ }
+ Boolean readMaxScore = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("readMaxScore", pns)));
+ if (readMaxScore != null) {
+ adlseqMapInfo.setReadMaxScore(readMaxScore);
+ }
+ Boolean readCompletionStatus = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("readCompletionStatus", pns)));
+ if (readCompletionStatus != null) {
+ adlseqMapInfo.setReadCompletionStatus(readCompletionStatus);
+ }
+ Boolean readProgressMeasure = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("readProgressMeasure", pns)));
+ if (readProgressMeasure != null) {
+ adlseqMapInfo.setReadProgressMeasure(readProgressMeasure);
+ }
+ Boolean writeRawScore = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("writeRawScore", pns)));
+ if (writeRawScore != null) {
+ adlseqMapInfo.setWriteRawScore(writeRawScore);
+ }
+ Boolean writeMinScore = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("writeMinScore", pns)));
+ if (writeMinScore != null) {
+ adlseqMapInfo.setWriteMinScore(writeMinScore);
+ }
+ Boolean writeMaxScore = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("writeMaxScore", pns)));
+ if (writeMaxScore != null) {
+ adlseqMapInfo.setWriteMaxScore(writeMaxScore);
+ }
+ Boolean writeCompletionStatus = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("writeCompletionStatus", pns)));
+ if (writeCompletionStatus != null) {
+ adlseqMapInfo.setWriteCompletionStatus(writeCompletionStatus);
+ }
+ Boolean writeProgressMeasure = parseBoolean(adlseqMapInfoNode.attributeValue(
+ rootQNameGenerator.adlseq("writeProgressMeasure", pns)));
+ if (writeProgressMeasure != null) {
+ adlseqMapInfo.setWriteProgressMeasure(writeProgressMeasure);
+ }
+ adlseqObjective.getMapInfoList().add(adlseqMapInfo);
+ }
+ }
+
+ private Boolean parseBoolean(String s) {
+ if ("true".equalsIgnoreCase(s)) {
+ return true;
+ } else if ("false".equalsIgnoreCase(s)) {
+ return false;
+ } else {
+ return null;
+ }
+ }
+
+ private Decimal parseDecimal(String s, int scale) {
+ if (s == null || "".equals(s)) {
+ return null;
+ }
+ try {
+ Double.parseDouble(s);
+ } catch (Exception e) {
+ return null;
+ }
+ return new Decimal(s, scale);
+ }
+
+ private NonNegativeInteger parseNonNegativeInteger(String s) {
+ if (s == null || "".equals(s)) {
+ return null;
+ }
+ try {
+ if (Integer.parseInt(s) < 0) {
+ return null;
+ }
+ return new NonNegativeInteger(s);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private class QNameGenerator {
+ private Namespace imscpNamespace;
+ private Namespace adlcpNamespace;
+ private Namespace adlseqNamespace;
+ private Namespace adlnavNamespace;
+ private Namespace imsssNamespace;
+ private Namespace lomNamespace;
+ private Namespace xmlNamespace;
+
+ QNameGenerator(Map namespaceMap) {
+ imscpNamespace = namespaceMap.getOrDefault(NAMESPACE_IMSCP, new Namespace(PREFIX_IMSCP, NAMESPACE_IMSCP));
+ adlcpNamespace = namespaceMap.getOrDefault(NAMESPACE_ADLCP, new Namespace(PREFIX_ADLCP, NAMESPACE_ADLCP));
+ adlseqNamespace = namespaceMap.getOrDefault(NAMESPACE_ADLSEQ, new Namespace(PREFIX_ADLSEQ, NAMESPACE_ADLSEQ));
+ adlnavNamespace = namespaceMap.getOrDefault(NAMESPACE_ADLNAV, new Namespace(PREFIX_ADLNAV, NAMESPACE_ADLNAV));
+ imsssNamespace = namespaceMap.getOrDefault(NAMESPACE_IMSSS, new Namespace(PREFIX_IMSSS, NAMESPACE_IMSSS));
+ lomNamespace = namespaceMap.getOrDefault(NAMESPACE_LOM, new Namespace(PREFIX_LOM, NAMESPACE_LOM));
+ xmlNamespace = namespaceMap.getOrDefault(NAMESPACE_XML, new Namespace(PREFIX_XML, NAMESPACE_XML));
+ }
+
+ QNameGenerator(QNameGenerator baseQNameGenerator, Map namespaceMap) {
+ imscpNamespace = namespaceMap.getOrDefault(NAMESPACE_IMSCP, baseQNameGenerator.imscpNamespace);
+ adlcpNamespace = namespaceMap.getOrDefault(NAMESPACE_ADLCP, baseQNameGenerator.adlcpNamespace);
+ adlseqNamespace = namespaceMap.getOrDefault(NAMESPACE_ADLSEQ, baseQNameGenerator.adlseqNamespace);
+ adlnavNamespace = namespaceMap.getOrDefault(NAMESPACE_ADLNAV, baseQNameGenerator.adlnavNamespace);
+ imsssNamespace = namespaceMap.getOrDefault(NAMESPACE_IMSSS, baseQNameGenerator.imsssNamespace);
+ lomNamespace = namespaceMap.getOrDefault(NAMESPACE_LOM, baseQNameGenerator.lomNamespace);
+ xmlNamespace = namespaceMap.getOrDefault(NAMESPACE_XML, baseQNameGenerator.xmlNamespace);
+ }
+
+ String imscp(String elementName) {
+ //return new QName(elementName, imscpNamespace);
+ return elementName;
+ }
+
+ String adlcp(String elementName) {
+ //return new QName(elementName, adlcpNamespace);
+ return elementName;
+ }
+
+ String adlseq(String elementName) {
+ //return new QName(elementName, adlseqNamespace);
+ return elementName;
+ }
+
+ String adlnav(String elementName) {
+ //return new QName(elementName, adlnavNamespace);
+ return elementName;
+ }
+
+ String imsss(String elementName) {
+ //return new QName(elementName, imsssNamespace);
+ return elementName;
+ }
+
+ String lom(String elementName) {
+ //return new QName(elementName, lomNamespace);
+ return elementName;
+ }
+
+ String xml(String elementName) {
+ //return new QName(elementName, xmlNamespace);
+ return elementName;
+ }
+
+ QName imscp(String attributeName, Namespace elementNamespace) {
+
+ if (Objects.equals(elementNamespace, imscpNamespace)) {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ } else {
+ //System.out.println(attributeName);
+ //System.out.println(imscpNamespace.asXML());
+ if(CHECK_NAMESPACE) {
+ return new QName(attributeName, imscpNamespace);
+ }else {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ }
+
+ }
+ }
+
+ QName adlcp(String attributeName, Namespace elementNamespace) {
+ if (Objects.equals(elementNamespace, adlcpNamespace)) {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ } else {
+ if(CHECK_NAMESPACE) {
+ return new QName(attributeName, adlcpNamespace);
+ }else {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ }
+ }
+ }
+
+ QName adlseq(String attributeName, Namespace elementNamespace) {
+ if (Objects.equals(elementNamespace, adlseqNamespace)) {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ } else {
+ if(CHECK_NAMESPACE) {
+ return new QName(attributeName, adlseqNamespace);
+ }else {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ }
+ }
+ }
+
+// QName adlnav(String attributeName, Namespace elementNamespace) {
+// if (Objects.equals(elementNamespace, adlnavNamespace)) {
+// return new QName(attributeName, Namespace.NO_NAMESPACE);
+// } else {
+// if(CHECK_NAMESPACE) {
+// return new QName(attributeName, adlnavNamespace);
+// }else {
+// return new QName(attributeName, Namespace.NO_NAMESPACE);
+// }
+// }
+// }
+
+ QName imsss(String attributeName, Namespace elementNamespace) {
+ if (Objects.equals(elementNamespace, imsssNamespace)) {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ } else {
+ if(CHECK_NAMESPACE) {
+ return new QName(attributeName, imsssNamespace);
+ }else {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ }
+ }
+ }
+
+ QName lom(String attributeName, Namespace elementNamespace) {
+ if (Objects.equals(elementNamespace, lomNamespace)) {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ } else {
+ if(CHECK_NAMESPACE) {
+ return new QName(attributeName, lomNamespace);
+ }else {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ }
+ }
+ }
+
+ QName xml(String attributeName, Namespace elementNamespace) {
+ if (Objects.equals(elementNamespace, xmlNamespace)) {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ } else {
+ if(CHECK_NAMESPACE) {
+ return new QName(attributeName, xmlNamespace);
+ }else {
+ return new QName(attributeName, Namespace.NO_NAMESPACE);
+ }
+ }
+ }
+ }
+
+// public static void main(String[] args) {
+// new ContentPackageGenerator().generateContentPackageFromFile("learningserver-scorm/scorm-test-pkg");
+// }
+
+}
diff --git a/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/ContentPackageValidator.java b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/ContentPackageValidator.java
new file mode 100644
index 00000000..cf5b203c
--- /dev/null
+++ b/modules/boe-module-scorm/src/main/java/com/xboe/module/scorm/cam/load/ContentPackageValidator.java
@@ -0,0 +1,2420 @@
+package com.xboe.module.scorm.cam.load;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.xboe.module.scorm.cam.model.AdlseqMapInfo;
+import com.xboe.module.scorm.cam.model.AdlseqObjective;
+import com.xboe.module.scorm.cam.model.AdlseqObjectives;
+import com.xboe.module.scorm.cam.model.Annotation;
+import com.xboe.module.scorm.cam.model.Classification;
+import com.xboe.module.scorm.cam.model.CompletionThreshold;
+import com.xboe.module.scorm.cam.model.ConditionRule;
+import com.xboe.module.scorm.cam.model.Content;
+import com.xboe.module.scorm.cam.model.ContentPackage;
+import com.xboe.module.scorm.cam.model.Contribute;
+import com.xboe.module.scorm.cam.model.Data;
+import com.xboe.module.scorm.cam.model.DateTime;
+import com.xboe.module.scorm.cam.model.Dependency;
+import com.xboe.module.scorm.cam.model.Duration;
+import com.xboe.module.scorm.cam.model.Educational;
+import com.xboe.module.scorm.cam.model.File;
+import com.xboe.module.scorm.cam.model.General;
+import com.xboe.module.scorm.cam.model.HideLMSUI;
+import com.xboe.module.scorm.cam.model.Item;
+import com.xboe.module.scorm.cam.model.LOM;
+import com.xboe.module.scorm.cam.model.LanguageString;
+import com.xboe.module.scorm.cam.model.LanguageStrings;
+import com.xboe.module.scorm.cam.model.LifeCycle;
+import com.xboe.module.scorm.cam.model.LimitConditions;
+import com.xboe.module.scorm.cam.model.Manifest;
+import com.xboe.module.scorm.cam.model.ManifestMetadata;
+import com.xboe.module.scorm.cam.model.Map;
+import com.xboe.module.scorm.cam.model.MapInfo;
+import com.xboe.module.scorm.cam.model.MetaMetadata;
+import com.xboe.module.scorm.cam.model.Metadata;
+import com.xboe.module.scorm.cam.model.Objective;
+import com.xboe.module.scorm.cam.model.Objectives;
+import com.xboe.module.scorm.cam.model.OrComposite;
+import com.xboe.module.scorm.cam.model.Organization;
+import com.xboe.module.scorm.cam.model.Organizations;
+import com.xboe.module.scorm.cam.model.Presentation;
+import com.xboe.module.scorm.cam.model.RandomizationControls;
+import com.xboe.module.scorm.cam.model.Relation;
+import com.xboe.module.scorm.cam.model.Requirement;
+import com.xboe.module.scorm.cam.model.Resource;
+import com.xboe.module.scorm.cam.model.Resources;
+import com.xboe.module.scorm.cam.model.Rights;
+import com.xboe.module.scorm.cam.model.RollupCondition;
+import com.xboe.module.scorm.cam.model.RollupConditions;
+import com.xboe.module.scorm.cam.model.RollupConsiderations;
+import com.xboe.module.scorm.cam.model.RollupRule;
+import com.xboe.module.scorm.cam.model.RollupRules;
+import com.xboe.module.scorm.cam.model.RuleCondition;
+import com.xboe.module.scorm.cam.model.RuleConditions;
+import com.xboe.module.scorm.cam.model.Sequencing;
+import com.xboe.module.scorm.cam.model.SequencingCollection;
+import com.xboe.module.scorm.cam.model.SequencingRules;
+import com.xboe.module.scorm.cam.model.Taxon;
+import com.xboe.module.scorm.cam.model.TaxonPath;
+import com.xboe.module.scorm.cam.model.Technical;
+import com.xboe.module.scorm.cam.model.Vocabulary;
+import com.xboe.module.scorm.cam.model.datatype.Token;
+import com.xboe.module.scorm.cam.model.datatype.VCard;
+import com.xboe.module.scorm.cam.model.util.CPUtils;
+import com.xboe.module.scorm.common.CommonUtils;
+
+@Slf4j
+public class ContentPackageValidator {
+
+ private static final String[] VT_TIME_LIMIT_ACTION
+ = {"exist,message", "exit,no message", "continue,message", "continue,no message"};
+ private static final String[] VT_SCORM_TYPE
+ = {"sco", "asset"};
+ private static final String[] VT_GENERAL_STRUCTURE
+ = {"atomic", "collection", "networked", "hierarchical", "linear"};
+ private static final String[] VT_GENERAL_AGGREGATION_LEVEL
+ = {"1", "2", "3", "4"};
+ private static final String[] VT_LIFE_CYCLE_STATUS
+ = {"draft", "final", "revised", "unavailable"};
+ private static final String[] VT_LIFE_CYCLE_CONTRIBUTE_ROLE
+ = {"author", "publisher", "unknown", "initiator", "terminator", "validator", "editor", "graphical designer",
+ "technical implementer", "content provider", "technical validator", "educational validator", "script write",
+ "instructional designer", "subject matter expert"};
+ private static final String[] VT_META_METADATA_CONTRIBUTE_ROLE
+ = {"creator", "validator"};
+ private static final String[] VT_OR_COMPOSITE_TYPE
+ = {"operating system", "browser"};
+ private static final String[] VT_OR_COMPOSITE_NAME_OS
+ = {"pc-dos", "ms-windows", "macos", "unix", "multi-os", "none"};
+ private static final String[] VT_OR_COMPOSITE_NAME_B
+ = {"any", "netscape communicator", "ms-internet explorer", "opera", "amaya"};
+ private static final String[] VT_EDUCATIONAL_INTERACTIVITY_TYPE
+ = {"active", "expositive", "mixed"};
+ private static final String[] VT_EDUCATIONAL_LEARNING_RESOURCE_TYPE
+ = {"exercise", "simulation", "questionnaire", "diagram", "figure", "graph", "index", "slide", "table",
+ "narrative text", "exam", "experiment", "problem statement", "self assessment", "lecture"};
+ private static final String[] VT_EDUCATIONAL_INTERACTIVITY_LEVEL
+ = {"very low", "low", "medium", "high", "very high"};
+ private static final String[] VT_EDUCATIONAL_SEMANTIC_DENSITY
+ = {"very low", "low", "medium", "hight", "very high"};
+ private static final String[] VT_EDUCATIONAL_INTENDED_END_USER_ROLE
+ = {"teacher", "author", "learner", "manager"};
+ private static final String[] VT_EDUCATIONAL_CONTEXT
+ = {"higher education", "training", "other"};
+ private static final String[] VT_EDUCATIONAL_DIFFICULTY
+ = {"very easy", "easy", "medium" ,"difficult", "very difficulty"};
+ private static final String[] VT_RIGHTS_COST
+ = {"yes", "no"};
+ private static final String[] VT_RIGHTS_COPYRIGHT_AND_OTHER_RESTRICTIONS
+ = {"yes", "no"};
+ private static final String[] VT_RELATION_KIND
+ = {"ispartof", "haspart", "isversionof", "hasversion", "isformatof", "hasformat", "references",
+ "isreferencedby", "isbasedon", "isbasisfor", "requires", "isrequiredby"};
+ private static final String[] VT_CLASSIFICATION_PURPOSE
+ = {"discipline", "idea", "prerequisite", "educational objective", "accessibility restrictions",
+ "educational level", "skill level", "security level", "competency"};
+ private static final String[] VT_SEQUENCING_RULES_CONDITION_COMBINATION
+ = {"all", "any"};
+ private static final String[] VT_SEQUENCING_RULES_RULE_CONDITION_OPERATOR
+ = {"not", "noOp"};
+ private static final String[] VT_SEQUENCING_RULES_RULE_CONDITION_CONDITION
+ = {"satisfied", "objectiveStatusKnown", "objectiveMeasureKnown", "objectiveMeasureGreaterThan",
+ "objectiveMeasureLessThan", "completed", "activityProgressKnown", "attempted", "attemptLimitExceeded",
+ "timeLimitExceeded", "outsideAvailableTimeRange", "always"};
+ private static final String[] VT_SEQUENCING_RULES_RULE_ACTION_PRE
+ = {"skip", "disabled", "hiddenFromChoice", "stopForwardTraversal"};
+ private static final String[] VT_SEQUENCING_RULES_RULE_ACTION_POST
+ = {"exitParent", "exitAll", "retry", "retryAll", "continue", "previous"};
+ private static final String[] VT_SEQUENCING_RULES_RULE_ACTION_EXIT
+ = {"exit"};
+ private static final String[] VT_SEQUENCING_ROLLUP_RULE_CHILD_ACTIVITY_SET
+ = {"all", "any", "none", "atLeastCount", "atLeastPercent"};
+ private static final String[] VT_SEQUENCING_ROLLUP_CONDITIONS_CONDITION_COMBINATION
+ = {"all", "any"};
+ private static final String[] VT_SEQUENCING_ROLLUP_CONDITION_OPERATOR
+ = {"not", "noOp"};
+ private static final String[] VT_SEQUENCING_ROLLUP_CONDITION_CONDITION
+ = {"satisfied", "objectiveStatusKnown", "objectiveMeasureKnown", "completed", "activityProgressKnown",
+ "attempted", "attemptLimitExceeded", "timeLimitExceeded", "outsideAvailableTimeRange"};
+ private static final String[] VT_SEQUENCING_ROLLUP_ACTION
+ = {"satisfied", "notSatisfied", "completed", "incomplete"};
+ private static final String[] VT_SEQUENCING_RANDOMIZATION_TIMING
+ = {"never", "once", "onEachNewAttempt"};
+ private static final String[] VT_SEQUENCING_SELECTION_TIMING
+ = {"never", "once", "onEachNewAttempt"};
+ private static final String[] VT_SEQUENCING_ROLLUP_CONSIDERATIONS
+ = {"always", "ifAttempted", "ifNotSkipped", "ifNotSuspended"};
+ private static final String[] VT_PRESENTATION_HIDE_LMS_UI
+ = {"previous", "continue", "exit", "exitAll", "abandon", "abandonAll", "suspendAll"};
+
+ private boolean isAssert;
+ private java.util.Map> errors;
+
+ public ContentPackageValidator() {
+ this(true);
+ }
+
+ public ContentPackageValidator(boolean isAssert) {
+ this.isAssert = isAssert;
+ this.errors = new HashMap<>();
+ }
+
+ public boolean validate(ContentPackage contentPackage, String pkgSaveDir) {
+ this.errors.clear();
+ if (contentPackage == null) {
+ recordError("ContentPackage", "cannot be null");
+ return false;
+ }
+
+ boolean result;
+
+ // independent validation
+ result = validateManifest(contentPackage.getManifest());
+ if (!result) {
+ return false;
+ }
+
+ // dependent validation
+ result = independentValidate(contentPackage.getManifest());
+
+ result &= validateContent(contentPackage.getContent(), pkgSaveDir);
+
+ return result;
+ }
+
+ private boolean validateContent(Content content, String pkgSaveDir) {
+ boolean result = true;
+ boolean flag;
+
+ for (String filePath : content.getPhysicalFilePathList()) {
+ result &= (flag = new java.io.File(pkgSaveDir + filePath).exists());
+ if (!flag) {
+ recordError("physical file", "not found: " + filePath);
+ }
+ }
+
+ return result;
+ }
+
+ private boolean independentValidate(Manifest manifest) {
+ boolean result = true;
+ boolean flag;
+
+ Set idSet = new HashSet<>();
+ Set organizationIdSet = new HashSet<>();
+ Set resourceIdSet = new HashSet<>();
+ int count = 0;
+
+ idSet.add(manifest.getIdentifier().getValue());
+ count++;
+
+ // #1: unique id within manifest
+ for (Organization organization : manifest.getOrganizations().getOrganizationList()) {
+ idSet.add(organization.getIdentifier().getValue());
+ organizationIdSet.add(organization.getIdentifier().getValue());
+ count++;
+ result &= (flag = idSet.size() == count);
+ if (!flag) {
+ recordError("...identifier",
+ "must be unique in the manifest: " + organization.getIdentifier().getValue());
+ count--;
+ if (isAssert) {
+ return false;
+ }
+ }
+ for (Item item : organization.getItemList()) {
+ idSet.add(item.getIdentifier().getValue());
+ count++;
+ result &= (flag = idSet.size() == count);
+ if (!flag) {
+ recordError("...- [.
- ].identifier",
+ "must be unique in the manifest: " + item.getIdentifier().getValue());
+ count--;
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+ }
+
+ for (Resource resource : manifest.getResources().getResourceList()) {
+ idSet.add(resource.getIdentifier().getValue());
+ resourceIdSet.add(resource.getIdentifier().getValue());
+ count++;
+ result &= (flag = idSet.size() == count);
+ if (!flag) {
+ recordError("...identifier",
+ "must be unique in the manifest: " + resource.getIdentifier().getValue());
+ count--;
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ if (manifest.getSequencingCollection() != null) {
+ for (Sequencing sequencing : manifest.getSequencingCollection().getSequencingList()) {
+ idSet.add(sequencing.getId().getValue());
+ count++;
+ result &= (flag = idSet.size() == count);
+ if (!flag) {
+ recordError("...ID",
+ "must be unique in the manifest: " + sequencing.getId().getValue());
+ count--;
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // #2: reference id
+ result &= (flag = organizationIdSet.contains(manifest.getOrganizations().getDefaultOrganizationID().getValue()));
+ if (!flag) {
+ recordError("..default",
+ "must reference an identifier attribute of an : "
+ + manifest.getOrganizations().getDefaultOrganizationID().getValue());
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ Set referencedResourceIdSet = new HashSet<>();
+ List
- leafItemList = new LinkedList<>();
+ for (Organization organization : manifest.getOrganizations().getOrganizationList()) {
+ for (Item item : organization.getItemList()) {
+ result &= (flag = validateItemIDRef(item, resourceIdSet, referencedResourceIdSet, leafItemList));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ }
+
+ for (Resource resource : manifest.getResources().getResourceList()) {
+ for (Dependency dependency : resource.getDependencyList()) {
+ result &= (flag = resourceIdSet.contains(dependency.getIdentifierref()));
+ if (!flag) {
+ recordError("....identifierref",
+ "must reference an identifier attribute of an :" + dependency.getIdentifierref());
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+ }
+
+ for (Organization organization : manifest.getOrganizations().getOrganizationList()) {
+ if (organization.getSequencing() != null && !ModelUtils.isIDRefEmpty(organization.getSequencing().getIdRef())) {
+ result &= (flag = organizationIdSet.contains(organization.getSequencing().getIdRef().getValue()));
+ if (!flag) {
+ recordError("..[.
- [.
- ]]..IDRef",
+ "must reference an ID attribute of an within : "
+ + organization.getSequencing().getIdRef().getValue());
+ }
+ }
+ }
+
+ // #3: referenced by leaf
-
+ Set referencedScoResourceIdSet = new HashSet<>();
+ for (Resource resource : manifest.getResources().getResourceList()) {
+ if (referencedResourceIdSet.contains(resource.getIdentifier().getValue())) {
+ result &= (flag = StringUtils.equals(resource.getType(), "webcontent"));
+ if (!flag) {
+ recordError("...type", "" +
+ "the type attribute of the referenced by a leaf
- must be \"webcontent\"");
+ if (isAssert) {
+ return false;
+ }
+ }
+ result &= (flag = StringUtils.isNotBlank(resource.getHref()));
+ if (!flag) {
+ recordError("...type", "" +
+ "the href attribute of the referenced by a leaf
- is required");
+ if (isAssert) {
+ return false;
+ }
+ }
+ if (StringUtils.equals(resource.getScormType(), "sco")) {
+ referencedScoResourceIdSet.add(resource.getIdentifier().getValue());
+ }
+ }
+ }
+
+ // #4: element only appear as a child of a leaf
- element that references a SCO resource
+ for (Item item : leafItemList) {
+ if (item.getData() != null) {
+ result &= (flag = referencedScoResourceIdSet.contains(item.getIdentifierref()));
+ if (!flag) {
+ recordError("..[.
- ].
- .",
+ "only appear as a child of a leaf
- element that references a SCO resource");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // TODO continue objective and mapInfo
+ // #5: objective unique within an activity.
+ for (Organization organization : manifest.getOrganizations().getOrganizationList()) {
+ if (organization.getSequencing() != null) {
+ result &= (flag = validateObjectiveID(organization.getSequencing(), manifest.getSequencingCollection()));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ for (Item item : organization.getItemList()) {
+ result &= (flag = validateObjectiveID(item, manifest.getSequencingCollection()));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateObjectiveID(Item item, SequencingCollection sequencingCollection) {
+ boolean result = true;
+ boolean flag;
+ if (item.getSequencing() != null) {
+ result = (flag = validateObjectiveID(item.getSequencing(), sequencingCollection));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ for (Item child : item.getItemList()) {
+ if (child.getSequencing() != null) {
+ result &= (flag = validateObjectiveID(child.getSequencing(), sequencingCollection));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean validateObjectiveID(Sequencing sequencing, SequencingCollection sequencingCollection) {
+ List objectiveList = new LinkedList<>();
+ List adlseqObjectiveList = new LinkedList<>();
+ List ruleConditionList = new LinkedList<>();
+ if (sequencing.getObjectives() != null) {
+ if (!ModelUtils.isAnyUriEmpty(sequencing.getObjectives().getPrimaryObjective().getObjectiveID())) {
+ objectiveList.add(sequencing.getObjectives().getPrimaryObjective());
+ }
+ objectiveList.addAll(sequencing.getObjectives().getObjectiveList());
+ }
+ if (sequencing.getAdlseqObjectives() != null) {
+ adlseqObjectiveList.addAll(sequencing.getAdlseqObjectives().getObjectiveList());
+ }
+ if (!ModelUtils.isIDRefEmpty(sequencing.getIdRef())) {
+ Sequencing refSeq = CPUtils.findSequencingByID(sequencingCollection, sequencing.getIdRef().getValue());
+ if (refSeq != null) {
+ if (refSeq.getObjectives() != null) {
+ objectiveList.add(refSeq.getObjectives().getPrimaryObjective());
+ objectiveList.addAll(refSeq.getObjectives().getObjectiveList());
+ }
+ if (refSeq.getAdlseqObjectives() != null) {
+ adlseqObjectiveList.addAll(refSeq.getAdlseqObjectives().getObjectiveList());
+ }
+ }
+ }
+ if (sequencing.getSequencingRules() != null) {
+ for (ConditionRule conditionRule : sequencing.getSequencingRules().getPreConditionRuleList()) {
+ ruleConditionList.addAll(conditionRule.getRuleConditions().getRuleConditionList());
+ }
+ for (ConditionRule conditionRule : sequencing.getSequencingRules().getPostConditionRuleList()) {
+ ruleConditionList.addAll(conditionRule.getRuleConditions().getRuleConditionList());
+ }
+ for (ConditionRule conditionRule : sequencing.getSequencingRules().getExitConditionRuleList()) {
+ ruleConditionList.addAll(conditionRule.getRuleConditions().getRuleConditionList());
+ }
+ }
+ return validateObjectiveID(objectiveList, adlseqObjectiveList, ruleConditionList);
+ }
+
+ private boolean validateObjectiveID(List objectiveList, List adlseqObjectiveList,
+ List ruleConditionList) {
+ boolean result;
+ boolean flag;
+ Set objectiveIDSet = new HashSet<>();
+ for (Objective objective : objectiveList) {
+ objectiveIDSet.add(objective.getObjectiveID().getValue());
+ }
+ result = (flag = objectiveIDSet.size() == objectiveList.size());
+ if (!flag) {
+ recordError("..[[.
- ].
- ]." +
+ "...objectiveID",
+ "must be unique in an item(exclude ancestor item or organization, " +
+ "include reference sequencing in sequencing Collection)");
+ if (isAssert) {
+ return false;
+ }
+ }
+ for (AdlseqObjective adlseqObjective : adlseqObjectiveList) {
+ result &= (flag = objectiveIDSet.contains(adlseqObjective.getObjectiveID().getValue()));
+ if (!flag) {
+ recordError("..[[.
- ].
- ]." +
+ "...objectiveID",
+ "must match one objectiveID of the IMS SS .objectiveID in the same item, " +
+ "(exclude ancestor item or organization, include reference sequencing in SequencingCollection)");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+ for (RuleCondition ruleCondition : ruleConditionList) {
+ if (StringUtils.isNotBlank(ruleCondition.getReferencedObjective())) {
+ result &= (flag = objectiveIDSet.contains(ruleCondition.getReferencedObjective()));
+ if (!flag) {
+ recordError("..[[.
- ].
- ]." +
+ "..(||" +
+ ")...referencedObjective",
+ "must be one objectiveID of the IMS SS .objectiveID in the same item, " +
+ "(exclude ancestor item or organization, include reference sequencing in SequencingCollection)");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean validateItemIDRef(Item item, Set resourceIdSet,
+ Set referencedResourceIdSet, List
- leafItemList) {
+ boolean result = true;
+ boolean flag;
+
+ if (item.getItemList().isEmpty()) {
+ result = (flag = resourceIdSet.contains(item.getIdentifierref()));
+ if (!flag) {
+ recordError("..[.
- ].
- ",
+ "must reference an identifier attribute of an : " + item.getIdentifierref());
+ if (isAssert) {
+ return false;
+ }
+ } else {
+ referencedResourceIdSet.add(item.getIdentifierref());
+ leafItemList.add(item);
+ }
+ } else {
+ for (Item childItem : item.getItemList()) {
+ result &= (flag = validateItemIDRef(childItem, resourceIdSet, referencedResourceIdSet, leafItemList));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateManifest(Manifest manifest) {
+ if (manifest == null) {
+ recordError("", "must exist 1 and only 1 time");
+ return false;
+ }
+
+ boolean result;
+ boolean flag;
+
+ result = (flag = !ModelUtils.isIDEmpty(manifest.getIdentifier()));
+ if (!flag) {
+ recordError(".identifier", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ }
+ // need to dv .identifier -> unique within the manifest element
+
+ if (!ModelUtils.isAnyUriEmpty(manifest.getXmlBase())) {
+ result &= (flag = ModelUtils.isAnyUriFormatCorrect(manifest.getXmlBase()));
+ if (!flag) {
+ recordError(".xml:base", "value format incorrect");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = validateManifestMetadata(manifest.getMetadata()));
+ if (!flag) {
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ result &= (flag = validateOrganizations(manifest.getOrganizations()));
+ if (!flag) {
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ result &= (flag = validateResources(manifest.getResources()));
+ if (!flag) {
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ if (manifest.getSequencingCollection() != null) {
+ result &= (flag = validateSequencingCollection(manifest.getSequencingCollection()));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateManifestMetadata(ManifestMetadata manifestMetadata) {
+ if (manifestMetadata == null) {
+ recordError(".", "must exist 1 and only 1 time");
+ return false;
+ }
+
+ boolean result;
+ boolean flag;
+
+ result = (flag = StringUtils.isNotBlank(manifestMetadata.getSchema()));
+ if (!flag) {
+ recordError("..", "must exist 1 and only 1 time");
+ if (isAssert) {
+ return false;
+ }
+ } else {
+ result = (flag = StringUtils.equals(manifestMetadata.getSchema(), "ADL SCORM"));
+ if (!flag) {
+ recordError("..", "value must be \"ADL SCORM\"");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = StringUtils.isNotBlank(manifestMetadata.getSchemaVersion()));
+ if (!flag) {
+ recordError("..", "must exist 1 and only 1 time");
+ if (isAssert) {
+ return false;
+ }
+ } else {
+ result &= (flag = StringUtils.equals(manifestMetadata.getSchemaVersion(), "2004 4th Edition"));
+ if (!flag) {
+ recordError("..", "value must be \"2004 4th Edition\"");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = validateMetadata(manifestMetadata.getMetadata()));
+ if (!flag && isAssert) {
+ return false;
+ }
+
+ return result;
+ }
+
+ private boolean validateOrganizations(Organizations organizations) {
+ if (organizations == null) {
+ recordError(".", "must exist 1 and only 1 time");
+ return false;
+ }
+
+ boolean result;
+ boolean flag;
+
+ result = (flag = !ModelUtils.isIDRefEmpty(organizations.getDefaultOrganizationID()));
+ if (!flag) {
+ recordError("..default", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ }
+ // need to dv ..default -> must reference an identifier attribute of an
+
+ for (Organization organization : organizations.getOrganizationList()) {
+ result &= (flag = validateOrganization(organization));
+ if (!flag) {
+ if (!isAssert) {
+ return false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateOrganization(Organization organization) {
+ boolean result;
+ boolean flag;
+
+ result = (flag = !ModelUtils.isIDEmpty(organization.getIdentifier()));
+ if (!flag) {
+ recordError("...identifier", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ }
+ // need to dv ...identifier -> unique within the manifest file
+
+ result &= (flag = StringUtils.isNotBlank(organization.getTitle()));
+ if (!flag) {
+ recordError("...",
+ "must exist 1 and only 1 time and value not empty");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ result &= (flag = !organization.getItemList().isEmpty());
+ if (!flag) {
+ recordError("...
- ", "must exist 1 or more times");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ for (Item item : organization.getItemList()) {
+ result &= (flag = validateItem(item));
+ if (!flag) {
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ if (organization.getCompletionThreshold() != null) {
+ result &= (flag = validateCompletionThreshold(organization.getCompletionThreshold()));
+ if (!flag) {
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = validateMetadata(organization.getMetadata()));
+ if (!flag && isAssert) {
+ return false;
+ }
+
+ if (organization.getSequencing() != null) {
+ result &= (flag = validateSequencing(organization.getSequencing(), false));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateItem(Item item) {
+ boolean result;
+ boolean flag;
+
+ result = (flag = !ModelUtils.isIDEmpty(item.getIdentifier()));
+ if (!flag) {
+ recordError("...
- [.
- ].identifier", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ }
+ // need to dv ...
- [.
- ].identifier -> unique within the manifest
+
+ if (item.getItemList().isEmpty()) { // is a leaf
-
+ result &= (flag = StringUtils.isNotBlank(item.getIdentifierref()));
+ if (!flag) {
+ recordError("...
- [.
- ].identifierref",
+ "a leaf
- is required to reference a resource");
+ if (isAssert) {
+ return false;
+ }
+ }
+ // need to dv ...
- [.
- ].identifierref -> reference to an identifier in resources section
+ // need to dv the resource must be type=webcontent,scormType=sco|asset,href is required
+ } else { // not a leaf
-
+ result &= (flag = StringUtils.isBlank(item.getIdentifierref()));
+ if (!flag) {
+ recordError("...
- [.
- ].identifierref",
+ "only a leaf
- can reference a resource");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ if (StringUtils.isNotBlank(item.getParameters())) {
+ result &= (flag = ModelUtils.isParametersFormatCorrect(item.getParameters()));
+ if (!flag) {
+ recordError("...
- [.
- ].parameters",
+ "invalid format defined in RFC 3986");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = StringUtils.isNotBlank(item.getTitle()));
+ if (!flag) {
+ recordError("...
- [.
- ].",
+ "must exist 1 and only 1 time");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ for (Item nestedItem : item.getItemList()) {
+ result &= (flag = validateItem(nestedItem));
+ if (!flag & isAssert) {
+ return false;
+ }
+ }
+
+ if (StringUtils.isNotBlank(item.getTimeLimitAction())) {
+ result &= (flag = ModelUtils.isLegalVocabulary(item.getTimeLimitAction(), VT_TIME_LIMIT_ACTION));
+ if (!flag) {
+ recordError("...
- [.
- ].",
+ CommonUtils.format("value must be one of following tokens: {}", Arrays.toString(VT_TIME_LIMIT_ACTION)));
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ if (item.getCompletionThreshold() != null) {
+ result &= (flag = validateCompletionThreshold(item.getCompletionThreshold()));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+
+ // need to dv CAM-3-36
+ if (item.getData() != null) {
+ result &= (flag == item.getItemList().isEmpty());
+ if (!flag) {
+ recordError("...
- [.
- ].",
+ "only appear as a child of a leaf
- ");
+ if (isAssert) {
+ return false;
+ }
+ } else {
+ result &= (flag = validateData(item.getData()));
+ if (!flag & isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = validateMetadata(item.getMetadata()));
+ if (!flag && isAssert) {
+ return false;
+ }
+
+ if (item.getSequencing() != null) {
+ result &= (flag = validateSequencing(item.getSequencing(), false));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+
+ if (item.getPresentation() != null) {
+ result &= (flag = item.getItemList().isEmpty());
+ if (!flag) {
+ recordError("...
- [.
- ].",
+ "only appear as a child of a leaf
- element");
+ if (isAssert) {
+ return false;
+ }
+ } else {
+ result &= (flag = validatePresentation(item.getPresentation()));
+ if (!flag && isAssert) {
+ return false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateCompletionThreshold(CompletionThreshold completionThreshold) {
+ boolean result;
+ boolean flag;
+
+ result = (flag = ModelUtils.isDecimalInRange(completionThreshold.getMinProgressMeasure(),
+ 0.0000, 1.0000, 4));
+ if (!flag) {
+ recordError("..[.
- [.
- ]]..minProgressMeasure",
+ "must in range [0.0000, 1.0000], with a precisions to at least 4 significant digits");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ result &= (flag = ModelUtils.isDecimalInRange(completionThreshold.getProgressWeight(),
+ 0.0000, 1.0000, 4));
+ if (!flag) {
+ recordError("..[.
- [.
- ]]..progressWeight",
+ "must in range [0.0000, 1.0000], with a precisions to at least 4 significant digits");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateData(Data data) {
+ boolean result;
+ boolean flag;
+
+ result = (flag = !data.getMapList().isEmpty());
+ if (!flag) {
+ recordError("...
- [.
- ]..",
+ "must exist 1 or more times");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ for (Map map : data.getMapList()) {
+ result &= (flag = ModelUtils.isAnyUriFormatCorrect(map.getTargetID()));
+ if (!flag) {
+ recordError("...
- [.
- ]...targetID",
+ "must exist and has correct format");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateResources(Resources resources) {
+ if (resources == null) {
+ recordError(".", "must exist 1 and only 1 time");
+ return false;
+ }
+
+ boolean result = true;
+ boolean flag;
+
+ if (!ModelUtils.isAnyUriEmpty(resources.getXmlBase())) {
+ result = (flag = ModelUtils.isAnyUriFormatCorrect(resources.getXmlBase()));
+ if (!flag) {
+ recordError("..xml:base", "value format incorrect");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ for (Resource resource : resources.getResourceList()) {
+ result &= (flag = validateResource(resource));
+ if (!flag & isAssert) {
+ return false;
+ }
+ }
+
+ return result;
+ }
+
+ private boolean validateResource(Resource resource) {
+ boolean result;
+ boolean flag;
+
+ result = (flag = !ModelUtils.isIDEmpty(resource.getIdentifier()));
+ if (!flag) {
+ recordError("...identifier", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ }
+ // need to dv ...identifier -> unique within the scope of its containing manifest
+
+ result &= (flag = StringUtils.isNotBlank(resource.getType()));
+ if (!flag) {
+ recordError("...type", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ }
+
+ if (!ModelUtils.isAnyUriEmpty(resource.getXmlBase())) {
+ result = (flag = ModelUtils.isAnyUriFormatCorrect(resource.getXmlBase()));
+ if (!flag) {
+ recordError("...xml:base", "value format incorrect");
+ if (isAssert) {
+ return false;
+ }
+ }
+ }
+
+ result &= (flag = StringUtils.isNotBlank(resource.getScormType()));
+ if (!flag) {
+ recordError("...adlcp:scormType", "must exist");
+ if (isAssert) {
+ return false;
+ }
+ } else {
+ result &= (flag = ModelUtils.isLegalVocabulary(resource.getScormType(), VT_SCORM_TYPE));
+ if (!flag) {
+ recordError("