diff --git a/xml.axi/src/org/netbeans/modules/xml/axi/AXIComponentFactory.java b/xml.axi/src/org/netbeans/modules/xml/axi/AXIComponentFactory.java --- a/xml.axi/src/org/netbeans/modules/xml/axi/AXIComponentFactory.java +++ b/xml.axi/src/org/netbeans/modules/xml/axi/AXIComponentFactory.java @@ -373,7 +373,7 @@ } copiedComponent = model.getComponentFactory(). createElement(); - ((Element)copiedComponent).setAbstract(element.getAbstract()); + ((Element)copiedComponent).setAbstract(element.isAbstract()); ((Element)copiedComponent).setBlock(element.getBlock()); ((Element)copiedComponent).setDefault(element.getDefault()); ((Element)copiedComponent).setFinal(element.getFinal()); diff --git a/xml.axi/src/org/netbeans/modules/xml/axi/Element.java b/xml.axi/src/org/netbeans/modules/xml/axi/Element.java --- a/xml.axi/src/org/netbeans/modules/xml/axi/Element.java +++ b/xml.axi/src/org/netbeans/modules/xml/axi/Element.java @@ -96,8 +96,18 @@ /** * Returns abstract property. + * @return the value of the {@code abstract} attribute of the element */ - public abstract boolean getAbstract(); + public abstract boolean isAbstract(); + + /** + * Returns abstract property. + * @deprecated use {@link #isAbstract()} instead + */ + @Deprecated + public boolean getAbstract() { + return isAbstract(); + } /** * Sets the abstract property. diff --git a/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementImpl.java b/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementImpl.java --- a/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementImpl.java +++ b/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementImpl.java @@ -98,7 +98,7 @@ /** * Returns abstract property. */ - public boolean getAbstract() { + public boolean isAbstract() { return isAbstract; } @@ -106,7 +106,7 @@ * Sets the abstract property. */ public void setAbstract(boolean value) { - boolean oldValue = getAbstract(); + boolean oldValue = isAbstract(); if(oldValue != value) { this.isAbstract = value; firePropertyChangeEvent(PROP_ABSTRACT, oldValue, value); diff --git a/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementProxy.java b/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementProxy.java --- a/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementProxy.java +++ b/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementProxy.java @@ -141,8 +141,8 @@ /** * Returns abstract property. */ - public boolean getAbstract() { - return getShared().getAbstract(); + public boolean isAbstract() { + return getShared().isAbstract(); } /** diff --git a/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementRef.java b/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementRef.java --- a/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementRef.java +++ b/xml.axi/src/org/netbeans/modules/xml/axi/impl/ElementRef.java @@ -150,8 +150,8 @@ /** * Returns abstract property. */ - public boolean getAbstract() { - return getReferent().getAbstract(); + public boolean isAbstract() { + return getReferent().isAbstract(); } /** diff --git a/xml.axi/src/org/netbeans/modules/xml/axi/impl/SchemaGeneratorUtil.java b/xml.axi/src/org/netbeans/modules/xml/axi/impl/SchemaGeneratorUtil.java --- a/xml.axi/src/org/netbeans/modules/xml/axi/impl/SchemaGeneratorUtil.java +++ b/xml.axi/src/org/netbeans/modules/xml/axi/impl/SchemaGeneratorUtil.java @@ -1673,8 +1673,8 @@ public static void populateElement( org.netbeans.modules.xml.schema.model.Element e, Element element) { if(e instanceof GlobalElement) { - if(element.getAbstract()) - ((GlobalElement)e).setAbstract(Boolean.valueOf(element.getAbstract())); + if(element.isAbstract()) + ((GlobalElement)e).setAbstract(Boolean.valueOf(element.isAbstract())); if(element.getPeer() instanceof GlobalElement && ((GlobalElement)element.getPeer()).getFinalEffective() != null && !((GlobalElement)element.getPeer()).getFinalEffective().isEmpty()) diff --git a/xml.schema.completion/src/org/netbeans/modules/xml/schema/completion/util/CompletionUtil.java b/xml.schema.completion/src/org/netbeans/modules/xml/schema/completion/util/CompletionUtil.java --- a/xml.schema.completion/src/org/netbeans/modules/xml/schema/completion/util/CompletionUtil.java +++ b/xml.schema.completion/src/org/netbeans/modules/xml/schema/completion/util/CompletionUtil.java @@ -44,11 +44,7 @@ package org.netbeans.modules.xml.schema.completion.util; import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; -import java.util.StringTokenizer; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -63,27 +59,21 @@ import org.netbeans.api.lexer.TokenSequence; import org.netbeans.api.xml.lexer.XMLTokenId; import org.netbeans.editor.BaseDocument; -import org.netbeans.modules.xml.axi.AXIComponent; -import org.netbeans.modules.xml.axi.AXIDocument; -import org.netbeans.modules.xml.axi.AXIModel; -import org.netbeans.modules.xml.axi.AXIModelFactory; -import org.netbeans.modules.xml.axi.AXIType; -import org.netbeans.modules.xml.axi.AbstractAttribute; -import org.netbeans.modules.xml.axi.AbstractElement; -import org.netbeans.modules.xml.axi.AnyAttribute; -import org.netbeans.modules.xml.axi.AnyElement; -import org.netbeans.modules.xml.axi.Attribute; -import org.netbeans.modules.xml.axi.Element; +import org.netbeans.modules.xml.axi.*; import org.netbeans.modules.xml.axi.datatype.Datatype; import org.netbeans.modules.xml.schema.completion.*; import org.netbeans.modules.xml.schema.completion.spi.CompletionModelProvider.CompletionModel; import org.netbeans.modules.xml.schema.model.Form; +import org.netbeans.modules.xml.schema.model.GlobalElement; +import org.netbeans.modules.xml.schema.model.SchemaComponent; +import org.netbeans.modules.xml.schema.model.SchemaModel; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.windows.TopComponent; /** - * + * Utility class containing methods to query the model for completion suggestions. + * * @author Samaresh (Samaresh.Panda@Sun.Com) */ public class CompletionUtil { @@ -243,28 +233,38 @@ } /** - * Returns the list of child-elements for a given element. + * Returns the list of child elements for a given element. This includes + * all available substitutions for child elements from all + * {@link CompletionModel}s in the specified completion context + * @param context the completion context for which to find all valid + * elements for insertion + * @return all elements appropriate to be inserted in the specified context, + * or {@code null} if no parent element can be found for the specified + * context. */ - public static List getElements( - CompletionContextImpl context) { + public static List getElements(CompletionContextImpl context) { Element element = findAXIElementAtContext(context); - if(element == null) + if(element == null) { return null; + } List results = new ArrayList(); for(AbstractElement ae: element.getChildElements()) { AXIComponent original = ae.getOriginal(); if(original.getTargetNamespace() == null) { //no namespace CompletionResultItem item = createResultItem(original, null, context); - if(item != null) + if(item != null) { results.add(item); - continue; - } - if(original instanceof AnyElement) { + } + } else if(original instanceof AnyElement) { results.addAll(substituteAny((AnyElement)original, context)); - continue; + } else if(original instanceof Element) { + Element childElement = (Element) original; + if(!childElement.isAbstract()) { + addNSAwareCompletionItems(original, context, null, results); + } + addSubstitutionCompletionItems(childElement, context, results); } - addNSAwareCompletionItems(original,context,null,results); } return results; } @@ -329,6 +329,76 @@ } return result; } + + /** + * Find all possible substitutions for the specified {@link Element} within + * the specified {@link CompletionContextImpl completion context}. + * @param forSubstitution the Element to find substitutions for. + * @param context the context to use to find available substitutions. All + * available {@link CompletionModel}s' {@link SchemaModel}s will be searched. + * @param results the result set to add the results to. + */ + private static void addSubstitutionCompletionItems(Element forSubstitution, CompletionContextImpl context, List results) { + AXIModel model = forSubstitution.getModel(); + String nsUri = forSubstitution.getTargetNamespace(); + String localName = forSubstitution.getName(); + for (CompletionModel completionModel : context.getCompletionModels()) { + SchemaModel schemaModel = completionModel.getSchemaModel(); + Set substitutions = schemaModel.resolveSubstitutions(nsUri, localName); + for (GlobalElement substitution : substitutions) { + AXIComponent substitutionElement = lookup(model, substitution); + addNSAwareCompletionItems(substitutionElement, context, completionModel, results); + } + } + } + + + /** + * Finds the {@link AXIComponent} representing the specified + * {@link SchemaComponent}, from the specified {@link AXIModel}. + * Modified from org.netbeans.modules.xml.axi.impl.Util.lookup(), + * org.netbeans.modules.xml.axi.impl.AXIModelImpl.lookup(), + * and org.netbeans.modules.xml.axi.impl.AXIModelImpl.lookupFromOtherModel(). + * @param model the model to search in to find the representation of the + * specified {@link SchemaComponent} + * @param schemaComponent the {@link SchemaComponent} to search for a + * representation of + * @return the AXI representation of the specified schema component, or null + * if no AXI representation could be found for the schema component. + */ + private static AXIComponent lookup(AXIModel model, SchemaComponent schemaComponent) { + if(model.getSchemaModel() == schemaComponent.getModel()) { + return findChild(model.getRoot(), schemaComponent); + } + if(!schemaComponent.isInDocumentModel()) { + return null; + } + AXIModelFactory factory = AXIModelFactory.getDefault(); + AXIModel otherModel = factory.getModel(schemaComponent.getModel()); + return otherModel == null ? null : findChild(otherModel.getRoot(), schemaComponent); + } + + /** + * Finds an {@link AXIComponent} representation of the specified + * {@link SchemaComponent}, by searching the children of the specified + * {@link AXIDocument}. + * Adapted from org.netbeans.modules.xml.axi.impl.AXIDocumentImpl.findChild(). + * @param document the document to search through to find the representation + * of the specified schema component + * @param child the schema component whose representation to find in the + * specified document + * @return the AXI representation of the specified schema component, from + * the specified document, or {@code null} if the schema component has no + * representation in the children of the document. + */ + private static AXIComponent findChild(AXIDocument document, SchemaComponent child) { + for(AXIComponent childRepresentation : document.getChildren()) { + if(childRepresentation.getPeer() == child) { + return childRepresentation; + } + } + return null; + } private static void addNSAwareCompletionItems(AXIComponent axi, CompletionContextImpl context, CompletionModel cm, List results) { diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractElementTest.java b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractElementTest.java new file mode 100644 --- /dev/null +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractElementTest.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU General + * Public License Version 2 only ("GPL") or the Common Development and + * Distribution License("CDDL") (collectively, the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy of the + * License at http://www.netbeans.org/cddl-gplv2.html or + * nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language + * governing permissions and limitations under the License. When distributing + * the software, include this License Header Notice in each file and include the + * License file at nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided by Oracle + * in the GPL Version 2 section of the License file that accompanied this code. + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL or only + * the GPL Version 2, indicate your decision by adding "[Contributor] elects to + * include this software in this distribution under the [CDDL or GPL Version 2] + * license." If you do not indicate a single choice of license, a recipient has + * the option to distribute your version of this file under either the CDDL, the + * GPL Version 2 or to extend the choice of license to its licensees as provided + * above. However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is made + * subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.xml.schema.completion; + +import java.util.List; +import junit.framework.Test; +import junit.framework.TestSuite; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +/** + * Tests for element substitution. + * @author Daniel Bell (dbell@netbeans.org) + */ +public class AbstractElementTest extends AbstractTestCase { + + static final String COMPLETION_DOCUMENT = "resources/AbstractElementCompletion.xml"; + static final String PARENT_SCHEMA = "resources/AbstractElementParent.xsd"; + static final String CHILD_SCHEMA_ONE = "resources/AbstractElementChildOne.xsd"; + static final String CHILD_SCHEMA_TWO = "resources/AbstractElementChildTwo.xsd"; + + public AbstractElementTest(String testName) { + super(testName); + } + + public static Test suite() { + TestSuite suite = new TestSuite(); + suite.addTest(new AbstractElementTest("shouldNotSuggestAbstractElement")); + suite.addTest(new AbstractElementTest("shouldExpandSubstitutionGroup")); + return suite; + } + + @Override + public void setUp() throws Exception { + setupCompletion(COMPLETION_DOCUMENT); + } + + /** + * Elements with {@code abstract="true"} should not be suggested + */ + public void shouldNotSuggestAbstractElement() { + List items = query(468); + assertThat(items, not(containsSuggestions("child"))); + } + + /** + * All available elements that can be substituted for elements in the + * completion context should be presented as completion options + */ + public void shouldExpandSubstitutionGroup() { + List items = query(468); + assertThat(items, containsOnlySuggestions("c1:child-one", "c2:child-two")); + } +} diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractTestCase.java b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractTestCase.java --- a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractTestCase.java +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/AbstractTestCase.java @@ -43,8 +43,13 @@ */ package org.netbeans.modules.xml.schema.completion; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import junit.framework.*; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; import org.netbeans.api.lexer.Language; import org.netbeans.api.xml.lexer.XMLTokenId; import org.netbeans.editor.BaseDocument; @@ -75,14 +80,30 @@ protected void tearDown() throws Exception { } - protected void setupCompletion(String path, StringBuffer buffer) throws Exception { + /** + * Set up the test for a particular XML document + * @param path the XML document + * @see #setupCompletion(java.lang.String, java.lang.StringBuffer) + */ + protected void setupCompletion(String path) throws Exception { + setupCompletion(path, null); + } + + + /** + * Set up the test for a particular XML document + * @param path the XML document + * @param content the content to insert into the document + * @see #setupCompletion(java.lang.String) + */ + protected void setupCompletion(String path, StringBuffer content) throws Exception { this.instanceResourcePath = path; this.instanceFileObject = Util.getResourceAsFileObject(path); this.instanceDocument = Util.getResourceAsDocument(path); this.support = ((XMLSyntaxSupport)instanceDocument.getSyntaxSupport()); - if(buffer != null) { + if(content != null) { instanceDocument.remove(0, instanceDocument.getLength()); - instanceDocument.insertString(0, buffer.toString(), null); + instanceDocument.insertString(0, content.toString(), null); } instanceDocument.putProperty(Language.class, XMLTokenId.language()); } @@ -90,15 +111,17 @@ /** * Queries and returns a list of completion items. * Each unit test is supposed to evaluate this result. + * @param caretOffset the caret offset at which code completion is invoked + * @return the code completion results */ - protected List query(int caretOffset) throws Exception { + protected List query(int caretOffset) { assert(instanceFileObject != null && instanceDocument != null); CompletionQuery instance = new CompletionQuery(instanceFileObject); return instance.getCompletionItems(instanceDocument, caretOffset); } protected void assertResult(List result, - String[] expectedResult) { + String... expectedResult) { if(result == null && expectedResult == null) { assert(true); return; @@ -136,6 +159,14 @@ } } + protected Matcher> containsSuggestions(String... suggestions) { + return new SuggestionsContaining(false, suggestions); + } + + protected Matcher> containsOnlySuggestions(String... suggestions) { + return new SuggestionsContaining(true, suggestions); + } + BaseDocument getDocument() { return instanceDocument; } @@ -153,4 +184,40 @@ context.initContext(); return context; } + + private class SuggestionsContaining extends TypeSafeMatcher> { + + private final List expectedSuggestionStrings; + private final boolean expectingExactMatch; + public SuggestionsContaining(boolean expectingExactMatch, String... suggestions) { + this.expectedSuggestionStrings = Arrays.asList(suggestions); + this.expectingExactMatch = expectingExactMatch; + } + + @Override + public boolean matchesSafely(List actual) { + if(actual == null) { + return false; + } + if(expectingExactMatch && actual.size() != expectedSuggestionStrings.size()) { + return false; + } + List actualSuggestionStrings = new ArrayList(actual.size()); + for (CompletionResultItem result : actual) { + actualSuggestionStrings.add(result.getItemText()); + } + return actualSuggestionStrings.containsAll(expectedSuggestionStrings); + } + + @Override + public void describeTo(Description d) { + d.appendText("completion results "); + if(expectingExactMatch) { + d.appendText("exactly matching "); + } else { + d.appendText("containing "); + } + d.appendValue(expectedSuggestionStrings); + } + } } diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementChildOne.xsd b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementChildOne.xsd new file mode 100644 --- /dev/null +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementChildOne.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementChildTwo.xsd b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementChildTwo.xsd new file mode 100644 --- /dev/null +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementChildTwo.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementCompletion.xml b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementCompletion.xml new file mode 100644 --- /dev/null +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementCompletion.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementParent.xsd b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementParent.xsd new file mode 100644 --- /dev/null +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/AbstractElementParent.xsd @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/Include.xml b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/Include.xml --- a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/Include.xml +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/Include.xml @@ -1,5 +1,5 @@ - < diff --git a/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/XMLSchema.xsd b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/XMLSchema.xsd new file mode 100644 --- /dev/null +++ b/xml.schema.completion/test/unit/src/org/netbeans/modules/xml/schema/completion/resources/XMLSchema.xsd @@ -0,0 +1,2473 @@ + + + + + + Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp + Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp + + + + + + The schema corresponding to this document is normative, + with respect to the syntactic constraints it expresses in the + XML Schema language. The documentation (within <documentation> elements) + below, is not normative, but rather highlights important aspects of + the W3C Recommendation of which this is a part + + + + + The simpleType element and all of its members are defined + towards the end of this schema document + + + + + + Get access to the xml: attribute groups for xml:lang + as declared on 'schema' and 'documentation' below + + + + + + + + This type is extended by almost all schema types + to allow attributes from other namespaces to be + added to user schemas. + + + + + + + + + + + + + This type is extended by all types which allow annotation + other than <schema> itself + + + + + + + + + + + + + + + + This group is for the + elements which occur freely at the top level of schemas. + All of their types are based on the "annotated" type by extension. + + + + + + + + + + + + + This group is for the + elements which can self-redefine (see <redefine> below). + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction} + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction, list, union} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for maxOccurs + + + + + + + + + + + + for all particles + + + + + + + for element, group and attributeGroup, + which both define and reference + + + + + + + + 'complexType' uses this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This branch is short for + <complexContent> + <restriction base="xs:anyType"> + ... + </restriction> + </complexContent> + + + + + + + + + + + + + + + Will be restricted to required or forbidden + + + + + + Not allowed if simpleContent child is chosen. + May be overriden by setting on complexContent child. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This choice is added simply to + make this a valid restriction per the REC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overrides any setting on complexType parent. + + + + + + + + + + + + + + + This choice is added simply to + make this a valid restriction per the REC + + + + + + + + + + + + + + + + + No typeDefParticle group reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {substitution, extension, + restriction} + + + + + + + + + + + + + + + + + + + + + + + + + The element element can be used either + at the top level to define an element-type binding globally, + or within a content model to either reference a globally-defined + element or type or declare an element-type binding locally. + The ref form is not allowed at the top level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for explicit groups, named top-level groups and + group references + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for the three kinds of group + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This choice with min/max is here to + avoid a pblm with the Elt:All/Choice/Seq + Particle derivation constraint + + + + + + + + + + restricted max/min + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Only elements allowed inside + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simple type for the value of the 'namespace' attr of + 'any' and 'anyAttribute' + + + + Value is + ##any - - any non-conflicting WFXML/attribute at all + + ##other - - any non-conflicting WFXML/attribute from + namespace other than targetNS + + ##local - - any unqualified non-conflicting WFXML/attribute + + one or - - any non-conflicting WFXML/attribute from + more URI the listed namespaces + references + (space separated) + + ##targetNamespace or ##local may appear in the above list, to + refer to the targetNamespace of the enclosing + schema or an absent targetNamespace respectively + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in selectors + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the following EBNF: + Selector ::= Path ( '|' Path )* + Path ::= ('.//')? Step ( '/' Step )* + Step ::= '.' | NameTest + NameTest ::= QName | '*' | NCName ':' '*' + child:: is also allowed + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in fields + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the same EBNF as for selector, + with the following change: + Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) + + + + + + + + + + + + + + + + + + + + + + + + + + + The three kinds of identity constraints, all with + type of or derived from 'keybase'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + A public identifier, per ISO 8879 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + notations for use within XML Schema schemas + + + + + + + + + Not the real urType, but as close an approximation as we can + get in the XML representation + + + + + + + + + + First the built-in primitive datatypes. These definitions are for + information only, the real built-in definitions are magic. + + + + For each built-in datatype in this schema (both primitive and + derived) can be uniquely addressed via a URI constructed + as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype + + For example, to address the int datatype, the URI is: + + http://www.w3.org/2001/XMLSchema#int + + Additionally, each facet definition element can be uniquely + addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the facet + + For example, to address the maxInclusive facet, the URI is: + + http://www.w3.org/2001/XMLSchema#maxInclusive + + Additionally, each facet usage in a built-in datatype definition + can be uniquely addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype, followed + by a period (".") followed by the name of the facet + + For example, to address the usage of the maxInclusive facet in + the definition of int, the URI is: + + http://www.w3.org/2001/XMLSchema#int.maxInclusive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOTATION cannot be used directly in a schema; rather a type + must be derived from it by specifying at least one enumeration + facet whose value is the name of a NOTATION declared in the + schema. + + + + + + + + + + Now the derived primitive types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern specifies the content of section 2.12 of XML 1.0e2 + and RFC 3066 (Revised version of RFC 1766). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 7 from the XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 5 from the XML spec + + + + + + + + + + + + + + + pattern matches production 4 from the Namespaces in XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + #all or (possibly empty) subset of {restriction, union, list} + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Can be restricted to required or forbidden + + + + + + + + + + + + + + + + + + Required at the top level + + + + + + + + + + + + + + + + + + + Forbidden when nested + + + + + + + + + + + + + + + + + + + We should use a substitution group for facets, but + that's ruled out because it would allow users to + add their own, which we're not ready for yet. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + base attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + itemType attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + + + memberTypes attribute must be non-empty or there must be + at least one simpleType child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/GlobalElement.java b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/GlobalElement.java --- a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/GlobalElement.java +++ b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/GlobalElement.java @@ -87,6 +87,11 @@ Set getFinalDefault(); Set getFinalEffective(); + /** + * The substitution group to which this element belongs + * @return the substitution group to which this element belongs, or null if + * the element does not belong to a substitution group + */ NamedComponentReference getSubstitutionGroup(); void setSubstitutionGroup(NamedComponentReference element); diff --git a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/SchemaModel.java b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/SchemaModel.java --- a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/SchemaModel.java +++ b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/SchemaModel.java @@ -44,6 +44,7 @@ package org.netbeans.modules.xml.schema.model; import java.util.Collection; +import java.util.Set; import org.netbeans.modules.xml.xam.NamedReferenceable; import org.netbeans.modules.xml.xam.Referenceable; import org.netbeans.modules.xml.xam.dom.DocumentModel; @@ -110,6 +111,17 @@ * @param type type of the component. */ T resolve(String namespace, String localName, Class type); + + /** + * Find all possible substitutions for the specified substitution group + * head (i.e a {@link GlobalElement} which other global elements declare + * as a {@code substitutionGroup}. + * @param namespace the namespace of the substitution group head + * @param localName local name of the substitution group head (must resolve + * to a global element) + * @return all possible substitutions for the specified type + */ + Set resolveSubstitutions(String namespace, String localName); /** * Returns true for schemas that are embedded inside other artifacts such as WSDLs and BPELs. diff --git a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/SchemaModelImpl.java b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/SchemaModelImpl.java --- a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/SchemaModelImpl.java +++ b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/SchemaModelImpl.java @@ -44,25 +44,15 @@ package org.netbeans.modules.xml.schema.model.impl; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; +import java.util.*; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.XMLConstants; import javax.xml.namespace.QName; -import org.netbeans.modules.xml.schema.model.Import; -import org.netbeans.modules.xml.schema.model.Include; -import org.netbeans.modules.xml.schema.model.Redefine; +import org.netbeans.modules.xml.schema.model.*; import org.netbeans.modules.xml.xam.locator.CatalogModelException; import org.netbeans.modules.xml.xam.ModelSource; -import org.netbeans.modules.xml.schema.model.Schema; -import org.netbeans.modules.xml.schema.model.SchemaComponent; -import org.netbeans.modules.xml.schema.model.SchemaComponentFactory; -import org.netbeans.modules.xml.schema.model.SchemaModel; -import org.netbeans.modules.xml.schema.model.SchemaModelFactory; -import org.netbeans.modules.xml.schema.model.SchemaModelReference; import org.netbeans.modules.xml.schema.model.impl.xdm.SyncUpdateVisitor; import org.netbeans.modules.xml.xam.ComponentUpdater; import org.netbeans.modules.xml.xam.Model; @@ -77,6 +67,7 @@ import org.netbeans.modules.xml.schema.model.impl.resolver.ChamelionResolver; import org.netbeans.modules.xml.schema.model.impl.resolver.ImportResolver; import org.netbeans.modules.xml.schema.model.impl.resolver.IncludeResolver; +import org.netbeans.modules.xml.schema.model.impl.resolver.SubstitutionGroupResolver; /** * @@ -154,6 +145,16 @@ } @Override + public Set resolveSubstitutions(String namespace, String localName) { + GlobalElement element = resolve(namespace, localName, GlobalElement.class); + if(element == null) { + return Collections.emptySet(); + } else { + return SubstitutionGroupResolver.resolveSubstitutions(this, element); + } + } + + @Override public T resolve(String namespace, String localName, Class type) { diff --git a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/resolver/SubstitutionGroupResolver.java b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/resolver/SubstitutionGroupResolver.java new file mode 100644 --- /dev/null +++ b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/impl/resolver/SubstitutionGroupResolver.java @@ -0,0 +1,131 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU General + * Public License Version 2 only ("GPL") or the Common Development and + * Distribution License("CDDL") (collectively, the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy of the + * License at http://www.netbeans.org/cddl-gplv2.html or + * nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language + * governing permissions and limitations under the License. When distributing + * the software, include this License Header Notice in each file and include the + * License file at nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided by Oracle + * in the GPL Version 2 section of the License file that accompanied this code. + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL or only + * the GPL Version 2, indicate your decision by adding "[Contributor] elects to + * include this software in this distribution under the [CDDL or GPL Version 2] + * license." If you do not indicate a single choice of license, a recipient has + * the option to distribute your version of this file under either the CDDL, the + * GPL Version 2 or to extend the choice of license to its licensees as provided + * above. However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is made + * subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.xml.schema.model.impl.resolver; + +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.modules.xml.schema.model.*; +import org.netbeans.modules.xml.schema.model.impl.SchemaImpl; +import org.netbeans.modules.xml.schema.model.impl.SchemaModelImpl; +import org.netbeans.modules.xml.schema.model.visitor.DeepSchemaVisitor; +import org.netbeans.modules.xml.xam.NamedReferenceable; +import org.netbeans.modules.xml.xam.dom.NamedComponentReference; +import org.netbeans.modules.xml.xam.locator.CatalogModelException; + +/** + * Resolves substitutions for {@link GlobalElement}s which are referenced by + * other + * {@link GlobalElement}s as substitution groups. + * + * @author Daniel Bell (dbell@netbeans.org) + * @see #resolveSubstitutions + */ +public class SubstitutionGroupResolver { + + private SubstitutionGroupResolver() { + } + + /** + * Find all substitutions for a {@link GlobalElement} that is referenced by + * other {@link GlobalElement}s as a substitution group. All referenced + * schemas (via <import> / <include> /<redefine>) are + * searched for substitution possibilities. + * + * @author Daniel Bell (dbell@netbeans.org) + */ + public static Set resolveSubstitutions(SchemaModelImpl model, GlobalElement substitutionGroupBase) { + Collection schemasToSearch = new LinkedList(); + + //Look in current schema + SchemaImpl startSchema = model.getSchema(); + schemasToSearch.add(startSchema); + + //Look in all referenced schemas + Collection referencedSchemas = startSchema.getSchemaReferences(); + for (SchemaModelReference reference : referencedSchemas) { + try { + SchemaModel referencedModel = reference.resolveReferencedModel(); + schemasToSearch.add(referencedModel.getSchema()); + } catch (CatalogModelException ex) { + Logger.getLogger(SubstitutionGroupResolver.class.getName()).log(Level.FINE, "Could not resolve schema reference", ex); + } + } + FindSubstitutionVisitor visitor = new FindSubstitutionVisitor(substitutionGroupBase); + for (Schema schema : schemasToSearch) { + schema.accept(visitor); + } + return Collections.unmodifiableSet(visitor.substitutions); + } + + private static final class FindSubstitutionVisitor extends DeepSchemaVisitor { + + private final Set substitutions = new LinkedHashSet(); + private final GlobalElement substitutionGroupBase; + + public FindSubstitutionVisitor(GlobalElement substitutionGroupBase) { + this.substitutionGroupBase = substitutionGroupBase; + } + + @Override + public void visit(ElementReference element) { + NamedComponentReference reference = element.getRef(); + if(!reference.isBroken()) { + addIfInSubstitutionGroup(reference.get()); + } + super.visit(element); + } + + @Override + public void visit(GlobalElement element) { + addIfInSubstitutionGroup(element); + super.visit(element); + } + + private void addIfInSubstitutionGroup(GlobalElement potentialSubstitute) { + NamedComponentReference substitutionGroupReference = potentialSubstitute.getSubstitutionGroup(); + boolean hasResolvableSubGroup = !(substitutionGroupReference == null || substitutionGroupReference.isBroken()); + if(hasResolvableSubGroup) { + GlobalElement referencedSubstitutionGroup = substitutionGroupReference.get(); + if (substitutionGroupBase.equals(referencedSubstitutionGroup)) { + substitutions.add(potentialSubstitute); + } + } + } + } +} diff --git a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/visitor/PreviewImpl.java b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/visitor/PreviewImpl.java --- a/xml.schema.model/src/org/netbeans/modules/xml/schema/model/visitor/PreviewImpl.java +++ b/xml.schema.model/src/org/netbeans/modules/xml/schema/model/visitor/PreviewImpl.java @@ -68,6 +68,14 @@ /** * Returns a collection of schema components, all of which, * reference the same global schema component. + * @return a Map of usages to their path from their respective schema's root.
+ * Example:
+ *
+     * { 
+     *    myElement : [mySchema > myElement], 
+     *    myOtherElement : [myOtherSchema > myType > myOtherElement] 
+     * }
+     * 
*/ public Map> getUsages() { return usages; diff --git a/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/TestCatalogModel.java b/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/TestCatalogModel.java --- a/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/TestCatalogModel.java +++ b/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/TestCatalogModel.java @@ -60,6 +60,9 @@ import java.util.HashMap; import java.util.Map; import javax.swing.text.Document; +import org.junit.rules.ExternalResource; +import org.junit.rules.MethodRule; +import org.junit.rules.TestRule; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.BaseKit; import org.netbeans.modules.xml.retriever.catalog.impl.CatalogFileWrapperDOMImpl; @@ -234,5 +237,24 @@ public void clearDocumentPool() { fileToDocumentMap = null; } + + /** + * A JUnit {@link TestRule} that stops tests from interfering with one + * another. JUnit will automatically set up/clean up the catalog when this + * rule is used.
+ * Usage:
+ * {@code @Rule public final TestRule catalogMaintainer = TestCatalogModel.maintainer()} + * @return the TestRule + */ + public static TestRule maintainer() { + return new ExternalResource() { + + @Override + protected void after() { + getDefault().clearDocumentPool(); + } + + }; + } } diff --git a/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/resolver/SubstitutionGroupResolverTest.java b/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/resolver/SubstitutionGroupResolverTest.java new file mode 100644 --- /dev/null +++ b/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/resolver/SubstitutionGroupResolverTest.java @@ -0,0 +1,129 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU General + * Public License Version 2 only ("GPL") or the Common Development and + * Distribution License("CDDL") (collectively, the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy of the + * License at http://www.netbeans.org/cddl-gplv2.html or + * nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language + * governing permissions and limitations under the License. When distributing + * the software, include this License Header Notice in each file and include the + * License file at nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided by Oracle + * in the GPL Version 2 section of the License file that accompanied this code. + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL or only + * the GPL Version 2, indicate your decision by adding "[Contributor] elects to + * include this software in this distribution under the [CDDL or GPL Version 2] + * license." If you do not indicate a single choice of license, a recipient has + * the option to distribute your version of this file under either the CDDL, the + * GPL Version 2 or to extend the choice of license to its licensees as provided + * above. However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is made + * subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.xml.schema.model.resolver; + +import java.util.Set; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import static org.junit.matchers.JUnitMatchers.hasItem; +import org.junit.rules.TestRule; +import org.netbeans.modules.xml.schema.model.*; +import org.netbeans.modules.xml.schema.model.impl.SchemaModelImpl; +import org.netbeans.modules.xml.schema.model.impl.resolver.SubstitutionGroupResolver; + +/** + * Tests for {@link SubstitutionGroupResolver} + * @author Daniel Bell (dbell@netbeans.org) + */ +public class SubstitutionGroupResolverTest { + public static final String PARENT_NS_URI = "urn:parent"; + public static final String SUBSTITUTION_GROUP_HEAD = "child"; + private static final String CHILD_ONE_NS_URI = "urn:child1"; + private static final String CHILD_TWO_NS_URI = "urn:child2"; + private static final String RESOURCE_ARCHIVE = "resources/substitutiongroup.zip"; + private static final String PARENT_SCHEMA = "substitutiongroup/SubstitutionGroupParent.xsd"; + private static final String CHILD_SCHEMA_ONE = "substitutiongroup/SubstitutionGroupChildOne.xsd"; + private static final String CHILD_SCHEMA_TWO = "substitutiongroup/SubstitutionGroupChildTwo.xsd"; + private static final String SUBSTITUTION_ELEMENT_ONE = "child-one"; + private static final String SUBSTITUTION_ELEMENT_TWO = "child-two"; + + @Rule + public final TestRule catalogMaintainer = TestCatalogModel.maintainer(); + + private SchemaModelImpl parentModel; + private SchemaModelImpl childModelOne; + private SchemaModelImpl childModelTwo; + + @Before + public void setUp() throws Exception { + childModelOne = load(CHILD_SCHEMA_ONE); + childModelTwo = load(CHILD_SCHEMA_TWO); + parentModel = load(PARENT_SCHEMA);//SchemaModelImpl) modelOne.findSchemas(PARENT_NS_URI).iterator().next().getModel(); + } + + /** + * Each schema imports a substitution group base, and defines a global + * element that is part of this substitution group. + * SubstitutionGroupResolver should resolve this substitution. + */ + @Test + public void shouldResolveSubstitutionsFromLinkedSchemas() { + GlobalElement substitutionGroupHead = getCachedElement(parentModel, SUBSTITUTION_GROUP_HEAD); + GlobalElement expectedSubstitutionOne = getCachedElement(childModelOne, SUBSTITUTION_ELEMENT_ONE); + GlobalElement expectedSubstitutionTwo = getCachedElement(childModelTwo, SUBSTITUTION_ELEMENT_TWO); + + Set possibleSubstitutionsOne = SubstitutionGroupResolver.resolveSubstitutions(childModelOne, substitutionGroupHead); + Set possibleSubstitutionsTwo = SubstitutionGroupResolver.resolveSubstitutions(childModelTwo, substitutionGroupHead); + + assertThat(possibleSubstitutionsOne.size(), is(1)); + assertThat(possibleSubstitutionsOne, hasItem(expectedSubstitutionOne)); + + assertThat(possibleSubstitutionsTwo.size(), is(1)); + assertThat(possibleSubstitutionsTwo, hasItem(expectedSubstitutionTwo)); //Should used cached substitution group head + } + + /** + * The same as {@link #shouldResolveSubstitutionsFromLinkedSchemas()}, but + * through the API + */ + @Test + public void shouldResolveSubstitutionsViaModel() { + Set possibleSubstitutionsOne = childModelOne.resolveSubstitutions(PARENT_NS_URI, SUBSTITUTION_GROUP_HEAD); + Set possibleSubstitutionsTwo = childModelTwo.resolveSubstitutions(PARENT_NS_URI, SUBSTITUTION_GROUP_HEAD); + + GlobalElement expectedSubstitutionOne = childModelOne.resolve(CHILD_ONE_NS_URI, SUBSTITUTION_ELEMENT_ONE, GlobalElement.class); + GlobalElement expectedSubstitutionTwo = childModelTwo.resolve(CHILD_TWO_NS_URI, SUBSTITUTION_ELEMENT_TWO, GlobalElement.class); + + assertThat(possibleSubstitutionsOne.size(), is(1)); + assertThat(possibleSubstitutionsOne, hasItem(expectedSubstitutionOne)); + + assertThat(possibleSubstitutionsTwo.size(), is(1)); + assertThat(possibleSubstitutionsTwo, hasItem(expectedSubstitutionTwo)); + } + + private static GlobalElement getCachedElement(SchemaModelImpl model, String localName) { + return model.getGlobalComponentsIndexSupport().findByNameAndType(localName, GlobalElement.class); + } + + private static SchemaModelImpl load(String schema) throws Exception { + return Util.toSchemaModelImpl(Util.loadSchemaModel2(RESOURCE_ARCHIVE, schema)); + } +} diff --git a/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/resources/substitutiongroup.zip b/xml.schema.model/test/unit/src/org/netbeans/modules/xml/schema/model/resources/substitutiongroup.zip new file mode 100644 index 0000000000000000000000000000000000000000..0f2162901f00889a579b2ae44515ed87972ed726 GIT binary patch literal 11302 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 diff --git a/xml.xam/src/org/netbeans/modules/xml/xam/dom/AbstractDocumentComponent.java b/xml.xam/src/org/netbeans/modules/xml/xam/dom/AbstractDocumentComponent.java --- a/xml.xam/src/org/netbeans/modules/xml/xam/dom/AbstractDocumentComponent.java +++ b/xml.xam/src/org/netbeans/modules/xml/xam/dom/AbstractDocumentComponent.java @@ -821,6 +821,11 @@ CatalogModel nr = (CatalogModel) getModel().getModelSource().getLookup().lookup(CatalogModel.class); + if(nr == null) { + String error = String.format("Cannot resolve file [hint = %s, backup = %s], because no CatalogModel exists in lookup", hint, backup); + throw new CatalogModelException(error); + } + // try hint ModelSource ms = resolveModelSource(hint, getModel().getModelSource(), nr);