001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/archive/tags/sakai_2-4-1/import-impl/src/java/org/sakaiproject/importer/impl/IMSFileParser.java $
003: * $Id: IMSFileParser.java 17726 2006-11-01 15:39:28Z lance@indiana.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.importer.impl;
021:
022: import java.io.File;
023: import java.io.FileInputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.util.ArrayList;
027: import java.util.Collection;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032:
033: import javax.xml.parsers.DocumentBuilder;
034: import javax.xml.parsers.DocumentBuilderFactory;
035: import org.sakaiproject.importer.api.Importable;
036: import org.sakaiproject.importer.api.IMSResourceTranslator;
037: import org.sakaiproject.importer.impl.importables.FileResource;
038: import org.sakaiproject.importer.impl.importables.Folder;
039: import org.w3c.dom.Document;
040: import org.w3c.dom.Node;
041: import org.w3c.dom.NodeList;
042:
043: public abstract class IMSFileParser extends ZipFileParser {
044: protected Map resourceMap = new HashMap();
045: protected Map translatorMap = new HashMap();
046: protected Document archiveManifest;
047: protected ResourceHelper resourceHelper;
048: protected ItemHelper itemHelper;
049: protected FileHelper fileHelper;
050: protected ManifestHelper manifestHelper;
051:
052: protected void awakeFromUnzip(String pathToData) {
053: this .pathToData = pathToData;
054: String absolutepathToManifest = pathToData + "/"
055: + "imsmanifest.xml";
056: absolutepathToManifest = absolutepathToManifest.replace('\\',
057: '/');
058: DocumentBuilder docBuilder;
059: try {
060: docBuilder = DocumentBuilderFactory.newInstance()
061: .newDocumentBuilder();
062: InputStream fis = new FileInputStream(
063: absolutepathToManifest);
064: this .archiveManifest = (Document) docBuilder.parse(fis);
065: } catch (Exception e) {
066: e.printStackTrace();
067: }
068: }
069:
070: public void setTranslatorMap(Map translatorMap) {
071: this .translatorMap = translatorMap;
072: }
073:
074: public boolean isValidArchive(byte[] fileData) {
075: if (super .isValidArchive(fileData)) {
076: if (!fileExistsInArchive("/imsmanifest.xml", fileData))
077: return false;
078: return true;
079: } else
080: return false;
081: }
082:
083: protected abstract Collection getCategoriesFromArchive(
084: String pathToData);
085:
086: protected Collection getImportableItemsFromArchive(String pathToData) {
087: Collection rv = new ArrayList();
088: Document manifest = this .archiveManifest;
089: List itemNodes = manifestHelper.getTopLevelItemNodes(manifest);
090: List resourceNodes = manifestHelper.getResourceNodes(manifest);
091: Node resourceNode;
092: // set up a Map of resource Nodes keyed on their identifier attribute
093: for (Iterator i = resourceNodes.iterator(); i.hasNext();) {
094: resourceNode = (Node) i.next();
095: resourceMap.put(resourceHelper.getId(resourceNode),
096: resourceNode);
097: }
098: Node itemNode;
099: for (Iterator i = itemNodes.iterator(); i.hasNext();) {
100: itemNode = (Node) i.next();
101: String title = itemHelper.getTitle(itemNode);
102: rv
103: .addAll(translateFromNodeToImportables(itemNode,
104: "", null));
105: }
106: // the remainder of resources in the resourcesMap need to be processed
107: Object[] remainingRes = resourceMap.values().toArray();
108: for (int i = 0; i < remainingRes.length; i++) {
109: resourceNode = (Node) remainingRes[i];
110: rv.addAll(translateFromNodeToImportables(resourceNode, "",
111: null));
112: resourceMap.remove(XPathHelper.getNodeValue(
113: "./attribute::identifier", resourceNode));
114: }
115: return rv;
116: }
117:
118: protected Collection translateFromNodeToImportables(Node node,
119: String contextPath, Importable parent) {
120: Collection branchOfImportables = new ArrayList();
121: String tag = node.getNodeName();
122: String itemResourceId = null;
123: if ("item".equals(tag)) {
124: itemResourceId = itemHelper.getResourceId(node);
125: } else if ("resource".equals(tag)) {
126: itemResourceId = resourceHelper.getId(node);
127: } else if ("file".equals(tag)) {
128: itemResourceId = resourceHelper.getId(node.getParentNode());
129: }
130: Document resourceDescriptor = resourceHelper
131: .getDescriptor(manifestHelper.getResourceForId(
132: itemResourceId, this .archiveManifest));
133: if (resourceHelper.isFolder(resourceDescriptor)
134: || ("item".equals(tag) && (XPathHelper.selectNodes(
135: "./item", node).size() > 0))
136: || ("item".equals(tag) && isCompoundDocument(
137: manifestHelper.getResourceForId(itemResourceId,
138: archiveManifest), resourceDescriptor))) {
139: String folderTitle = getTitleForNode(node);
140: Folder folder = new Folder();
141: folder.setPath(contextPath);
142: folder.setTitle(folderTitle);
143: folder.setDescription(getDescriptionForNode(node));
144: if (parent != null) {
145: folder.setParent(parent);
146: folder.setLegacyGroup(parent.getLegacyGroup());
147: } else
148: folder.setLegacyGroup(folderTitle);
149: // now we take care of the folder's child Nodes
150: // construct a new path and make sure we replace any forward slashes from the resource title
151: String folderPath = contextPath
152: + folderTitle.replaceAll("/", "_") + "/";
153: if (isCompoundDocument(manifestHelper.getResourceForId(
154: itemResourceId, archiveManifest),
155: resourceDescriptor)) {
156: branchOfImportables
157: .addAll(translateFromNodeToImportables(
158: manifestHelper
159: .getResourceForId(
160: itemResourceId,
161: archiveManifest),
162: folderPath, folder));
163: } else {
164: List children = XPathHelper.selectNodes("./item", node);
165: for (Iterator i = children.iterator(); i.hasNext();) {
166: branchOfImportables
167: .addAll(translateFromNodeToImportables(
168: (Node) i.next(), folderPath, folder));
169: }
170: }
171: resourceMap.remove(itemResourceId);
172: branchOfImportables.add(folder);
173: } // node is folder
174:
175: else if ("item".equals(tag)) {
176: // this item is a leaf, so we handle the resource associated with it
177: Node resourceNode = manifestHelper.getResourceForId(
178: itemResourceId, this .archiveManifest);
179: if (resourceNode != null) {
180: if (parent == null) {
181: parent = new Folder();
182: parent.setLegacyGroup(itemHelper.getTitle(node));
183: }
184: branchOfImportables
185: .addAll(translateFromNodeToImportables(
186: resourceNode, contextPath, parent));
187: }
188: } else if ("file".equals(tag)) {
189: FileResource file = new FileResource();
190: try {
191: String fileName = fileHelper.getFilenameForNode(node);
192: file.setFileName(fileName);
193: // If
194: if (node.getParentNode().getChildNodes().getLength() > 1) {
195: file.setDescription("");
196: } else
197: file.setDescription(resourceHelper
198: .getDescription(node.getParentNode()));
199: file.setFileBytes(fileHelper.getFileBytesForNode(node,
200: contextPath));
201: file.setDestinationResourcePath(fileHelper
202: .getFilePathForNode(node, contextPath));
203: file.setContentType(this .mimeTypes
204: .getContentType(fileName));
205: file.setTitle(fileHelper.getTitle(node));
206: if (parent != null) {
207: file.setParent(parent);
208: file.setLegacyGroup(parent.getLegacyGroup());
209: } else
210: file.setLegacyGroup("");
211: } catch (IOException e) {
212: resourceMap.remove(resourceHelper.getId(node
213: .getParentNode()));
214: return branchOfImportables;
215: }
216: branchOfImportables.add(file);
217: resourceMap.remove(resourceHelper.getId(node
218: .getParentNode()));
219: return branchOfImportables;
220: } else if ("resource".equals(tag)) {
221: // TODO handle a resource node
222: Importable resource = null;
223: boolean processResourceChildren = true;
224: IMSResourceTranslator translator = (IMSResourceTranslator) translatorMap
225: .get(resourceHelper.getType(node));
226: if (translator != null) {
227: resource = translator.translate(node, resourceHelper
228: .getDescriptor(node), contextPath,
229: this .pathToData);
230: processResourceChildren = translator
231: .processResourceChildren();
232: }
233: if (resource != null) {
234: if (parent != null) {
235: resource.setParent(parent);
236: resource.setLegacyGroup(parent.getLegacyGroup());
237: } else
238: resource.setLegacyGroup(resourceHelper
239: .getTitle(node));
240: branchOfImportables.add(resource);
241: parent = resource;
242: }
243: // processing the child nodes implies that their files can wind up in the Resources tool.
244: // this is not always desireable, such as the QTI files from assessments.
245: if (processResourceChildren) {
246: NodeList children = node.getChildNodes();
247: for (int i = 0; i < children.getLength(); i++) {
248: branchOfImportables
249: .addAll(translateFromNodeToImportables(
250: children.item(i), contextPath,
251: parent));
252: }
253: }
254: resourceMap.remove(itemResourceId);
255: }
256: return branchOfImportables;
257: }
258:
259: protected String getTitleForNode(Node node) {
260: if ("item".equals(node.getNodeName())) {
261: return itemHelper.getTitle(node);
262: } else if ("resource".equals(node.getNodeName())) {
263: return resourceHelper.getTitle(node);
264: } else
265: return "";
266: }
267:
268: protected String getDescriptionForNode(Node node) {
269: if ("item".equals(node.getNodeName())) {
270: return itemHelper.getDescription(node);
271: } else if ("resource".equals(node.getNodeName())) {
272: return resourceHelper.getDescription(node);
273: } else
274: return "";
275: }
276:
277: protected abstract boolean isCompoundDocument(Node node,
278: Document resourceDescriptor);
279:
280: public void addResourceTranslator(IMSResourceTranslator t) {
281: translatorMap.put(t.getTypeName(), t);
282: }
283:
284: protected abstract class ResourceHelper implements ManifestResource {
285: }
286:
287: protected abstract class ItemHelper implements ManifestItem {
288:
289: public String getResourceId(Node itemNode) {
290: return XPathHelper.getNodeValue("./@identifierref",
291: itemNode);
292: }
293: }
294:
295: protected abstract class FileHelper implements ManifestFile {
296:
297: public byte[] getFileBytesForNode(Node node, String contextPath)
298: throws IOException {
299: String filePath = getFilePathForNode(node, contextPath);
300: return getBytesFromFile(new File(pathToData + "/"
301: + filePath));
302: }
303:
304: public String getFilePathForNode(Node node, String contextPath) {
305: return contextPath + "/" + getFilenameForNode(node);
306: }
307:
308: public String getTitle(Node fileNode) {
309: // if the resource that this file belongs to has multiple files,
310: // we just want to use the filename as the title
311: if (fileNode.getParentNode().getChildNodes().getLength() > 1) {
312: return getFilenameForNode(fileNode);
313: } else
314: return resourceHelper
315: .getTitle(fileNode.getParentNode());
316: }
317:
318: public String getFilenameForNode(Node node) {
319: String sourceFilePath = XPathHelper.getNodeValue("./@href",
320: node);
321: return (sourceFilePath.lastIndexOf("/") < 0) ? sourceFilePath
322: : sourceFilePath.substring(sourceFilePath
323: .lastIndexOf("/") + 1);
324: }
325: }
326:
327: protected abstract class ManifestHelper implements Manifest {
328: }
329:
330: }
|