/*
* Copyright (c) 2010-2014 Evolveum
*
* 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 com.evolveum.midpoint.prism.schema;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.cxf.wsdl.WSDLConstants;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
public class SchemaDescription implements DebugDumpable {
private static final Trace LOGGER = TraceManager.getTrace(SchemaDescription.class);
private String path;
private String usualPrefix;
private String namespace;
private String sourceDescription;
private InputStreamable streamable;
private Node node;
private boolean isPrismSchema = false;
private boolean isDefault = false;
private boolean isDeclaredByDefault = false;
private PrismSchema schema;
private Package compileTimeClassesPackage;
private Map<QName, Class<?>> xsdTypeTocompileTimeClassMap;
private SchemaDescription(String sourceDescription) {
this.sourceDescription = sourceDescription;
}
public String getPath() {
return path;
}
public void setResourcePath(String path) {
this.path = path;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getUsualPrefix() {
return usualPrefix;
}
public void setUsualPrefix(String usualPrefix) {
this.usualPrefix = usualPrefix;
}
public String getSourceDescription() {
return sourceDescription;
}
public void setSourceDescription(String sourceDescription) {
this.sourceDescription = sourceDescription;
}
public void setPath(String path) {
this.path = path;
}
public boolean isPrismSchema() {
return isPrismSchema;
}
public void setPrismSchema(boolean isMidPointSchema) {
this.isPrismSchema = isMidPointSchema;
}
public boolean isDefault() {
return isDefault;
}
public void setDefault(boolean isDefault) {
this.isDefault = isDefault;
}
public boolean isDeclaredByDefault() {
return isDeclaredByDefault;
}
public void setDeclaredByDefault(boolean isDeclaredByDefault) {
this.isDeclaredByDefault = isDeclaredByDefault;
}
public PrismSchema getSchema() {
return schema;
}
public void setSchema(PrismSchema schema) {
this.schema = schema;
}
public Package getCompileTimeClassesPackage() {
return compileTimeClassesPackage;
}
public void setCompileTimeClassesPackage(Package compileTimeClassesPackage) {
this.compileTimeClassesPackage = compileTimeClassesPackage;
}
public Map<QName, Class<?>> getXsdTypeTocompileTimeClassMap() {
return xsdTypeTocompileTimeClassMap;
}
public void setXsdTypeTocompileTimeClassMap(Map<QName, Class<?>> xsdTypeTocompileTimeClassMap) {
this.xsdTypeTocompileTimeClassMap = xsdTypeTocompileTimeClassMap;
}
public static SchemaDescription parseResource(final String resourcePath) throws SchemaException {
SchemaDescription desc = new SchemaDescription("system resource "+resourcePath);
desc.path = resourcePath;
desc.streamable = new InputStreamable() {
@Override
public InputStream openInputStream() {
InputStream inputStream = SchemaRegistry.class.getClassLoader().getResourceAsStream(resourcePath);
if (inputStream == null) {
throw new IllegalStateException("Cannot fetch system resource for schema " + resourcePath);
}
return inputStream;
}
};
desc.parseFromInputStream();
return desc;
}
public static List<SchemaDescription> parseWsdlResource(final String resourcePath) throws SchemaException {
List<SchemaDescription> schemaDescriptions = new ArrayList<>();
InputStream inputStream = SchemaRegistry.class.getClassLoader().getResourceAsStream(resourcePath);
if (inputStream == null) {
throw new IllegalStateException("Cannot fetch system resource for schema " + resourcePath);
}
Node node;
try {
node = DOMUtil.parse(inputStream);
} catch (IOException e) {
throw new SchemaException("Cannot parse schema from system resource " + resourcePath, e);
}
Element rootElement = node instanceof Element ? (Element)node : DOMUtil.getFirstChildElement(node);
QName rootElementQName = DOMUtil.getQName(rootElement);
if (WSDLConstants.QNAME_DEFINITIONS.equals(rootElementQName)) {
Element types = DOMUtil.getChildElement(rootElement, WSDLConstants.QNAME_TYPES);
if (types == null) {
LOGGER.warn("No <types> section in WSDL document in system resource " + resourcePath);
return schemaDescriptions;
}
List<Element> schemaElements = DOMUtil.getChildElements(types, DOMUtil.XSD_SCHEMA_ELEMENT);
if (schemaElements.isEmpty()) {
LOGGER.warn("No schemas in <types> section in WSDL document in system resource " + resourcePath);
return schemaDescriptions;
}
int number = 1;
for (Element schemaElement : schemaElements) {
SchemaDescription desc = new SchemaDescription("schema #" + (number++) + " in system resource " + resourcePath);
desc.node = schemaElement;
desc.fetchBasicInfoFromSchema();
schemaDescriptions.add(desc);
LOGGER.trace("Schema registered from {}", desc.getSourceDescription());
}
return schemaDescriptions;
} else {
throw new SchemaException("WSDL system resource "+resourcePath+" does not start with wsdl:definitions element");
}
}
public static SchemaDescription parseFile(final File file) throws FileNotFoundException, SchemaException {
SchemaDescription desc = new SchemaDescription("file "+file.getPath());
desc.path = file.getPath();
desc.streamable = new InputStreamable() {
@Override
public InputStream openInputStream() {
InputStream inputStream;
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new IllegalStateException("Cannot fetch file for schema " + file,e);
}
return inputStream;
}
};
desc.parseFromInputStream();
return desc;
}
private void parseFromInputStream() throws SchemaException {
InputStream inputStream = streamable.openInputStream();
try {
node = DOMUtil.parse(inputStream);
} catch (IOException e) {
throw new SchemaException("Cannot parse schema from " + sourceDescription, e);
}
fetchBasicInfoFromSchema();
}
public static SchemaDescription parseNode(Node node, String sourceDescription) throws SchemaException {
SchemaDescription desc = new SchemaDescription(sourceDescription);
desc.node = node;
desc.fetchBasicInfoFromSchema();
return desc;
}
private void fetchBasicInfoFromSchema() throws SchemaException {
Element rootElement = getDomElement();
if (DOMUtil.XSD_SCHEMA_ELEMENT.equals(DOMUtil.getQName(rootElement))) {
String targetNamespace = DOMUtil.getAttribute(rootElement,DOMUtil.XSD_ATTR_TARGET_NAMESPACE);
if (targetNamespace != null) {
this.namespace = targetNamespace;
} else {
throw new SchemaException("Schema "+sourceDescription+" does not have targetNamespace attribute");
}
} else {
throw new SchemaException("Schema "+sourceDescription+" does not start with xsd:schema element");
}
}
public boolean canInputStream() {
return (streamable != null);
}
public InputStream openInputStream() {
if (!canInputStream()) {
throw new IllegalStateException("Schema "+sourceDescription+" cannot provide input stream");
}
return streamable.openInputStream();
}
public Source getSource() {
Source source = null;
if (canInputStream()) {
InputStream inputStream = openInputStream();
// Return stream source as a first option. It is less effcient,
// but it provides information about line numbers
source = new StreamSource(inputStream);
} else {
source = new DOMSource(node);
}
source.setSystemId(path);
return source;
}
public Element getDomElement() {
if (node instanceof Element) {
return (Element)node;
}
return DOMUtil.getFirstChildElement(node);
}
private interface InputStreamable {
InputStream openInputStream();
}
@Override
public String debugDump() {
return debugDump(0);
}
@Override
public String debugDump(int indent) {
StringBuilder sb = new StringBuilder();
DebugUtil.indentDebugDump(sb, indent);
sb.append(path);
if (schema != null) {
sb.append(" ");
sb.append(schema.toString());
}
return sb.toString();
}
}