/*
* Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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
*
* 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.wso2.carbon.registry.extensions.handlers.utils;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaExternal;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.wso2.carbon.registry.core.*;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.extensions.utils.CommonConstants;
import org.xml.sax.InputSource;
import java.io.ByteArrayOutputStream;
import java.util.*;
public class SchemaFileProcessor {
private Registry registry;
private ArrayList schemaPath;
/**
* Buffer to hold associations untill all the resources are added. We should add associations
* only after both ends (resources) of the association is added to the registry. Otherwise, it
* will cause an error as the registry tries to find the both end resources before setting the
* association.
*/
private List<Association> associationsBuffer = new ArrayList<Association>();
// remove this when it is not needed
private int i;
public SchemaFileProcessor(Registry registry) {
this.registry = registry;
schemaPath = new ArrayList();
}
/**
* Import a schema file to the registry after saving all its includes and imports to the
* registry and updating the schema locations accordingly.
*
* @param location the original schema location
* @param registryBasePath base path of the registry
* @param processIncludes true if we should recurse through includes
* @param metadata template Resource from which to obtain media-type, description, etc.
* @return the resulting path of the new resource
* @throws org.wso2.carbon.registry.core.exceptions.RegistryException
*
*/
public String saveSchemaFileToRegistry(String location,
String registryBasePath,
boolean processIncludes,
Resource metadata)
throws RegistryException {
return saveSchemaFileToRegistry(location, new HashMap(), registryBasePath, processIncludes,
metadata);
}
/**
* Import a schema file to the registry after saving all its includes and imports to the
* registry and updating the schema locations accordingly.
*
* @param location the original schema location
* @param processedSchemaMap a Map from original URI (String) to new schema location (String)
* @param registryBasePath base path of the registry
* @param processIncludes true if we should recurse through includes
* @param metadata template Resource from which to obtain media-type, description,
* etc.
* @return the resulting path of the new resource
* @throws RegistryException
*/
public String saveSchemaFileToRegistry(String location,
Map processedSchemaMap,
String registryBasePath,
boolean processIncludes,
Resource metadata)
throws RegistryException {
XmlSchemaCollection xmlSchemaCollection = new XmlSchemaCollection();
InputSource inputSource = new InputSource(location);
// Here we assue schema is correct. Schema validation is beyond our scope, so we don't
// bother with a ValidationEventHandler.
XmlSchema xmlSchema = xmlSchemaCollection.read(inputSource, null);
String baseUri = xmlSchema.getSourceURI();
String xsdFileName = baseUri.substring(baseUri.lastIndexOf("/") + 1);
String savedName = xsdFileName.substring(0, xsdFileName.indexOf(".")) + ".xsd";
// this is not an inline wsdl schema. so pass null to change map.
calculateNewSchemaNames(xmlSchema, processedSchemaMap, new HashSet(), false,
processIncludes);
saveSchemaFileToRegistry(xmlSchema, processedSchemaMap, null, new HashSet(), false,
registryBasePath, processIncludes, metadata);
return savedName;
}
/**
* calculate the new schema file names to save the schema. Here we can not save the schema file
* as it is since there may be recursive imports. So what we have to do is to first determine
* the schema names to be saved and then change the schema locations accordingly. In this method
* first we iterate through the imports and includes and find the names. have used the
* visitedSchemas variable to keep track of the visited schemas to avoid the recursion.
*
* @param xmlSchema the schema to we'd like to save into the registry
* @param processedSchemaMap a Map from original URI (String) to new schema location (String)
* @param visitedSchemas a Set of previously visited schema source uris
* @param isWSDLInlineSchema true if the given schema is an inline schema of a wsdl - if so, we
* do not need to calculate a name for it
* @param processIncludes true if we should process includes
*/
public void calculateNewSchemaNames(XmlSchema xmlSchema,
Map processedSchemaMap,
Set visitedSchemas,
boolean isWSDLInlineSchema,
boolean processIncludes) {
if (processIncludes) {
// first process the imports and includes
XmlSchemaObjectCollection includes = xmlSchema.getIncludes();
// set this as an visited schema to stop recursion
visitedSchemas.add(xmlSchema.getSourceURI());
if (includes != null) {
Object externalComponent;
XmlSchemaExternal xmlSchemaExternal;
XmlSchema innerSchema;
for (Iterator iter = includes.getIterator(); iter.hasNext();) {
externalComponent = iter.next();
if (externalComponent instanceof XmlSchemaExternal) {
xmlSchemaExternal = (XmlSchemaExternal)externalComponent;
innerSchema = xmlSchemaExternal.getSchema();
String sourceURI = innerSchema.getSourceURI();
// Process if we haven't already encountered this one
if (!processedSchemaMap.containsKey(sourceURI) &&
!visitedSchemas.contains(sourceURI)) {
calculateNewSchemaNames(innerSchema, processedSchemaMap, visitedSchemas,
false, processIncludes);
}
}
}
}
}
// after processing includes and imports save the xml schema
if (!isWSDLInlineSchema) {
String baseUri = xmlSchema.getSourceURI();
String xsdFileName = baseUri.substring(baseUri.lastIndexOf("/") + 1);
String fileNameToSave = xsdFileName.substring(0, xsdFileName.indexOf(".")) + ".xsd";
while (processedSchemaMap.containsValue(fileNameToSave)) {
fileNameToSave = xsdFileName.substring(0, xsdFileName.indexOf(".")) + ++i + ".xsd";
}
// add this entry to the processed wsdl map
processedSchemaMap.put(baseUri, fileNameToSave);
}
}
/**
* Save the schemas to the registry. used the calculated names in the processedSchemaMap to
* change the schema locations.
*
* @param xmlSchema the schema to save
* @param processedSchemaMap a Map from original URI (String) to new schema location (String)
* @param changeSchemaNames a Map from original URIs to changed URIs. Used to update the WSDL
* inline schema imports/includes when saving WSDLs.
* @param visitedSchemas a Set of schema URIs (Strings) that we've already visited
* @param isWSDLInlineSchema true if this is an inline schema from a WSDL
* @param registryBasePath the base path of the registry
* @param processIncludes true if we should recurse into includes of this schema
* @param metadata template Resource for metadata (media-type, description)
* @throws RegistryException
*/
public void saveSchemaFileToRegistry(XmlSchema xmlSchema,
Map processedSchemaMap,
Map changeSchemaNames,
Set visitedSchemas,
boolean isWSDLInlineSchema,
String registryBasePath,
boolean processIncludes,
Resource metadata) throws RegistryException {
List associations = new ArrayList();
if (processIncludes) {
// first process the imports and includes
XmlSchemaObjectCollection includes = xmlSchema.getIncludes();
// set this as an visited schema to stop recursion
visitedSchemas.add(xmlSchema.getSourceURI());
if (includes != null) {
for (Iterator iter = includes.getIterator(); iter.hasNext();) {
Object externalComponent = iter.next();
if (externalComponent instanceof XmlSchemaExternal) {
XmlSchemaExternal xmlSchemaExternal = (XmlSchemaExternal)externalComponent;
String sourceURI = xmlSchemaExternal.getSchema().getSourceURI();
if (!visitedSchemas.contains(sourceURI)) {
saveSchemaFileToRegistry(xmlSchemaExternal.getSchema(),
processedSchemaMap, null, visitedSchemas,
false, registryBasePath, processIncludes,
null);
}
// add the new name to changeschema map
// have to do before change the schema location
String newLocation = (String)processedSchemaMap.get(sourceURI);
if (isWSDLInlineSchema) {
changeSchemaNames.put(xmlSchemaExternal.getSchemaLocation(),
newLocation);
} else {
}
// set the new location
xmlSchemaExternal.setSchemaLocation(newLocation);
String innerFileNameToSave = (String)processedSchemaMap.get(sourceURI);
String innerXSDPath = getXSDPath(registryBasePath, innerFileNameToSave);
associations.add(innerXSDPath);
}
}
}
}
// after processing includes and imports save the xml schema
if (!isWSDLInlineSchema) {
String fileNameToSave = (String)processedSchemaMap.get(xmlSchema.getSourceURI());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
xmlSchema.write(byteArrayOutputStream);
byte[] xsdContent = byteArrayOutputStream.toByteArray();
Resource xsdResource = new ResourceImpl();
if (metadata != null) {
xsdResource.setMediaType(metadata.getMediaType());
xsdResource.setDescription(metadata.getDescription());
}
xsdResource.setContent(xsdContent);
String xsdPath = getXSDPath(registryBasePath, fileNameToSave);
String targetNamespace = xmlSchema.getTargetNamespace();
xsdResource.addProperty("targetNamespace", targetNamespace);
registry.put(xsdPath, xsdResource);
schemaPath.add(xsdPath);
String associationPath;
for (Object association : associations) {
associationPath = (String)association;
associationsBuffer.add(new Association(xsdPath, associationPath,
CommonConstants.DEPENDS));
}
}
}
private String getXSDPath(String registryBasePath, String fileNameToSave) {
String xsdPath;
if (RegistryConstants.ROOT_PATH.equals(registryBasePath)) {
xsdPath = RegistryConstants.ROOT_PATH + fileNameToSave;
} else {
xsdPath = registryBasePath + RegistryConstants.PATH_SEPARATOR + fileNameToSave;
}
return xsdPath;
}
public void persistAssociations() throws RegistryException {
Iterator<Association> associationIterator = associationsBuffer.iterator();
while (associationIterator.hasNext()) {
Association association = associationIterator.next();
registry.addAssociation(association.getSourcePath(), association.getDestinationPath(),
association.getAssociationType());
}
}
public ArrayList getSchemaPath() {
return schemaPath;
}
}