blob: 22ee5fa66d4d490e6bd9f105788b2d3eea1774b1 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2015 See4sys, itemis and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* See4sys - Initial API and implementation
* itemis - [409510] Enable resource scope-sensitive proxy resolutions without forcing metamodel implementations to subclass EObjectImpl
* itemis - [442342] Sphinx doen't trim context information from proxy URIs when serializing proxyfied cross-document references
*
* </copyright>
*/
package org.eclipse.sphinx.emf.resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.XMLHelper;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.SAXXMIHandler;
import org.eclipse.emf.ecore.xml.type.AnyType;
import org.eclipse.emf.ecore.xml.type.XMLTypePackage;
import org.eclipse.emf.ecore.xml.type.util.XMLTypeUtil;
import org.eclipse.sphinx.emf.metamodel.IMetaModelDescriptor;
import org.eclipse.sphinx.emf.util.EcoreResourceUtil;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class ExtendedSAXXMIHandler extends SAXXMIHandler {
protected static final String XMI_VERSION_ATTRIBUTE = XMIResource.XMI_NS + ":" + XMIResource.VERSION_NAME; //$NON-NLS-1$
protected ExtendedResource extendedResource;
protected IMetaModelDescriptor resourceVersion = null;
protected boolean recordLineAndColumnNumbers;
public ExtendedSAXXMIHandler(XMLResource xmiResource, XMLHelper helper, Map<?, ?> options) {
super(xmiResource, helper, options);
extendedResource = ExtendedResourceAdapterFactory.INSTANCE.adapt(xmlResource);
Object value = options.get(ExtendedResource.OPTION_RESOURCE_VERSION_DESCRIPTOR);
if (value instanceof IMetaModelDescriptor) {
resourceVersion = (IMetaModelDescriptor) value;
}
if (options.get(ExtendedResource.OPTION_RECORD_LINE_AND_COLUMN_NUMBERS) == Boolean.TRUE) {
recordLineAndColumnNumbers = true;
}
}
/*
* @see org.eclipse.emf.ecore.xmi.impl.XMLHandler#resolveEntity(java.lang.String, java.lang.String)
*/
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
try {
// Try to resolve entity as is; this will work out in case that entity has already been resolved by
// underlying XML parser or is resolvable against resource under load and actually exists at the
// resolved location
return super.resolveEntity(publicId, systemId);
} catch (SAXException ex) {
// Try to resolve only the last segment of system id in case there is any
String lastSegment = URI.createURI(systemId).lastSegment();
if (lastSegment != null) {
try {
URI uri = URI.createURI(lastSegment);
uri = helper.resolve(uri, resourceURI);
InputStream inputStream;
inputStream = getURIConverter().createInputStream(uri, null);
InputSource result = new InputSource(inputStream);
result.setPublicId(publicId);
result.setSystemId(systemId);
return result;
} catch (Exception ex1) {
// Ignore exception
}
}
// Try to resolve by relying on resource namespace instead of system id
String resourceNamespace = null;
if (resourceVersion != null) {
resourceNamespace = resourceVersion.getNamespace();
} else {
resourceNamespace = EcoreResourceUtil.readModelNamespace(xmlResource);
}
if (resourceNamespace != null) {
try {
URI uri = URI.createURI(resourceNamespace);
uri = helper.resolve(uri, resourceURI);
InputStream inputStream;
inputStream = getURIConverter().createInputStream(uri, null);
InputSource result = new InputSource(inputStream);
result.setPublicId(publicId);
result.setSystemId(systemId);
return result;
} catch (Exception ex1) {
// Ignore exception (rather than throwing a SAXException and aborting the load process)
}
}
// Ignore exception (rather than re-throwing SAXException and aborting the load process)
return null;
}
}
/*
* Overridden to enable use of workspace-aware URIConverter
* @see org.eclipse.emf.ecore.xmi.impl.XMLHandler#getURIConverter()
*/
@Override
protected URIConverter getURIConverter() {
return EcoreResourceUtil.getURIConverter(resourceSet);
}
/*
* Overridden to make sure that parsing may be continued even in case of fatal errors (typically XML well-formedness
* problems or I/O errors) if the underlying SAX parser has been configured to operate that way. In case that Apache
* Xerces parser is used the parser feature http://apache.org/xml/features/continue-after-fatal-error needs to be
* set to true for this purpose.
* @see org.eclipse.emf.ecore.xmi.impl.XMLHandler#fatalError(org.xml.sax.SAXParseException)
*/
@Override
public void fatalError(SAXParseException e) throws SAXException {
try {
super.fatalError(e);
} catch (SAXException ex) {
// Ignore exception
}
}
@Override
protected void processTopObject(EObject object) {
if (object != null) {
if (deferredExtent != null) {
deferredExtent.add(object);
} else {
extent.addUnique(object);
}
if (extendedMetaData != null && !mixedTargets.isEmpty()) {
FeatureMap featureMap = mixedTargets.pop();
EStructuralFeature target = null;
EList<EAttribute> allAttributes = object.eClass().getEAllAttributes();
for (Object element : allAttributes) {
EAttribute attribute = (EAttribute) element;
if (ExtendedResourceConstants.OUTER_CONTENT_ATTRIBUTE_NAME.equals(attribute.getName())) {
target = attribute;
break;
}
}
if (target == null) {
target = extendedMetaData.getMixedFeature(object.eClass());
}
if (target != null) {
FeatureMap otherFeatureMap = (FeatureMap) object.eGet(target);
for (FeatureMap.Entry entry : new ArrayList<FeatureMap.Entry>(featureMap)) {
// Ignore a whitespace only text entry at the beginning.
//
if (entry.getEStructuralFeature() != XMLTypePackage.Literals.XML_TYPE_DOCUMENT_ROOT__TEXT
|| !"".equals(XMLTypeUtil.normalize(entry.getValue().toString(), true))) { //$NON-NLS-1$
otherFeatureMap.add(entry.getEStructuralFeature(), entry.getValue());
}
}
}
text = null;
}
}
processObject(object);
}
/*
* Overridden to enable delegation of actual proxy URI creation to {@link ExtendedResourceAdapter extended resource
* adapter} and to augment proxy URIs to context-aware proxy URIs required to honor their {@link IResourceScope
* resource scope}s when they are being resolved and to support the resolution of proxified references between
* objects from different metamodels.
* @see org.eclipse.emf.ecore.xmi.impl.XMLHandler#handleProxy(org.eclipse.emf.ecore.InternalEObject,
* java.lang.String)
*/
@Override
protected void handleProxy(InternalEObject proxy, String uriLiteral) {
URI proxyURI;
if (oldStyleProxyURIs) {
uriLiteral = uriLiteral.startsWith(ExtendedResource.URI_SEGMENT_SEPARATOR) ? uriLiteral : ExtendedResource.URI_SEGMENT_SEPARATOR
+ uriLiteral;
proxyURI = URI.createURI(uriLiteral);
proxy.eSetProxyURI(proxyURI);
} else {
if (extendedResource != null) {
proxyURI = extendedResource.createURI(uriLiteral, proxy.eClass());
} else {
proxyURI = URI.createURI(uriLiteral);
}
if (uriHandler != null) {
proxyURI = uriHandler.resolve(proxyURI);
} else if (resolve
&& proxyURI.isRelative()
&& proxyURI.hasRelativePath()
&& (extendedMetaData == null ? !packageRegistry.containsKey(proxyURI.trimFragment().toString()) : extendedMetaData
.getPackage(proxyURI.trimFragment().toString()) == null)) {
proxyURI = helper.resolve(proxyURI, resourceURI);
}
proxy.eSetProxyURI(proxyURI);
}
// Test for a same document reference that would usually be handled as an IDREF
if (proxyURI.trimFragment().equals(resourceURI)) {
sameDocumentProxies.add(proxy);
}
if (extendedResource != null) {
extendedResource.augmentToContextAwareProxy(proxy);
}
}
/*
* @see org.eclipse.emf.ecore.xmi.impl.SAXXMIHandler#handleObjectAttribs(org.eclipse.emf.ecore.EObject)
*/
@Override
protected void handleObjectAttribs(EObject obj) {
if (attribs != null) {
InternalEObject internalEObject = (InternalEObject) obj;
for (int i = 0, size = attribs.getLength(); i < size; ++i) {
String name = attribs.getQName(i);
if (name.equals(ID_ATTRIB)) {
xmlResource.setID(internalEObject, attribs.getValue(i));
} else if (name.equals(hrefAttribute) && (!recordUnknownFeature || types.peek() != UNKNOWN_FEATURE_TYPE || obj.eClass() != anyType)) {
handleProxy(internalEObject, attribs.getValue(i));
} else if (isNamespaceAware) {
String namespace = attribs.getURI(i);
/*
* Provides a workaround for potential bug in
* org.apache.xerces.impl.xs.XMLSchemaValidator.addDefaultAttributes(QName, XMLAttributes,
* XSAttributeGroupDecl) (line 3027) which attempts to add xmi:version as default attribute if not
* present yet but misses to initialize the attribute's prefix field
*/
if (ExtendedMetaData.XMI_URI.equals(namespace) && XMIResource.VERSION_NAME.equals(name)) {
continue;
}
if (!ExtendedMetaData.XSI_URI.equals(namespace) && !notFeatures.contains(name)) {
setAttribValue(obj, name, attribs.getValue(i));
}
} else if (!name.startsWith(XMLResource.XML_NS) && !notFeatures.contains(name)) {
setAttribValue(obj, name, attribs.getValue(i));
}
}
}
if (recordLineAndColumnNumbers) {
AnyType extension = getExtension(obj);
EStructuralFeature lineNumberAttribute = extendedMetaData.demandFeature(null, ExtendedResource.LINE_NUMBER_KEY_NAME, false);
extension.eSet(lineNumberAttribute, getLineNumber());
EStructuralFeature columnAttribute = extendedMetaData.demandFeature(null, ExtendedResource.COLUMN_NUMBER_KEY_NAME, false);
extension.eSet(columnAttribute, getColumnNumber());
}
}
}