package org.sakaiproject.lessonbuildertool.cc;
/***********
* This code is based on a reference implementation done for the IMS Consortium.
* The copyright notice for that implementation is included below.
* All modifications are covered by the following copyright notice.
*
* Copyright (c) 2011 Rutgers, the State University of New Jersey
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**********************************************************************************
* $URL: http://ims-dev.googlecode.com/svn/trunk/cc/IMS_CCParser_v1p0/src/main/java/org/imsglobal/cc/Parser.java $
* $Id: Parser.java 227 2011-01-08 18:26:55Z drchuck $
**********************************************************************************
*
* Copyright (c) 2010 IMS GLobal Learning Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
**********************************************************************************/
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;
/**
*
* This is a simple cartridge parser library for IMS common cartridges. This parser is non validating, and has been
* written against version 1.0 of the CC specification.
*
* Users of the parser need to do the following:
* 1) Create a Cartridge Loader, and supply it with a ZIP file.
* 2) Create a Parser: the createCartridgeParser(CartridgeLoader) method in this class.
* 3) Override DefaultHandler to process the events that arise during the parse process.
* 4) Call Parser.parse(DefaultHandler) with your default Handler.
*
* The parser will read the manifest file, as well as any declared xml resources (question banks, assessments, discussions,
* and weblinks). DefaultHandler will always return xml in the form of JDOM elements, as well as (in some cases), java
* objects (strings mostly). The parser will also return the details for authorization services, metadata and indicate if
* a resource is protected or not.
*
* @author Phil Nicholls
* @version 1.0
*
*/
public class Parser extends AbstractParser {
CartridgeLoader utils;
private static final Map<String, ContentParser> parsers;
private static final String IMS_MANIFEST="imsmanifest.xml";
private static final String LAR0="associatedcontent/imscc_xmlv1p0/learning-application-resource";
private static final String LAR1="associatedcontent/imscc_xmlv1p1/learning-application-resource";
private static final String LAR2="associatedcontent/imscc_xmlv1p2/learning-application-resource";
private static final String DISCUSSION0="imsdt_xmlv1p0";
private static final String DISCUSSION1="imsdt_xmlv1p1";
private static final String DISCUSSION2="imsdt_xmlv1p2";
private static final String ASSESSMENT0="imsqti_xmlv1p2/imscc_xmlv1p0/assessment";
private static final String ASSESSMENT1="imsqti_xmlv1p2/imscc_xmlv1p1/assessment";
private static final String ASSESSMENT2="imsqti_xmlv1p2/imscc_xmlv1p2/assessment";
private static final String WEBLINK0="imswl_xmlv1p0";
private static final String WEBLINK1="imswl_xmlv1p1";
private static final String WEBLINK2="imswl_xmlv1p2";
private static final String WEBCONTENT="webcontent";
private static final String QUESTION_BANK0="imsqti_xmlv1p2/imscc_xmlv1p0/question-bank";
private static final String QUESTION_BANK1="imsqti_xmlv1p2/imscc_xmlv1p1/question-bank";
private static final String QUESTION_BANK2="imsqti_xmlv1p2/imscc_xmlv1p2/question-bank";
private static final String CC_BLTI0="imsbasiclti_xmlv1p0";
private static final String CC_BLTI1="imsbasiclti_xmlv1p1";
private static final String AUTH_QUERY="/ims:manifest/auth:authorizations";
private static final String ITEM_QUERY="/ims:manifest/ims:organizations/ims:organization/ims:item";
private static final QuestionBankParser qbp;
private static final String AUTH_IMPORT="import";
private static final String AUTH_ACCESS="access";
private static final String AUTH_ACCESS_CARTRIDGE="cartridge";
private static final String AUTH_ACCESS_RESOURCE="resource";
private static final String AUTH_AUTHORIZATION="authorization";
private static final String AUTH_CCID="cartridgeId";
private static final String AUTH_WEBSERVICE="webservice";
private static final String MD="metadata";
private static final String MD_SCHEMA="schema";
private static final String MD_SCHEMA_VERSION="schemaversion";
private static final String MD_ROOT="lom";
private static final String CC_ITEM="item";
private static final String CC_ITEM_ID="identifier";
private static final String CC_ITEM_IDREF="identifierref";
private static final String CC_ITEM_TITLE="title";
private static final String CC_RESOURCE="resource";
private static final String CC_RESOURCES="resources";
private static final String CC_RES_TYPE="type";
static {
qbp=new QuestionBankParser();
parsers=new HashMap<String, ContentParser>();
parsers.put(LAR0, new LearningApplicationResourceParser());
parsers.put(LAR1, new LearningApplicationResourceParser());
parsers.put(LAR2, new LearningApplicationResourceParser());
parsers.put(DISCUSSION0, new DiscussionParser());
parsers.put(DISCUSSION1, new DiscussionParser());
parsers.put(DISCUSSION2, new DiscussionParser());
parsers.put(ASSESSMENT0, new AssessmentParser());
parsers.put(ASSESSMENT1, new AssessmentParser());
parsers.put(ASSESSMENT2, new AssessmentParser());
parsers.put(WEBLINK0, new WebLinkParser());
parsers.put(WEBLINK1, new WebLinkParser());
parsers.put(WEBLINK2, new WebLinkParser());
parsers.put(WEBCONTENT, new WebContentParser());
parsers.put(CC_BLTI0, new BLTIParser());
parsers.put(CC_BLTI1, new BLTIParser());
// there is no CC+BLTI2
}
private
Parser(CartridgeLoader the_cu) {
super();
utils=the_cu;
}
public void
parse(DefaultHandler the_handler) throws FileNotFoundException, ParseException {
try {
Element manifest=this.getXML(utils, IMS_MANIFEST);
processManifest(manifest, the_handler);
} catch (Exception e) {
the_handler.getSimplePageBean().setErrKey("simplepage.cc-error", "");
System.out.println("parse error, stack trace follows " + e);
e.printStackTrace();
// throw new ParseException(e.getMessage(),e);
}
}
private void
processManifest(Element the_manifest, DefaultHandler the_handler) throws ParseException {
ns = new Ns();
the_handler.setNs(ns);
// figure out which version we have, and set up ns to know about it
int v = 0;
for (; v < ns.getVersions(); v++) {
ns.setVersion(v);
// see if the namespace from the main manifest entry matches the candidate
if (the_manifest.getNamespace().equals(ns.cc_ns()))
break;
}
if (v >= ns.getVersions()) {
the_handler.getSimplePageBean().setErrMessage(
the_handler.getSimplePageBean().getMessageLocator().getMessage("simplepage.wrong-cc-version"));
return;
}
//System.out.println("Found version " + ns.cc_ns());
the_handler.startManifest();
the_handler.setManifestXml(the_manifest);
if (processAuthorization(the_manifest, the_handler))
return; // don't process CCs with authorization
processManifestMetadata(the_manifest, the_handler);
try {
XPath path=XPath.newInstance(ITEM_QUERY);
path.addNamespace(ns.cc_ns());
Element item = (Element)path.selectSingleNode(the_manifest);
if (item!=null) {
for (Iterator iter=item.getChildren(CC_ITEM, ns.cc_ns()).iterator();iter.hasNext();) {
Element thisitem = (Element)iter.next();
processItem((Element)thisitem, the_manifest.getChild(CC_RESOURCES, ns.cc_ns()), the_handler);
}
}
//now we need to check for the question bank and omitted dependencies
if (the_manifest.getChild(CC_RESOURCES, ns.cc_ns()) != null &&
the_manifest.getChild(CC_RESOURCES, ns.cc_ns()).getChildren(CC_RESOURCE, ns.cc_ns()) != null)
for (Iterator iter=the_manifest.getChild(CC_RESOURCES, ns.cc_ns()).getChildren(CC_RESOURCE, ns.cc_ns()).iterator(); iter.hasNext(); ) {
Element resource=(Element)iter.next();
if (resource.getAttributeValue(CC_RES_TYPE).equals(QUESTION_BANK0) ||
resource.getAttributeValue(CC_RES_TYPE).equals(QUESTION_BANK1) ||
resource.getAttributeValue(CC_RES_TYPE).equals(QUESTION_BANK2)) {
// I know it's not really an item, but it uses the same code as an assessment
the_handler.setCCItemXml(null, resource, this, utils, true);
processResource(resource, the_handler);
qbp.parseContent(the_handler, utils, resource, isProtected(resource));
} else {
// create the resource if it wasn't already on a page
the_handler.setCCItemXml(null, resource, this, utils, true);
processResource(resource, the_handler);
}
}
the_handler.endManifest();
} catch (JDOMException e) {
System.err.println(e.getMessage());
throw new ParseException(e.getMessage(),e);
}
}
private void
processManifestMetadata(Element manifest,
DefaultHandler the_handler) {
Element metadata=manifest.getChild(MD, ns.cc_ns());
if (metadata!=null) {
the_handler.startManifestMetadata(metadata.getChildText(MD_SCHEMA, ns.cc_ns()),
metadata.getChildText(MD_SCHEMA_VERSION, ns.cc_ns()));
the_handler.checkCurriculum(metadata);
Element lom=metadata.getChild(MD_ROOT, ns.lomimscc_ns());
if (lom!=null) {
the_handler.setManifestMetadataXml(lom);
the_handler.endManifestMetadata();
} else
the_handler.setManifestMetadataXml(null);
} else
the_handler.setManifestMetadataXml(null);
}
private boolean
processAuthorization(Element the_manifest,
DefaultHandler the_handler) throws ParseException {
try {
XPath path=XPath.newInstance(AUTH_QUERY);
path.addNamespace(ns.cc_ns());
path.addNamespace(ns.auth_ns());
Element result=(Element)path.selectSingleNode(the_manifest);
if (result!=null) {
the_handler.getSimplePageBean().setErrMessage(
the_handler.getSimplePageBean().getMessageLocator().getMessage("simplepage.cc-uses-auth"));
return true;
// String import_scope=result.getAttributeValue(AUTH_IMPORT);
// String access_scope=result.getAttributeValue(AUTH_ACCESS);
// if (access_scope.equals(AUTH_ACCESS_CARTRIDGE)) {
// the_handler.startAuthorization(true, false, Boolean.parseBoolean(import_scope));
// } else {
// if (access_scope.equals(AUTH_ACCESS_RESOURCE)) {
// the_handler.startAuthorization(false, true, Boolean.parseBoolean(import_scope));
// }
// }
// Element authorizationElement = result.getChild(AUTH_AUTHORIZATION, AUTH_NS);
// the_handler.setAuthorizationServiceXml(authorizationElement);
// the_handler.setAuthorizationService(authorizationElement.getChildText(AUTH_CCID, AUTH_NS),
// authorizationElement.getChildText(AUTH_WEBSERVICE, AUTH_NS));
// the_handler.endAuthorization();
// }
}
} catch (Exception e) {
throw new ParseException(e.getMessage(),e);
}
return false;
}
private void
processItem(Element the_item,
Element the_resources,
DefaultHandler the_handler) throws ParseException {
try {
if (the_item.getAttributeValue(CC_ITEM_IDREF)!=null) {
Element resource=findResource(the_handler.getNs(),the_item.getAttributeValue(CC_ITEM_IDREF), the_resources);
if (resource == null) {
the_handler.getSimplePageBean().setErrKey("simplepage.cc-noresource", the_item.getAttributeValue(CC_ITEM_IDREF));
return;
}
// System.out.println("process item " + the_item + " resources " + the_resources + " resource " + resource);
the_handler.startCCItem(the_item.getAttributeValue(CC_ITEM_ID),
the_item.getChildText(CC_ITEM_TITLE, ns.cc_ns()));
the_handler.setCCItemXml(the_item, resource, this, utils, false);
ContentParser parser=parsers.get(resource.getAttributeValue(CC_RES_TYPE));
if (parser==null) {
System.out.println("content type not recognised " + resource.getAttributeValue(CC_RES_TYPE));
return;
}
processResource(resource,
the_handler);
parser.parseContent(the_handler, utils, resource, isProtected(resource));
the_handler.endCCItem();
} else {
the_handler.startCCFolder(the_item);
for (Iterator iter=the_item.getChildren(CC_ITEM, ns.cc_ns()).iterator();iter.hasNext();) {
processItem((Element)iter.next(), the_resources, the_handler);
}
the_handler.endCCFolder();
}
} catch (Exception e) {
e.printStackTrace();
if (the_item == null)
System.out.println("processitem the item null");
else
System.out.println("processitem failed " + the_item.getAttributeValue(CC_ITEM_IDREF));
}
}
public static Parser
createCartridgeParser(CartridgeLoader the_cartridge) throws FileNotFoundException, IOException {
return new Parser(the_cartridge);
}
}