diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java index 9298e6c53..720ee1d3b 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java @@ -37,7 +37,13 @@ public interface CMElementDeclaration { * * @return the declared element name with the given prefix. */ - String getName(String prefix); + default String getName(String prefix) { + String name = getName(); + if (prefix == null || prefix.isEmpty()) { + return name; + } + return prefix + ":" + name; + } /** * Returns the attributes of this declared element. diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelManager.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelManager.java index a87d8c837..0b4b9f7b9 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelManager.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelManager.java @@ -10,27 +10,20 @@ */ package org.eclipse.lsp4xml.extensions.contentmodel.model; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.apache.xerces.impl.Constants; -import org.apache.xerces.impl.xs.XSLoaderImpl; -import org.apache.xerces.xs.XSModel; import org.eclipse.lsp4xml.dom.Element; -import org.eclipse.lsp4xml.dom.NoNamespaceSchemaLocation; -import org.eclipse.lsp4xml.dom.SchemaLocation; import org.eclipse.lsp4xml.dom.XMLDocument; import org.eclipse.lsp4xml.extensions.contentmodel.settings.XMLFileAssociation; import org.eclipse.lsp4xml.extensions.contentmodel.uriresolver.XMLCacheResolverExtension; import org.eclipse.lsp4xml.extensions.contentmodel.uriresolver.XMLCatalogResolverExtension; import org.eclipse.lsp4xml.extensions.contentmodel.uriresolver.XMLFileAssociationResolverExtension; -import org.eclipse.lsp4xml.extensions.contentmodel.xsd.XSDDocument; -import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadingException; import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager; import org.eclipse.lsp4xml.utils.URIUtils; -import org.w3c.dom.DOMError; -import org.w3c.dom.DOMErrorHandler; /** * Content model manager used to load XML Schema, DTD. @@ -38,36 +31,19 @@ */ public class ContentModelManager { -// private static final ContentModelManager INSTANCE = new ContentModelManager(); -// -// public static ContentModelManager getInstance() { -// return INSTANCE; -// } - - private final XSLoaderImpl loader; - private final Map cmDocumentCache; + private final URIResolverExtensionManager resolverManager; + private final List modelProviders; + private final XMLCacheResolverExtension cacheResolverExtension; private final XMLCatalogResolverExtension catalogResolverExtension; private final XMLFileAssociationResolverExtension fileAssociationResolver; - private final URIResolverExtensionManager resolverManager; public ContentModelManager(URIResolverExtensionManager resolverManager) { this.resolverManager = resolverManager; + modelProviders = new ArrayList<>(); cmDocumentCache = Collections.synchronizedMap(new HashMap<>()); - loader = new XSLoaderImpl(); - loader.setParameter("http://apache.org/xml/properties/internal/entity-resolver", resolverManager); - loader.setParameter(Constants.DOM_ERROR_HANDLER, new DOMErrorHandler() { - - @Override - public boolean handleError(DOMError error) { - if (error.getRelatedException() instanceof CacheResourceDownloadingException) { - throw ((CacheResourceDownloadingException) error.getRelatedException()); - } - return false; - } - }); fileAssociationResolver = new XMLFileAssociationResolverExtension(); resolverManager.registerResolver(fileAssociationResolver); catalogResolverExtension = new XMLCatalogResolverExtension(); @@ -100,57 +76,80 @@ public CMDocument findCMDocument(Element element, String namespaceURI) { } public CMDocument findCMDocument(XMLDocument xmlDocument, String namespaceURI) { - String systemId = null; - SchemaLocation schemaLocation = xmlDocument.getSchemaLocation(); - if (schemaLocation != null) { - systemId = schemaLocation.getLocationHint(namespaceURI); - } else { - NoNamespaceSchemaLocation noNamespaceSchemaLocation = xmlDocument.getNoNamespaceSchemaLocation(); - if (noNamespaceSchemaLocation != null) { - if (namespaceURI != null) { - // xsi:noNamespaceSchemaLocation doesn't define namespaces - return null; - } - systemId = noNamespaceSchemaLocation.getLocation(); - } else { - // TODO : implement with DTD - } - } - return findCMDocument(xmlDocument.getDocumentURI(), namespaceURI, systemId); + ContentModelProvider modelProvider = getModelProviderByStandardAssociation(xmlDocument); + String systemId = modelProvider != null ? modelProvider.getSystemId(xmlDocument, namespaceURI) : null; + return findCMDocument(xmlDocument.getDocumentURI(), namespaceURI, systemId, modelProvider); } /** * Returns the content model document loaded by the given uri and null * otherwise. * - * @param publicId the public identifier. - * @param systemId the expanded system identifier. + * @param publicId the public identifier. + * @param systemId the expanded system identifier. + * @param modelProvider * @return the content model document loaded by the given uri and null * otherwise. */ - private CMDocument findCMDocument(String uri, String publicId, String systemId) { + private CMDocument findCMDocument(String uri, String publicId, String systemId, + ContentModelProvider modelProvider) { + // Resolve the XML Schema/DTD uri (file, http, etc) String key = resolverManager.resolve(uri, publicId, systemId); if (key == null) { return null; } + // the XML Schema, DTD can be resolved + if (modelProvider == null) { + // the model provider cannot be get with standard mean (xsi:schemaLocation, + // xsi:noNamespaceSchemaLocation, doctype) + // try to get it by using extension (ex: .xsd, .dtd) + modelProvider = getModelProviderByURI(key); + } + if (modelProvider == null) { + return null; + } CMDocument cmDocument = null; boolean isCacheable = isCacheable(key); if (isCacheable) { cmDocument = cmDocumentCache.get(key); } if (cmDocument == null) { - XSModel model = loader.loadURI(key); - if (model != null) { - // XML Schema can be loaded - cmDocument = new XSDDocument(model); - if (isCacheable) { - cmDocumentCache.put(key, cmDocument); - } + cmDocument = modelProvider.createCMDocument(key); + if (isCacheable && cmDocument != null) { + cmDocumentCache.put(key, cmDocument); } } return cmDocument; } + /** + * Returns the content model provider by using standard association + * (xsi:schemaLocation, xsi:noNamespaceSchemaLocation, doctype) an dnull + * otherwise. + * + * @param xmlDocument + * @return the content model provider by using standard association + * (xsi:schemaLocation, xsi:noNamespaceSchemaLocation, doctype) an dnull + * otherwise. + */ + private ContentModelProvider getModelProviderByStandardAssociation(XMLDocument xmlDocument) { + for (ContentModelProvider modelProvider : modelProviders) { + if (modelProvider.adaptFor(xmlDocument)) { + return modelProvider; + } + } + return null; + } + + private ContentModelProvider getModelProviderByURI(String uri) { + for (ContentModelProvider modelProvider : modelProviders) { + if (modelProvider.adaptFor(uri)) { + return modelProvider; + } + } + return null; + } + private boolean isCacheable(String uri) { return !URIUtils.isFileResource(uri); } @@ -192,4 +191,12 @@ public void setUseCache(boolean useCache) { cacheResolverExtension.setUseCache(useCache); } + public void registerModelProvider(ContentModelProvider modelProvider) { + modelProviders.add(modelProvider); + } + + public void unregisterModelProvider(ContentModelProvider modelProvider) { + modelProviders.remove(modelProvider); + } + } \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelProvider.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelProvider.java new file mode 100644 index 000000000..786a234b6 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/ContentModelProvider.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2018 Angelo ZERR + * 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: + * Angelo Zerr - initial API and implementation + */ +package org.eclipse.lsp4xml.extensions.contentmodel.model; + +import org.eclipse.lsp4xml.dom.XMLDocument; + +/** + * Content model provider API. + * + */ +public interface ContentModelProvider { + + /** + * Returns the content model provider by using standard association + * (xsi:schemaLocation, xsi:noNamespaceSchemaLocation, doctype) an dnull + * otherwise. + * + * @param document + * @return the content model provider by using standard association + * (xsi:schemaLocation, xsi:noNamespaceSchemaLocation, doctype) an dnull + * otherwise. + */ + boolean adaptFor(XMLDocument document); + + boolean adaptFor(String uri); + + String getSystemId(XMLDocument xmlDocument, String namespaceURI); + + CMDocument createCMDocument(String key); +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/DTDPlugin.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/DTDPlugin.java new file mode 100644 index 000000000..8a56be64a --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/DTDPlugin.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018 Angelo ZERR. + * 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: + * Angelo Zerr - initial API and implementation + */ +package org.eclipse.lsp4xml.extensions.dtd; + +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager; +import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelProvider; +import org.eclipse.lsp4xml.extensions.dtd.contentmodel.DTDContentModelProvider; +import org.eclipse.lsp4xml.services.extensions.IXMLExtension; +import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry; +import org.eclipse.lsp4xml.services.extensions.save.ISaveContext; + +/** + * DTD plugin. + */ +public class DTDPlugin implements IXMLExtension { + + public DTDPlugin() { + } + + @Override + public void doSave(ISaveContext context) { + + } + + @Override + public void start(InitializeParams params, XMLExtensionsRegistry registry) { + // register DTD content model provider + ContentModelProvider modelProvider = new DTDContentModelProvider(registry.getResolverExtensionManager()); + ContentModelManager modelManager = registry.getComponent(ContentModelManager.class); + modelManager.registerModelProvider(modelProvider); + } + + @Override + public void stop(XMLExtensionsRegistry registry) { + } +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDContentModelProvider.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDContentModelProvider.java new file mode 100644 index 000000000..490593547 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDContentModelProvider.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2018 Angelo ZERR. + * 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: + * Angelo Zerr - initial API and implementation + */ +package org.eclipse.lsp4xml.extensions.dtd.contentmodel; + +import org.apache.xerces.impl.dtd.DTDGrammar; +import org.apache.xerces.impl.dtd.XMLDTDLoader; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.eclipse.lsp4xml.dom.DocumentType; +import org.eclipse.lsp4xml.dom.XMLDocument; +import org.eclipse.lsp4xml.extensions.contentmodel.model.CMDocument; +import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelProvider; +import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager; +import org.eclipse.lsp4xml.utils.DOMUtils; + +/** + * DTD content model provider. + */ +public class DTDContentModelProvider implements ContentModelProvider { + + private final URIResolverExtensionManager resolverExtensionManager; + + private XMLDTDLoader loader; + + public DTDContentModelProvider(URIResolverExtensionManager resolverExtensionManager) { + this.resolverExtensionManager = resolverExtensionManager; + } + + @Override + public boolean adaptFor(XMLDocument document) { + return document.hasDTD(); + } + + @Override + public boolean adaptFor(String uri) { + return DOMUtils.isDTD(uri); + } + + @Override + public String getSystemId(XMLDocument xmlDocument, String namespaceURI) { + /* + * + */ + DocumentType documentType = xmlDocument.getDoctype(); + // FIXME!!! get system if correctly with XMLDocument + String content = documentType.getContent(); + int lastQuoteIndex = content.lastIndexOf("\""); + if (lastQuoteIndex != -1) { + content = content.substring(0, lastQuoteIndex); + lastQuoteIndex = content.lastIndexOf("\""); + if (lastQuoteIndex != -1) { + content = content.substring(lastQuoteIndex + 1, content.length()); + } + } + return content; // documentType.getSystemId(); + } + + @Override + public CMDocument createCMDocument(String key) { + DTDGrammar model; + try { + model = (DTDGrammar) getLoader().loadGrammar(new XMLInputSource(null, key, null)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + if (model != null) { + // XML Schema can be loaded + return new DTDDocument(model); + } + return null; + } + + public XMLDTDLoader getLoader() { + if (loader == null) { + loader = getSynchLoader(); + } + return loader; + } + + private synchronized XMLDTDLoader getSynchLoader() { + if (loader != null) { + return loader; + } + XMLDTDLoader loader = new XMLDTDLoader(); + loader.setEntityResolver(resolverExtensionManager); + /* + * loader.setErrorHandler(new DOMErrorHandler() { + * + * @Override public boolean handleError(DOMError error) { if + * (error.getRelatedException() instanceof CacheResourceDownloadingException) { + * throw ((CacheResourceDownloadingException) error.getRelatedException()); } + * return false; } }); + */ + return loader; + } + +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDDocument.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDDocument.java new file mode 100644 index 000000000..55fbe445d --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDDocument.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2018 Angelo ZERR + * 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: + * Angelo Zerr - initial API and implementation + */ +package org.eclipse.lsp4xml.extensions.dtd.contentmodel; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.xerces.impl.dtd.DTDGrammar; +import org.eclipse.lsp4xml.dom.Element; +import org.eclipse.lsp4xml.extensions.contentmodel.model.CMDocument; +import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration; + +/** + * DTD document. + * @author azerr + * + */ +public class DTDDocument implements CMDocument { + + private final DTDGrammar grammar; + private List elements; + + public DTDDocument(DTDGrammar grammar) { + this.grammar = grammar; + } + + @Override + public Collection getElements() { + elements = null; + if (elements == null) { + elements = new ArrayList<>(); + int index = grammar.getFirstElementDeclIndex(); + while (index != -1) { + DTDElementDeclaration elementDecl = new DTDElementDeclaration(); + grammar.getElementDecl(index, elementDecl); + elements.add(elementDecl); + index = grammar.getNextElementDeclIndex(index); + } + } + return elements; + } + + @Override + public CMElementDeclaration findCMElement(Element element, String namespace) { + List paths = new ArrayList<>(); + while (element != null && (namespace == null || namespace.equals(element.getNamespaceURI()))) { + paths.add(0, element); + element = element.getParentNode() instanceof Element ? (Element) element.getParentNode() : null; + } + CMElementDeclaration declaration = null; + for (int i = 0; i < paths.size(); i++) { + Element elt = paths.get(i); + if (i == 0) { + declaration = findElementDeclaration(elt.getLocalName(), namespace); + } else { + declaration = declaration.findCMElement(elt.getLocalName(), namespace); + } + if (declaration == null) { + break; + } + } + return declaration; + } + + private CMElementDeclaration findElementDeclaration(String tag, String namespace) { + for (CMElementDeclaration cmElement : getElements()) { + if (cmElement.getName().equals(tag)) { + return cmElement; + } + } + return null; + } + + +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDElementDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDElementDeclaration.java new file mode 100644 index 000000000..a1a7b6efe --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/DTDElementDeclaration.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2018 Angelo ZERR + * 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: + * Angelo Zerr - initial API and implementation + */ +package org.eclipse.lsp4xml.extensions.dtd.contentmodel; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.xerces.impl.dtd.XMLElementDecl; +import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration; +import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration; + +/** + * DTD element declaration. + * + * @author azerr + * + */ +public class DTDElementDeclaration extends XMLElementDecl implements CMElementDeclaration { + + private List elements; + + @Override + public String getName() { + return super.name.localpart; + } + + @Override + public String getNamespace() { + return null; + } + + @Override + public Collection getAttributes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection getElements() { + if (elements == null) { + elements = new ArrayList<>(); + // collectElementsDeclaration(elementDeclaration, elements); + } + return elements; + } + + @Override + public CMElementDeclaration findCMElement(String tag, String namespace) { + // TODO Auto-generated method stub + return null; + } + + @Override + public CMAttributeDeclaration findCMAttribute(String attributeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getDocumentation() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isEmpty() { + // TODO Auto-generated method stub + return false; + } + + @Override + public Collection getEnumerationValues() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java index 81b99f3dd..a6b199ffa 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java @@ -11,6 +11,9 @@ package org.eclipse.lsp4xml.extensions.xsd; import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager; +import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelProvider; +import org.eclipse.lsp4xml.extensions.xsd.contentmodel.XSDContentModelProvider; import org.eclipse.lsp4xml.extensions.xsd.participants.XSDCompletionParticipant; import org.eclipse.lsp4xml.extensions.xsd.participants.diagnostics.XSDDiagnosticsParticipant; import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant; @@ -42,8 +45,14 @@ public void doSave(ISaveContext context) { @Override public void start(InitializeParams params, XMLExtensionsRegistry registry) { + // Register resolver uiResolver = new XSDURIResolverExtension(registry.getDocumentProvider()); registry.getResolverExtensionManager().registerResolver(uiResolver); + // register XSD content model provider + ContentModelProvider modelProvider = new XSDContentModelProvider(registry.getResolverExtensionManager()); + ContentModelManager modelManager = registry.getComponent(ContentModelManager.class); + modelManager.registerModelProvider(modelProvider); + // register completion, diagnostic particpant registry.registerCompletionParticipant(completionParticipant); registry.registerDiagnosticsParticipant(diagnosticsParticipant); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDAnnotationModel.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDAnnotationModel.java similarity index 94% rename from org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDAnnotationModel.java rename to org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDAnnotationModel.java index d95791ce0..173472c18 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDAnnotationModel.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDAnnotationModel.java @@ -8,7 +8,7 @@ * Contributors: * Angelo Zerr - initial API and implementation */ -package org.eclipse.lsp4xml.extensions.contentmodel.xsd; +package org.eclipse.lsp4xml.extensions.xsd.contentmodel; import static org.eclipse.lsp4xml.utils.StringUtils.normalizeSpace; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDAttributeDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDAttributeDeclaration.java similarity index 95% rename from org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDAttributeDeclaration.java rename to org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDAttributeDeclaration.java index ab8625ffd..4c9252684 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDAttributeDeclaration.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDAttributeDeclaration.java @@ -8,7 +8,7 @@ * Contributors: * Angelo Zerr - initial API and implementation */ -package org.eclipse.lsp4xml.extensions.contentmodel.xsd; +package org.eclipse.lsp4xml.extensions.xsd.contentmodel; import java.util.Collection; import java.util.Collections; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDContentModelProvider.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDContentModelProvider.java new file mode 100644 index 000000000..c9dcbed75 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDContentModelProvider.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2018 Angelo ZERR. + * 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: + * Angelo Zerr - initial API and implementation + */ +package org.eclipse.lsp4xml.extensions.xsd.contentmodel; + +import org.apache.xerces.impl.Constants; +import org.apache.xerces.impl.xs.XSLoaderImpl; +import org.apache.xerces.xs.XSModel; +import org.eclipse.lsp4xml.dom.NoNamespaceSchemaLocation; +import org.eclipse.lsp4xml.dom.SchemaLocation; +import org.eclipse.lsp4xml.dom.XMLDocument; +import org.eclipse.lsp4xml.extensions.contentmodel.model.CMDocument; +import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelProvider; +import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadingException; +import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager; +import org.eclipse.lsp4xml.utils.DOMUtils; +import org.w3c.dom.DOMError; +import org.w3c.dom.DOMErrorHandler; + +/** + * XSD content model provider. + */ +public class XSDContentModelProvider implements ContentModelProvider { + + private final URIResolverExtensionManager resolverExtensionManager; + + private XSLoaderImpl loader; + + public XSDContentModelProvider(URIResolverExtensionManager resolverExtensionManager) { + this.resolverExtensionManager = resolverExtensionManager; + } + + @Override + public boolean adaptFor(XMLDocument document) { + return document.hasSchemaLocation() || document.hasNoNamespaceSchemaLocation(); + } + + @Override + public boolean adaptFor(String uri) { + return DOMUtils.isXSD(uri); + } + + @Override + public String getSystemId(XMLDocument xmlDocument, String namespaceURI) { + SchemaLocation schemaLocation = xmlDocument.getSchemaLocation(); + if (schemaLocation != null) { + return schemaLocation.getLocationHint(namespaceURI); + } else { + NoNamespaceSchemaLocation noNamespaceSchemaLocation = xmlDocument.getNoNamespaceSchemaLocation(); + if (noNamespaceSchemaLocation != null) { + if (namespaceURI != null) { + // xsi:noNamespaceSchemaLocation doesn't define namespaces + return null; + } + return noNamespaceSchemaLocation.getLocation(); + } + } + return null; + } + + @Override + public CMDocument createCMDocument(String key) { + XSModel model = getLoader().loadURI(key); + if (model != null) { + // XML Schema can be loaded + return new XSDDocument(model); + } + return null; + } + + public XSLoaderImpl getLoader() { + if (loader == null) { + loader = getSynchLoader(); + } + return loader; + } + + private synchronized XSLoaderImpl getSynchLoader() { + if (loader != null) { + return loader; + } + XSLoaderImpl loader = new XSLoaderImpl(); + loader.setParameter("http://apache.org/xml/properties/internal/entity-resolver", resolverExtensionManager); + loader.setParameter(Constants.DOM_ERROR_HANDLER, new DOMErrorHandler() { + + @Override + public boolean handleError(DOMError error) { + if (error.getRelatedException() instanceof CacheResourceDownloadingException) { + throw ((CacheResourceDownloadingException) error.getRelatedException()); + } + return false; + } + }); + return loader; + } + +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDDocument.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDDocument.java similarity index 92% rename from org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDDocument.java rename to org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDDocument.java index 0ab3007a6..e7cf210e4 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDDocument.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDDocument.java @@ -8,7 +8,7 @@ * Contributors: * Angelo Zerr - initial API and implementation */ -package org.eclipse.lsp4xml.extensions.contentmodel.xsd; +package org.eclipse.lsp4xml.extensions.xsd.contentmodel; import java.util.ArrayList; import java.util.Collection; @@ -69,11 +69,13 @@ public Collection getElements() { void collectElement(XSElementDeclaration elementDeclaration, Collection elements) { if (elementDeclaration.getAbstract()) { // element declaration is marked as abstract - // ex with xsl: + // ex with xsl: XSObjectList list = model.getSubstitutionGroup(elementDeclaration); if (list != null) { - // it exists elements list bind with this abstract declaration with substitutionGroup - // ex xsl : + // it exists elements list bind with this abstract declaration with + // substitutionGroup + // ex xsl : for (int i = 0; i < list.getLength(); i++) { XSObject object = list.item(i); if (object.getType() == XSConstants.ELEMENT_DECLARATION) { @@ -92,9 +94,8 @@ void collectElement(XSElementDeclaration elementDeclaration, Collection paths = new ArrayList<>(); - Element element = node; while (element != null && (namespace == null || namespace.equals(element.getNamespaceURI()))) { paths.add(0, element); element = element.getParentNode() instanceof Element ? (Element) element.getParentNode() : null; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDElementDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDElementDeclaration.java similarity index 93% rename from org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDElementDeclaration.java rename to org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDElementDeclaration.java index fdf459f8c..1d6eb4761 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/xsd/XSDElementDeclaration.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/XSDElementDeclaration.java @@ -8,7 +8,7 @@ * Contributors: * Angelo Zerr - initial API and implementation */ -package org.eclipse.lsp4xml.extensions.contentmodel.xsd; +package org.eclipse.lsp4xml.extensions.xsd.contentmodel; import java.util.ArrayList; import java.util.Collection; @@ -61,15 +61,6 @@ public String getNamespace() { return elementDeclaration.getNamespace(); } - @Override - public String getName(String prefix) { - String name = getName(); - if (prefix == null || prefix.isEmpty()) { - return name; - } - return prefix + ":" + name; - } - @Override public Collection getAttributes() { if (attributes == null) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsl/XSLURIResolverExtension.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsl/XSLURIResolverExtension.java index 04f405d42..9b131d96c 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsl/XSLURIResolverExtension.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsl/XSLURIResolverExtension.java @@ -36,7 +36,7 @@ public class XSLURIResolverExtension implements URIResolverExtension { */ private static final String XSL_NAMESPACE_URI = "http://www.w3.org/1999/XSL/Transform"; //$NON-NLS-1$ - private static final ResourceToDeploy XML_SCHEMA_10 = new ResourceToDeploy("https://www.w3.org/1999/11/xslt10.xsl", + private static final ResourceToDeploy XML_SCHEMA_10 = new ResourceToDeploy("https://www.w3.org/1999/11/xslt10.xsd", "/schemas/xslt/xslt-1.0.xsd"); private static final Map XSL_RESOURCES; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/DOMUtils.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/DOMUtils.java index 44d8f4ac6..90300ad63 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/DOMUtils.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/DOMUtils.java @@ -11,6 +11,8 @@ public class DOMUtils { private static final String XSD_EXTENSION = ".xsd"; + private static final String DTD_EXTENSION = ".dtd"; + private static final String HTTP_WWW_W3_ORG_2001_XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema"; private static final String URN_OASIS_NAMES_TC_ENTITY_XMLNS_XML_CATALOG_NS = "urn:oasis:names:tc:entity:xmlns:xml:catalog"; @@ -26,7 +28,7 @@ private DOMUtils() { */ public static boolean isXSD(XMLDocument document) { String uri = document.getDocumentURI(); - if (uri != null && uri.endsWith(XSD_EXTENSION)) { + if (isXSD(uri)) { return true; } // check root element is bound with XML Schema namespace @@ -34,6 +36,26 @@ public static boolean isXSD(XMLDocument document) { return checkRootNamespace(document, HTTP_WWW_W3_ORG_2001_XML_SCHEMA_NS); } + /** + * Returns true if the given URI is a XML Schema and false otherwise. + * + * @param uri the URI to check + * @return true if the given URI is a XML Schema and false otherwise. + */ + public static boolean isXSD(String uri) { + return uri != null && uri.endsWith(XSD_EXTENSION); + } + + /** + * Returns true if the given URI is a DTD and false otherwise. + * + * @param uri the URI to check + * @return true if the given URI is a DTD and false otherwise. + */ + public static boolean isDTD(String uri) { + return uri != null && uri.endsWith(DTD_EXTENSION); + } + /** * Returns true if the XML document is a XML Catalog and false otherwise. * diff --git a/org.eclipse.lsp4xml/src/main/resources/META-INF/services/org.eclipse.lsp4xml.services.extensions.IXMLExtension b/org.eclipse.lsp4xml/src/main/resources/META-INF/services/org.eclipse.lsp4xml.services.extensions.IXMLExtension index 7b6b6af12..35121248c 100644 --- a/org.eclipse.lsp4xml/src/main/resources/META-INF/services/org.eclipse.lsp4xml.services.extensions.IXMLExtension +++ b/org.eclipse.lsp4xml/src/main/resources/META-INF/services/org.eclipse.lsp4xml.services.extensions.IXMLExtension @@ -1,5 +1,6 @@ org.eclipse.lsp4xml.extensions.contentmodel.ContentModelPlugin org.eclipse.lsp4xml.extensions.references.XMLReferencesPlugin org.eclipse.lsp4xml.extensions.xsd.XSDPlugin +org.eclipse.lsp4xml.extensions.dtd.DTDPlugin org.eclipse.lsp4xml.extensions.xsl.XSLPlugin org.eclipse.lsp4xml.extensions.catalog.XMLCatalogPlugin \ No newline at end of file