[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