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