blob: fc97384f3f85b6ded51ea8d4f23f2aa769ae7b81 [file] [log] [blame]
/**
* Copyright (c) 2002-2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*/
package org.eclipse.xsd.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDDiagnostic;
import org.eclipse.xsd.XSDDiagnosticSeverity;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFactory;
import org.eclipse.xsd.XSDNamedComponent;
import org.eclipse.xsd.XSDPackage;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDPlugin;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDTerm;
import org.eclipse.xsd.XSDWildcard;
import org.eclipse.xsd.impl.XSDNamedComponentImpl;
import org.eclipse.xsd.impl.XSDSchemaImpl;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
*/
public final class XSDUtil extends XSDConstants
{
/**
* Returns the singleton instance of the schema for schemas.
*/
public static XSDSchema getSchemaForSchema(String schemaForSchemaURI)
{
return XSDSchemaImpl.getSchemaForSchema(schemaForSchemaURI);
}
/**
* A cross referencer that finds each usage of an EObject or collection of EObjects,
* excluding uninteresting derived references.
*/
public static class UsageCrossReferencer extends EcoreUtil.UsageCrossReferencer
{
private static final long serialVersionUID = 1L;
protected Collection<?> eObjectsOfInterest;
protected UsageCrossReferencer(EObject eObject)
{
super(eObject);
}
protected UsageCrossReferencer(Resource resource)
{
super(resource);
}
protected UsageCrossReferencer(ResourceSet resourceSet)
{
super(resourceSet);
}
protected UsageCrossReferencer(Collection<?> emfObjects)
{
super(emfObjects);
}
@Override
protected boolean crossReference(EObject eObject, EReference eReference, EObject crossReferencedEObject)
{
return
!eReference.isVolatile() &&
eReference.isChangeable() &&
eObjectsOfInterest.contains(crossReferencedEObject);
}
@Override
protected Collection<EStructuralFeature.Setting> findUsage(EObject eObject)
{
eObjectsOfInterest = Collections.singleton(eObject);
crossReference();
this.eObjectsOfInterest = null;
done();
return getCollection(eObject);
}
@Override
protected Map<EObject, Collection<EStructuralFeature.Setting>> findAllUsage(Collection<?> eObjectsOfInterest)
{
this.eObjectsOfInterest = eObjectsOfInterest;
crossReference();
this.eObjectsOfInterest = null;
done();
return this;
}
public static Collection<EStructuralFeature.Setting> find(EObject eObjectOfInterest, EObject eObject)
{
return new UsageCrossReferencer(eObject).findUsage(eObjectOfInterest);
}
public static Collection<EStructuralFeature.Setting> find(EObject eObjectOfInterest, Resource resource)
{
return new UsageCrossReferencer(resource).findUsage(eObjectOfInterest);
}
public static Collection<EStructuralFeature.Setting> find(EObject eObjectOfInterest, ResourceSet resourceSet)
{
return new UsageCrossReferencer(resourceSet).findUsage(eObjectOfInterest);
}
public static Collection<EStructuralFeature.Setting> find(EObject eObjectOfInterest, Collection<?> emfObjectsToSearch)
{
return new UsageCrossReferencer(emfObjectsToSearch).findUsage(eObjectOfInterest);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<?> eObjectsOfInterest, EObject eObject)
{
return new UsageCrossReferencer(eObject).findAllUsage(eObjectsOfInterest);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<?> eObjectsOfInterest, Resource resource)
{
return new UsageCrossReferencer(resource).findAllUsage(eObjectsOfInterest);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<?> eObjectsOfInterest, ResourceSet resourceSet)
{
return new UsageCrossReferencer(resourceSet).findAllUsage(eObjectsOfInterest);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<?> eObjectsOfInterest, Collection<?> emfObjectsToSearch)
{
return new UsageCrossReferencer(emfObjectsToSearch).findAllUsage(eObjectsOfInterest);
}
}
/**
* A cross referencer that finds all XSDNamedComponents and each usage of an XSDNamedComponent.
*/
public static class XSDNamedComponentCrossReferencer extends EcoreUtil.CrossReferencer
{
private static final long serialVersionUID = 1L;
protected XSDNamedComponentCrossReferencer(EObject eObject)
{
super(eObject);
}
protected XSDNamedComponentCrossReferencer(Resource resource)
{
super(resource);
}
protected XSDNamedComponentCrossReferencer(ResourceSet resourceSet)
{
super(resourceSet);
}
protected XSDNamedComponentCrossReferencer(Collection<?> emfObjects)
{
super(emfObjects);
}
@Override
protected boolean containment(EObject eObject)
{
// Create an empty setting collection for any named component.
//
if (eObject instanceof XSDNamedComponent)
{
getCollection(eObject);
}
return true;
}
@Override
protected boolean crossReference(EObject eObject, EReference eReference, EObject crossReferencedEObject)
{
// Add a setting for any named component in an interesting reference.
//
return
!eReference.isVolatile() &&
eReference.isChangeable() &&
crossReferencedEObject instanceof XSDNamedComponent;
}
/**
* Returns a map of all XSDNamedComponent cross references in the content tree.
*/
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(EObject eObject)
{
XSDNamedComponentCrossReferencer result = new XSDNamedComponentCrossReferencer(eObject);
result.crossReference();
result.done();
return result;
}
/**
* Returns a map of all XSDNamedComponent cross references in the content tree.
*/
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(Resource resource)
{
XSDNamedComponentCrossReferencer result = new XSDNamedComponentCrossReferencer(resource);
result.crossReference();
result.done();
return result;
}
/**
* Returns a map of all XSDNamedComponent cross references in the content tree.
*/
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(ResourceSet resourceSet)
{
XSDNamedComponentCrossReferencer result = new XSDNamedComponentCrossReferencer(resourceSet);
result.crossReference();
result.done();
return result;
}
}
/**
* A cross referencer that finds each usage of URI or collection of URIs.
*/
public static class URICrossReferencer extends EcoreUtil.CrossReferencer
{
private static final long serialVersionUID = 1L;
protected String [] uris;
protected URICrossReferencer(EObject eObject)
{
super(eObject);
}
protected URICrossReferencer(Resource resource)
{
super(resource);
}
protected URICrossReferencer(ResourceSet resourceSet)
{
super(resourceSet);
}
protected URICrossReferencer(Collection<?> emfObjects)
{
super(emfObjects);
}
@Override
protected boolean containment(EObject eObject)
{
// Create an empty setting collection for any named component.
//
if (eObject instanceof XSDNamedComponent)
{
XSDNamedComponent xsdNamedComponent = (XSDNamedComponent)eObject;
for (int i = 0; i < uris.length; ++i)
{
String uri = uris[i];
if (xsdNamedComponent.hasURI(uri))
{
getCollection(eObject);
break;
}
}
}
return true;
}
@Override
protected boolean crossReference(EObject eObject, EReference eReference, EObject crossReferencedEObject)
{
if (crossReferencedEObject instanceof XSDNamedComponent &&
!eReference.isVolatile() &&
eReference.isChangeable())
{
XSDNamedComponent xsdNamedComponent = (XSDNamedComponent)crossReferencedEObject;
for (int i = 0; i < uris.length; ++i)
{
String uri = uris[i];
if (xsdNamedComponent.hasURI(uri))
{
return true;
}
}
}
return false;
}
protected Map<EObject, Collection<EStructuralFeature.Setting>> findURI(String uri)
{
uris = new String [] { uri };
crossReference();
uris = null;
done();
return this;
}
protected Map<EObject, Collection<EStructuralFeature.Setting>> findAllURI(Collection<String> uris)
{
this.uris = uris.toArray(new String [uris.size()]);
crossReference();
uris = null;
done();
return this;
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(String uri, EObject eObject)
{
return new URICrossReferencer(eObject).findURI(uri);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(String uri, Resource resource)
{
return new URICrossReferencer(resource).findURI(uri);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(String uri, ResourceSet resourceSet)
{
return new URICrossReferencer(resourceSet).findURI(uri);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> find(String uri, Collection<?> emfObjectsToSearch)
{
return new URICrossReferencer(emfObjectsToSearch).findURI(uri);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<String> uris, EObject eObject)
{
return new URICrossReferencer(eObject).findAllURI(uris);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<String> uris, Resource resource)
{
return new URICrossReferencer(resource).findAllURI(uris);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<String> uris, ResourceSet resourceSet)
{
return new URICrossReferencer(resourceSet).findAllURI(uris);
}
public static Map<EObject, Collection<EStructuralFeature.Setting>> findAll(Collection<String> uris, Collection<?> emfObjectsToSearch)
{
return new URICrossReferencer(emfObjectsToSearch).findAllURI(uris);
}
}
/**
* This is a wrapper type for representing values of type hexBin or base64Binary.
*/
public static interface ByteSequence
{
byte[] getBytes();
}
/**
* Returns the matching named component in the sorted list of named components,
* or <code>null</null> if there isn't one.
* This list is expected to be one of a {@link XSDSchema schema's} list of named components.
* @param xsdNamedComponents a sorted lists of named components.
* @param namespace the namespace to search
* @param name the name to search
* @return the matching component, if any.
*/
public static XSDNamedComponent findInSortedList(List<? extends XSDNamedComponent> xsdNamedComponents, String namespace, String name)
{
return XSDNamedComponentImpl.findInSortedList(xsdNamedComponents, namespace, name);
}
/**
* Checks the given element's content against the complex type with a given name retrieved from the given schema.
* @param schema the schema used to retrieve the complex type definition.
* @param complexTypeName the name of the complex type.
* @param part annotation part.
* @param anchor annotation anchor.
* @param element the element whose content to check.
* @return a list of partially populated XSDDiagnostics---the primary component is not set---or the empty list if the content is valid.
* @since 2.5
*/
public static List<XSDDiagnostic> checkComplexContent(XSDSchema schema, String complexTypeName, String part, String anchor, Element element)
{
XSDComplexTypeDefinition xsdComplexTypeDefinition = schema.resolveComplexTypeDefinition(complexTypeName);
return checkComplexContent(xsdComplexTypeDefinition, part, anchor, element);
}
/**
* Checks the given element's content against the given complex type.
* @param xsdComplexTypeDefinition the reference type.
* @param part annotation part.
* @param anchor annotation anchor.
* @param element the element whose content to check.
* @return a list of partially populated XSDDiagnostics---the primary component is not set---or the empty list if the content is valid.
* @since 2.5
*/
public static List<XSDDiagnostic> checkComplexContent(XSDComplexTypeDefinition xsdComplexTypeDefinition, String part, String anchor, Element element)
{
List<XSDDiagnostic> result = new ArrayList<XSDDiagnostic>();
XSDParticle complexType = xsdComplexTypeDefinition.getComplexType();
boolean mixed = xsdComplexTypeDefinition.isMixed();
XSDParticle.DFA dfa = complexType.getDFA();
XSDParticle.DFA.State state = dfa.getInitialState();
XSDFactory xsdFactory = XSDPackage.eINSTANCE.getXSDFactory();
boolean invalidContentDetected = false;
for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling())
{
switch (child.getNodeType())
{
case Node.ELEMENT_NODE:
{
if (invalidContentDetected)
{
continue;
}
XSDParticle.DFA.Transition transition = state.accept(child.getNamespaceURI(), child.getLocalName());
if (transition != null)
{
state = transition.getState();
}
else
{
XSDDiagnostic xsdDiagnostic = xsdFactory.createXSDDiagnostic();
xsdDiagnostic.setSeverity(XSDDiagnosticSeverity.ERROR_LITERAL);
xsdDiagnostic.setMessage
(XSDPlugin.INSTANCE.getString
("_UI_XSDError_message",
new Object []
{
populateDiagnostic
(xsdDiagnostic,
"content-valid.1",
new Object [] { XSDConstants.uri(child), xsdComplexTypeDefinition.getURI(), getExpected(state) })
}));
xsdDiagnostic.setAnnotationURI(part + "#" + anchor);
xsdDiagnostic.setNode(child);
result.add(xsdDiagnostic);
invalidContentDetected = true;
}
break;
}
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
{
if (!mixed)
{
String text = child.getNodeValue();
if (text != null)
{
for (int i = 0, length = text.length(); i < length; ++i)
{
char character = text.charAt(i);
if (character != '\n' && character != '\r' && character != ' ' && character != '\t')
{
XSDDiagnostic xsdDiagnostic = xsdFactory.createXSDDiagnostic();
xsdDiagnostic.setSeverity(XSDDiagnosticSeverity.ERROR_LITERAL);
xsdDiagnostic.setMessage
(XSDPlugin.INSTANCE.getString
("_UI_XSDError_message",
new Object []
{
populateDiagnostic
(xsdDiagnostic,
"content-valid.3",
new Object [] { text.substring(i), xsdComplexTypeDefinition.getURI() })
}));
xsdDiagnostic.setAnnotationURI(part + "#" + anchor);
xsdDiagnostic.setNode(child);
result.add(xsdDiagnostic);
break;
}
}
}
}
break;
}
}
}
boolean incompleteContent = !invalidContentDetected && !state.isAccepting();
if (incompleteContent)
{
XSDDiagnostic xsdDiagnostic = xsdFactory.createXSDDiagnostic();
xsdDiagnostic.setSeverity(XSDDiagnosticSeverity.ERROR_LITERAL);
xsdDiagnostic.setMessage
(XSDPlugin.INSTANCE.getString
("_UI_XSDError_message",
new Object []
{
populateDiagnostic
(xsdDiagnostic,
"content-valid.2",
new Object [] { xsdComplexTypeDefinition.getURI(), getExpected(state) })
}));
xsdDiagnostic.setAnnotationURI(part + "#" + anchor);
xsdDiagnostic.setNode(element);
result.add(xsdDiagnostic);
}
return result;
}
private static String getExpected(XSDParticle.DFA.State state)
{
StringBuffer result = new StringBuffer();
for (XSDParticle.DFA.Transition transition : state.getTransitions())
{
XSDParticle xsdParticle = transition.getParticle();
XSDTerm xsdTerm = xsdParticle.getTerm();
if (xsdTerm instanceof XSDElementDeclaration)
{
XSDElementDeclaration xsdElementDeclaration = (XSDElementDeclaration)xsdTerm;
if (result.length() != 0)
{
result.append(" | ");
}
result.append(xsdElementDeclaration.getName());
}
else if (xsdTerm instanceof XSDWildcard)
{
XSDWildcard xsdWildcard = (XSDWildcard)xsdTerm;
if (result.length() != 0)
{
result.append(" | ");
}
result.append(xsdWildcard.getStringLexicalNamespaceConstraint());
}
}
if (state.isAccepting())
{
if (result.length() != 0)
{
result.append(" | ");
}
result.append(XSDPlugin.INSTANCE.getString("expecting_nothing"));
}
return result.length() == 0 ? XSDPlugin.INSTANCE.getString("expecting_nothing") : result.toString();
}
private static String populateDiagnostic(XSDDiagnostic xsdDiagnostic, String key, Object [] substitutions)
{
xsdDiagnostic.setKey(key);
if (substitutions != null)
{
List<String> values = xsdDiagnostic.getSubstitutions();
for (int i = 0; i < substitutions.length; ++i)
{
Object value = substitutions[i];
values.add(value == null ? null : value.toString());
}
return XSDPlugin.INSTANCE.getString(key, substitutions);
}
else
{
return XSDPlugin.INSTANCE.getString(key);
}
}
}