add a mechanism for catching invalid mods to some of the global collections
git-svn-id: https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/XmlSchema@1022772 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index b8606aa..e38b7e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -214,11 +214,15 @@
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
+ <version>2.6</version>
<configuration>
<forkMode>never</forkMode>
<additionalClasspathElements>
<additionalClasspathElement>w3c-testcases</additionalClasspathElement>
</additionalClasspathElements>
+ <systemPropertyVariables>
+ <org.apache.ws.commons.schema.protectReadOnlyCollections>true</org.apache.ws.commons.schema.protectReadOnlyCollections>
+ </systemPropertyVariables>
</configuration>
</plugin>
<plugin>
@@ -386,7 +390,7 @@
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
- <version>2.7.0</version>
+ <version>2.7.1</version>
</dependency>
</dependencies>
<build>
@@ -394,23 +398,15 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <systemProperties>
-<!--
- The default xalan TransformerFactory on the ibm jdk is
- org.apache.xalan.processor.TransformerFactoryImpl which has a
- known issue with implicit namespaces. Set this property to use
- the xsltc TransformerFactory (which the sun jdk seems to
- default to).
- -->
- <property>
- <name>
- javax.xml.transform.TransformerFactory
- </name>
- <value>
- org.apache.xalan.xsltc.trax.TransformerFactoryImpl
- </value>
- </property>
- </systemProperties>
+ <systemPropertyVariables>
+ <!--
+ The default xalan TransformerFactory on the ibm jdk is
+ org.apache.xalan.processor.TransformerFactoryImpl which has a
+ known issue with implicit namespaces. Set this property to use
+ the xsltc TransformerFactory (which the sun jdk seems to
+ default to). -->
+ <javax.xml.transform.TransformerFactory>org.apache.xalan.xsltc.trax.TransformerFactoryImpl</javax.xml.transform.TransformerFactory>
+ </systemPropertyVariables>
</configuration>
</plugin>
</plugins>
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchema.java b/src/main/java/org/apache/ws/commons/schema/XmlSchema.java
index 772fa48..e6d91b0 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchema.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchema.java
@@ -45,6 +45,7 @@
import org.w3c.dom.Document;
import org.apache.ws.commons.schema.XmlSchemaSerializer.XmlSchemaSerializerException;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.NamespaceContextOwner;
import org.apache.ws.commons.schema.utils.NamespacePrefixList;
@@ -201,22 +202,28 @@
* Return a map containing all the defined attribute groups of this schema. The keys are QNames, where the
* namespace will always be the target namespace of this schema. This makes it easier to look up items for
* cross-schema references.
+ * <br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a map that checks at runtime.
*
* @return the map of attribute groups.
*/
public Map<QName, XmlSchemaAttributeGroup> getAttributeGroups() {
- return attributeGroups;
+ return CollectionFactory.getProtectedMap(attributeGroups);
}
/**
* Return a map containing all the defined attributes of this schema. The keys are QNames, where the
* namespace will always be the target namespace of this schema. This makes it easier to look up items for
* cross-schema references.
+ * <br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a map that checks at runtime.
*
* @return the map of attributes.
*/
public Map<QName, XmlSchemaAttribute> getAttributes() {
- return attributes;
+ return CollectionFactory.getProtectedMap(attributes);
}
/**
@@ -260,20 +267,26 @@
* Return a map containing all the defined elements of this schema. The keys are QNames, where the
* namespace will always be the target namespace of this schema. This makes it easier to look up items for
* cross-schema references.
+ * <br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a map that checks at runtime
*
* @return the map of elements.
*/
public Map<QName, XmlSchemaElement> getElements() {
- return elements;
+ return CollectionFactory.getProtectedMap(elements);
}
/**
* Return all of the includes, imports, and redefines for this schema.
+ * <br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a list that checks at runtime
*
* @return a list of the objects representing includes, imports, and redefines.
*/
public List<XmlSchemaExternal> getExternals() {
- return externals;
+ return CollectionFactory.getProtectedList(externals);
}
/**
@@ -296,12 +309,14 @@
/**
* Return a map containing all the defined groups of this schema. The keys are QNames, where the namespace
* will always be the target namespace of this schema. This makes it easier to look up items for
- * cross-schema references.
+ * cross-schema references.<br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a map that checks at runtime
*
* @return the map of groups.
*/
public Map<QName, XmlSchemaGroup> getGroups() {
- return groups;
+ return CollectionFactory.getProtectedMap(groups);
}
/**
@@ -315,10 +330,14 @@
}
/**
+ * Return all of the global items in this schema.<br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a map that checks at runtime.
* @return <strong>all</strong> of the global items from this schema.
+ *
*/
public List<XmlSchemaObject> getItems() {
- return items;
+ return CollectionFactory.getProtectedList(items);
}
/**
@@ -353,11 +372,14 @@
* Return a map containing all the defined notations of this schema. The keys are QNames, where the
* namespace will always be the target namespace of this schema. This makes it easier to look up items for
* cross-schema references.
+ * <br/>
+ * If org.apache.ws.commons.schema.protectReadOnlyCollections
+ * is 'true', this will return a map that checks at runtime.
*
* @return the map of notations.
*/
public Map<QName, XmlSchemaNotation> getNotations() {
- return notations;
+ return CollectionFactory.getProtectedMap(notations);
}
/**
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttribute.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttribute.java
index c466111..3d99040 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttribute.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttribute.java
@@ -21,6 +21,7 @@
import javax.xml.namespace.QName;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedWithForm;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedWithFormImpl;
import org.apache.ws.commons.schema.utils.XmlSchemaRef;
@@ -53,8 +54,13 @@
namedDelegate.setRefObject(ref);
ref.setNamedObject(namedDelegate);
use = XmlSchemaUse.NONE;
+ final XmlSchema fSchema = schema;
if (topLevel) {
- schema.getItems().add(this);
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ fSchema.getItems().add(XmlSchemaAttribute.this);
+ }
+ });
}
}
@@ -130,17 +136,23 @@
}
public void setName(String name) {
+ final String fName = name;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
- if (namedDelegate.isTopLevel() && namedDelegate.getName() != null) {
- namedDelegate.getParent().getAttributes().remove(getQName());
- }
- namedDelegate.setName(name);
- if (namedDelegate.isTopLevel()) {
- if (name == null) {
- throw new XmlSchemaException("Top-level attributes may not be anonymous");
+ public void run() {
+ if (namedDelegate.isTopLevel() && namedDelegate.getName() != null) {
+ namedDelegate.getParent().getAttributes().remove(getQName());
+ }
+ namedDelegate.setName(fName);
+ if (namedDelegate.isTopLevel()) {
+ if (fName == null) {
+ throw new XmlSchemaException("Top-level attributes may not be anonymous");
+ }
+ namedDelegate.getParent().getAttributes().put(getQName(), XmlSchemaAttribute.this);
+ }
}
- namedDelegate.getParent().getAttributes().put(getQName(), this);
- }
+
+ });
}
public boolean isFormSpecified() {
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttributeGroup.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttributeGroup.java
index ac18697..a41100b 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttributeGroup.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaAttributeGroup.java
@@ -19,20 +19,20 @@
package org.apache.ws.commons.schema;
-import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.XmlSchemaNamed;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedImpl;
/**
- * Class for attribute groups. Groups a set of attribute declarations so that
+ * Class for attribute groups. Groups a set of attribute declarations so that
* they can be incorporated as a
- * group into complex type definitions. Represents the World Wide Web
+ * group into complex type definitions. Represents the World Wide Web
* consortium (W3C) attributeGroup element when it does <i>not</i> have a 'ref='
- * attribute.
+ * attribute.
*/
public class XmlSchemaAttributeGroup extends XmlSchemaAnnotated implements XmlSchemaNamed,
@@ -45,10 +45,16 @@
* Creates new XmlSchemaAttributeGroup
*/
public XmlSchemaAttributeGroup(XmlSchema parent) {
+ final XmlSchema fParent = parent;
namedDelegate = new XmlSchemaNamedImpl(parent, true);
- parent.getItems().add(this);
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ fParent.getItems().add(XmlSchemaAttributeGroup.this);
+ }
+ });
+
// we can't be put in the map until we have a name. Perhaps we should be forced to have a name ?
- attributes = new ArrayList<XmlSchemaAttributeGroupMember>();
+ attributes = CollectionFactory.getList(XmlSchemaAttributeGroupMember.class);
}
public XmlSchemaAnyAttribute getAnyAttribute() {
@@ -84,10 +90,15 @@
}
public void setName(String name) {
- if (name != null) {
- namedDelegate.getParent().getAttributeGroups().remove(getQName());
- }
- namedDelegate.setName(name);
- namedDelegate.getParent().getAttributeGroups().put(getQName(), this);
+ final String fName = name;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ if (fName != null) {
+ namedDelegate.getParent().getAttributeGroups().remove(getQName());
+ }
+ namedDelegate.setName(fName);
+ namedDelegate.getParent().getAttributeGroups().put(getQName(), XmlSchemaAttributeGroup.this);
+ }
+ });
}
}
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaElement.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaElement.java
index cc0d7c9..288b79f 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaElement.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaElement.java
@@ -25,6 +25,7 @@
import javax.xml.namespace.QName;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedWithForm;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedWithFormImpl;
import org.apache.ws.commons.schema.utils.XmlSchemaRef;
@@ -93,8 +94,13 @@
nillable = false;
finalDerivation = XmlSchemaDerivationMethod.NONE;
block = XmlSchemaDerivationMethod.NONE;
+ final XmlSchema fParentSchema = parentSchema;
if (topLevel) {
- parentSchema.getItems().add(this);
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ fParentSchema.getItems().add(XmlSchemaElement.this);
+ }
+ });
}
}
@@ -215,13 +221,18 @@
}
public void setName(String name) {
- if (namedDelegate.isTopLevel() && namedDelegate.getName() != null) {
- namedDelegate.getParent().getElements().remove(getQName());
- }
- namedDelegate.setName(name);
- if (namedDelegate.isTopLevel()) {
- namedDelegate.getParent().getElements().put(getQName(), this);
- }
+ final String fName = name;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ if (namedDelegate.isTopLevel() && namedDelegate.getName() != null) {
+ namedDelegate.getParent().getElements().remove(getQName());
+ }
+ namedDelegate.setName(fName);
+ if (namedDelegate.isTopLevel()) {
+ namedDelegate.getParent().getElements().put(getQName(), XmlSchemaElement.this);
+ }
+ }
+ });
}
public XmlSchemaForm getForm() {
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaExternal.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaExternal.java
index 7089c9d..e3ca7a0 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaExternal.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaExternal.java
@@ -19,6 +19,8 @@
package org.apache.ws.commons.schema;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
+
/**
* Common class for include, import, and redefine. All have in common two items:
* the location of the referenced schema (required) and an optional
@@ -33,8 +35,14 @@
* Creates new XmlSchemaExternal
*/
protected XmlSchemaExternal(XmlSchema parent) {
- parent.getExternals().add(this);
- parent.getItems().add(this);
+ final XmlSchema fParent = parent;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+
+ public void run() {
+ fParent.getExternals().add(XmlSchemaExternal.this);
+ fParent.getItems().add(XmlSchemaExternal.this);
+ }
+ });
}
public XmlSchema getSchema() {
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaGroup.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaGroup.java
index 37d0436..ed2257f 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaGroup.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaGroup.java
@@ -22,6 +22,7 @@
import javax.xml.namespace.QName;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.XmlSchemaNamed;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedImpl;
@@ -31,17 +32,22 @@
* the World Wide Web Consortium (W3C) group element.
*/
-public class XmlSchemaGroup extends XmlSchemaAnnotated implements XmlSchemaNamed,
+public class XmlSchemaGroup extends XmlSchemaAnnotated implements XmlSchemaNamed,
XmlSchemaChoiceMember, XmlSchemaSequenceMember {
private XmlSchemaGroupParticle particle;
private XmlSchemaNamedImpl namedDelegate;
-
+
public XmlSchemaGroup(XmlSchema parent) {
namedDelegate = new XmlSchemaNamedImpl(parent, true);
- parent.getItems().add(this);
+ final XmlSchema fParent = parent;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ fParent.getItems().add(XmlSchemaGroup.this);
+ }
+ });
}
-
+
public XmlSchemaGroupParticle getParticle() {
return particle;
@@ -72,13 +78,17 @@
}
public void setName(String name) {
- if (namedDelegate.getQName() != null) {
- namedDelegate.getParent().getGroups().remove(namedDelegate.getQName());
- }
- namedDelegate.setName(name);
- if (name != null) {
- namedDelegate.getParent().getGroups().put(namedDelegate.getQName(), this);
- }
+ final String fName = name;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ if (namedDelegate.getQName() != null) {
+ namedDelegate.getParent().getGroups().remove(namedDelegate.getQName());
+ }
+ namedDelegate.setName(fName);
+ if (fName != null) {
+ namedDelegate.getParent().getGroups().put(namedDelegate.getQName(), XmlSchemaGroup.this);
+ }
+ }
+ });
}
-
}
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaNotation.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaNotation.java
index 15d3e2a..a715799 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaNotation.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaNotation.java
@@ -21,6 +21,7 @@
import javax.xml.namespace.QName;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.XmlSchemaNamed;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedImpl;
@@ -41,7 +42,12 @@
*/
public XmlSchemaNotation(XmlSchema parent) {
namedDelegate = new XmlSchemaNamedImpl(parent, true);
- parent.getItems().add(this);
+ final XmlSchema fParent = parent;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ fParent.getItems().add(XmlSchemaNotation.this);
+ }
+ });
}
public String getPublic() {
@@ -93,10 +99,15 @@
}
public void setName(String name) {
- if (namedDelegate.getName() != null) {
- namedDelegate.getParent().getNotations().remove(getQName());
- }
- namedDelegate.setName(name);
- namedDelegate.getParent().getNotations().put(getQName(), this);
+ final String fName = name;
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+ public void run() {
+ if (namedDelegate.getName() != null) {
+ namedDelegate.getParent().getNotations().remove(getQName());
+ }
+ namedDelegate.setName(fName);
+ namedDelegate.getParent().getNotations().put(getQName(), XmlSchemaNotation.this);
+ }
+ });
}
}
diff --git a/src/main/java/org/apache/ws/commons/schema/XmlSchemaType.java b/src/main/java/org/apache/ws/commons/schema/XmlSchemaType.java
index 25d7ebf..2017581 100644
--- a/src/main/java/org/apache/ws/commons/schema/XmlSchemaType.java
+++ b/src/main/java/org/apache/ws/commons/schema/XmlSchemaType.java
@@ -21,6 +21,7 @@
import javax.xml.namespace.QName;
+import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.XmlSchemaNamed;
import org.apache.ws.commons.schema.utils.XmlSchemaNamedImpl;
@@ -42,10 +43,16 @@
* Creates new XmlSchemaType
*/
protected XmlSchemaType(XmlSchema schema, boolean topLevel) {
+ final XmlSchema fSchema = schema;
namedDelegate = new XmlSchemaNamedImpl(schema, topLevel);
finalDerivation = XmlSchemaDerivationMethod.NONE;
if (topLevel) {
- schema.getItems().add(this);
+ CollectionFactory.withSchemaModifiable(new Runnable() {
+
+ public void run() {
+ fSchema.getItems().add(XmlSchemaType.this);
+ }
+ });
}
}
diff --git a/src/main/java/org/apache/ws/commons/schema/utils/CollectionFactory.java b/src/main/java/org/apache/ws/commons/schema/utils/CollectionFactory.java
index 3adb0d4..88f4b99 100644
--- a/src/main/java/org/apache/ws/commons/schema/utils/CollectionFactory.java
+++ b/src/main/java/org/apache/ws/commons/schema/utils/CollectionFactory.java
@@ -23,24 +23,79 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
- * There are many collections of XML Schema objects inside XmlSchema.
- * This class provides consistent construction to centralize policy
- * for thread synchronization and the like.
+ * There are many collections of XML Schema objects inside XmlSchema. This class provides consistent
+ * construction to centralize policy for thread synchronization and the like.
*/
public final class CollectionFactory {
-
+
+ private static final String PROTECT_READ_ONLY_COLLECTIONS_PROP =
+ "org.apache.ws.commons.schema.protectReadOnlyCollections";
+
+ private static final ThreadLocal<Boolean> PROTECT_READ_ONLY_COLLECTIONS = new ThreadLocal<Boolean>() {
+
+ @Override
+ protected Boolean initialValue() {
+ return Boolean.parseBoolean(System.getProperty(PROTECT_READ_ONLY_COLLECTIONS_PROP));
+ }
+ };
+
private CollectionFactory() {
}
-
+
public static <T> List<T> getList(Class<T> type) {
return Collections.synchronizedList(new ArrayList<T>());
}
-
+
public static <T> Set<T> getSet(Class<T> type) {
return Collections.synchronizedSet(new HashSet<T>());
}
+ /**
+ * Call this to obtain a list to return from a public API where the caller is not supposed to modify the
+ * list. If org.apache.ws.commons.schema.protectReadOnlyCollections is 'true', this will return a list
+ * that checks at runtime.
+ *
+ * @param <T> Generic parameter type of the list.
+ * @param list the list.
+ * @return
+ */
+ public static <T> List<T> getProtectedList(List<T> list) {
+ if (PROTECT_READ_ONLY_COLLECTIONS.get().booleanValue()) {
+ return Collections.unmodifiableList(list);
+ } else {
+ return list;
+ }
+ }
+
+ /**
+ * Call this to obtain a map to return from a public API where the caller is not supposed to modify the
+ * map. If org.apache.ws.commons.schema.protectReadOnlyCollections is 'true', this will return a map that
+ * checks at runtime.
+ *
+ * @param <K> key type
+ * @param <V> value type
+ * @param map the map.
+ * @return
+ */
+ public static <K, V> Map<K, V> getProtectedMap(Map<K, V> map) {
+ if (PROTECT_READ_ONLY_COLLECTIONS.get().booleanValue()) {
+ return Collections.unmodifiableMap(map);
+ } else {
+ return map;
+ }
+ }
+
+ public static void withSchemaModifiable(Runnable action) {
+ Boolean saved = PROTECT_READ_ONLY_COLLECTIONS.get();
+ try {
+ PROTECT_READ_ONLY_COLLECTIONS.set(Boolean.FALSE);
+ action.run();
+ } finally {
+ PROTECT_READ_ONLY_COLLECTIONS.set(saved);
+ }
+ }
}