Java tutorial
/****************************************************************************** ** ** Advanced Distributed Learning Co-Laboratory (ADL Co-Lab) Hub grants you ** ("Licensee") a non-exclusive, royalty free, license to use, modify and ** redistribute this software in source and binary code form, provided that ** i) this copyright notice and license appear on all copies of the software; ** and ii) Licensee does not utilize the software in a manner which is ** disparaging to ADL Co-Lab Hub. ** ** This software is provided "AS IS," without a warranty of any kind. ALL ** EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ** ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE ** OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. ADL Co-Lab Hub AND ITS LICENSORS ** SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF ** USING, MODIFYING OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO ** EVENT WILL ADL Co-Lab Hub OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, ** PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, ** INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE ** THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE ** SOFTWARE, EVEN IF ADL Co-Lab Hub HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH ** DAMAGES. ** *******************************************************************************/ package org.adl.datamodels.ieee; import java.io.Serializable; import java.util.Hashtable; import java.util.Vector; import org.adl.datamodels.Children; import org.adl.datamodels.Count; import org.adl.datamodels.DMDelimiter; import org.adl.datamodels.DMDelimiterDescriptor; import org.adl.datamodels.DMElement; import org.adl.datamodels.DMElementDescriptor; import org.adl.datamodels.DMErrorCodes; import org.adl.datamodels.DMProcessingInfo; import org.adl.datamodels.DMRequest; import org.adl.datamodels.RequestDelimiter; import org.adl.datamodels.RequestToken; import org.adl.datamodels.datatypes.InteractionTrunc; import org.adl.datamodels.datatypes.InteractionValidator; import org.adl.datamodels.datatypes.LangStringValidator; import org.adl.datamodels.datatypes.SPMRangeValidator; import org.adl.datamodels.datatypes.VocabularyValidator; import org.adl.logging.DetailedLogMessageCollection; import org.adl.util.LogMessage; import org.adl.util.MessageType; import org.apache.commons.lang.StringUtils; /** * <br><br> * * <strong>Filename:</strong> SCORM_2004_DMElement.java<br><br> * * <strong>Description:</strong><br><br> * * <strong>Design Issues:</strong><br><br> * * <strong>Implementation Issues:</strong><br><br> * * <strong>Known Problems:</strong><br><br> * * <strong>Side Effects:</strong><br><br> * * <strong>References:</strong><br> * <ul> * <li>SCORM 2004 * </ul> * * @author ADL Technical Team */ public class SCORM_2004_DMElement extends DMElement implements Serializable { private static final long serialVersionUID = 1L; /** * Describes the data model this element is a member of */ protected SCORM_2004_DM mDM = null; /** * Default constructor required for serialization support. */ public SCORM_2004_DMElement() { // The default constructor - no explicit functionallity defined } /** * Initializes one data model element of the SCORM 2004 data model. * * @param iDescription Identies which data characteristics of the data model * element. * * @param iParent Describes the parent of this data model element. * * @param iDM Describes the data model containing this data model * element. */ public SCORM_2004_DMElement(DMElementDescriptor iDescription, DMElement iParent, SCORM_2004_DM iDM) { // Set this element's description mDescription = iDescription; // Set the parent mParent = iParent; if (iParent != null && !iParent.getChildren().containsValue(this)) { iParent.putChild(iDescription.mBinding, this); } // Set the data model mDM = iDM; // Check if this element is managing records -- is it an array container? if (mDescription.mSPM != -1 && mDescription.mChildren != null && mDescription.mChildren.size() > 0) { // Initialize the set of records mRecords = new Vector<DMElement>(); } // Check if this element has children if (mDescription.mChildren != null && mDescription.mChildren.size() > 0) { // Initialize the set of children bindings mChildrenBindings = new Vector<String>(); for (int i = 0; i < mDescription.mChildren.size(); i++) { DMElementDescriptor desc = mDescription.mChildren.get(i); mChildrenBindings.add(desc.mBinding); // If this is not an array container if (mRecords == null) { if (mChildren == null) { mChildren = new Hashtable<String, DMElement>(); } // Initialize the leaf child element SCORM_2004_DMElement element = new SCORM_2004_DMElement(desc, this, mDM); mChildren.put(desc.mBinding, element); } } } else { // This must be a leaf element, so initialize if (mDescription.mDelimiters != null && mDescription.mDelimiters.size() > 0) { // Initialize this element's set of delimiters mDelimiters = new Vector<DMDelimiter>(); for (int i = 0; i < mDescription.mDelimiters.size(); i++) { DMDelimiterDescriptor desc = mDescription.mDelimiters.get(i); // Create the child element DMDelimiter delimit = new DMDelimiter(desc); mDelimiters.add(delimit); } } if (mDescription.mInitial != null) { mValue = mDescription.mInitial; mInitialized = true; } else { // The value is considered uninitialized until the SCO sets it mInitialized = false; mValue = ""; } } } /** * Compares the provided value to the value stored in this data model * element. * * @param iValue A token (<code>RequestToken</code>) object that provides the * value to be compared against the exiting value; this request * may include a set of delimiters. * @param iValidate Describes if the value being compared should be * validated first. * * @return An abstract data model error code indicating the result of this * operation. */ @Override public int equals(RequestToken iValue, boolean iValidate) { // Assume there is nothing to compare int result = DMErrorCodes.COMPARE_NOTHING; // Make sure there is something to compare if (mValue != null && iValue != null) { // Assume these values are equal boolean equal = true; if (iValidate) { // Make sure the value is valid against this element's type result = validate(iValue); } if (result == DMErrorCodes.TYPE_MISMATCH) { // An invalid value cannot be equal to any valid (set) data model // element. equal = false; } if (equal) { int i = 0; // If delimiters are defined on this data model element, make sure // any provided delimiters are equal, or the existing delimiter // value is the default. if (mDelimiters != null && mDelimiters.size() > 0) { Vector<Boolean> checked = new Vector<Boolean>(); // Set all delimiters to 'not checked' for (int j = 0; j < mDelimiters.size(); j++) { checked.add(false); } boolean found = true; // Compare all delimiters provided with this value for (; i < iValue.getDelimiterCount() && equal; i++) { RequestDelimiter del = iValue.getDelimiterAt(i); // Assume this delimiter is not defined for the element found = false; // Check if this element includes the specified delimiter for (int j = 0; j < mDelimiters.size() && equal; j++) { DMDelimiter toCheck = mDelimiters.get(j); if (toCheck.mDescription.mName.equals(del.getName())) { // Make sure we haven't already checked this delimter boolean alreadyChecked = checked.get(j).booleanValue(); if (!alreadyChecked) { // Remember we've checked this delimiter found = true; checked.set(j, true); // Check if the value is the delimiter's default if (toCheck.mValue == null) { if (toCheck.mDescription.mDefault != null) { // Compare the provided value with the default if (toCheck.mDescription.mValidator == null) { equal = equal && toCheck.mDescription.mDefault.equals(del.getValue()); } else { equal = equal && toCheck.mDescription.mValidator.compare( toCheck.mDescription.mDefault, del.getValue(), mDelimiters); } } else { equal = false; } } else { // Compare the two delimiter values if (toCheck.mDescription.mValidator == null) { equal = equal && toCheck.mValue.equals(del.getValue()); } else { equal = equal && toCheck.mDescription.mValidator.compare(toCheck.mValue, del.getValue(), mDelimiters); // If the first compare doesn't work // test the SPM if (!equal) { equal = toCheck.mDescription.mValidator.compare( toCheck.mDescription.mValidator.trunc(toCheck.mValue), del.getValue(), mDelimiters); } } } } break; } } if (!found) { break; } } if (equal) { // Make sure any delimters not included in the request are // equal to their defaults for (int j = 0; j < mDelimiters.size() && equal; j++) { boolean check = !checked.get(j).booleanValue(); if (check) { DMDelimiter toCheck = mDelimiters.get(j); if (toCheck.mValue != null) { // Compare the current value to the default if (toCheck.mDescription.mValidator != null) { equal = equal && toCheck.mDescription.mValidator.compare( toCheck.mDescription.mDefault, toCheck.mValue, mDelimiters); } else { equal = false; } } } } } } // Only compare the data model values if its delimiters where equal if (equal) { StringBuilder compareWith = new StringBuilder(); // Add all 'undefined' delimiters to the validation string for (; i < iValue.getDelimiterCount(); i++) { RequestDelimiter del = iValue.getDelimiterAt(i); compareWith.append(del.showDotNotation()); } compareWith.append(iValue.getValue()); // If no comparison method is defined, just do a string compare if (mDescription.mValidator == null) { equal = StringUtils.equals(compareWith.toString(), mValue); } else { equal = mDescription.mValidator.compare(compareWith.toString(), mValue, mDelimiters); // If the first compare doesn't work test the SPM if (!equal) { equal = mDescription.mValidator.compare(compareWith.toString(), mDescription.mValidator.trunc(mValue), mDelimiters); } } } } // Set the correct result value if (equal) { result = DMErrorCodes.COMPARE_EQUAL; } else { result = DMErrorCodes.COMPARE_NOTEQUAL; } } return result; } /** * Provides the internal value stored for the data model element. * * @return The element's internal value */ /* package */ String getInternalValue() { return mValue; } /** * Attempt to get the value of this data model element, which may include * default delimiters. * * @param iArguments Describes the arguments for this getValue() call. * * @param iAdmin Describes if this request is an administrative action. * * @param iDelimiters Indicates if the data model element's default * delimiters should be included in the return string. * * @param oInfo Provides the value of this data model element. * <b>Note: The caller of this function must provide an * initialized (new) <code>DMProcessingInfo</code> to * hold the return value.</b> * * @return An abstract data model error code indicating the result of this * operation. */ @Override public int getValue(RequestToken iArguments, boolean iAdmin, boolean iDelimiters, DMProcessingInfo oInfo) { // Assume no processing errors int result = DMErrorCodes.NO_ERROR; // The SCORM 2004 run-time data model does not have any data model // elements requiring arguments. Any attempt to use delimiters with // a GetValue() request would be an invalid SCORM 2004 DM request. if (iArguments != null) { result = DMErrorCodes.INVALID_REQUEST; } else { // Parent data model elments do not directly store data if (mDescription.mSPM == -1) { // Is the element write only? if (mDescription.mIsReadable || iAdmin) { // Is the element initialized? if (mInitialized) { // Initialize the return string oInfo.mValue = ""; // Add delimiters as required if (mDelimiters != null && mDelimiters.size() > 0) { for (int i = 0; i < mDelimiters.size(); i++) { DMDelimiter del = mDelimiters.get(i); oInfo.mValue += del.getDotNotation(iDelimiters); } } // Add the element's value oInfo.mValue += mValue; } else { result = DMErrorCodes.NOT_INITIALIZED; } } else { result = DMErrorCodes.WRITE_ONLY; } } else { // This should not occur result = DMErrorCodes.UNDEFINED_ELEMENT; } } return result; } /** * Handles setting an interaction's correct_responses element datatype and * SPM based on the interaction type element. * * @param ioChild The <code>DMElementDescriptor</code> for the interaction * element whose type is being changed. * @param validatorFactory TODO */ private void handleCorrectResponses(DMElementDescriptor ioChild, IValidatorFactory validatorFactory) { // We know what data model element we will be validating, so inform the // validator. String e = "cmi.interactions.n.correct_responses.n.pattern"; DMDelimiterDescriptor del = null; // Find 'pattern' child to make the correct descriptor changes DMElementDescriptor curPattern = ioChild.mChildren.get(0); DMElementDescriptor pattern = (DMElementDescriptor) curPattern.clone(); // Clear attributes that may have been cloned from another type pattern.mIsUnique = false; pattern.mDelimiters = null; pattern.mValidator = null; if (pattern.mBinding.equals("pattern")) { if (mValue.equals("true-false")) { ioChild.mSPM = 1; ioChild.mMaximum = true; String[] boolVocab = { "true", "false" }; pattern.mValidator = new VocabularyValidator(boolVocab); } else if (mValue.equals("choice")) { ioChild.mSPM = 10; ioChild.mMaximum = false; pattern.mIsUnique = true; pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.MULTIPLE_CHOICE, e); } else if (mValue.equals("fill-in")) { // Add the case_matters delimiter String[] boolVocab = { "true", "false" }; del = new DMDelimiterDescriptor("case_matters", "false", new VocabularyValidator(boolVocab)); pattern.mDelimiters = new Vector<DMDelimiterDescriptor>(); pattern.mDelimiters.add(del); // Add the order_matters delimiter del = new DMDelimiterDescriptor("order_matters", "true", new VocabularyValidator(boolVocab)); pattern.mDelimiters.add(del); pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.FILL_IN, e); // Set the spm ioChild.mSPM = 5; ioChild.mMaximum = false; } else if (mValue.equals("long-fill-in")) { // Add the case_matters delimiter String[] boolVocab = { "true", "false" }; del = new DMDelimiterDescriptor("case_matters", "false", new VocabularyValidator(boolVocab)); pattern.mDelimiters = new Vector<DMDelimiterDescriptor>(); pattern.mDelimiters.add(del); // Add the lang delimiter del = new DMDelimiterDescriptor("lang", "en", new LangStringValidator()); pattern.mDelimiters.add(del); pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.LONG_FILL_IN, e); // Set the patterns spm ioChild.mSPM = 5; ioChild.mMaximum = false; } else if (mValue.equals("likert")) { ioChild.mSPM = 1; ioChild.mMaximum = true; pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.LIKERT, e); } else if (mValue.equals("matching")) { ioChild.mSPM = 5; ioChild.mMaximum = false; pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.MATCHING, false, e); } else if (mValue.equals("performance")) { // Add the order_matters delimiter String[] boolVocab = { "true", "false" }; del = new DMDelimiterDescriptor("order_matters", "true", new VocabularyValidator(boolVocab)); pattern.mDelimiters = new Vector<DMDelimiterDescriptor>(); pattern.mDelimiters.add(del); pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.PERFORMANCE, false, e); ioChild.mSPM = 5; ioChild.mMaximum = false; } else if (mValue.equals("sequencing")) { ioChild.mSPM = 5; ioChild.mMaximum = false; pattern.mIsUnique = true; pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.SEQUENCING, e); } else if (mValue.equals("numeric")) { ioChild.mSPM = 1; ioChild.mMaximum = true; pattern.mValueSPM = -2; pattern.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.NUMERIC, e); } else if (mValue.equals("other")) { ioChild.mSPM = 1; ioChild.mMaximum = true; pattern.mValueSPM = SCORM_2004_DM.LONG_SPM; pattern.mValidator = new SPMRangeValidator(SCORM_2004_DM.LONG_SPM); } } else { // Really bad error condition } // Replace existing descriptor ioChild.mChildren.remove(0); ioChild.mChildren.add(0, pattern); } /** * Handles setting an interaction's learner_response element datatype and * SPM based on the interaction type element. * * @param ioChild The <code>DMElementDescriptor</code> for the interaction * element whose type is being changed. * @param validatorFactory TODO */ private void handleLearnerResponse(DMElementDescriptor ioChild, IValidatorFactory validatorFactory) { // We know what data model element we will be validating, so inform the // validator. String e = "cmi.interactions.n.learner_response"; DMDelimiterDescriptor del = null; if (mValue.equals("true-false")) { String[] boolVocab = { "true", "false" }; ioChild.mValidator = new VocabularyValidator(boolVocab); } else if (mValue.equals("choice")) { ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.MULTIPLE_CHOICE, e); } else if (mValue.equals("fill-in")) { ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.FILL_IN, e); } else if (mValue.equals("long-fill-in")) { // Add the lang delimiter del = new DMDelimiterDescriptor("lang", "en", SCORM_2004_DM.SHORT_SPM, new LangStringValidator()); ioChild.mDelimiters = new Vector<DMDelimiterDescriptor>(); ioChild.mDelimiters.add(del); ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.LONG_FILL_IN, e); } else if (mValue.equals("likert")) { ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.LIKERT, e); } else if (mValue.equals("matching")) { ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.MATCHING, e); } else if (mValue.equals("performance")) { ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.PERFORMANCE, false, e); } else if (mValue.equals("sequencing")) { ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.SEQUENCING, e); } else if (mValue.equals("numeric")) { ioChild.mValueSPM = -2; ioChild.mValidator = validatorFactory.newInteractionValidator(InteractionValidator.NUMERIC, e); } else if (mValue.equals("other")) { ioChild.mValueSPM = SCORM_2004_DM.LONG_SPM; ioChild.mValidator = new SPMRangeValidator(SCORM_2004_DM.LONG_SPM); } } /** * Processes a data model request on this data model element. This method * will enforce data model element depedencies and keyword application. * * @param ioRequest Provides the dot-notation request being applied to this * data model element. The <code>DMRequest</code> will be * updated to account for processing against this data * model element. * * @param oInfo Provides the value of this data model element. * <b>Note: The caller of this function must provide an * initialized (new) <code>DMProcessingInfo</code> to * hold the return value.</b> * * @return An abstract data model error code indicating the result of this * operation. */ @Override public int processRequest(DMRequest ioRequest, DMProcessingInfo oInfo) { // Assume no processing errors int result = DMErrorCodes.NO_ERROR; // Make sure there are more tokens to process if (ioRequest.hasMoreTokens()) { // Get the next token RequestToken tok = ioRequest.getNextToken(); // Check if this token is an index or an element if (tok.getType() == RequestToken.TOKEN_ELEMENT) { // Check for keyword data model elements if (tok.getValue().equals("_children")) { if (mDescription.mChildren != null && mDescription.mChildren.size() > 0) { // Make sure we are allowed to process this request if (mDescription.mShowChildren == true) { oInfo.mElement = new Children(mChildrenBindings); DMElementDescriptor desc = new DMElementDescriptor("_children", null, null); oInfo.mElement.setDescription(desc); } else { result = DMErrorCodes.DOES_NOT_HAVE_CHILDREN; } } else { result = DMErrorCodes.DOES_NOT_HAVE_CHILDREN; } } else if (tok.getValue().equals("_count")) { if (mRecords != null) { oInfo.mElement = new Count(mRecords.size()); DMElementDescriptor desc = new DMElementDescriptor("_count", null, null); oInfo.mElement.setDescription(desc); } else { result = DMErrorCodes.DOES_NOT_HAVE_COUNT; } } else if (tok.getValue().equals("_version")) { // This should have been handled by the DataModel result = DMErrorCodes.DOES_NOT_HAVE_VERSION; } else { // Make sure we are not expecting an index if (mRecords != null && mRecords.size() > 0) { result = DMErrorCodes.UNDEFINED_ELEMENT; } else { // Make sure this element has children if (mChildrenBindings != null && mChildrenBindings.size() > 0) { // Look for this element in the children set int idx = mChildrenBindings.indexOf(tok.getValue()); if (idx != -1) { DMElement element = mChildren.get(tok.getValue()); DMElementDescriptor desc = element.getDescription(); // Is this a SetValue or a GetValue request if (ioRequest.isGetValueRequest()) { oInfo.mElement = element; } else // SetValue() { boolean ok = true; // Enforce data model dependencies, if they exist if (desc.mDependentOn != null && desc.mDependentOn.size() > 0) { for (int i = 0; i < desc.mDependentOn.size(); i++) { String check = desc.mDependentOn.get(i); // Ensure the dependent element is initialized DMElement e = mChildren.get(check); if (e != null) { if (!e.isInitialized()) { // Dependend element is not initalized result = DMErrorCodes.DEP_NOT_ESTABLISHED; ok = false; break; } } else { // The specified dependent element does not // exist -- unknown state result = DMErrorCodes.UNKNOWN_EXCEPTION; ok = false; break; } } } // Should we attempt to set this element? if (ok) { oInfo.mElement = element; } } } else { result = DMErrorCodes.UNDEFINED_ELEMENT; } } else { // This element has no children result = DMErrorCodes.UNDEFINED_ELEMENT; } } } } else if (tok.getType() == RequestToken.TOKEN_INDEX) { // Process the index token int idx = Integer.parseInt(tok.getValue()); // Is this a SetValue or a GetValue request if (ioRequest.isGetValueRequest()) { // Check if the request record has already been created if (idx < mRecords.size()) { // Provide the requested record oInfo.mElement = mRecords.get(idx); } else { // index out of range error result = DMErrorCodes.OUT_OF_RANGE; } } else // SetValue() { // Check if this record is in a valid range if (idx >= mRecords.size()) { // Is this a new record request if (idx == mRecords.size() && !(mDescription.mMaximum && mRecords.size() == mDescription.mSPM)) { boolean ok = true; // Enforce data model dependencies // We can assume that we only need to look at the next // token because the dependency will be defined on that // token's element RequestToken lookAt = ioRequest.getCurToken(); boolean found = false; boolean isWriteable = true; if (lookAt != null) { DMElementDescriptor desc = null; // Look for this element in the children of the record // NOTE: This implementation assumes there are no // nested arrays -- SCORM 2004 does not require them for (int i = 0; i < mDescription.mChildren.size(); i++) { desc = mDescription.mChildren.get(i); if (desc.mBinding.equals(lookAt.getValue())) { // Found it, so we're done found = true; isWriteable = desc.mIsWriteable; break; } } if (!found) { // The request child element does not exist, so // there is no reason to create the new record result = DMErrorCodes.UNDEFINED_ELEMENT; ok = false; } else { if (desc.mDependentOn != null && desc.mDependentOn.size() > 0) { // Dependend element is not initalized result = DMErrorCodes.DEP_NOT_ESTABLISHED; ok = false; } } } else { // No next token, so there is no reason to create the // new record result = DMErrorCodes.UNDEFINED_ELEMENT; ok = false; } if (ok) { // Only create the new record if the array is not // read-only and is not an admin request if (isWriteable || ioRequest.isAdminRequest()) { // Create the new record DMElementDescriptor desc = (DMElementDescriptor) mDescription.clone(); desc.mOldSPM = desc.mSPM; desc.mSPM = -1; SCORM_2004_DMElement element = new SCORM_2004_DMElement(desc, this, mDM); mRecords.add(element); // Provide the requested record oInfo.mElement = element; oInfo.mRecords = mRecords; // Check if the new size exceeds the SPM if (mRecords.size() > mDescription.mSPM) { // String dn = getDotNotation(mDM); String warn = "Collection SPM exceeded for element."; // Add the SPM Exceeded warning // to the message log DetailedLogMessageCollection.getInstance() .addMessage(new LogMessage(MessageType.WARNING, warn)); } } else { // Don't create this record when trying to set a // read-only child result = DMErrorCodes.READ_ONLY; } } } else { if (mRecords.size() == mDescription.mSPM && mDescription.mMaximum) { // Exceeds absolute maximum -- nothing was created result = DMErrorCodes.MAX_EXCEEDED; } else { // index out of of order result = DMErrorCodes.SET_OUT_OF_ORDER; } } } else { // Provide the record requested oInfo.mElement = mRecords.get(idx); } } } else { // Wrong type of token, this shouldn't happen result = DMErrorCodes.UNKNOWN_EXCEPTION; } } else { // No more tokens, this shouldn't happen result = DMErrorCodes.UNKNOWN_EXCEPTION; } return result; } /** * Attempt to set the value of this data model element to the value * indicated by the dot-notation token. * * @param iValue A token (<code>RequestToken</code>) object that provides * the value to be set and may include a set of delimiters. * @param iAdmin Indicates if this operation is administrative or not. If * the operation is administrative, read/write and data type * characteristics of the data model element should be * ignored. * * @return An abstract data model error code indicating the result of this * operation. */ @Override public int setValue(RequestToken iValue, boolean iAdmin, IValidatorFactory validatorFactory) { String oldValue = null; // Assume no processing errors int result = DMErrorCodes.NO_ERROR; // We are not allowed to write to a collection if (mDescription.mSPM != -1) { result = DMErrorCodes.UNDEFINED_ELEMENT; } else { // If this is not an administrative action, validate the data type if (!iAdmin) { // Make sure we are allowed to set this element if (mDescription.mIsWriteable) { result = validate(iValue); } else { result = DMErrorCodes.READ_ONLY; } } } // If no validation errors, set the indicated value if (result == DMErrorCodes.NO_ERROR) { // Remember the old value oldValue = mValue; // Clear the current value mValue = ""; // Remember where the last 'good' delimiter was located int i = 0; if (mDelimiters != null && mDelimiters.size() > 0) { boolean found = true; Vector<Boolean> set = new Vector<Boolean>(); // Set all delimiters to their defaults for (int j = 0; j < mDelimiters.size(); j++) { DMDelimiter del = mDelimiters.get(j); del.mValue = null; // Remember that we have not set this delimiter yet set.add(false); } // Attempt to set all delimiters provided with this value for (; i < iValue.getDelimiterCount() && found; i++) { RequestDelimiter del = iValue.getDelimiterAt(i); // Assume this delimiter is not defined for the element found = false; // Check if this element includes the specified delimiter for (int j = 0; j < mDelimiters.size() && !found; j++) { DMDelimiter toSet = mDelimiters.get(j); if (toSet.mDescription.mName.equals(del.getName())) { // Make sure we haven't set this delimiter yet boolean setAlready = set.get(j).booleanValue(); if (!setAlready) { found = true; set.add(j, false); // Check if the value is the delimiter's default if (toSet.mDescription.mDefault != null) { if (!toSet.mDescription.mDefault.equals(del.getValue())) { String val = del.getValue(); // Check to see if we need to trucate the value if (mTruncSPM && toSet.mDescription.mValueSPM != -1) { val = val.substring(0, toSet.mDescription.mValueSPM - 1); } // Set the delimiter's value toSet.mValue = val; } else { // Reset the default default toSet.mValue = null; } } else { // Set the delimiter's value toSet.mValue = del.getValue(); } } break; } } } // If we didn't find the delimiter, move back one if (!found) { i--; } } // Add all 'undefined' delimiters to the validation string for (; i < iValue.getDelimiterCount(); i++) { RequestDelimiter del = iValue.getDelimiterAt(i); mValue += del.showDotNotation(); } // Add the token value to the validation string mValue += iValue.getValue(); // Check to see if we need to trucate the value if (mTruncSPM && (mDescription.mValueSPM != -1)) { if (mDescription.mValueSPM == -2) { mValue = InteractionTrunc.trunc(mValue, ((InteractionValidator) mDescription.mValidator).getType()); } else { if (mValue.length() > mDescription.mValueSPM) { mValue = mValue.substring(0, mDescription.mValueSPM); } } } // This data model element is now initialized mInitialized = true; // Setting descriptors based on interaction type // This is a special case. The interaction type element affects // the SPM and datatype of the correct_responses and learner_response if (mDescription.mBinding.equals("type")) { // Make sure the type acctually changed if (!mInitialized || !mValue.equals(oldValue)) { // Get the cmi.interatctions.x container descriptor DMElementDescriptor desc = mParent.getDescription(); // Look for correct_responses for (int j = 0; j < desc.mChildren.size(); j++) { DMElementDescriptor curChild = desc.mChildren.get(j); DMElementDescriptor child = null; // Test to see if we need to clone the child descriptor // If so, use a clone for the following if-switch // and set the ith index of the mChildren array when finished // newDesc.mChildren.replaceAt(j, clone); if (curChild.mBinding.equals("correct_responses")) { child = (DMElementDescriptor) curChild.clone(); // Switch the SPM and data type of the correct_responses handleCorrectResponses(child, validatorFactory); // Create an element of the appropriate type & replace SCORM_2004_DMElement element = new SCORM_2004_DMElement(child, mParent, mDM); mParent.putChild(child.mBinding, element); } else if (curChild.mBinding.equals("learner_response")) { child = (DMElementDescriptor) curChild.clone(); // Switch the SPM and data type of the learner_response handleLearnerResponse(child, validatorFactory); // Create an element of the appropriate type & replace SCORM_2004_DMElement element = new SCORM_2004_DMElement(child, mParent, mDM); mParent.putChild(child.mBinding, element); } } } } } return result; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append(getDotNotation(mDM)); b.append("="); b.append(mValue); return b.toString(); } /** * Validates a dot-notation token against this data model's defined data * type. * * @param iValue A token (<code>RequestToken</code>) object that provides * the value to be checked, possibily including a set of * delimiters. * * @return An abstract data model error code indicating the result of this * operation. */ @Override public int validate(RequestToken iValue) { // Assume no processing errors int result = DMErrorCodes.NO_ERROR; // Remember where the last 'good' delimiter was located int i = 0; if (mDelimiters != null && mDelimiters.size() > 0) { Vector<Boolean> checked = new Vector<Boolean>(); // Set all delimiters to 'not checked' for (int j = 0; j < mDelimiters.size(); j++) { checked.add(false); } boolean found = true; // Validate all delimiters provided with this value for (; i < iValue.getDelimiterCount() && found; i++) { RequestDelimiter del = iValue.getDelimiterAt(i); // Assume this delimiter is not defined for the element found = false; // Check for invalid whitespace around a 'known' delimiter String trim = del.getName(); /* .trim(); */ boolean ws = trim.length() != del.getName().length(); // Check if this element includes the specified delimiter for (int j = 0; j < mDelimiters.size() && !found; j++) { DMDelimiter toCheck = mDelimiters.get(j); if (toCheck.mDescription.mName.equals(trim)) { // Make sure we haven't already checked this delimter boolean alreadyChecked = checked.get(j).booleanValue(); if (!alreadyChecked) { // If a known delimiter has whitespace, flag the error if (ws) { result = DMErrorCodes.TYPE_MISMATCH; break; } else { // Remember we've checked this delimiter found = true; checked.set(j, true); // Validate the delimiter's value if (toCheck.mDescription.mValidator != null) { result = toCheck.mDescription.mValidator.validate(del.getValue()); // If any of the delimiters are invalid, we are done if (result != DMErrorCodes.NO_ERROR) { break; } } } } else { // We already found this delimiter break; } } } if (result != DMErrorCodes.NO_ERROR) { break; } } // If we didn't find this delimiter, move back one in those provided if (!found) { i--; } } // Validate the value if all of the delimiters are valid if (result == DMErrorCodes.NO_ERROR) { StringBuilder toValidate = new StringBuilder(); // Add all 'undefined' delimiters to the validation string for (; i < iValue.getDelimiterCount(); i++) { RequestDelimiter del = iValue.getDelimiterAt(i); toValidate.append(del.showDotNotation()); } // Add the token value to the validation string toValidate.append(iValue.getValue()); if (mDescription.mValidator != null) { result = mDescription.mValidator.validate(toValidate.toString()); if (result == DMErrorCodes.SPM_EXCEEDED) { // Type validation SPM Exceeded, create warning. String warn = mDescription.mValidator.getTypeName() + " SPM exceeded"; // Add the SPM Exceeded warning to the message log DetailedLogMessageCollection.getInstance() .addMessage(new LogMessage(MessageType.WARNING, warn)); // Clear this error result = DMErrorCodes.NO_ERROR; } } } return result; } } // end SCORM_2004_DMElement