All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sakaiproject.importer.impl.IMSFileParser Maven / Gradle / Ivy

The newest version!
/**********************************************************************************
 * $URL$
 * $Id$
 ***********************************************************************************
 *
 * Copyright (c) 2006, 2007, 2008 The Sakai 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://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.
 *
 **********************************************************************************/

package org.sakaiproject.importer.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import lombok.extern.slf4j.Slf4j;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;

import org.sakaiproject.importer.api.Importable;
import org.sakaiproject.importer.api.IMSResourceTranslator;
import org.sakaiproject.importer.impl.importables.FileResource;
import org.sakaiproject.importer.impl.importables.Folder;

@Slf4j
public abstract class IMSFileParser extends ZipFileParser {
	protected Map resourceMap = new HashMap();
	protected Map translatorMap = new HashMap();
	protected Map dependencies = new HashMap();
	protected Document archiveManifest;
	protected ResourceHelper resourceHelper;
	protected ItemHelper itemHelper;
	protected FileHelper fileHelper;
	protected ManifestHelper manifestHelper;
	
	protected void awakeFromUnzip(String pathToData) {
		this.pathToData = pathToData;
		String absolutepathToManifest = pathToData + "/" + "imsmanifest.xml";
	    absolutepathToManifest = absolutepathToManifest.replace('\\', '/');
	    InputStream fis = null;
        try {
            fis = new FileInputStream(absolutepathToManifest);
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            DocumentBuilder docBuilder = builderFactory.newDocumentBuilder();
            this.archiveManifest = (Document) docBuilder.parse(fis);
        } catch (FileNotFoundException e) {
            log.error(e.getMessage(), e);
        } catch (ParserConfigurationException e) {
            log.error(e.getMessage(), e);
        } catch (SAXException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
	}
	
	public void setTranslatorMap(Map translatorMap) {
		this.translatorMap = translatorMap;	
	}
	
	public boolean isValidArchive(InputStream fileData) {
		if (super.isValidArchive(fileData)) {
			if (!fileExistsInArchive("/imsmanifest.xml", fileData)) 
				return false;
			return true;
		} else return false;
	}

	protected abstract Collection getCategoriesFromArchive(String pathToData);

	protected Collection getImportableItemsFromArchive(String pathToData) {
		Collection rv = new ArrayList();
		Document manifest = this.archiveManifest;
		List itemNodes = manifestHelper.getTopLevelItemNodes(manifest);
		List resourceNodes = manifestHelper.getResourceNodes(manifest);
		Node resourceNode;
		// set up a Map of resource Nodes keyed on their identifier attribute
		for(Iterator i = resourceNodes.iterator(); i.hasNext();) {
			resourceNode = (Node) i.next();
			resourceMap.put(resourceHelper.getId(resourceNode), resourceNode);
		}
		Node itemNode;
		int priority = 1;
		for(Iterator i = itemNodes.iterator(); i.hasNext(); ) {
			itemNode = (Node) i.next();
			String title = itemHelper.getTitle(itemNode);
			rv.addAll(translateFromNodeToImportables(itemNode, "", priority, null));
			priority++;
		}
		// the remainder of resources in the resourcesMap need to be processed
		Object[] remainingRes = resourceMap.values().toArray();
		for (int i = 0;i < remainingRes.length; i++) {
			resourceNode = (Node)remainingRes[i];
			rv.addAll(translateFromNodeToImportables(resourceNode, "",i+1, null));
			resourceMap.remove(XPathHelper.getNodeValue("./attribute::identifier", resourceNode));
		}
		return rv;
	}

	protected Collection translateFromNodeToImportables(Node node, String contextPath, int priority, Importable parent) {
		Collection branchOfImportables = new ArrayList();
		String tag = node.getNodeName();
		String itemResourceId = null;
		if ("item".equals(tag)) {
			itemResourceId = itemHelper.getResourceId(node);
		} else if ("resource".equals(tag)) {
			itemResourceId = resourceHelper.getId(node);
		} else if ("file".equals(tag)) {
			itemResourceId = resourceHelper.getId(node.getParentNode());
		}
		Document resourceDescriptor = resourceHelper.getDescriptor(manifestHelper.getResourceForId(itemResourceId, this.archiveManifest));
		if (resourceHelper.isFolder(resourceDescriptor) || 
	  		    ("item".equals(tag) && (XPathHelper.selectNodes("./item", node).size() > 0)) ||
	  		    ( "item".equals(tag) && 
	  	          isCompoundDocument(manifestHelper.getResourceForId(itemResourceId, archiveManifest),resourceDescriptor)
	  		    )) {
			String folderTitle = getTitleForNode(node);
			Folder folder = new Folder();
			folder.setPath(contextPath);
			folder.setTitle(folderTitle);
			folder.setDescription(getDescriptionForNode(node));
			folder.setSequenceNum(priority);
			if (parent != null) {
  				folder.setParent(parent);
  				folder.setLegacyGroup(parent.getLegacyGroup());
  			} else folder.setLegacyGroup(folderTitle);
			// now we take care of the folder's child Nodes
			// construct a new path and make sure we replace any forward slashes from the resource title
			String folderPath = contextPath + folderTitle.replaceAll("/", "_") + "/";
			if (isCompoundDocument(manifestHelper.getResourceForId(itemResourceId, archiveManifest),resourceDescriptor)) {
				if (wantsCompanionForCompoundDocument()) {
					priority++;
					folder.setSequenceNum(priority);
					branchOfImportables.add(getCompanionForCompoundDocument(resourceDescriptor, folder));
				}
				branchOfImportables.addAll(translateFromNodeToImportables(manifestHelper.getResourceForId(itemResourceId, archiveManifest), folderPath, priority, folder));
			} else {
	  			List children = XPathHelper.selectNodes("./item", node);
	  			int childPriority = 1;
	  			for (Iterator i = children.iterator(); i.hasNext();) {
	  				branchOfImportables.addAll(
	  						translateFromNodeToImportables((Node)i.next(),folderPath, childPriority, folder));
	  				childPriority++;
	  			}
			}
  			resourceMap.remove(itemResourceId);
  			branchOfImportables.add(folder);
		} // node is folder
		
		else if("item".equals(tag)) {
			// this item is a leaf, so we handle the resource associated with it
			Node resourceNode = manifestHelper.getResourceForId(itemResourceId, this.archiveManifest);
  			if (resourceNode != null) {
  				if (parent == null) {
  					parent = new Folder();
  					parent.setLegacyGroup(itemHelper.getTitle(node));
  				}
  				branchOfImportables.addAll(
  						translateFromNodeToImportables(resourceNode,contextPath, priority, parent));
  			}
		} else if("file".equals(tag)) {
			FileResource file = new FileResource();
			try {
				String fileName = fileHelper.getFilenameForNode(node);
 				file.setFileName(fileName);
				// If 
				if (node.getParentNode().getChildNodes().getLength() > 1) {
					file.setDescription("");
				} else file.setDescription(resourceHelper.getDescription(node.getParentNode()));
				//Takes too much memory just pass file
				//file.setFileBytes(fileHelper.getFileBytesForNode(node, contextPath));
				file.setInputStream(fileHelper.getInputStreamForNode(node,contextPath));
				file.setDestinationResourcePath(fileHelper.getFilePathForNode(node, contextPath));
				file.setContentType(this.mimeTypes.getContentType(fileName));
				file.setTitle(fileHelper.getTitle(node));
				if(parent != null) {
					file.setParent(parent);
					file.setLegacyGroup(parent.getLegacyGroup());
				} else file.setLegacyGroup("");
			} catch (IOException e) {
				resourceMap.remove(resourceHelper.getId(node.getParentNode()));
				return branchOfImportables;
			}
			branchOfImportables.add(file);
			resourceMap.remove(resourceHelper.getId(node.getParentNode()));
			return branchOfImportables;
		} else if("resource".equals(tag)) {
			// TODO handle a resource node
			Importable resource = null;
			boolean processResourceChildren = true;
			IMSResourceTranslator translator = (IMSResourceTranslator)translatorMap.get(resourceHelper.getType(node));
			if (translator != null) {
				String title = resourceHelper.getTitle(node);
				((Element)node).setAttribute("title", title);
				((Element)node).setAttribute("priority", Integer.toString(priority));
				resource = translator.translate(node, resourceHelper.getDescriptor(node), contextPath, this.pathToData);
				processResourceChildren = translator.processResourceChildren();
			}
			if (resource != null) {
				// make a note of a dependency if there is one.
				String dependency = resourceHelper.getDependency(node);
				if (!"".equals(dependency)) {
					dependencies.put(resourceHelper.getId(node), dependency);
				}
				// section to twiddle with the Importable's legacyGroup,
				// which we only want to do if it hasn't already been set.
				if ((resource.getLegacyGroup() == null) || ("".equals(resource.getLegacyGroup()))) {
					// find out if something depends on this.
					if (dependencies.containsValue(resourceHelper.getId(node))) {
						resource.setLegacyGroup("mandatory");
					} else if (parent != null) {
						resource.setParent(parent);
						resource.setLegacyGroup(parent.getLegacyGroup());
					} else resource.setLegacyGroup(resourceHelper.getTitle(node));
				}
				branchOfImportables.add(resource);
				parent = resource;
			}
			// processing the child nodes implies that their files can wind up in the Resources tool.
			// this is not always desirable, such as the QTI files from assessments.
			if (processResourceChildren) {
				NodeList children = node.getChildNodes();
		  		for (int i = 0;i < children.getLength();i++) {
		  			branchOfImportables.addAll(translateFromNodeToImportables(children.item(i), contextPath, priority, parent));
		  			}
			}
			resourceMap.remove(itemResourceId);
		}
		return branchOfImportables;
	}

	protected abstract Importable getCompanionForCompoundDocument(Document resourceForId, Folder folder);

	protected abstract boolean wantsCompanionForCompoundDocument();

	protected String getTitleForNode(Node node) {
		if ("item".equals(node.getNodeName())) {
			return itemHelper.getTitle(node);
		} else if ("resource".equals(node.getNodeName())) {
			return resourceHelper.getTitle(node);
		} else return "";
	}

	protected String getDescriptionForNode(Node node){
		if ("item".equals(node.getNodeName())) {
			return itemHelper.getDescription(node);
		} else if ("resource".equals(node.getNodeName())) {
			return resourceHelper.getDescription(node);
		} else return "";
	}


	protected abstract boolean isCompoundDocument(Node node, Document resourceDescriptor);
	
	public void addResourceTranslator(IMSResourceTranslator t) {
		translatorMap.put(t.getTypeName(), t);
	}
	
	protected abstract static class ResourceHelper implements ManifestResource {

		public String getDependency(Node node) {
			return XPathHelper.getNodeValue("./dependency/@identifierref", node);
		}
	}
	
	protected abstract static class ItemHelper implements ManifestItem {
		
		public String getResourceId(Node itemNode) {
			return XPathHelper.getNodeValue("./@identifierref", itemNode);
		}
	}
	
	protected abstract class FileHelper implements ManifestFile {
		
		//This should be avoided
		public byte[] getFileBytesForNode(Node node, String contextPath) throws IOException {
			String filePath = getFilePathForNode(node, contextPath);
			return getBytesFromFile(new File(pathToData + "/" + filePath));
		}
		
		
		public InputStream getInputStreamForNode(Node node, String contextPath) throws IOException{
			String filePath = getFilePathForNode(node,contextPath);
			InputStream is = new FileInputStream(filePath);
			return is;
		}
		
		public String getFilePathForNode(Node node, String contextPath) {
			return contextPath + "/" + getFilenameForNode(node);
		}

		public String getTitle(Node fileNode) {
			// if the resource that this file belongs to has multiple files,
			// we just want to use the filename as the title
			if (fileNode.getParentNode().getChildNodes().getLength() > 1) {
				return getFilenameForNode(fileNode);
			} else return resourceHelper.getTitle(fileNode.getParentNode());
		}
		
		public String getFilenameForNode(Node node) {
			String sourceFilePath = XPathHelper.getNodeValue("./@href", node).replaceAll("\\\\", "/");
			return (sourceFilePath.lastIndexOf("/") < 0) ? sourceFilePath 
					: sourceFilePath.substring(sourceFilePath.lastIndexOf("/") + 1);
		}
	}
	
	protected abstract static class ManifestHelper implements Manifest {}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy