Home » xmlbeans-2.5.0-src » org.apache » xmlbeans » [javadoc | source]

    1   /*   Copyright 2004 The Apache Software Foundation
    2    *
    3    *   Licensed under the Apache License, Version 2.0 (the "License");
    4    *   you may not use this file except in compliance with the License.
    5    *   You may obtain a copy of the License at
    6    *
    7    *       http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    *   Unless required by applicable law or agreed to in writing, software
   10    *   distributed under the License is distributed on an "AS IS" BASIS,
   11    *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12    *   See the License for the specific language governing permissions and
   13    *  limitations under the License.
   14    */
   15   
   16   package org.apache.xmlbeans;
   17   
   18   import javax.xml.namespace.QName;
   19   
   20   import java.util.Set;
   21   import java.util.HashSet;
   22   import java.util.Iterator;
   23   import java.util.Collections;
   24   import java.util.Arrays;
   25   
   26   /**
   27    * This interface represents a lattice of finite and infinite sets of QNames.
   28    * The lattice the minimal one that is closed under union, intersection, and
   29    * inverse, and contains individual QNames as well as entire namespaces.
   30    * Here is a summary of the two kinds of QNameSets:
   31    * <ul>
   32    * <li>A QNameSet can cover a finite set of namespaces, additionally including a finite
   33    *     set of QNames outside those namespaces, and with the exception of
   34    *     a finite set of QNames excluded from those namespaes:
   35    *   <ul>
   36    *   <li>excludedQNamesInIncludedURIs == the set of excluded QNames from coveredURIs namespaces
   37    *   <li>excludedURIs == null
   38    *   <li>includedURIs == the set of covered namespace URIs
   39    *   <li>includedQNamesInExcludedURIs == set of additional QNames outside coveredURIs namespaces
   40    *   </ul>
   41    * </li>
   42    * <li>A QNameSet can cover all namespaces except for a finite number of excluded ones,
   43    *     additionally including a finite set of QNames within the excluded namespaces,
   44    *     and with the exception of a finite set of QNames outside the excluded namespaces:
   45    *   <ul>
   46    *   <li>excludedQNamesInIncludedURIs == the set of excluded QNames outside uncoveredURIs namespaces
   47    *   <li>excludedURIs == the set of uncovered namespace URIs
   48    *   <li>includedURIs == null
   49    *   <li>includedQNamesInExcludedURIs == set of additional QNames from uncoveredURIs namespaces
   50    *   </ul>
   51    * </li>
   52    * </ul>
   53    * <p>
   54    * Notice that a finite set of QNames is a degenerate case of the first
   55    * category outlined above:
   56    * <ul>
   57    * <li>A QnameSet can contain a finite number of QNames:
   58    *   <ul>
   59    *   <li>excludedQNamesInIncludedURIs == empty set
   60    *   <li>excludedURIs == null
   61    *   <li>includedURIs == empty set
   62    *   <li>includedQNamesInExcludedURIs == set of included QNames
   63    *   </ul>
   64    * </li>
   65    * </ul>
   66    *
   67    * @see QNameSetBuilder
   68    */
   69   public final class QNameSet implements QNameSetSpecification, java.io.Serializable
   70   {
   71       private static final long serialVersionUID = 1L;
   72       
   73       private final boolean _inverted;
   74       private final Set _includedURIs;
   75       private final Set _excludedQNames;
   76       private final Set _includedQNames;
   77   
   78       /**
   79        * The empty QNameSet.
   80        */
   81       public static final QNameSet EMPTY = new QNameSet(null, Collections.EMPTY_SET, Collections.EMPTY_SET, Collections.EMPTY_SET);
   82   
   83       /**
   84        * The QNameSet containing all QNames.
   85        */
   86       public static final QNameSet ALL = new QNameSet(Collections.EMPTY_SET, null, Collections.EMPTY_SET, Collections.EMPTY_SET);
   87   
   88       /**
   89        * The QNameSet containing all QNames in the local (no-)namespace.
   90        */
   91       public static final QNameSet LOCAL = new QNameSet(null, Collections.singleton(""), Collections.EMPTY_SET, Collections.EMPTY_SET);
   92   
   93       /**
   94        * The QNameSet containing all QNames except for those in the local (no-)namespace.
   95        */
   96       public static final QNameSet NONLOCAL = new QNameSet(Collections.singleton(""), null, Collections.EMPTY_SET, Collections.EMPTY_SET);
   97   
   98       /**
   99        * Private function to minimize object creation when copying sets.
  100        */
  101       private static Set minSetCopy(Set original)
  102       {
  103           if (original == null)
  104               return null;
  105           if (original.isEmpty())
  106               return Collections.EMPTY_SET;
  107           if (original.size() == 1)
  108               return Collections.singleton(original.iterator().next());
  109           return new HashSet(original);
  110       }
  111   
  112       /**
  113        * Returns a QNameSet based on the given sets of excluded URIs,
  114        * included URIs, excluded QNames in included namespaces, and included
  115        * QNames in excluded namespaces.
  116        * 
  117        * @param excludedURIs the finite set of namespace URI strings to exclude from the set, or null if this set is infinite
  118        * @param includedURIs the finite set of namespace URI strings to include in the set, or null if this set is infinite
  119        * @param excludedQNamesInIncludedURIs the finite set of exceptional QNames to exclude from the included namespaces
  120        * @param excludedQNamesInIncludedURIs the finite set of exceptional QNames to include that are in the excluded namespaces
  121        * 
  122        * @return the constructed QNameSet
  123        */
  124       public static QNameSet forSets(Set excludedURIs, Set includedURIs, Set excludedQNamesInIncludedURIs, Set includedQNamesInExcludedURIs)
  125       {
  126           if ((excludedURIs != null) == (includedURIs != null))
  127               throw new IllegalArgumentException("Exactly one of excludedURIs and includedURIs must be null");
  128   
  129           if (excludedURIs == null && includedURIs.isEmpty() && includedQNamesInExcludedURIs.isEmpty())
  130               return EMPTY;
  131           if (includedURIs == null && excludedURIs.isEmpty() && excludedQNamesInIncludedURIs.isEmpty())
  132               return ALL;
  133           if (excludedURIs == null && includedURIs.size() == 1 && includedURIs.contains("") &&
  134               includedQNamesInExcludedURIs.isEmpty() && excludedQNamesInIncludedURIs.isEmpty())
  135               return LOCAL;
  136           if (includedURIs == null && excludedURIs.size() == 1 && excludedURIs.contains("") &&
  137               excludedQNamesInIncludedURIs.isEmpty() && includedQNamesInExcludedURIs.isEmpty())
  138               return NONLOCAL;
  139   
  140           return new QNameSet(
  141                   minSetCopy(excludedURIs),
  142                   minSetCopy(includedURIs),
  143                   minSetCopy(excludedQNamesInIncludedURIs),
  144                   minSetCopy(includedQNamesInExcludedURIs));
  145       }
  146   
  147       /**
  148        * Returns a QNameSet based on the given array of included QNames
  149        * 
  150        * @param includedQNames the array of included QNames
  151        */
  152       public static QNameSet forArray(QName[] includedQNames)
  153       {
  154           if (includedQNames == null)
  155               throw new IllegalArgumentException("includedQNames cannot be null");
  156   
  157           return new QNameSet(null, Collections.EMPTY_SET, Collections.EMPTY_SET, new HashSet(Arrays.asList(includedQNames)));
  158       }
  159   
  160       /**
  161        * Returns a QNameSet with the same contents as the given
  162        * QNameSetSpecification.
  163        * @return the copied QNameSet
  164        */
  165       public static QNameSet forSpecification(QNameSetSpecification spec)
  166       {
  167           if (spec instanceof QNameSet)
  168               return (QNameSet)spec;
  169           return QNameSet.forSets(spec.excludedURIs(), spec.includedURIs(), spec.excludedQNamesInIncludedURIs(), spec.includedQNamesInExcludedURIs());
  170       }
  171   
  172       /**
  173        * Returns a QNameSet corresponding to the given wildcard namespace string.
  174        * This is a space-separated list of URIs, plus special tokens as specified
  175        * in the XML Schema specification (##any, ##other, ##targetNamespace, ##local).
  176        * @return the constructed QNameSet
  177        */
  178       public static QNameSet forWildcardNamespaceString(String wildcard, String targetURI)
  179       {
  180           return QNameSet.forSpecification(new QNameSetBuilder(wildcard, targetURI));
  181       }
  182   
  183       /**
  184        * Returns a QNameSet containing only the given QName.
  185        * @return the constructed QNameSet
  186        */
  187       public static QNameSet singleton(QName name)
  188       {
  189           return new QNameSet(null, Collections.EMPTY_SET, Collections.EMPTY_SET, Collections.singleton(name));
  190       }
  191   
  192       /**
  193        * Constructs a QNameSetBuilder whose contents are given by
  194        * the four sets.
  195        *
  196        * This constuctor is PRIVATE because it uses the given
  197        * sets directly, and it trusts its callers to set only immutable values.
  198        * This constructor is is only called by the static builder methods on
  199        * QNameSet: those methods are all careful assign only unchanging sets.
  200        */
  201       private QNameSet(Set excludedURIs, Set includedURIs, Set excludedQNamesInIncludedURIs, Set includedQNamesInExcludedURIs)
  202       {
  203           if (includedURIs != null && excludedURIs == null)
  204           {
  205               _inverted = false;
  206               _includedURIs = includedURIs;
  207               _excludedQNames = excludedQNamesInIncludedURIs;
  208               _includedQNames = includedQNamesInExcludedURIs;
  209           }
  210           else if (excludedURIs != null && includedURIs == null)
  211           {
  212               _inverted = true;
  213               _includedURIs = excludedURIs;
  214               _excludedQNames = includedQNamesInExcludedURIs;
  215               _includedQNames = excludedQNamesInIncludedURIs;
  216           }
  217           else
  218               throw new IllegalArgumentException("Exactly one of excludedURIs and includedURIs must be null");
  219       }
  220   
  221       /**
  222        * Local xml names are hased using "" as the namespace.
  223        */
  224       private static String nsFromName(QName xmlName)
  225       {
  226           String ns = xmlName.getNamespaceURI();
  227           return ns == null ? "" : ns;
  228       }
  229   
  230       /**
  231        * True if this ModelTransitionSet contains the given qname.
  232        */
  233       public boolean contains(QName name)
  234       {
  235           boolean in = _includedURIs.contains(nsFromName(name)) ?
  236                        !_excludedQNames.contains(name) :
  237                         _includedQNames.contains(name);
  238           return _inverted ^ in;
  239       }
  240   
  241       /**
  242        * True if this ModelTransitionSet contains all QNames.
  243        */
  244       public boolean isAll()
  245       {
  246           return _inverted && _includedURIs.isEmpty() && _includedQNames.isEmpty();
  247       }
  248   
  249       /**
  250        * True if this ModelTransitionSet contains no QNames.
  251        */
  252       public boolean isEmpty()
  253       {
  254           return !_inverted && _includedURIs.isEmpty() && _includedQNames.isEmpty();
  255       }
  256   
  257       /**
  258        * Returns a new QNameSet that is the intersection of this one and another.
  259        * @param set the set to insersect with
  260        * @return the intersection
  261        */
  262       public QNameSet intersect(QNameSetSpecification set)
  263       {
  264           QNameSetBuilder result = new QNameSetBuilder(this);
  265           result.restrict(set);
  266           return result.toQNameSet();
  267       }
  268   
  269       /**
  270        * Returns a new QNameSet that is the union of this one and another.
  271        * @param set the set to union with
  272        * @return the union
  273        */
  274       public QNameSet union(QNameSetSpecification set)
  275       {
  276           QNameSetBuilder result = new QNameSetBuilder(this);
  277           result.addAll(set);
  278           return result.toQNameSet();
  279       }
  280   
  281       /**
  282        * Returns a new QNameSet that is the inverse of this one.
  283        */
  284       public QNameSet inverse()
  285       {
  286           if (this == EMPTY)
  287               return ALL;
  288           if (this == ALL)
  289               return EMPTY;
  290           if (this == LOCAL)
  291               return NONLOCAL;
  292           if (this == NONLOCAL)
  293               return LOCAL;
  294           return new QNameSet(includedURIs(), excludedURIs(), includedQNamesInExcludedURIs(), excludedQNamesInIncludedURIs());
  295       }
  296   
  297       /**
  298        * True if the given set is a subset of this one.
  299        * @param set the set to test
  300        * @return true if this contains all QNames contained by the given set
  301        */
  302       public boolean containsAll(QNameSetSpecification set)
  303       {
  304           // a.contains(b) == a.inverse.isDisjoint(b)
  305           if (!_inverted && set.excludedURIs() != null)
  306               return false;
  307           
  308           return inverse().isDisjoint(set);
  309       }
  310   
  311       /**
  312        * True if the given set is disjoint from this one.
  313        * @param set the set to test
  314        * @return true if the set is disjoint from this set
  315        */
  316       public boolean isDisjoint(QNameSetSpecification set)
  317       {
  318           if (_inverted && set.excludedURIs() != null)
  319               return false;
  320   
  321           if (_inverted)
  322               return isDisjointImpl(set, this);
  323           else
  324               return isDisjointImpl(this, set);
  325       }
  326       
  327       private boolean isDisjointImpl(QNameSetSpecification set1, QNameSetSpecification set2)
  328       {
  329           Set includeURIs = set1.includedURIs();
  330           Set otherIncludeURIs = set2.includedURIs();
  331           if (otherIncludeURIs != null)
  332           {
  333               for (Iterator i = includeURIs.iterator(); i.hasNext(); )
  334               {
  335                   if (otherIncludeURIs.contains(i.next()))
  336                       return false;
  337               }
  338           }
  339           else
  340           {
  341               Set otherExcludeURIs = set2.excludedURIs();
  342               for (Iterator i = includeURIs.iterator(); i.hasNext(); )
  343               {
  344                   if (!otherExcludeURIs.contains(i.next()))
  345                       return false;
  346               }
  347           }
  348   
  349           for (Iterator i = set1.includedQNamesInExcludedURIs().iterator(); i.hasNext(); )
  350           {
  351               if (set2.contains((QName)i.next()))
  352                   return false;
  353           }
  354   
  355           if (includeURIs.size() > 0)
  356               for (Iterator i = set2.includedQNamesInExcludedURIs().iterator(); i.hasNext(); )
  357           {
  358               if (set1.contains((QName)i.next()))
  359                   return false;
  360           }
  361   
  362           return true;
  363       }
  364   
  365       
  366       /**
  367        * Namespaces that are fully excluded from the set except for a finite
  368        * number of individual QName exceptions.  Returns null if this set is infinite.
  369        * @return the set of excluded namespace URI strings
  370        */ 
  371       public Set excludedURIs()
  372       {
  373           if (_inverted) return Collections.unmodifiableSet(_includedURIs);
  374           return null;
  375       }
  376   
  377       /**
  378        * Namespaces that are fully included in set except for a finite
  379        * number of individual QName exceptions. Returns null if this set is infinite.
  380        * @return the set of included namespace URI strings
  381        */ 
  382       public Set includedURIs()
  383       {
  384           if (!_inverted) return _includedURIs;
  385           return null;
  386       }
  387       
  388       /**
  389        * The set of QNames excluded from the set even though they are within
  390        * a namespace that is otherwise fully included in the set.
  391        * @return the set of excluded QNames from within includedURI namespaces
  392        */ 
  393       public Set excludedQNamesInIncludedURIs()
  394       {
  395           return Collections.unmodifiableSet(_inverted ? _includedQNames : _excludedQNames);
  396       }
  397   
  398       /**
  399        * The set of QNames included in the set even though they are within
  400        * a namespace that is otherwise fully included in the set.
  401        * @return the set of included QNames from within excludedURI namespaces
  402        */ 
  403       public Set includedQNamesInExcludedURIs()
  404       {
  405           return Collections.unmodifiableSet(_inverted ? _excludedQNames : _includedQNames);
  406       }
  407       
  408       private String prettyQName(QName name)
  409       {
  410           if (name.getNamespaceURI() == null)
  411               return name.getLocalPart();
  412           return name.getLocalPart() + "@" + name.getNamespaceURI();
  413       }
  414   
  415       /**
  416        * Returns a string representation useful for debugging, subject to change.
  417        */ 
  418       public String toString()
  419       {
  420           StringBuffer sb = new StringBuffer();
  421           sb.append("QNameSet");
  422           sb.append(_inverted ? "-(" : "+(");
  423           for (Iterator i = _includedURIs.iterator(); i.hasNext(); )
  424           {
  425               sb.append("+*@");
  426               sb.append(i.next());
  427               sb.append(", ");
  428           }
  429           for (Iterator i = _excludedQNames.iterator(); i.hasNext(); )
  430           {
  431               sb.append("-");
  432               sb.append(prettyQName((QName)i.next()));
  433               sb.append(", ");
  434           }
  435           for (Iterator i = _includedQNames.iterator(); i.hasNext(); )
  436           {
  437               sb.append("+");
  438               sb.append(prettyQName((QName)i.next()));
  439               sb.append(", ");
  440           }
  441           int index = sb.lastIndexOf(", ");
  442           if (index > 0)
  443               sb.setLength(index);
  444           sb.append(')');
  445           return sb.toString();
  446       }
  447   }

Home » xmlbeans-2.5.0-src » org.apache » xmlbeans » [javadoc | source]