org.sakaiproject.lessonbuildertool.cc.PrintHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.lessonbuildertool.cc.PrintHandler.java

Source

/**
 * Copyright (c) 2003-2017 The Apereo Foundation
 *
 * 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://opensource.org/licenses/ecl2
 *
 * 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.
 */
package org.sakaiproject.lessonbuildertool.cc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/***********
 * 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/PrintHandler.java $
 * $Id: PrintHandler.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 org.apache.commons.io.IOUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.output.XMLOutputter;
import org.jsoup.Jsoup;
import org.jdom.filter.ElementFilter;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.util.Iterator;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.CharArrayWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.net.URLEncoder;
import org.jdom.output.DOMOutputter;

import org.sakaiproject.util.FormattedText;
import org.sakaiproject.util.Validator;
import org.sakaiproject.tool.api.ToolManager;
import org.sakaiproject.content.cover.ContentHostingService;
import org.sakaiproject.content.cover.ContentTypeImageService;
import org.sakaiproject.lessonbuildertool.SimplePage;
import org.sakaiproject.lessonbuildertool.SimplePageItem;
import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean;
import org.sakaiproject.lessonbuildertool.service.GroupPermissionsService;
import org.sakaiproject.lessonbuildertool.model.SimplePageToolDao;
import org.sakaiproject.content.api.ContentCollectionEdit;
import org.sakaiproject.content.api.ContentCollection;
import org.sakaiproject.content.api.ContentResourceEdit;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.ResourcePropertiesEdit;
import org.sakaiproject.exception.IdUsedException;
import org.sakaiproject.event.api.NotificationService;
import org.sakaiproject.lessonbuildertool.cc.QtiImport;
import org.sakaiproject.lessonbuildertool.service.LessonEntity;
import org.sakaiproject.lessonbuildertool.service.QuizEntity;
import org.sakaiproject.lessonbuildertool.service.ForumInterface;
import org.sakaiproject.lessonbuildertool.service.BltiInterface;
import org.sakaiproject.lessonbuildertool.service.AssignmentInterface;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.cover.ServerConfigurationService;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.sakaiproject.tool.assessment.services.qti.QTIService;
import org.sakaiproject.tool.assessment.qti.constants.QTIVersion;

/* PJN NOTE:
 * This class is an example of what an implementer might want to do as regards overloading DefaultHandler.
 * In this case, messages are written to the screen. If a method in default handler is not overridden, then it does
 * nothing.
 */

public class PrintHandler extends DefaultHandler
        implements AssessmentHandler, DiscussionHandler, AuthorizationHandler, MetadataHandler,
        LearningApplicationResourceHandler, QuestionBankHandler, WebContentHandler, WebLinkHandler {
    private static final Logger log = LoggerFactory.getLogger(PrintHandler.class);

    private static final String HREF = "href";
    private static final String TYPE = "type";
    private static final String FILE = "file";
    private static final String XML = ".xml";
    private static final String URL = "url";
    private static final String TITLE = "title";
    private static final String ID = "id";
    private static final String TEXT = "text";
    private static final String TEXTTYPE = "texttype";
    private static final String TEXTHTML = "text/html";
    private static final String DESCRIPTION = "description";
    private static final String GENERAL = "general";
    private static final String STRING = "string";
    private static final String LANGSTRING = "langstring";
    private static final String ATTACHMENT = "attachment";
    private static final String ATTACHMENTS = "attachments";
    private static final String INTENDEDUSE = "intendeduse";
    private static final String VARIANT = "variant";
    private static final String IDENTIFIERREF = "identifierref";
    private static final String IDENTIFIER = "identifier";
    private static final String RESOURCES = "resources";
    private static final String RESOURCE = "resource";

    private static final String CC_ITEM_TITLE = "title";
    private static final String CC_ITEM_METADATA = "metadata";
    private static final String LOM_LOM = "lom";
    private static final String LOM_GENERAL = "general";
    private static final String LOM_STRUCTURE = "structure";
    private static final String LOM_SOURCE = "source";
    private static final String LOM_VALUE = "value";
    private static final String CC_WEBCONTENT = "webcontent";
    private static final String LAR = "learning-application-resource";
    private static final String WEBLINK = "webLink";
    private static final String TOPIC = "topic";
    private static final String QUESTIONS = "questestinterop";
    private static final String ASSESSMENT = "assessment";
    private static final String ASSIGNMENT = "assignment";
    private static final String QUESTION_BANK = "question-bank";
    private static final String CART_LTI_LINK = "cartridge_basiclti_link";
    private static final String BLTI = "basiclti";
    private static final String UNKNOWN = "unknown";
    private static final int MAX_ATTEMPTS = 100;

    private List<SimplePage> pages = new ArrayList<SimplePage>();
    // list parallel to pages containing sequence of last item on the page
    private List<Integer> sequences = new ArrayList<Integer>();
    CartridgeLoader utils = null;
    SimplePageToolDao simplePageToolDao = null;

    private String title = null;
    private String description = null;
    private String baseName = null;
    private String baseUrl = null;
    private String siteId = null;
    private LessonEntity quiztool = null;
    private LessonEntity topictool = null;
    private LessonEntity bltitool = null;
    private LessonEntity assigntool = null;
    private Set<String> roles = null;
    boolean usesRole = false;
    boolean usesPatternMatch = false;
    boolean usesCurriculum = false;
    boolean importtop = false;
    Integer assignmentNumber = 1;
    Element manifestXml = null;

    // this is the CC file name for all files added
    private Set<String> filesAdded = new HashSet<String>();
    // This keeps track of what files are added to what (possibly truncated) name, this is pre-populated
    private Map<String, String> fileNames = new HashMap<String, String>();
    // this is the CC file name (of the XML file) -> Sakaiid for non-file items
    private Map<String, String> itemsAdded = new HashMap<String, String>();
    private Map<String, String> assignsAdded = new HashMap<String, String>();
    private Set<String> badTypes = new HashSet<String>();
    static private Map<String, String> badTypeNames = null;

    private Map<String, String> getBadTypeNames() {
        Map<String, String> badNames = new HashMap<String, String>();

        badNames.put("imsapip_zipv1p0", simplePageBean.getMessageLocator().getMessage("simplepage.cc_apip"));
        badNames.put("imsiwb_iwbv1p0", simplePageBean.getMessageLocator().getMessage("simplepage.cc_iwb"));
        badNames.put("idpfepub_epubv3p0", simplePageBean.getMessageLocator().getMessage("simplepage.cc_epub3"));
        badNames.put("assignment_xmlv1p0",
                simplePageBean.getMessageLocator().getMessage("simplepage.cc_ext_assignment"));

        return badNames;
    }

    public PrintHandler(SimplePageBean bean, CartridgeLoader utils, SimplePageToolDao dao, LessonEntity q,
            LessonEntity l, LessonEntity b, LessonEntity a, boolean itop) {
        super();
        this.utils = utils;
        this.simplePageBean = bean;
        this.simplePageToolDao = dao;
        this.siteId = bean.getCurrentSiteId();
        this.quiztool = q;
        this.topictool = l;
        this.bltitool = b;
        this.assigntool = a;
        this.importtop = itop;
    }

    public void setAssessmentDetails(String the_ident, String the_title) {
        if (log.isDebugEnabled())
            log.debug("assessment ident: " + the_ident + " title: " + the_title);
    }

    public void endCCFolder() {
        if (log.isDebugEnabled())
            log.debug("cc folder ends");
        int top = pages.size() - 1;
        sequences.remove(top);
        pages.remove(top);
    }

    public void endCCItem() {
        if (log.isDebugEnabled())
            log.debug("cc item ends");
    }

    public void startCCFolder(Element folder) {
        String title = this.title;
        if (folder != null)
            title = folder.getChildText(TITLE, ns.getNs());

        // add top level pages to left margin
        SimplePage page = null;
        if (pages.size() == 0) {
            page = simplePageBean.addPage(title, false); // add new top level page
            if (description != null && !description.trim().equals("")) {
                SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), 1, SimplePageItem.TEXT, "", "");
                item.setHtml(Validator.escapeHtml(description));
                simplePageBean.saveItem(item);
                sequences.add(2);
            } else
                sequences.add(1);
        } else {
            // we're adding a level. We're at sequence 1 in the new top level,
            // but we continue the current sequence count at the old top level, which
            // is where the new folder is added
            page = simplePageToolDao.makePage("0", siteId, title, 0L, 0L);
            simplePageBean.saveItem(page);
            // index of old top level
            int top = pages.size() - 1;
            SimplePage parent = pages.get(top);
            int seq = sequences.get(top);

            SimplePageItem item = simplePageToolDao.makeItem(parent.getPageId(), seq, SimplePageItem.PAGE,
                    Long.toString(page.getPageId()), title);
            simplePageBean.saveItem(item);
            // increment sequence to after this folder
            sequences.set(top, seq + 1);
            // inside the new folder we start at sequence 1
            sequences.add(1);
        }
        pages.add(page);
    }

    public void startCCItem(String the_id, String the_title) {
        if (log.isDebugEnabled()) {
            log.debug("cc item " + the_id + " begins");
            log.debug("title: " + the_title);
        }
    }

    private ContentCollection makeBaseFolder(String name) {

        if (siteId == null) {
            simplePageBean.setErrKey("simplepage.nosite", "");
            return null;
        }

        if (importtop) {
            try {
                ContentCollection top = ContentHostingService
                        .getCollection(ContentHostingService.getSiteCollection(siteId));
                return top;
            } catch (Exception e) {
                simplePageBean.setErrKey("simplepage.create.resource.failed", name + " " + e);
                return null;
            }
        }

        if (name == null)
            name = "Common Cartridge";
        if (name.trim().length() == 0)
            name = "Common Cartridge";

        // we must reject certain characters that we cannot even escape and get into Tomcat via a URL                               

        StringBuffer newname = new StringBuffer(ContentHostingService.getSiteCollection(siteId));

        int length = name.length();
        for (int i = 0; i < length; i++) {
            if (Validator.INVALID_CHARS_IN_RESOURCE_ID.indexOf(name.charAt(i)) != -1)
                newname.append("_");
            else
                newname.append(name.charAt(i));
        }

        length = newname.length();
        if (length > (ContentHostingService.MAXIMUM_RESOURCE_ID_LENGTH - 5))
            length = ContentHostingService.MAXIMUM_RESOURCE_ID_LENGTH - 5; // for trailing / and possible count
        newname.setLength(length);

        name = newname.toString() + "1";

        ContentCollectionEdit collection = null;
        int tries = 1;
        int olength = name.length();
        for (; tries <= MAX_ATTEMPTS; tries++) {
            try {
                collection = ContentHostingService.addCollection(name + "/"); // append / here because we may hack on the name

                String display = name;
                int main = name.lastIndexOf("/");
                if (main >= 0)
                    display = display.substring(main + 1);
                collection.getPropertiesEdit().addProperty(ResourceProperties.PROP_DISPLAY_NAME, display);

                ContentHostingService.commitCollection(collection);
                break; // got it
            } catch (IdUsedException e) {
                name = name.substring(0, olength) + "-" + tries;
            } catch (Exception e) {
                simplePageBean.setErrKey("simplepage.create.resource.failed", name + " " + e);
                return null;
            }
        }
        if (collection == null) {
            simplePageBean.setErrKey("simplepage.resource100", name);
            return null;
        }
        return collection;
    }

    //Get filename from a resource element
    //Return the href attribute, otherwise return the href value of a file child
    private String getFileName(Element resource) {
        //The problem here is there might be multiple <file> elements, so should trust the href on the resource first
        String href = resource.getAttributeValue(HREF);
        if (href == null) {
            Element file = resource.getChild(FILE, ns.getNs());
            //This will return null if there is no HREF on the file
            return file.getAttributeValue(HREF);
        }
        return href;
    }

    public String getGroupForRole(String role) {
        // if group already exists, this will return the existing one
        try {
            String g = GroupPermissionsService.makeGroup(siteId, role, role, null, simplePageBean);
            return g;
            //     return GroupPermissionsService.makeGroup(siteId, role);
        } catch (Exception e) {
            log.debug("Unable to create group " + role);
            return null;
        }
    }

    //Fix external references in an htmlString to point to the correct location
    public String fixupInlineReferences(String htmlString) {
        // and fix relative URLs to absolute, since this is going to be inserted inline
        // in a page that's not in resources.
        // These fixups are inserted as identified by the lessons exporter
        if (htmlString.startsWith("<!--fixups:")) {
            int fixend = htmlString.indexOf("-->");
            String fixString = htmlString.substring(11, fixend);
            htmlString = htmlString.substring(fixend + 3);
            String[] fixups = fixString.split(",");
            // iterate backwards since once we fix something, offsets
            // further in the string are bad
            for (int i = (fixups.length - 1); i >= 0; i--) {
                String fixup = fixups[i];
                // these are offsets of a URL. The URL is for a file in attachments, so we need
                // to map it to a full URL. The file should be attachments/item-xx.html in the
                // package. relFixup will have added ../ to it to get to the base.
                try {
                    int offset = Integer.parseInt(fixup);
                    htmlString = htmlString.substring(0, offset) + baseUrl + htmlString.substring(offset + 3);
                } catch (Exception e) {
                    log.info("exception " + e);
                }
            }
            //Otherwise try jsoup to do the fixups
        } else {
            /*
             Now we need to go through the string looking for other references that weren't identified by the fixups
             Using full class names here because of conflict in names
             I think the ideal here would be that this only updates resources that are in the manifest, but really any
             relative resources are going to be incorrect pulled out of a package and need an update.
             */
            org.jsoup.nodes.Document doc = Jsoup.parseBodyFragment(htmlString);
            org.jsoup.select.Elements hrefs = doc.select("[href]");
            org.jsoup.select.Elements srcs = doc.select("[src]");

            log.debug("BaseURL is: {}", baseUrl);

            // Have to look for both href and src tags
            for (org.jsoup.nodes.Element element : srcs) {
                String src = element.attr("src");
                if (src != null && !src.startsWith("http")) {
                    for (Map.Entry<String, String> entry : fileNames.entrySet()) {
                        if (entry.getKey() != null && entry.getValue() != null && entry.getValue().contains(src)) {
                            // Found key, set it and stop looking
                            log.debug(String.format("Updating tag %s: <%s> to <%s>", element.tagName(), src,
                                    baseUrl + src));
                            element.attr("src", baseUrl + entry.getValue());
                            break;
                        }
                    }
                }
            }

            for (org.jsoup.nodes.Element element : hrefs) {
                String href = element.attr("href");
                if (href != null && !href.startsWith("http")) {
                    for (Map.Entry<String, String> entry : fileNames.entrySet()) {
                        if (entry.getKey() != null && entry.getValue() != null && entry.getValue().contains(href)) {
                            // Found key, set it and stop looking
                            log.debug(String.format("Updating a: <%s> to <%s> (%s)", href, baseUrl + href,
                                    element.text()));
                            element.attr("href", baseUrl + entry.getValue());
                            break;
                        }
                    }
                }
            }
            htmlString = doc.body().html();
        }

        return htmlString;
    }

    public void setCCItemXml(Element the_xml, Element resource, AbstractParser parser, CartridgeLoader loader,
            boolean nopage) {
        if (log.isDebugEnabled()) {
            String pageTitle = "";
            if (pages.size() >= 1)
                pageTitle = pages.get(pages.size() - 1).getTitle();
            log.debug("\nadd item to page " + pageTitle + " xml: " + the_xml + " title "
                    + (the_xml == null ? "Question Pool" : the_xml.getChildText(CC_ITEM_TITLE, ns.getNs()))
                    + " type " + resource.getAttributeValue(TYPE) + " href " + resource.getAttributeValue(HREF));
        }

        String type = ns.normType(resource.getAttributeValue(TYPE));
        boolean isBank = type.equals(QUESTION_BANK);

        // first question: is this the resource we want to use, or is there are preferable variant?
        Element variant = resource.getChild(VARIANT, ns.cpx_ns());
        Set<String> seen = new HashSet<String>();
        while (variant != null) {
            String variantId = variant.getAttributeValue(IDENTIFIERREF);
            // prevent loop. If we've seen it, exit
            if (seen.contains(variantId))
                break;
            seen.add(variantId);
            variant = null; // to stop loop unless we find a valid next variant
            Element variantResource = null;
            if (variantId != null) {
                Element resourcesNode = manifestXml.getChild(RESOURCES, ns.cc_ns());
                if (resourcesNode != null) {
                    List<Element> resources = resourcesNode.getChildren(RESOURCE, ns.cc_ns());
                    if (resources != null) {
                        for (Element e : resources) {
                            if (variantId.equals(e.getAttributeValue(IDENTIFIER))) {
                                variantResource = e;
                                break;
                            }
                        }
                    }
                }
                if (variantResource == null) {
                    // should be impossible. means there was a variant pointing to a non-existent resource
                } else {
                    // we now have the variant resource. Only use it if we recognize the type         
                    String variantType = ns.normType(variantResource.getAttributeValue(TYPE));
                    // if we recognize the type, use the variant. By definition the variant is preferred, so we'll use
                    // it if we recognize it.
                    if (!UNKNOWN.equals(variantType)) {
                        log.debug("Using variant {} of type {} for resource {}", variantResource, variantType,
                                resource);
                        type = variantType;
                        resource = variantResource;
                    } else {
                        log.debug("NOT using variant {} of type {} for resource {}", variantResource, variantType,
                                resource);
                    }
                    // next step for loop. want to check next one even if the source was unusable
                    variant = variantResource.getChild(VARIANT, ns.cpx_ns());
                }
            }
        }

        boolean hide = false;
        Set<String> roles = new HashSet<String>();
        // version 1 and higher are different formats, hence a slightly weird test
        Iterator mdroles = resource.getDescendants(new ElementFilter("intendedEndUserRole", ns.lom_ns()));
        if (mdroles != null) {
            while (mdroles.hasNext()) {
                Element role = (Element) mdroles.next();
                Iterator values = role.getDescendants(new ElementFilter("value", ns.lom_ns()));
                if (values != null) {
                    while (values.hasNext()) {
                        Element value = (Element) values.next();
                        String roleName = value.getTextTrim();
                        if (!"Learner".equals(roleName)) {
                            // roles currently only implemented for visible objects. We may want to fix that.
                            if (!hide && !isBank) {
                                usesRole = true;
                            }
                        }
                        if ("Mentor".equals(roleName)) {
                            roles.add(getGroupForRole("Mentor"));
                        }
                        if ("Instructor".equals(roleName)) {
                            roles.add(getGroupForRole("Instructor"));
                        }
                    }
                }
            }
        }
        if (nopage)
            hide = true;

        // for question banks we don't need a current page, as we don't put banks on a page
        if (pages.size() == 0 && !isBank && !nopage)
            startCCFolder(null);

        int top = pages.size() - 1;
        SimplePage page = (isBank || nopage) ? null : pages.get(top);

        Integer seq = (isBank || nopage) ? 0 : sequences.get(top);
        String title = null;
        if (the_xml == null)
            title = "Question Pool";
        else
            title = the_xml.getChildText(CC_ITEM_TITLE, ns.getNs());

        // metadata is used for special Sakai data
        boolean inline = false;
        String mmDisplayType = null;
        Element metadata = null;
        if (the_xml != null)
            metadata = the_xml.getChild(CC_ITEM_METADATA, ns.cc_ns());
        if (metadata != null) {
            metadata = metadata.getChild(LOM_LOM, ns.lom_ns());
        }
        if (metadata != null) {
            metadata = metadata.getChild(LOM_GENERAL, ns.lom_ns());
        }
        if (metadata != null) {
            metadata = metadata.getChild(LOM_STRUCTURE, ns.lom_ns());
        }
        if (metadata != null) {
            List<Element> properties = metadata.getChildren();
            Iterator<Element> propertiesIt = properties.iterator();
            while (propertiesIt.hasNext()) {
                Element nameElt = propertiesIt.next();
                if (!propertiesIt.hasNext())
                    break;
                Element valueElt = propertiesIt.next();
                if (!"source".equals(nameElt.getName())) {
                    log.info("first item in structure not source " + nameElt.getName());
                    break;
                }
                if (!"value".equals(valueElt.getName())) {
                    log.info("second item in structure not source " + valueElt.getName());
                    break;
                }
                String name = nameElt.getText();
                String value = valueElt.getText();
                if (("inline.lessonbuilder.sakaiproject.org".equals(name) && "true".equals(value)))
                    inline = true;
                else if ("mmDisplayType.lessonbuilder.sakaiproject.org".equals(name))
                    mmDisplayType = value;
            }
        }

        boolean forceInline = ServerConfigurationService.getBoolean("lessonbuilder.cc.import.forceinline", false);

        try {
            if ((type.equals(CC_WEBCONTENT) || (type.equals(UNKNOWN))) && !hide) {
                // note: when this code is called the actual sakai resource hasn't been created yet
                String href = resource.getAttributeValue(HREF);
                // for unknown item types, may have a file with an HREF but no HREF in the actual resource
                // of course someone might define an extension resource without that.
                if (href == null) {
                    Element fileElement = resource.getChild(FILE, ns.cc_ns());
                    href = fileElement.getAttributeValue(HREF);
                }

                String sakaiId = baseName + href;
                String extension = Validator.getFileExtension(sakaiId);
                String mime = ContentTypeImageService.getContentType(extension);
                String intendedUse = resource.getAttributeValue(INTENDEDUSE);
                SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq, SimplePageItem.RESOURCE,
                        sakaiId, title);
                item.setHtml(mime);
                item.setSameWindow(true);

                title = the_xml.getChildText(CC_ITEM_TITLE, ns.cc_ns());

                boolean nofile = false;
                //Only text type files can be inlined
                if ((inline || forceInline) && mime != null && mime.startsWith("text/")) {
                    StringBuilder html = new StringBuilder();
                    String htmlString = null;

                    // type 3 is a link, so it's handled below
                    // get contents of file for types where we don't need a file in contents
                    if (mmDisplayType == null || "1".equals(mmDisplayType)) {
                        nofile = true;

                        // read the file containing the HTML
                        String fileName = getFileName(resource);
                        InputStream fileStream = null;

                        if (fileName != null)
                            fileStream = utils.getFile(fileName);
                        if (fileStream != null) {
                            byte[] buffer = new byte[8096];
                            int n = 0;
                            while ((n = fileStream.read(buffer, 0, 8096)) >= 0) {
                                if (n > 0)
                                    html.append(new String(buffer, 0, n, "UTF-8"));
                            }
                        }

                        htmlString = html.toString();

                        // remove stuff the exporter added
                        int off = htmlString.indexOf("<body>");
                        if (off > 0)
                            htmlString = htmlString.substring(off + 7);
                        off = htmlString.lastIndexOf("</body>");
                        if (off > 0)
                            htmlString = htmlString.substring(0, off);

                        htmlString = fixupInlineReferences(htmlString);

                    }

                    // inline can be multimedia or text. If mmdisplaytype set, it's multimedia
                    if (mmDisplayType != null) {
                        //     1 -- embed code, 2 -- av type, 3 -- oembed, 4 -- iframe
                        // 3 is output as a link, so it's handled below
                        item.setType(SimplePageItem.MULTIMEDIA);
                        if ("1".equals(mmDisplayType)) {
                            item.setAttribute("multimediaEmbedCode", htmlString);
                        }
                        item.setAttribute("multimediaDisplayType", mmDisplayType);
                    } else {
                        // must be text item
                        item.setType(SimplePageItem.TEXT);
                        item.setHtml(htmlString);
                    }
                }

                if (intendedUse != null) {
                    intendedUse = intendedUse.toLowerCase();
                    if (intendedUse.equals("lessonplan"))
                        item.setDescription(
                                simplePageBean.getMessageLocator().getMessage("simplepage.import_cc_lessonplan"));
                    else if (intendedUse.equals("syllabus"))
                        item.setDescription(
                                simplePageBean.getMessageLocator().getMessage("simplepage.import_cc_syllabus"));
                    else if (assigntool != null && intendedUse.equals("assignment")) {
                        String fileName = getFileName(resource);

                        if (itemsAdded.get(fileName) == null) {
                            // itemsAdded.put(fileName, SimplePageItem.DUMMY); // don't add the same test more than once
                            AssignmentInterface a = (AssignmentInterface) assigntool;
                            // file hasn't been written yet to contenthosting. A2 requires it to be there
                            addFile(href);
                            String assignmentId = a.importObject(title, sakaiId, mime, false); // sakaiid for assignment
                            if (assignmentId != null) {
                                item = simplePageToolDao.makeItem(page.getPageId(), seq, SimplePageItem.ASSIGNMENT,
                                        assignmentId, title);
                                sakaiId = assignmentId;
                            }
                        }
                    }
                }
                simplePageBean.saveItem(item);
                if (roles.size() > 0) { // has to be written already or we can't set groups
                    // file hasn't been written yet to contenthosting. setitemgroups requires it to be there
                    addFile(href);
                    simplePageBean.setItemGroups(item, roles.toArray(new String[0]));
                }
                sequences.set(top, seq + 1);
            } else if (type.equals(CC_WEBCONTENT) || type.equals(UNKNOWN)) { // i.e. hidden. if it's an assignment have to load it
                String intendedUse = resource.getAttributeValue(INTENDEDUSE);
                if (assigntool != null && intendedUse != null && intendedUse.equals("assignment")) {
                    String fileName = getFileName(resource);
                    if (itemsAdded.get(fileName) == null) {
                        itemsAdded.put(fileName, SimplePageItem.DUMMY); // don't add the same test more than once
                        String sakaiId = baseName + resource.getAttributeValue(HREF);
                        String extension = Validator.getFileExtension(sakaiId);
                        String mime = ContentTypeImageService.getContentType(extension);
                        AssignmentInterface a = (AssignmentInterface) assigntool;
                        // file hasn't been written yet to contenthosting. A2 requires it to be there
                        addFile(resource.getAttributeValue(HREF));
                        // in this case there's no item to take a title from
                        String atitle = simplePageBean.getMessageLocator()
                                .getMessage("simplepage.importcc-assigntitle")
                                .replace("{}", (assignmentNumber++).toString());
                        String assignmentId = a.importObject(atitle, sakaiId, mime, true); // sakaiid for assignment
                    }
                }
            } else if (type.equals(WEBLINK)) {
                Element linkXml = null;
                String filename = getFileName(resource);
                if (filename != null) {
                    linkXml = parser.getXML(loader, filename);
                } else {
                    linkXml = resource.getChild(WEBLINK, ns.link_ns());
                    filename = resource.getAttributeValue(ID) + XML;
                }
                Namespace linkNs = ns.link_ns();
                Element urlElement = linkXml.getChild(URL, linkNs);
                String url = urlElement.getAttributeValue(HREF);

                // the name must end in XML, so we can just turn it into URL
                filename = filename.substring(0, filename.length() - 3) + "url";
                String sakaiId = baseName + filename;

                if (!inline && !filesAdded.contains(filename)) {
                    // we store the URL as a text/url resource
                    ContentResourceEdit edit = ContentHostingService.addResource(sakaiId);
                    edit.setContentType("text/url");
                    edit.setResourceType("org.sakaiproject.content.types.urlResource");
                    edit.setContent(url.getBytes("UTF-8"));
                    edit.getPropertiesEdit().addProperty(ResourceProperties.PROP_DISPLAY_NAME,
                            Validator.escapeResourceName(filename));
                    ContentHostingService.commitResource(edit, NotificationService.NOTI_NONE);
                    filesAdded.add(filename);
                }

                if (inline && "3".equals(mmDisplayType)) {
                    // inline can be either oembed or youtube. Handle oembed here
                    SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq,
                            SimplePageItem.MULTIMEDIA, sakaiId, title);
                    item.setAttribute("multimediaUrl", url);
                    item.setAttribute("multimediaDisplayType", "3");
                    simplePageBean.saveItem(item);

                } else if (!hide) {
                    // now create the Sakai item
                    SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq, SimplePageItem.RESOURCE,
                            sakaiId, title);
                    if (inline) {
                        // should just be youtube. null displaytype is right for that
                        item.setType(SimplePageItem.MULTIMEDIA);
                    } else {
                        item.setHtml(simplePageBean.getTypeOfUrl(url)); // checks the web site to see what it actually is
                        item.setSameWindow(true);
                    }
                    simplePageBean.saveItem(item);
                    if (roles.size() > 0)
                        simplePageBean.setItemGroups(item, roles.toArray(new String[0]));
                    sequences.set(top, seq + 1);
                }

            } else if (type.equals(TOPIC)) {
                if (topictool != null) {
                    Element topicXml = null;
                    String filename = getFileName(resource);
                    if (filename != null) {
                        topicXml = parser.getXML(loader, filename);
                    } else {
                        topicXml = resource.getChild(TOPIC, ns.topic_ns());
                    }
                    Namespace topicNs = ns.topic_ns();
                    String topicTitle = topicXml.getChildText(TITLE, topicNs);
                    if (topicTitle == null)
                        topicTitle = simplePageBean.getMessageLocator().getMessage("simplepage.cc-defaulttopic");
                    String text = topicXml.getChildText(TEXT, topicNs);
                    boolean texthtml = false;
                    if (text != null) {
                        Element textNode = topicXml.getChild(TEXT, topicNs);
                        String textformat = textNode.getAttributeValue(TEXTTYPE);
                        if (TEXTHTML.equalsIgnoreCase(textformat))
                            texthtml = true;
                    }

                    String base = baseUrl;
                    if (filename != null) {
                        base = baseUrl + filename;
                        int slash = base.lastIndexOf("/");
                        if (slash >= 0)
                            base = base.substring(0, slash + 1); // include trailing slash
                    }

                    // collection id rather than URL
                    String baseDir = baseName;
                    if (filename != null) {
                        baseDir = baseName + filename;
                        int slash = baseDir.lastIndexOf("/");
                        if (slash >= 0)
                            baseDir = baseDir.substring(0, slash + 1); // include trailing slash
                    }

                    if (texthtml) {
                        text = text.replaceAll("\\$IMS-CC-FILEBASE\\$", base);
                    }

                    // I'm going to assume that URLs in the CC files are legal, but if
                    // I add to them I nneed to URLencode what I add

                    // filebase will be directory name for discussion.xml, since attachments are relative to that
                    String filebase = "";
                    if (filename != null) {
                        filebase = filename;
                        int slash = filebase.lastIndexOf("/");
                        if (slash >= 0)
                            filebase = filebase.substring(0, slash + 1); // include trailing slash
                    }

                    Element attachmentlist = topicXml.getChild(ATTACHMENTS, topicNs);
                    List<Element> attachments = new ArrayList<Element>();
                    if (attachmentlist != null)
                        attachments = attachmentlist.getChildren();
                    List<String> attachmentHrefs = new ArrayList<String>();
                    for (Element a : attachments) {
                        // file has to be there for the forum attachment handling to work
                        addFile(removeDotDot(filebase + a.getAttributeValue(HREF)));
                        attachmentHrefs.add(a.getAttributeValue(HREF));
                    }

                    ForumInterface f = (ForumInterface) topictool;

                    if (nopage)
                        title = simplePageBean.getMessageLocator().getMessage("simplepage.cc-defaultforum");

                    log.debug("about to call forum import base " + base);
                    // title is for the cartridge. That will be used as the forum
                    // if already added, don't do it again
                    String sakaiId = itemsAdded.get(filename);
                    if (sakaiId == null) {
                        if (f != null)
                            sakaiId = f.importObject(title, topicTitle, text, texthtml, base, baseDir, siteId,
                                    attachmentHrefs, hide);
                        if (sakaiId != null)
                            itemsAdded.put(filename, sakaiId);
                    }

                    if (!hide) {
                        log.debug("about to add formum item");
                        SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq,
                                SimplePageItem.FORUM, sakaiId, title);
                        simplePageBean.saveItem(item);
                        if (roles.size() > 0)
                            simplePageBean.setItemGroups(item, roles.toArray(new String[0]));
                        sequences.set(top, seq + 1);
                        log.debug("finished with forum item");
                    }
                }
            } else if (type.equals(ASSESSMENT) || type.equals(QUESTION_BANK)) {
                if (quiztool != null) {
                    String fileName = getFileName(resource);
                    String sakaiId = null;
                    String base = baseUrl;
                    org.w3c.dom.Document quizDoc = null;
                    InputStream instream = null;

                    // not already added
                    if (fileName == null || itemsAdded.get(fileName) == null) {

                        File qtitemp = File.createTempFile("ccqti", "txt");
                        PrintWriter outwriter = new PrintWriter(qtitemp);

                        // assessment in file
                        if (fileName != null) {

                            itemsAdded.put(fileName, SimplePageItem.DUMMY); // don't add the same test more than once

                            instream = utils.getFile(fileName);

                            // I'm going to assume that URLs in the CC files are legal, but if
                            // I add to them I nneed to URLencode what I add
                            base = baseUrl + fileName;
                            int slash = base.lastIndexOf("/");
                            if (slash >= 0)
                                base = base.substring(0, slash + 1); // include trailing slash

                            // assessment inline
                        } else {
                            Element quizXml = (Element) resource.getChild(QUESTIONS, ns.qticc_ns()).clone();
                            // we work in jdom. Qti parser needs w3c
                            quizDoc = new DOMOutputter().output(new org.jdom.Document(quizXml));
                        }

                        QtiImport imp = new QtiImport();
                        try {
                            boolean thisUsesPattern = imp.mainproc(instream, outwriter, isBank, base, siteId,
                                    simplePageBean, quizDoc);
                            if (thisUsesPattern)
                                usesPatternMatch = true;
                            if (imp.getUsesCurriculum())
                                usesCurriculum = true;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        outwriter.close();
                        InputStream inputStream = new FileInputStream(qtitemp);

                        try {
                            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
                            builderFactory.setNamespaceAware(true);
                            builderFactory.setFeature("http://xml.org/sax/features/external-general-entities",
                                    false);
                            builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities",
                                    false);
                            DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
                            org.w3c.dom.Document document = documentBuilder.parse(inputStream);

                            QuizEntity q = (QuizEntity) quiztool;

                            sakaiId = q.importObject(document, isBank, siteId, hide);
                            if (sakaiId == null)
                                sakaiId = SimplePageItem.DUMMY;

                        } catch (Exception e) {
                            log.info("CC import error creating or parsing QTI file " + fileName + " " + e);
                            simplePageBean.setErrKey("simplepage.create.object.failed", e.toString());
                        }

                        inputStream.close();
                        qtitemp.delete();

                    }

                    // question banks don't appear on the page
                    if (!isBank && !hide) {
                        SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq,
                                SimplePageItem.ASSESSMENT, (sakaiId == null ? SimplePageItem.DUMMY : sakaiId),
                                title);
                        simplePageBean.saveItem(item);
                        if (roles.size() > 0)
                            simplePageBean.setItemGroups(item, roles.toArray(new String[0]));
                        sequences.set(top, seq + 1);
                    }
                }
            } else if (type.equals(QUESTION_BANK)) {
                ; // handled elsewhere
                // current code seems to assume that BLTI tool is part of the page so skip if no page
            } else if (type.equals(BLTI)) {
                if (!nopage) {
                    String filename = getFileName(resource);
                    Element ltiXml = null;
                    if (filename != null)
                        ltiXml = parser.getXML(loader, filename);
                    else {
                        ltiXml = resource.getChild(CART_LTI_LINK, ns.lticc_ns());
                    }
                    XMLOutputter outputter = new XMLOutputter();
                    String strXml = outputter.outputString(ltiXml);
                    Namespace bltiNs = ns.blti_ns();
                    String bltiTitle = ltiXml.getChildText(TITLE, bltiNs);

                    Element customElement = ltiXml.getChild("custom", bltiNs);
                    List<Element> customs = new ArrayList<Element>();
                    if (customElement != null)
                        customs = customElement.getChildren();
                    StringBuffer sb = new StringBuffer();
                    String custom = null;
                    for (Element a : customs) {
                        String key = a.getAttributeValue("name");
                        String value = a.getText();
                        if (key == null)
                            continue;
                        key = key.trim();
                        if (value == null)
                            continue;
                        sb.append(key.trim());
                        sb.append("=");
                        sb.append(value.trim());
                        sb.append("\n");
                    }
                    if (sb.length() > 0)
                        custom = sb.toString();

                    String launchUrl = ltiXml.getChildTextTrim("secure_launch_url", bltiNs);
                    if (launchUrl == null)
                        launchUrl = ltiXml.getChildTextTrim("launch_url", bltiNs);

                    String sakaiId = null;
                    if (bltitool != null) {
                        sakaiId = ((BltiInterface) bltitool).doImportTool(launchUrl, bltiTitle, strXml, custom);
                    }

                    if (!hide) {
                        if (sakaiId != null) {
                            log.debug("Adding LTI content item " + sakaiId);
                            SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq,
                                    SimplePageItem.BLTI, sakaiId, title);
                            item.setHeight(""); // default depends upon format, so it's supplied at runtime
                            simplePageBean.saveItem(item);
                            if (roles.size() > 0)
                                simplePageBean.setItemGroups(item, roles.toArray(new String[0]));
                            sequences.set(top, seq + 1);
                        } else {
                            log.info("LTI Import Failed..");
                        }
                    }
                }
            } else if (type.equals(ASSIGNMENT)) {
                Element assignXml = null;
                String filename = getFileName(resource);
                if (filename != null) {
                    assignXml = parser.getXML(loader, filename);
                } else {
                    assignXml = resource.getChild(ASSIGNMENT, ns.assign_ns());
                }
                Namespace assignNs = ns.assign_ns();

                // filebase will be directory name for discussion.xml, since attachments are relative to that
                String filebase = "";
                if (filename != null) {
                    filebase = filename;
                    int slash = filebase.lastIndexOf("/");
                    if (slash >= 0)
                        filebase = filebase.substring(0, slash + 1); // include trailing slash
                }

                String base = baseUrl;
                if (filename != null) {
                    base = baseUrl + filename;
                    int slash = base.lastIndexOf("/");
                    if (slash >= 0)
                        base = base.substring(0, slash + 1); // include trailing slash
                }

                // collection id rather than URL
                String baseDir = baseName;
                if (filename != null) {
                    baseDir = baseName + filename;
                    int slash = baseDir.lastIndexOf("/");
                    if (slash >= 0)
                        baseDir = baseDir.substring(0, slash + 1); // include trailing slash
                }

                // let importobject handle most of this, but we have to
                // process the attachments to make sure they're present

                Element attachmentlist = assignXml.getChild(ATTACHMENTS, assignNs);
                List<Element> attachments = new ArrayList<Element>();
                if (attachmentlist != null)
                    attachments = attachmentlist.getChildren();
                List<String> attachmentHrefs = new ArrayList<String>();
                // note that we ignore the role attribute. No obvious way to implement it.
                for (Element a : attachments) {
                    // file has to be there
                    addFile(removeDotDot(filebase + a.getAttributeValue(HREF)));
                    attachmentHrefs.add(a.getAttributeValue(HREF));
                }

                // need to prevent duplicates, as we're likely to see the same resource more than once.
                // Remember that we've produced this resource ID.
                String resourceId = resource.getAttributeValue(IDENTIFIER);
                String assignmentId = assignsAdded.get(resourceId);
                if (assignmentId == null) {
                    AssignmentInterface a = (AssignmentInterface) assigntool;
                    assignmentId = a.importObject(assignXml, assignNs, base, baseDir, attachmentHrefs, hide); // sakaiid for assignment
                    if (assignmentId != null)
                        assignsAdded.put(resourceId, assignmentId);
                }

                if (assignmentId != null && !hide) {
                    SimplePageItem item = simplePageToolDao.makeItem(page.getPageId(), seq,
                            SimplePageItem.ASSIGNMENT, assignmentId, title);
                    simplePageBean.saveItem(item);
                    if (roles.size() > 0)
                        simplePageBean.setItemGroups(item, roles.toArray(new String[0]));
                    sequences.set(top, seq + 1);
                }

            } else if (((type.equals(CC_WEBCONTENT) || (type.equals(UNKNOWN))) && hide) || type.equals(LAR)) {
                // handled elsewhere
            }
            if (type.equals(UNKNOWN)) {
                badTypes.add(resource.getAttributeValue(TYPE));
                log.debug("unknown type: " + resource.getAttributeValue(TYPE));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.debug("Exception ", e);
        }

    }

    public void addAttachment(String attachment_path) {
        log.debug("adding an attachment: " + attachment_path);
    }

    public void endDiscussion() {
        log.debug("end discussion");
    }

    public void startManifest() {
        log.debug("start manifest");
    }

    public void checkCurriculum(Element the_xml) {
        Element md = the_xml.getChild("curriculumStandardsMetadataSet", ns.csmd_ns());
        if (md != null) {
            if (md.getChild("curriculumStandardsMetadata", ns.csmd_ns()) != null) {
                usesCurriculum = true;
            }
        }
    }

    public void setManifestXml(Element the_xml) {
        manifestXml = the_xml;

        log.debug("manifest xml: " + the_xml);

    }

    public void endManifest() {
        log.debug("end manifest");
        if (usesRole)
            simplePageBean.setErrKey("simplepage.cc-uses-role", null);
        // the pattern match is restricted enough that we can actually do it
        // if (usesPatternMatch)
        //  simplePageBean.setErrKey("simplepage.import_cc_usespattern", null);
        if (usesCurriculum)
            simplePageBean.setErrKey("simplepage.cc-uses-curriculum", null);
        if (badTypes.size() > 0) {
            String typeList = "";
            if (badTypeNames == null)
                badTypeNames = getBadTypeNames();
            for (String badType : badTypes) {
                String typeName = badTypeNames.get(badType);
                if (typeName == null)
                    typeName = badType;
                typeList = typeList + ", " + typeName;
            }
            simplePageBean.setErrKey("simplepage.cc-has-badtypes", typeList.substring(2));
        }
    }

    public void startDiscussion(String topic_name, String text_type, String text, boolean isProtected) {
        if (log.isDebugEnabled()) {
            log.debug("start a discussion: " + topic_name);
            log.debug("text type: " + text_type);
            log.debug("text: " + text);
            log.debug("protected: " + isProtected);
        }
    }

    public void endWebLink() {
        if (log.isDebugEnabled())
            log.debug("end weblink");
    }

    public void startWebLink(String the_title, String the_url, String the_target, String the_window_features,
            boolean isProtected) {
        if (log.isDebugEnabled()) {
            log.debug("start weblink: " + the_title);
            log.debug("link to: " + the_url);
            log.debug("target window: " + the_target);
            log.debug("window features: " + the_window_features);
            log.debug("protected: " + isProtected);
        }
    }

    public void setWebLinkXml(Element the_link) {
        if (log.isDebugEnabled())
            log.debug("weblink xml: " + the_link);
    }

    public void preProcessFile(String the_file_id) {
        String original_file_id = the_file_id;
        //Restrict file length to 250
        if ((baseName + original_file_id).length() > 250) {
            the_file_id = original_file_id.substring(0, 250 - baseName.length());
            log.debug("Length restricted, new file name" + baseName + the_file_id);
        }
        fileNames.put(original_file_id, the_file_id);
    }

    public void addFile(Element elem) {
        //These are processed as standard text
        String href = elem.getAttributeValue(HREF);
        String sakaiId = baseName + elem.getAttributeValue(HREF);
        String extension = Validator.getFileExtension(sakaiId);
        String mime = ContentTypeImageService.getContentType(extension);
        if (mime != null && mime.startsWith("text/"))
            return;
        addFile(href);
    }

    public void addFile(String the_file_id) {

        if (filesAdded.contains(the_file_id))
            return;

        InputStream infile = null;
        for (int tries = 1; tries < 3; tries++) {
            try {
                infile = utils.getFile(the_file_id);
                String name = the_file_id;
                int slash = the_file_id.lastIndexOf("/");
                if (slash >= 0)
                    name = name.substring(slash + 1);
                String extension = Validator.getFileExtension(name);
                String type = ContentTypeImageService.getContentType(extension);

                //Now the new truncated name from the map
                if (fileNames.containsKey(the_file_id)) {
                    the_file_id = fileNames.get(the_file_id);
                    log.debug("Found " + the_file_id + " in pre-load map");
                } else {
                    log.info("Could not find " + the_file_id + " in preload map, File may not work.");
                }

                log.debug("Preparing to add file" + baseName + the_file_id);
                log.debug("Length of baseName is:" + baseName.length());
                log.debug("Length of the_file_id is:" + the_file_id.length());

                ContentResourceEdit edit = ContentHostingService.addResource(baseName + the_file_id);

                edit.setContentType(type);
                edit.setContent(infile);
                edit.getPropertiesEdit().addProperty(ResourceProperties.PROP_DISPLAY_NAME, name);
                // if roles specified for this resource and student not in it, hide it
                if (roles != null && !roles.contains("Learner"))
                    edit.setAvailability(true, null, null);
                ContentHostingService.commitResource(edit, NotificationService.NOTI_NONE);
                filesAdded.add(the_file_id);

            } catch (IdUsedException e) {
                // remove existing if we are importing whole site.
                // otherwise this is an error (and should be impossible, as this is a new directory)
                if (importtop && tries == 1) {
                    try {
                        ContentHostingService.removeResource(baseName + the_file_id);
                        continue;
                    } catch (Exception e1) {
                    }
                }
                simplePageBean.setErrKey("simplepage.create.resource.failed", e + ": " + the_file_id);
                log.info("CC loader: unable to get file " + the_file_id + " error: " + e);
            } catch (Exception e) {
                simplePageBean.setErrKey("simplepage.create.resource.failed", e + ": " + the_file_id);
                log.info("CC loader: unable to get file " + the_file_id + " error: " + e);
            }
            break; // if we get to the end, no need to retry; really a goto would be clearer
        }
    }

    public void endWebContent() {
        if (log.isDebugEnabled())
            log.debug("ending webcontent");
    }

    public void startWebContent(String entry_point, boolean isProtected) {
        if (log.isDebugEnabled()) {
            log.debug("start web content");
            log.debug("protected: " + isProtected);
            if (entry_point != null) {
                log.debug("entry point is: " + entry_point);
            }
        }
    }

    public void endLearningApplicationResource() {
        if (log.isDebugEnabled())
            log.debug("end learning application resource");
    }

    public void startLearningApplicationResource(String entry_point, boolean isProtected) {
        if (log.isDebugEnabled()) {
            log.debug("start learning application resource");
            log.debug("protected: " + isProtected);
            if (entry_point != null) {
                log.debug("entry point is: " + entry_point);
            }
        }
    }

    public void endAssessment() {
        if (log.isDebugEnabled())
            log.debug("end assessment");
    }

    public void setAssessmentXml(Element xml) {
        if (log.isDebugEnabled())
            log.debug("assessment xml: " + xml);
    }

    public void startAssessment(String the_file_name, boolean isProtected) {
        if (log.isDebugEnabled()) {
            log.debug("start assessment contained in: " + the_file_name);
            log.debug("protected: " + isProtected);
        }
    }

    public void endQuestionBank() {
        if (log.isDebugEnabled())
            log.debug("end question bank");
    }

    public void setQuestionBankXml(Element the_xml) {
        if (log.isDebugEnabled())
            log.debug("question bank xml: " + the_xml);
    }

    public void startQuestionBank(String the_file_name, boolean isProtected) {
        if (log.isDebugEnabled()) {
            log.debug("start question bank in: " + the_file_name);
            log.debug("protected: " + isProtected);
        }
    }

    public void setAuthorizationServiceXml(Element the_node) {
        if (log.isDebugEnabled())
            log.debug(the_node.toString());
    }

    public void setAuthorizationService(String cartridgeId, String webservice_url) {
        if (log.isDebugEnabled())
            log.debug("adding auth service for " + cartridgeId + " @ " + webservice_url);
    }

    public void endAuthorization() {
        if (log.isDebugEnabled())
            log.debug("end of authorizations");
    }

    public void startAuthorization(boolean isCartridgeScope, boolean isResourceScope, boolean isImportScope) {
        if (log.isDebugEnabled()) {
            log.debug("start of authorizations");
            log.debug("protect all: " + isCartridgeScope);
            log.debug("protect resources: " + isResourceScope);
            log.debug("protect import: " + isImportScope);
        }
    }

    public void endManifestMetadata() {
        if (log.isDebugEnabled())
            log.debug("end of manifest metadata");
    }

    public void startManifestMetadata(String schema, String schema_version) {
        if (log.isDebugEnabled()) {
            log.debug("start manifest metadata");
            log.debug("schema: " + schema);
            log.debug("schema_version: " + schema_version);
        }
    }

    public void setPresentationXml(Element the_xml) {
        if (log.isDebugEnabled())
            log.debug("QTI presentation xml: " + the_xml);
    }

    public void setQTICommentXml(Element the_xml) {
        if (log.isDebugEnabled())
            log.debug("QTI comment xml: " + the_xml);
    }

    public void setSection(String ident, String title) {
        if (log.isDebugEnabled()) {
            log.debug("set section ident: " + ident);
            log.debug("set section title: " + title);
        }
    }

    public void setSectionXml(Element the_xml) {
        if (log.isDebugEnabled())
            log.debug("set Section Xml: " + the_xml);
    }

    public void endQTIMetadata() {
        if (log.isDebugEnabled())
            log.debug("end of QTI metadata");
    }

    public void setManifestMetadataXml(Element the_md) {
        if (log.isDebugEnabled())
            log.debug("manifest md xml: " + the_md);
        // NOTE: need to handle languages
        if (the_md != null) {
            Element general = the_md.getChild(GENERAL, ns.getLom());
            if (general != null) {
                Element tnode = general.getChild(TITLE, ns.getLom());
                if (tnode != null) {
                    title = tnode.getChildTextTrim(LANGSTRING, ns.getLom());
                    if (title == null || title.equals(""))
                        title = tnode.getChildTextTrim(STRING, ns.getLom());
                }
                Element tdescription = general.getChild(DESCRIPTION, ns.getLom());
                if (tdescription != null) {
                    description = tdescription.getChildTextTrim(STRING, ns.getLom());
                }

            }
        }
        if (title == null || title.equals(""))
            title = "Cartridge";
        if ("".equals(description))
            description = null;
        ContentCollection baseCollection = makeBaseFolder(title);
        baseName = baseCollection.getId();
        baseUrl = baseCollection.getUrl();
        // kill the hostname part. We want to use relative URLs
        int relPart = baseUrl.indexOf("/access/");
        if (relPart >= 0)
            baseUrl = baseUrl.substring(relPart);
        //Start a folder to hold the entire sites content rather than create new top level pages
        boolean singlePage = ServerConfigurationService.getBoolean("lessonbuilder.cc.import.singlepage", false);
        if (singlePage == true)
            startCCFolder(null);

    }

    public void setResourceMetadataXml(Element the_md) {
        // version 1 and higher are different formats, hence a slightly weird test
        Iterator mdroles = the_md.getDescendants(new ElementFilter("intendedEndUserRole", ns.lom_ns()));
        if (mdroles != null) {
            while (mdroles.hasNext()) {
                Element role = (Element) mdroles.next();
                Iterator values = role.getDescendants(new ElementFilter("value", ns.lom_ns()));
                if (values != null) {
                    while (values.hasNext()) {
                        if (roles == null)
                            roles = new HashSet<String>();
                        Element value = (Element) values.next();
                        String roleName = value.getTextTrim();
                        roles.add(roleName);
                    }
                }
            }
        }

        if (log.isDebugEnabled())
            log.debug("resource md xml: " + the_md);
    }

    public void addQTIMetadataField(String label, String entry) {
        if (log.isDebugEnabled()) {
            log.debug("QTI md label: " + label);
            log.debug("QTI md entry: " + entry);
        }
    }

    public void setQTIComment(String the_comment) {
        if (log.isDebugEnabled())
            log.debug("QTI comment: " + the_comment);
    }

    public void endDependency() {
        if (log.isDebugEnabled())
            log.debug("end dependency");
    }

    public void startDependency(String source, String target) {
        if (log.isDebugEnabled())
            log.debug("start dependency- resource : " + source + " is dependent upon: " + target);
    }

    public void startResource(String id, boolean isProtected) {

        roles = null;
        if (log.isDebugEnabled())
            log.debug("start resource: " + id + " protected: " + isProtected);
    }

    public void setResourceXml(Element the_xml) {
        if (log.isDebugEnabled())
            log.debug("resource xml: " + the_xml);
    }

    public void endResource() {
        if (log.isDebugEnabled())
            log.debug("end resource");
    }

    public void addAssessmentItem(QTIItem the_item) {
        if (log.isDebugEnabled())
            log.debug("add QTI assessment item: " + the_item.toString());

    }

    public void addQTIMetadataXml(Element the_md) {
        if (log.isDebugEnabled())
            log.debug("add QTI metadata xml: " + the_md);

    }

    public void startQTIMetadata() {
        if (log.isDebugEnabled())
            log.debug("start QTI metadata");
    }

    public void setDiscussionXml(Element the_element) {
        if (log.isDebugEnabled())
            log.debug("set discussion xml: " + the_element);
    }

    public void addQuestionBankItem(QTIItem the_item) {
        if (log.isDebugEnabled())
            log.debug("add QTI QB item: " + the_item.toString());
    }

    public void setQuestionBankDetails(String the_ident) {
        if (log.isDebugEnabled())
            log.debug("set qti qb details: " + the_ident);
    }

    // xxx/abc/../ccc
    // xxx/ccc
    // xxx/../ccc
    // ccc

    public String removeDotDot(String s) {
        while (true) {
            int i = s.indexOf("/../");
            if (i < 1)
                return s;
            int j = s.lastIndexOf("/", i - 1);
            if (j < 0)
                j = 0;
            else
                j = j + 1;
            s = s.substring(0, j) + s.substring(i + 4);
        }
    }

}