[prev in list] [next in list] [prev in thread] [next in thread]
List: xmlbeans-dev
Subject: Re: Wildcard matching rule for XML namespaces?
From: Oliver Newell <olivern () LL ! mit ! edu>
Date: 2007-09-03 20:20:23
Message-ID: 46DC6C87.6000203 () LL ! mit ! edu
[Download RAW message or body]
Hi -
I was successful in implementing the behavior I was after. Attached is
an implementation of a WildcardMatchMap class that allows for mapping of
multiple keys that match a given pattern to a single common string. I
included some comments in the class header describing how it can be used
in conjunction with XmlBeans - basically identical to the steps David
provided. The upshot is that I can now set up my services that are based
on XmlBeans so that they still work when messages are received from
Service Clients based on newer versions of a service schema.
Thanks for the help!
-Oliver
Oliver Newell wrote:
> David - Thanks - that does help!
>
> Maybe I can just create a suitable WildcardMatchMap<String,String>
> object that conforms to the Map interface? I'll give that a shot and
> post on success :-) (or failure :-( )
>
> -Oliver
>
> David Jencks wrote:
>> AFAIK there's no wildcard support but there is support for swapping
>> known namespaces when you read a document.
>>
>> private static Map<String, String> NAMESPACE_UPDATES = new
>> HashMap<String, String>();
>> //load the map somehow
>>
>> XmlOptions options = new XmlOptions();
>> options.setLoadSubstituteNamespaces(NAMESPACE_UPDATES);
>> XmlObject parsed = XmlObject.Factory.parse(element, options);
>>
>> If you want to pursue wildcards I'd look for what happens to this map
>> in the xmlbeans code. I think that its fed into the STAX layer:
>> OpenEJB does something similar with something that just plugs into STAX.
>>
>> hope this helps
>> david jencks
>>
>>
>> On Aug 31, 2007, at 12:57 PM, Oliver Newell wrote:
>>
>>>
>>> Hi - I'm working with a set of schemas that embed numbers of the
>>> form $major.$minor in the XML namespaces to convey version
>>> information. The numbering scheme extends to the imported namespaces
>>> - not just the target namespace of the schema, as shown below. By
>>> convention, minor numbers are used to indicate changes that are
>>> backwards compatible with previous versions - /additions/ to the
>>> schema. Major numbers are incremented when changes are made that
>>> break backwards compatibility.
>>> <?xml version="1.0" encoding="UTF-8" ?>
>>> <WMS_Capabilities xmlns="http://www.opengis.net/wms/1.3"
>>> xmlns:gml="http://www.opengis.net/gml/3.1" >
>>> <Service>
>>> <Name>Web Map Service</Name>
>>> <KeywordList>
>>> <Keyword>Topography</Keyword>
>>> <Keyword>Contour</Keyword>
>>> </KeywordList>
>>>
>>> etc.....
>>>
>>> </Service>
>>> </WMS_Capabilities>
>>>
>>> I am interested in having the capability for XMLBeans-based
>>> applications to happily consume instance documents that are produced
>>> using new minor versions of a schema. I also want to only generating
>>> new package names for new major versions of the schema. I see there
>>> is a facility in the XMLBeans configuration mechanism to specify the
>>> package names that are generated, so I can do the latter. I don't
>>> see a way to do the former - it seems like some kind of a wildcard
>>> mapping rule is needed so that an application based on version 1.3
>>> of a schema will know how to unmarshall a version 1.4 instance
>>> document to the 1.3 data model. In other words, a configuration rule
>>> that stated something like:
>>>
>>> http://www.opengis.net/wms/1.* --> mapsTo -->
>>> http://www.opengis.net/wms/1
>>>
>>> so that all versions with major number of 1 map to the same
>>> namespace as far as XMLbeans is concerned.
>>>
>>> The usefulness of the wildcard capability wouldn't be limited to the
>>> particular $major.$minor numbering approach shown above. It should
>>> also be capable of supporting some of the W3C namespace versioning
>>> practices (which tend to embed a year in the namespace). This
>>> capability would allow for significantly increased system agility as
>>> schemas evolve. Right now, if I bump a minor version number in a
>>> schema namespace, I have to make sure ALL XMLBeans consumers are
>>> updated to use the new namespace, which isn't very practical for the
>>> distributed, 24/7 systems we are working on.
>>>
>>> Is there an existing way to accomplish what I am describing in
>>> XMLBeans? If not, is there any developer interest, or can someone
>>> give me a hint as to how hard this would be to implement? (and where
>>> to start?)
>>>
>>> Thanks!
>>>
>>> -Oliver
>>>
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe@xmlbeans.apache.org
>>> For additional commands, e-mail: dev-help@xmlbeans.apache.org
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe@xmlbeans.apache.org
>> For additional commands, e-mail: dev-help@xmlbeans.apache.org
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@xmlbeans.apache.org
> For additional commands, e-mail: dev-help@xmlbeans.apache.org
["WildcardMatchMap.java" (text/plain)]
/*
*/
package edu.mit.ll.common.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.regex.*;
/**
* Map implementation that supports wildcards. This allows for a number
* of strings that matches a given pattern to be mapped to a single
* common string.
*
* This class was originally written for the purpose of mapping XML
* namespaces to their 'ancestors' when the namespaces are set up to
* include versioning information (typically using major/minor version
* numbers at the end of each namespace). This is useful when implementing
* applications using Java/XML binding technologies (e.g. XMLBeans) to
* allow the applications to remain compatible with incoming XML messages
* whose namespaces have been modified to reflect new (but backwards-
* compatible) capabilities.
*
* The following code fragment illustrates the usage in the context of
* XmlBeans. In this example, the application is being built with
* objects that use the "http://www.opengis.net/wcs/1.1" and
* "http://www.opengis.net/ows/2.1" XML namespaces. In order to make
* the application work with new versions of the schema that are
* backwards-compatible (only minor version changes), mappings are
* created that make namespaces matching the patterns 'wcs/1.*' and
* 'ows/2.*' map to 'wcs/1.1' and 'ows/2.1', respectively.
*
* ...
*
* WildcardMatchMap map = new WildcardMatchMap();
* map.put("http://www.opengis.net/wcs/1\\..*",
* "http://www.opengis.net/wcs/1.1" );
* map.put("http://www.opengis.net/ows/2\\..*",
* "http://www.opengis.net/ows/2.1" );
*
* XmlOptions options = new XmlOptions();
* options.setLoadSubstituteNamespaces( map );
*
* InputStream xmlStream = getClass().getResourceAsStream( resourceName );
*
* net.opengis.wcs.v1.GetCapabilitiesDocument getCapDoc =
* net.opengis.wcs.v1.GetCapabilitiesDocument.Factory.parse( xmlStream,
* options );
*
* ....
*
*
* Note that XmlBeans is tolerant of unknown elements as well as newly-added
* namespaces. Coupled with the namespace mapping capability above, this
* provides the flexibility needed to manage the schema versioning problem.
*
*/
public class WildcardMatchMap extends AbstractMap
implements Cloneable, Serializable
{
/**
* Map entry - The assumption is that each key is a regular expression
* pattern. For efficiency, the pattern is compiled once when the Entry
* is created.
*/
static class Entry implements Map.Entry {
protected Object key, value;
protected Pattern patt;
public Entry(Object key, Object value) {
this.key = key;
this.value = value;
this.patt = Pattern.compile( (String)key, Pattern.DOTALL );
}
public Object getKey() {
return key;
}
public Object getValue() {
return value;
}
public Pattern getPatt() {
return patt;
}
public Object setValue(Object newValue) {
Object oldValue = value;
value = newValue;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry e = (Map.Entry)o;
return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
(value==null ? e.getValue()==null : value.equals(e.getValue()));
}
public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
public String toString() {
return key + "=" + value;
}
}
private Set entries = null;
private ArrayList list;
public WildcardMatchMap() {
list = new ArrayList();
}
public WildcardMatchMap(Map map) {
list = new ArrayList();
putAll(map);
}
public WildcardMatchMap(int initialCapacity) {
list = new ArrayList(initialCapacity);
}
public Set entrySet() {
if (entries==null) {
entries = new AbstractSet() {
public void clear() {
list.clear();
}
public Iterator iterator() {
return list.iterator();
}
public int size() {
return list.size();
}
};
}
return entries;
}
public Object put(Object key, Object value) {
int size = list.size();
Entry entry = null;
int i;
if (key==null) {
for (i=0; i<size; i++) {
entry = (Entry)(list.get(i));
if (entry.getKey() == null) {
break;
}
}
} else {
for (i=0; i<size; i++) {
entry = (Entry)(list.get(i));
if (key.equals(entry.getKey())) {
break;
}
}
}
Object oldValue = null;
if (i<size) {
oldValue = entry.getValue();
entry.setValue(value);
} else {
list.add(new Entry(key, value));
}
return oldValue;
}
/**
* Check if map contains the specified key. Unlike most map implementations,
* an exact match is not performed in this case. Rather, a check is made
* to see if the key matches one of the wildcard patterns stored in the
* map, and if so, returns true.
*/
public boolean containsKey(Object key) {
Iterator<Entry> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry e = i.next();
//
// Check direct comparison first, then pattern match (optimization)
//
if (key.equals(e.getKey()))
return true;
Matcher matcher = e.getPatt().matcher( (String)key );
if( matcher.find() )
return true;
}
}
return false;
}
/**
* Get the object associated with the specified key. Unlike most map
* implementations, an exact match is not performed in this case.
* Rather, a check is made to see if the key matches one of the
* wildcard patterns stored in the map, and if so, the map value
* associated with the pattern is returned.
*/
public Object get(Object key) {
Iterator<Entry> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry e = i.next();
//
// Check direct comparison first, then pattern match (optimization)
//
if (key.equals(e.getKey()))
{
return e.getValue();
}
else
{
Matcher matcher = e.getPatt().matcher( (String)key );
if( matcher.find() )
{
return e.getValue();
}
}
}
}
return null;
}
public Object clone() {
return new WildcardMatchMap(this);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@xmlbeans.apache.org
For additional commands, e-mail: dev-help@xmlbeans.apache.org
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic