1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.xs;
  22 
  23 import java.util.HashMap;
  24 import java.util.Map;
  25 import java.util.Vector;
  26 
  27 /**
  28  * A class used to hold the internal schema grammar set for the current instance
  29  *
  30  * @xerces.internal
  31  *
  32  * @author Sandy Gao, IBM
  33  * @version $Id: XSGrammarBucket.java,v 1.7 2010-11-01 04:39:55 joehw Exp $
  34  */
  35 public class XSGrammarBucket {
  36 
  37     // Data
  38 
  39     /**
  40      * Map that maps between Namespace and a Grammar
  41      */
  42     Map<String, SchemaGrammar> fGrammarRegistry = new HashMap();
  43     SchemaGrammar fNoNSGrammar = null;
  44 
  45     /**
  46      * Get the schema grammar for the specified namespace
  47      *
  48      * @param namespace
  49      * @return SchemaGrammar associated with the namespace
  50      */
  51     public SchemaGrammar getGrammar(String namespace) {
  52         if (namespace == null)
  53             return fNoNSGrammar;
  54         return (SchemaGrammar)fGrammarRegistry.get(namespace);
  55     }
  56 
  57     /**
  58      * Put a schema grammar into the registry
  59      * This method is for internal use only: it assumes that a grammar with
  60      * the same target namespace is not already in the bucket.
  61      *
  62      * @param grammar   the grammar to put in the registry
  63      */
  64     public void putGrammar(SchemaGrammar grammar) {
  65         if (grammar.getTargetNamespace() == null)
  66             fNoNSGrammar = grammar;
  67         else
  68             fGrammarRegistry.put(grammar.getTargetNamespace(), grammar);
  69     }
  70 
  71     /**
  72      * put a schema grammar and any grammars imported by it (directly or
  73      * inderectly) into the registry. when a grammar with the same target
  74      * namespace is already in the bucket, and different from the one being
  75      * added, it's an error, and no grammar will be added into the bucket.
  76      *
  77      * @param grammar   the grammar to put in the registry
  78      * @param deep      whether to add imported grammars
  79      * @return          whether the process succeeded
  80      */
  81     public boolean putGrammar(SchemaGrammar grammar, boolean deep) {
  82         // whether there is one with the same tns
  83         SchemaGrammar sg = getGrammar(grammar.fTargetNamespace);
  84         if (sg != null) {
  85             // if the one we have is different from the one passed, it's an error
  86             return sg == grammar;
  87         }
  88         // not deep import, then just add this one grammar
  89         if (!deep) {
  90             putGrammar(grammar);
  91             return true;
  92         }
  93 
  94         // get all imported grammars, and make a copy of the Vector, so that
  95         // we can recursively process the grammars, and add distinct ones
  96         // to the same vector
  97         Vector currGrammars = (Vector)grammar.getImportedGrammars();
  98         if (currGrammars == null) {
  99             putGrammar(grammar);
 100             return true;
 101         }
 102 
 103         Vector grammars = ((Vector)currGrammars.clone());
 104         SchemaGrammar sg1, sg2;
 105         Vector gs;
 106         // for all (recursively) imported grammars
 107         for (int i = 0; i < grammars.size(); i++) {
 108             // get the grammar
 109             sg1 = (SchemaGrammar)grammars.elementAt(i);
 110             // check whether the bucket has one with the same tns
 111             sg2 = getGrammar(sg1.fTargetNamespace);
 112             if (sg2 == null) {
 113                 // we need to add grammars imported by sg1 too
 114                 gs = sg1.getImportedGrammars();
 115                 // for all grammars imported by sg2, but not in the vector
 116                 // we add them to the vector
 117                 if(gs == null) continue;
 118                 for (int j = gs.size() - 1; j >= 0; j--) {
 119                     sg2 = (SchemaGrammar)gs.elementAt(j);
 120                     if (!grammars.contains(sg2))
 121                         grammars.addElement(sg2);
 122                 }
 123             }
 124             // we found one with the same target namespace
 125             // if the two grammars are not the same object, then it's an error
 126             else if (sg2 != sg1) {
 127                 return false;
 128             }
 129         }
 130 
 131         // now we have all imported grammars stored in the vector. add them
 132         putGrammar(grammar);
 133         for (int i = grammars.size() - 1; i >= 0; i--)
 134             putGrammar((SchemaGrammar)grammars.elementAt(i));
 135 
 136         return true;
 137     }
 138 
 139     /**
 140      * put a schema grammar and any grammars imported by it (directly or
 141      * inderectly) into the registry. when a grammar with the same target
 142      * namespace is already in the bucket, and different from the one being
 143      * added, no grammar will be added into the bucket.
 144      *
 145      * @param grammar        the grammar to put in the registry
 146      * @param deep           whether to add imported grammars
 147      * @param ignoreConflict whether to ignore grammars that already exist in the grammar
 148      *                       bucket or not - including 'grammar' parameter.
 149      * @return               whether the process succeeded
 150      */
 151     public boolean putGrammar(SchemaGrammar grammar, boolean deep, boolean ignoreConflict) {
 152         if (!ignoreConflict) {
 153             return putGrammar(grammar, deep);
 154         }
 155 
 156         // if grammar already exist in the bucket, we ignore the request
 157         SchemaGrammar sg = getGrammar(grammar.fTargetNamespace);
 158         if (sg == null) {
 159             putGrammar(grammar);
 160         }
 161 
 162         // not adding the imported grammars
 163         if (!deep) {
 164             return true;
 165         }
 166 
 167         // get all imported grammars, and make a copy of the Vector, so that
 168         // we can recursively process the grammars, and add distinct ones
 169         // to the same vector
 170         Vector currGrammars = (Vector)grammar.getImportedGrammars();
 171         if (currGrammars == null) {
 172             return true;
 173         }
 174 
 175         Vector grammars = ((Vector)currGrammars.clone());
 176         SchemaGrammar sg1, sg2;
 177         Vector gs;
 178         // for all (recursively) imported grammars
 179         for (int i = 0; i < grammars.size(); i++) {
 180             // get the grammar
 181             sg1 = (SchemaGrammar)grammars.elementAt(i);
 182             // check whether the bucket has one with the same tns
 183             sg2 = getGrammar(sg1.fTargetNamespace);
 184             if (sg2 == null) {
 185                 // we need to add grammars imported by sg1 too
 186                 gs = sg1.getImportedGrammars();
 187                 // for all grammars imported by sg2, but not in the vector
 188                 // we add them to the vector
 189                 if(gs == null) continue;
 190                 for (int j = gs.size() - 1; j >= 0; j--) {
 191                     sg2 = (SchemaGrammar)gs.elementAt(j);
 192                     if (!grammars.contains(sg2))
 193                         grammars.addElement(sg2);
 194                 }
 195             }
 196             // we found one with the same target namespace, ignore it
 197             else  {
 198                 grammars.remove(sg1);
 199             }
 200         }
 201 
 202         // now we have all imported grammars stored in the vector. add them
 203         for (int i = grammars.size() - 1; i >= 0; i--) {
 204             putGrammar((SchemaGrammar)grammars.elementAt(i));
 205         }
 206 
 207         return true;
 208     }
 209 
 210     /**
 211      * get all grammars in the registry
 212      *
 213      * @return an array of SchemaGrammars.
 214      */
 215     public SchemaGrammar[] getGrammars() {
 216         // get the number of grammars
 217         int count = fGrammarRegistry.size() + (fNoNSGrammar==null ? 0 : 1);
 218         SchemaGrammar[] grammars = new SchemaGrammar[count];
 219         // get grammars with target namespace
 220         int i = 0;
 221         for(Map.Entry<String, SchemaGrammar> entry : fGrammarRegistry.entrySet()){
 222             grammars[i++] = entry.getValue();
 223         }
 224 
 225         // add the grammar without target namespace, if any
 226         if (fNoNSGrammar != null)
 227             grammars[count-1] = fNoNSGrammar;
 228         return grammars;
 229     }
 230 
 231     /**
 232      * Clear the registry.
 233      * REVISIT: update to use another XSGrammarBucket
 234      */
 235     public void reset() {
 236         fNoNSGrammar = null;
 237         fGrammarRegistry.clear();
 238     }
 239 
 240 } // class XSGrammarBucket