1 /*
   2  * Copyright (c) 2015, 2017, 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  * $Id: SymbolTable.java,v 1.5 2005/09/28 13:48:16 pvedula Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  25 
  26 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
  27 import java.util.ArrayList;
  28 import java.util.HashMap;
  29 import java.util.List;
  30 import java.util.Map;
  31 import java.util.Stack;
  32 import java.util.StringTokenizer;
  33 
  34 /**
  35  * @author Jacek Ambroziak
  36  * @author Santiago Pericas-Geertsen
  37  * @author Morten Jorgensen
  38  * @LastModified: Oct 2017
  39  */
  40 final class SymbolTable {
  41 
  42     // These maps are used for all stylesheets
  43     private final Map<String, Stylesheet> _stylesheets = new HashMap<>();
  44     private final Map<String, List<MethodType>> _primops = new HashMap<>();
  45 
  46     // These maps are used for some stylesheets
  47     private Map<String, VariableBase> _variables = null;
  48     private Map<String, Template> _templates = null;
  49     private Map<String, AttributeSet> _attributeSets = null;
  50     private Map<String, String> _aliases = null;
  51     private Map<String, Integer> _excludedURI = null;
  52     private Stack<Map<String, Integer>>     _excludedURIStack = null;
  53     private Map<String, DecimalFormatting> _decimalFormats = null;
  54     private Map<String, Key> _keys = null;
  55 
  56     public DecimalFormatting getDecimalFormatting(QName name) {
  57         if (_decimalFormats == null) return null;
  58         return(_decimalFormats.get(name.getStringRep()));
  59     }
  60 
  61     public void addDecimalFormatting(QName name, DecimalFormatting symbols) {
  62         if (_decimalFormats == null) _decimalFormats = new HashMap<>();
  63         _decimalFormats.put(name.getStringRep(), symbols);
  64     }
  65 
  66     public Key getKey(QName name) {
  67         if (_keys == null) return null;
  68         return _keys.get(name.getStringRep());
  69     }
  70 
  71     public void addKey(QName name, Key key) {
  72         if (_keys == null) _keys = new HashMap<>();
  73         _keys.put(name.getStringRep(), key);
  74     }
  75 
  76     public Stylesheet addStylesheet(QName name, Stylesheet node) {
  77         return _stylesheets.put(name.getStringRep(), node);
  78     }
  79 
  80     public Stylesheet lookupStylesheet(QName name) {
  81         return _stylesheets.get(name.getStringRep());
  82     }
  83 
  84     public Template addTemplate(Template template) {
  85         final QName name = template.getName();
  86         if (_templates == null) _templates = new HashMap<>();
  87         return _templates.put(name.getStringRep(), template);
  88     }
  89 
  90     public Template lookupTemplate(QName name) {
  91         if (_templates == null) return null;
  92         return _templates.get(name.getStringRep());
  93     }
  94 
  95     public Variable addVariable(Variable variable) {
  96         if (_variables == null) _variables = new HashMap<>();
  97         final String name = variable.getName().getStringRep();
  98         return (Variable)_variables.put(name, variable);
  99     }
 100 
 101     public Param addParam(Param parameter) {
 102         if (_variables == null) _variables = new HashMap<>();
 103         final String name = parameter.getName().getStringRep();
 104         return (Param)_variables.put(name, parameter);
 105     }
 106 
 107     public Variable lookupVariable(QName qname) {
 108         if (_variables == null) return null;
 109         final String name = qname.getStringRep();
 110         final VariableBase obj = _variables.get(name);
 111         return obj instanceof Variable ? (Variable)obj : null;
 112     }
 113 
 114     public Param lookupParam(QName qname) {
 115         if (_variables == null) return null;
 116         final String name = qname.getStringRep();
 117         final VariableBase obj = _variables.get(name);
 118         return obj instanceof Param ? (Param)obj : null;
 119     }
 120 
 121     public SyntaxTreeNode lookupName(QName qname) {
 122         if (_variables == null) return null;
 123         final String name = qname.getStringRep();
 124         return (SyntaxTreeNode)_variables.get(name);
 125     }
 126 
 127     public AttributeSet addAttributeSet(AttributeSet atts) {
 128         if (_attributeSets == null) _attributeSets = new HashMap<>();
 129         return _attributeSets.put(atts.getName().getStringRep(), atts);
 130     }
 131 
 132     public AttributeSet lookupAttributeSet(QName name) {
 133         if (_attributeSets == null) return null;
 134         return _attributeSets.get(name.getStringRep());
 135     }
 136 
 137     /**
 138      * Add a primitive operator or function to the symbol table. To avoid
 139      * name clashes with user-defined names, the prefix <tt>PrimopPrefix</tt>
 140      * is prepended.
 141      */
 142     public void addPrimop(String name, MethodType mtype) {
 143         List<MethodType> methods = _primops.get(name);
 144         if (methods == null) {
 145             _primops.put(name, methods = new ArrayList<>());
 146         }
 147         methods.add(mtype);
 148     }
 149 
 150     /**
 151      * Lookup a primitive operator or function in the symbol table by
 152      * prepending the prefix <tt>PrimopPrefix</tt>.
 153      */
 154     public List<MethodType> lookupPrimop(String name) {
 155         return _primops.get(name);
 156     }
 157 
 158     /**
 159      * This is used for xsl:attribute elements that have a "namespace"
 160      * attribute that is currently not defined using xmlns:
 161      */
 162     private int _nsCounter = 0;
 163 
 164     public String generateNamespacePrefix() {
 165         return("ns"+(_nsCounter++));
 166     }
 167 
 168     /**
 169      * Use a namespace prefix to lookup a namespace URI
 170      */
 171     private SyntaxTreeNode _current = null;
 172 
 173     public void setCurrentNode(SyntaxTreeNode node) {
 174         _current = node;
 175     }
 176 
 177     public String lookupNamespace(String prefix) {
 178         if (_current == null) return(Constants.EMPTYSTRING);
 179         return(_current.lookupNamespace(prefix));
 180     }
 181 
 182     /**
 183      * Adds an alias for a namespace prefix
 184      */
 185     public void addPrefixAlias(String prefix, String alias) {
 186         if (_aliases == null) _aliases = new HashMap<>();
 187         _aliases.put(prefix,alias);
 188     }
 189 
 190     /**
 191      * Retrieves any alias for a given namespace prefix
 192      */
 193     public String lookupPrefixAlias(String prefix) {
 194         if (_aliases == null) return null;
 195         return _aliases.get(prefix);
 196     }
 197 
 198     /**
 199      * Register a namespace URI so that it will not be declared in the output
 200      * unless it is actually referenced in the output.
 201      */
 202     public void excludeURI(String uri) {
 203         // The null-namespace cannot be excluded
 204         if (uri == null) return;
 205 
 206         // Create a new map of exlcuded URIs if none exists
 207         if (_excludedURI == null) _excludedURI = new HashMap<>();
 208 
 209         // Register the namespace URI
 210         Integer refcnt = _excludedURI.get(uri);
 211         if (refcnt == null)
 212             refcnt = 1;
 213         else
 214             refcnt = refcnt + 1;
 215         _excludedURI.put(uri,refcnt);
 216     }
 217 
 218     /**
 219      * Exclude a series of namespaces given by a list of whitespace
 220      * separated namespace prefixes.
 221      */
 222     public void excludeNamespaces(String prefixes) {
 223         if (prefixes != null) {
 224             StringTokenizer tokens = new StringTokenizer(prefixes);
 225             while (tokens.hasMoreTokens()) {
 226                 final String prefix = tokens.nextToken();
 227                 final String uri;
 228                 if (prefix.equals("#default"))
 229                     uri = lookupNamespace(Constants.EMPTYSTRING);
 230                 else
 231                     uri = lookupNamespace(prefix);
 232                 if (uri != null) excludeURI(uri);
 233             }
 234         }
 235     }
 236 
 237     /**
 238      * Check if a namespace should not be declared in the output (unless used)
 239      */
 240     public boolean isExcludedNamespace(String uri) {
 241         if (uri != null && _excludedURI != null) {
 242             final Integer refcnt = _excludedURI.get(uri);
 243             return (refcnt != null && refcnt > 0);
 244         }
 245         return false;
 246     }
 247 
 248     /**
 249      * Turn of namespace declaration exclusion
 250      */
 251     public void unExcludeNamespaces(String prefixes) {
 252         if (_excludedURI == null) return;
 253         if (prefixes != null) {
 254             StringTokenizer tokens = new StringTokenizer(prefixes);
 255             while (tokens.hasMoreTokens()) {
 256                 final String prefix = tokens.nextToken();
 257                 final String uri;
 258                 if (prefix.equals("#default"))
 259                     uri = lookupNamespace(Constants.EMPTYSTRING);
 260                 else
 261                     uri = lookupNamespace(prefix);
 262                 Integer refcnt = _excludedURI.get(uri);
 263                 if (refcnt != null)
 264                     _excludedURI.put(uri, refcnt - 1);
 265             }
 266         }
 267     }
 268     /**
 269      * Exclusion of namespaces by a stylesheet does not extend to any stylesheet
 270      * imported or included by the stylesheet.  Upon entering the context of a
 271      * new stylesheet, a call to this method is needed to clear the current set
 272      * of excluded namespaces temporarily.  Every call to this method requires
 273      * a corresponding call to {@link #popExcludedNamespacesContext()}.
 274      */
 275     public void pushExcludedNamespacesContext() {
 276         if (_excludedURIStack == null) {
 277             _excludedURIStack = new Stack<>();
 278         }
 279         _excludedURIStack.push(_excludedURI);
 280         _excludedURI = null;
 281     }
 282 
 283     /**
 284      * Exclusion of namespaces by a stylesheet does not extend to any stylesheet
 285      * imported or included by the stylesheet.  Upon exiting the context of a
 286      * stylesheet, a call to this method is needed to restore the set of
 287      * excluded namespaces that was in effect prior to entering the context of
 288      * the current stylesheet.
 289      */
 290     public void popExcludedNamespacesContext() {
 291         _excludedURI = _excludedURIStack.pop();
 292         if (_excludedURIStack.isEmpty()) {
 293             _excludedURIStack = null;
 294         }
 295     }
 296 
 297 }