/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.fileresource.types; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.PathUtils; import org.olat.ims.qti21.QTI21ContentPackage; import org.olat.ims.qti21.model.xml.BadRessourceHelper; import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader; import uk.ac.ed.ph.jqtiplus.xmlutils.XmlReadResult; import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ChainedResourceLocator; import uk.ac.ed.ph.jqtiplus.xmlutils.locators.NetworkHttpResourceLocator; import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator; /** * Description:<br> * Try to validate the resource against QTI 2.1 * * <P> * Initial Date: Jun 24, 2009 <br> * @author matthai */ public class ImsQTI21Resource extends FileResource { private static final OLog log = Tracing.createLoggerFor(ImsQTI21Resource.class); private static final String IMS_MANIFEST = "imsmanifest.xml"; /** * IMS QTI 21 file resource identifier. */ public static final String TYPE_NAME = "FileResource.IMSQTI21"; public ImsQTI21Resource() { super(TYPE_NAME); } public static ResourceEvaluation evaluate(File file, String filename) { ResourceEvaluation eval = new ResourceEvaluation(); try { ImsManifestFileFilter visitor = new ImsManifestFileFilter(); Path fPath = PathUtils.visit(file, filename, visitor); if(visitor.isValid()) { Path realManifestPath = visitor.getManifestPath(); Path manifestPath = fPath.resolve(realManifestPath); RootSearcher rootSearcher = new RootSearcher(); Files.walkFileTree(fPath, rootSearcher); if(rootSearcher.foundRoot()) { manifestPath = rootSearcher.getRoot().resolve(IMS_MANIFEST); } else { manifestPath = fPath.resolve(IMS_MANIFEST); } QTI21ContentPackage cp = new QTI21ContentPackage(manifestPath); if(validateImsManifest(cp, new PathResourceLocator(manifestPath.getParent()))) { eval.setValid(true); } else { eval.setValid(false); } } else { eval.setValid(false); } } catch (IOException | IllegalArgumentException e) { log.error("", e); eval.setValid(false); } return eval; } public static boolean validateImsManifest(QTI21ContentPackage cp, ResourceLocator resourceLocator) { try { if(cp.hasTest()) { URI test = cp.getTest().toUri(); ResourceLocator chainedResourceLocator = createResolvingResourceLocator(resourceLocator); XmlReadResult result = new QtiXmlReader().read(chainedResourceLocator, test, true, true); if(result != null && !result.isSchemaValid()) { StringBuilder out = new StringBuilder(); BadRessourceHelper.extractMessage(result.getXmlParseResult(), out); log.warn(out.toString()); } return result != null && result.isSchemaValid() || true; } return false; } catch (Exception e) { log.error("", e); return false; } } public static boolean validate(File resource) { try { PathResourceLocator resourceLocator = new PathResourceLocator(resource.getParentFile().toPath()); ResourceLocator chainedResourceLocator = createResolvingResourceLocator(resourceLocator); XmlReadResult result = new QtiXmlReader().read(chainedResourceLocator, resource.toURI(), true, true); return result != null && result.isSchemaValid(); } catch (Exception e) { log.error("", e); return false; } } public static ResourceLocator createResolvingResourceLocator(ResourceLocator resourceLocator) { final ResourceLocator result = new ChainedResourceLocator( resourceLocator, QtiXmlReader.JQTIPLUS_PARSER_RESOURCE_LOCATOR, /* (to resolve internal HTTP resources, e.g. RP templates) */ new NetworkHttpResourceLocator() /* (to resolve external HTTP resources, e.g. RP templates, external items) */ ); return result; } public static class PathResourceLocator implements ResourceLocator { private Path root; public PathResourceLocator(Path root) { this.root = root; } @Override public InputStream findResource(URI systemId) { if ("file".equals(systemId.getScheme())) { try { return new FileInputStream(new File(systemId)); } catch (final Exception e) { log.info("File {} does not exist:" + systemId); return null; } } else if("jar".equals(systemId.getScheme())) { try { String toPath = systemId.toString(); if(toPath.contains("!")) { int index = toPath.indexOf('!'); String relative = toPath.substring(index + 1); Path newPath = root.resolve(relative); return Files.newInputStream(newPath); } } catch (Exception e) { log.error("File {} does not exist:" + systemId, e); return null; } } else if("zip".equals(systemId.getScheme())) { try { String toPath = systemId.toString(); if(toPath.contains(":")) { int index = toPath.indexOf(':'); String relative = toPath.substring(index + 1); Path newPath = root.resolve(relative); return Files.newInputStream(newPath); } } catch (Exception e) { log.error("File {} does not exist:" + systemId, e); return null; } } return null; } } private static class ImsManifestFileFilter extends SimpleFileVisitor<Path> { private boolean manifestFile; private Path manifestPath; @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String filename = file.getFileName().toString(); if(IMS_MANIFEST.equals(filename)) { manifestFile = true; manifestPath = file; } return manifestFile ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE; } public boolean isValid() { return manifestFile; } public Path getManifestPath() { return manifestPath; } } }