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