/*
* Copyright (C) 2005-2012 BetaCONCEPT Limited
*
* This file is part of Astroboa.
*
* Astroboa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Astroboa is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Astroboa. If not, see <http://www.gnu.org/licenses/>.
*/
package org.betaconceptframework.astroboa.engine.definition.xsom;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.BetaConceptNamespaceConstants;
import org.betaconceptframework.astroboa.configuration.W3CRelatedSchemaEntityResolver;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Used to resolve imports from definition xsd files
* and mainly to provide the right xsd definition file
* for internal astroboa types
*
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*
*/
public class EntityResolverForBuiltInSchemas implements EntityResolver{
private final Logger logger = LoggerFactory.getLogger(getClass());
private W3CRelatedSchemaEntityResolver w3cRelatedSchemaEntityResolver = new W3CRelatedSchemaEntityResolver();
private String builtInDefinitionSchemaHomeDir;
public void setBuiltInDefinitionSchemaHomeDir(
String builtInDefinitionSchemaHomeDir) {
this.builtInDefinitionSchemaHomeDir = builtInDefinitionSchemaHomeDir;
}
public String getBuiltInDefinitionSchemaHomeDir() {
return builtInDefinitionSchemaHomeDir;
}
@Override
/**
* According to XSOM library
* By setting EntityResolver to XSOMParser, you can redirect <xs:include>s and <xs:import>s to different resources.
* For imports, the namespace URI of the target schema is passed as the public ID,
* and the absolutized value of the schemaLocation attribute will be passed as the system ID.
*/
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
if (publicId != null && publicId.startsWith(BetaConceptNamespaceConstants.ASTROBOA_SCHEMA_URI) &&
systemId != null){
try {
String schemaFilename = systemId;
//We are only interested in file name
if (schemaFilename.contains(CmsConstants.FORWARD_SLASH)){
schemaFilename = StringUtils.substringAfterLast(systemId, CmsConstants.FORWARD_SLASH);
}
URL definitionFileURL = locateBuiltinDefinitionURL(schemaFilename);
if (definitionFileURL != null){
InputSource is = new InputSource( definitionFileURL.openStream() );
/*
* SystemId is actual the path to the resource
* although its content has been loaded to input source.
* Provided value (sustemId) is not set because if in XSD file in the corresponding import's
* schemaLocation contains only the schema filename, then XSOM parser will
* consider it as a relative path and will prefix it with the correct absolute path.
*
* For example, in cases where two different schemas, located in two different directories (portal-1.0.xsd and basicText-1.0.xsd),
* import schema astroboa-model-1.2.xsd, xs import will look like
*
* <xs:import
namespace="http://www.betaconceptframework.org/schema/astroboa/model"
schemaLocation="astroboa-model-1.2.xsd" />
*
* When XSOM parser will try to load astroboa-model-1.2.xsd, it will append
* schemaLocation with the path of the directory where each of the parent XSDs are located.
*
* SchemaLocation value refers to systemId and if it is provided in this input source, XSOM parser
* will have two different instances of InputSource referring to the same xsd file (definitionFileURL),
* having the same publiId (namespace) but different systemIds (appended schemaLocation).
*
* This situation causes XSOM parser to throw an exception when the second input source is loaded
* complaining that it found the same type(s) defined already, which is true since as far as XSOM parser
* concerns these input sources are not the same.
*
* To overcome this, we set as system id the URL of the XSD file which is the same
* in both situations.
*/
is.setSystemId(definitionFileURL.toString());
is.setPublicId(publicId);
if (systemId.startsWith(BetaConceptNamespaceConstants.ASTROBOA_SCHEMA_URI)){
logger.warn("Schema Location for XSD Schema "+ schemaFilename + ", which contains built in Astroboa model, is not relative but absolute." +
" Unless this absolute location really 'serves' XSD, there will be a problem " +
" when importing XML which contain xml elements derived from this Schema If this location is not real, then you are advised to delete it and leave only" +
"the schema file name. Nevertheless the contents of "+ schemaFilename+ " have been found internally and have been successfully loaded to Astroboa");
}
return is;
}
} catch (Exception e) {
throw new IOException(e);
}
}
//Returning null allow XSD Parser to continue with default behavior
//and will try to resolve entity using its own implementation
return resolveXmlSchemaRelatedToW3C(publicId, systemId);
}
public URL locateBuiltinDefinitionURL(String schemaFilename) throws IOException,
Exception {
//Any other file will reside in /META-INF/builtin-definition-schemas-directory
return loadDefinitionFile(builtInDefinitionSchemaHomeDir+CmsConstants.FORWARD_SLASH+schemaFilename);
}
public URL loadDefinitionFile(String absolutePath) throws Exception {
return EntityResolverForBuiltInSchemas.class.getResource(absolutePath);
}
/**
* Resolves Entities with the following properties
*
* publicId = http://www.w3.org/XML/1998/namespace
* systemId = http://www.w3.org/2001/03/xml.xsd
*
* publicId=-//W3C//DTD XMLSCHEMA 200102//EN
* systemId=http://www.w3.org/2001/03/XMLSchema.dtd
*
* publicId = -//W3C//DTD XMLSCHEMA 200102//EN
* systemId = http://www.w3.org/2001/03/XMLSchema.dtd
*
* If above schemata are not reachable (probably due to a lack of Internet access),
* they are loaded from the package
*
* @return
* @throws IOException
* @throws SAXException
*/
public InputSource resolveXmlSchemaRelatedToW3C(String publicId, String systemId) throws IOException, SAXException{
return w3cRelatedSchemaEntityResolver.resolveEntity(publicId, systemId);
}
}