| /************************************************************************ |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER |
| * |
| * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. |
| * |
| * Use is subject to license terms. |
| * |
| * 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. You can also |
| * obtain a copy of the License at http://odftoolkit.org/docs/license.txt |
| * |
| * 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.odftoolkit.odfvalidator; |
| |
| import javax.xml.XMLConstants; |
| |
| import javax.xml.validation.Schema; |
| import javax.xml.validation.SchemaFactory; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.io.InputStream; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import javax.xml.transform.stream.StreamSource; |
| import javax.xml.validation.Validator; |
| |
| public class ODFValidator implements ODFValidatorProvider { |
| |
| // Prefix used to distinguish internal resources from external ones |
| // Loglevel |
| private Logger.LogLevel m_nLogLevel; |
| // Validator provider |
| private SchemaFactory m_aRNGSchemaFactory = null; |
| private SchemaFactory m_aXSDSchemaFactory = null; |
| // User provided Configuration |
| protected Configuration m_aConfig = null; |
| // User provided ODF version |
| protected OdfVersion m_aVersion = null; |
| protected OdfVersion mOdfPackageVersion = null; |
| // User provided preference for using Math DTD |
| protected boolean m_bUseMathDTD = false; |
| // Validatore and configuration cache |
| private HashMap<String, Schema> m_aSchemaMap = null; |
| private HashMap<OdfVersion, Configuration> m_aConfigurationMap = null; |
| // Generator from last validateFile or validateStream call |
| private String m_aGenerator = ""; |
| |
| /** Creates a new instance of Validator */ |
| public ODFValidator(Configuration aConfig, |
| Logger.LogLevel nLogLevel, |
| OdfVersion aVersion, |
| boolean bUseMathDTD) throws ODFValidatorException { |
| |
| m_nLogLevel = nLogLevel; |
| m_aConfig = aConfig; |
| m_aVersion = aVersion; |
| m_bUseMathDTD = bUseMathDTD; |
| Logger.enableHTML(false); |
| } |
| |
| /** Creates a new instance of Validator */ |
| public ODFValidator(Configuration aConfig, |
| Logger.LogLevel nLogLevel, |
| boolean logAsHTML, |
| OdfVersion aVersion, |
| boolean bUseMathDTD) throws ODFValidatorException { |
| |
| m_nLogLevel = nLogLevel; |
| m_aConfig = aConfig; |
| m_aVersion = aVersion; |
| m_bUseMathDTD = bUseMathDTD; |
| Logger.enableHTML(logAsHTML); |
| |
| } |
| |
| /** Returns either the ODF version request to validate or the ODF version of the root document as fall-back */ |
| public String getOdfVersion() { |
| String version = null; |
| if (m_aVersion != null) { |
| version = m_aVersion.toString(); |
| } else { |
| version = mOdfPackageVersion.toString(); |
| } |
| return version; |
| } |
| |
| public boolean validate(PrintStream aOut, Configuration aConfig, OdfValidatorMode eMode) throws ODFValidatorException { |
| List<String> aFileNames = aConfig.getListPropety(Configuration.PATH); |
| String aExcludeRegExp = aConfig.getProperty(Configuration.EXCLUDE); |
| String aRecursive = aConfig.getProperty(Configuration.RECURSIVE); |
| boolean bRecursive = aRecursive != null ? Boolean.valueOf(aRecursive) : false; |
| String aFilterFileName = aConfig.getProperty(Configuration.FILTER); |
| |
| return validate(aOut, aFileNames, aExcludeRegExp, eMode, bRecursive, aFilterFileName); |
| } |
| |
| public boolean validate(PrintStream aOut, List<String> aFileNames, String aExcludeRegExp, |
| OdfValidatorMode eMode, boolean bRecursive, |
| String aFilterFileName) throws ODFValidatorException { |
| boolean bRet = false; |
| |
| SAXParseExceptionFilter aFilter = null; |
| if (aFilterFileName != null && aFilterFileName.length() != 0) { |
| aFilter = new ValidationOOoTaskIdErrorFilter(new File(aFilterFileName), aOut); |
| } |
| |
| FileFilter aFileFilter = new ODFFileFilter(aExcludeRegExp, bRecursive); |
| |
| Iterator<String> aIter = aFileNames.iterator(); |
| while (aIter.hasNext()) { |
| File aFile = new File(aIter.next()); |
| bRet |= aFile.isDirectory() ? validateDir(aOut, aFile, aFileFilter, eMode, aFilter) |
| : validateFile(aOut, aFile, eMode, aFilter); |
| } |
| |
| return bRet; |
| } |
| |
| public boolean validate(PrintStream aOut, InputStream aInputStream, String aBaseURI, |
| OdfValidatorMode eMode, |
| SAXParseExceptionFilter aFilter) throws ODFValidatorException { |
| return validateStream(aOut, aInputStream, aBaseURI, eMode, aFilter); |
| } |
| |
| private boolean validateDir(PrintStream aOut, final File aDir, FileFilter aFileFilter, OdfValidatorMode eMode, SAXParseExceptionFilter aFilter) throws ODFValidatorException { |
| boolean bRet = true; |
| File[] aFiles = aDir.listFiles(aFileFilter); |
| |
| if (aFiles != null) { |
| for (int i = 0; i < aFiles.length; ++i) { |
| File aFile = aFiles[i]; |
| if (aFile.isDirectory()) { |
| bRet |= validateDir(aOut, aFile, aFileFilter, eMode, aFilter); |
| } else { |
| bRet |= validateFile(aOut, aFile, eMode, aFilter); |
| } |
| } |
| } |
| return bRet; |
| } |
| |
| /** |
| * validate the input File |
| * |
| * <p>After validation the getGenerator method can be called to get the generator of the validated file</p> |
| */ |
| public boolean validateFile(PrintStream aOut, File aDocFile, OdfValidatorMode eMode, SAXParseExceptionFilter aFilter) throws ODFValidatorException { |
| ODFFileValidator aFileValidator = |
| new ODFFileValidator(aDocFile, m_nLogLevel, eMode, m_aVersion, aFilter, this); |
| |
| boolean result = aFileValidator.validate(aOut); |
| mOdfPackageVersion = aFileValidator.mOdfPackageVersion; |
| m_aGenerator = aFileValidator.getGenerator(); |
| return result; |
| } |
| |
| /** |
| * validate the input Stream |
| * |
| * <p>After validation the getGenerator method can be called to get the generator of the validated file</p> |
| */ |
| public boolean validateStream(PrintStream aOut, InputStream aInputStream, String aBaseURI, |
| OdfValidatorMode eMode, |
| SAXParseExceptionFilter aFilter) throws ODFValidatorException { |
| ODFStreamValidator aStreamValidator = |
| new ODFStreamValidator(aInputStream, aBaseURI, m_nLogLevel, eMode, m_aVersion, aFilter, this); |
| boolean result = aStreamValidator.validate(aOut); |
| m_aGenerator = aStreamValidator.getGenerator(); |
| mOdfPackageVersion = aStreamValidator.mOdfPackageVersion; |
| return result; |
| } |
| |
| public Validator getManifestValidator(PrintStream aOut, OdfVersion aVersion) throws ODFValidatorException { |
| return getValidatorForSchema(aOut, getSchemaFileName(Configuration.MANIFEST_SCHEMA, aVersion)); |
| } |
| |
| public Validator getValidator(PrintStream aOut, OdfVersion aVersion) throws ODFValidatorException { |
| return getValidatorForSchema(aOut, getSchemaFileName(Configuration.SCHEMA, aVersion)); |
| } |
| |
| public Validator getStrictValidator(PrintStream aOut, OdfVersion aVersion) throws ODFValidatorException { |
| return getValidatorForSchema(aOut, getSchemaFileName(Configuration.STRICT_SCHEMA, aVersion)); |
| } |
| |
| public Validator getMathMLValidator(PrintStream aOut, OdfVersion aVersion) throws ODFValidatorException { |
| return getValidatorForSchema(aOut, getSchemaFileName(Configuration.MATHML2_SCHEMA, aVersion)); |
| } |
| |
| public String getMathMLDTDSystemId(OdfVersion aVersion) throws ODFValidatorException { |
| String aDTD = null; |
| if (m_bUseMathDTD) { |
| Configuration aConfig = |
| m_aConfig != null ? m_aConfig : getConfiguration(aVersion); |
| |
| aDTD = aConfig.getProperty(Configuration.MATHML_SCHEMA); |
| if (m_aConfig == null && aDTD != null && aDTD.length() > 0) { |
| aDTD = InternalResources.createInternalResourceIdentifier(aDTD); |
| } |
| } |
| |
| return aDTD; |
| } |
| |
| public Validator getDSigValidator(PrintStream aOut, OdfVersion aVersion) throws ODFValidatorException { |
| return getValidatorForSchema(aOut, getSchemaFileName(Configuration.DSIG_SCHEMA, aVersion)); |
| } |
| |
| public void resetValidatorProvider() { |
| m_aRNGSchemaFactory = null; |
| m_aXSDSchemaFactory = null; |
| m_aSchemaMap = null; |
| } |
| |
| private String getSchemaFileName(String aConfigName, OdfVersion aVersion) throws ODFValidatorException { |
| Configuration aConfig = |
| m_aConfig != null ? m_aConfig : getConfiguration(aVersion); |
| |
| String aFileName = aConfig.getProperty(aConfigName); |
| if (m_aConfig == null && aFileName != null && aFileName.length() > 0) { |
| aFileName = InternalResources.createInternalResourceIdentifier(aFileName); |
| } |
| |
| return aFileName; |
| } |
| |
| private Configuration getConfiguration(OdfVersion aVersion) throws ODFValidatorException { |
| if (m_aConfigurationMap == null) { |
| m_aConfigurationMap = new HashMap<OdfVersion, Configuration>(); |
| } |
| |
| Configuration aConfig = m_aConfigurationMap.get(aVersion); |
| if (aConfig == null) { |
| String aConfigName = null; |
| if (aVersion == null || aVersion == OdfVersion.V1_2) { |
| aConfigName = "/schema/odf1_2.properties"; |
| } else if (aVersion == OdfVersion.V1_1) { |
| aConfigName = "/schema/odf1_1.properties"; |
| } else if (aVersion == OdfVersion.V1_0) { |
| aConfigName = "/schema/odf1_0.properties"; |
| } else { |
| throw new ODFValidatorException("unsupported ODF version: ".concat(aVersion.toString())); |
| } |
| |
| InputStream aInStream = getClass().getResourceAsStream(aConfigName); |
| if (aInStream == null) { |
| throw new ODFValidatorException("Internal configuration file is missing: ".concat(aConfigName)); |
| } |
| |
| aConfig = new Configuration(); |
| try { |
| aConfig.loadFromXML(aInStream); |
| aInStream.close(); |
| } catch (IOException e) { |
| throw new ODFValidatorException(e); |
| } |
| |
| m_aConfigurationMap.put(aVersion, aConfig); |
| } |
| |
| return aConfig; |
| } |
| |
| private Validator getValidatorForSchema(PrintStream aOut, String aSchemaFileName) throws ODFValidatorException { |
| if (m_aSchemaMap == null) { |
| m_aSchemaMap = new HashMap<String, Schema>(); |
| } |
| |
| Schema aSchema = m_aSchemaMap.get(aSchemaFileName); |
| if (aSchema == null) { |
| aSchema = createSchema(aOut, aSchemaFileName); |
| m_aSchemaMap.put(aSchemaFileName, aSchema); |
| } |
| |
| return aSchema.newValidator(); |
| } |
| |
| private Schema createSchema(PrintStream aOut, String aSchemaFileName) throws ODFValidatorException { |
| Logger aLogger = new Logger(aSchemaFileName, "", aOut, m_nLogLevel); |
| |
| if (aSchemaFileName == null || aSchemaFileName.length() == 0) { |
| return null; |
| } |
| |
| String aSchemaLanguage = |
| aSchemaFileName.endsWith("xsd") ? XMLConstants.W3C_XML_SCHEMA_NS_URI |
| : XMLConstants.RELAXNG_NS_URI; |
| |
| |
| SchemaFactory aSchemaFactory = getSchemaFactory(aSchemaLanguage); |
| SchemaErrorHandler aErrorHandler = new SchemaErrorHandler(aLogger, null); |
| aSchemaFactory.setErrorHandler(aErrorHandler); |
| aSchemaFactory.setResourceResolver(new SchemaResourceResolver(aLogger, aSchemaFileName)); |
| Schema aSchema = null; |
| |
| StreamSource aSource = null; |
| if (InternalResources.isInternalResourceIdentifer(aSchemaFileName)) { |
| String aPath = InternalResources.getResourcePath(aSchemaFileName); |
| InputStream aInStream = getClass().getResourceAsStream(aPath); |
| if (aInStream == null) { |
| throw new ODFValidatorException("Internal schema file is missing: ".concat(aPath)); |
| } |
| |
| aSource = new StreamSource(aInStream, aSchemaFileName); |
| } else { |
| File aFile = new File(aSchemaFileName); |
| if (!aFile.exists()) { |
| aLogger.logFatalError(aSchemaFileName + ": file does not exist"); |
| return null; |
| } |
| aSource = new StreamSource(aFile); |
| } |
| |
| // Workaround: MSV seems not to call error handler |
| try { |
| aSchema = aSchemaFactory.newSchema(aSource); |
| } catch (org.xml.sax.SAXParseException e) { |
| aLogger.logFatalError(e); |
| } catch (org.xml.sax.SAXException e) { |
| aLogger.logFatalError(e.getMessage()); |
| } |
| |
| if (aLogger.hasError()) { |
| throw new ODFValidatorException(aSchemaFileName, "", "Schema has validation errors."); |
| } |
| |
| aLogger.logInfo("parsed.", false); |
| return aSchema; |
| } |
| |
| private SchemaFactory getSchemaFactory(String aSchemaLanguage) throws ODFValidatorException { |
| boolean isRNG = aSchemaLanguage.equals(XMLConstants.RELAXNG_NS_URI); |
| SchemaFactory aSchemaFactory = isRNG ? m_aRNGSchemaFactory |
| : m_aXSDSchemaFactory; |
| if (aSchemaFactory == null) { |
| try { |
| if (isRNG) { |
| System.setProperty("javax.xml.validation.SchemaFactory:" + aSchemaLanguage, "org.iso_relax.verifier.jaxp.validation.RELAXNGSchemaFactoryImpl"); |
| SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI); |
| m_aRNGSchemaFactory = SchemaFactory.newInstance(aSchemaLanguage); |
| aSchemaFactory = m_aRNGSchemaFactory; |
| } else { |
| System.setProperty("javax.xml.validation.SchemaFactory:" + aSchemaLanguage, "org.apache.xerces.jaxp.validation.XMLSchemaFactory"); |
| m_aXSDSchemaFactory = SchemaFactory.newInstance(aSchemaLanguage); |
| aSchemaFactory = m_aXSDSchemaFactory; |
| } |
| } catch (IllegalArgumentException e) { |
| throw new ODFValidatorException(aSchemaLanguage + " support is not installed: " + e.getMessage()); |
| } |
| } |
| return aSchemaFactory; |
| } |
| |
| /** |
| * get Generator from last validateFile or validateStream call |
| */ |
| public String getGenerator() { |
| return m_aGenerator; |
| } |
| } |