001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: DecimalFormatting.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import org.apache.bcel.generic.ConstantPoolGen;
025    import org.apache.bcel.generic.GETSTATIC;
026    import org.apache.bcel.generic.INVOKESPECIAL;
027    import org.apache.bcel.generic.INVOKEVIRTUAL;
028    import org.apache.bcel.generic.InstructionList;
029    import org.apache.bcel.generic.NEW;
030    import org.apache.bcel.generic.PUSH;
031    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
032    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
033    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
034    import org.apache.xalan.xsltc.compiler.util.Type;
035    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
036    import org.apache.xml.utils.XML11Char;
037    
038    /**
039     * @author Jacek Ambroziak
040     * @author Santiago Pericas-Geertsen
041     * @author Morten Jorgensen
042     */
043    final class DecimalFormatting extends TopLevelElement {
044    
045        private static final String DFS_CLASS = "java.text.DecimalFormatSymbols";
046        private static final String DFS_SIG   = "Ljava/text/DecimalFormatSymbols;";
047    
048        private QName _name = null;
049    
050        /**
051         * No type check needed for the <xsl:decimal-formatting/> element
052         */
053        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
054            return Type.Void;
055        }
056    
057        /**
058         * Parse the name of the <xsl:decimal-formatting/> element
059         */
060        public void parseContents(Parser parser) {
061            // Get the name of these decimal formatting symbols
062            final String name = getAttribute("name");
063            if (name.length() > 0) {
064                if (!XML11Char.isXML11ValidQName(name)){
065                    ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
066                    parser.reportError(Constants.ERROR, err);           
067                }
068            }
069            _name = parser.getQNameIgnoreDefaultNs(name);
070            if (_name == null) {
071                _name = parser.getQNameIgnoreDefaultNs(EMPTYSTRING);
072            }         
073    
074            // Check if a set of symbols has already been registered under this name
075            SymbolTable stable = parser.getSymbolTable();
076            if (stable.getDecimalFormatting(_name) != null) {
077                reportWarning(this, parser, ErrorMsg.SYMBOLS_REDEF_ERR,
078                    _name.toString());
079            }
080            else {
081                stable.addDecimalFormatting(_name, this);
082            }
083        }
084    
085        /**
086         * This method is called when the constructor is compiled in
087         * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
088         */
089        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
090    
091            ConstantPoolGen cpg = classGen.getConstantPool();
092            InstructionList il = methodGen.getInstructionList();
093            
094            // DecimalFormatSymbols.<init>(Locale);
095            // xsl:decimal-format - except for the NaN and infinity attributes.
096            final int init = cpg.addMethodref(DFS_CLASS, "<init>",
097                                              "("+LOCALE_SIG+")V");
098    
099            // Push the format name on the stack for call to addDecimalFormat()
100            il.append(classGen.loadTranslet());
101            il.append(new PUSH(cpg, _name.toString()));
102    
103            // Manufacture a DecimalFormatSymbols on the stack
104            // for call to addDecimalFormat()
105            // Use the US Locale as the default, as most of its settings
106            // are equivalent to the default settings required of
107            il.append(new NEW(cpg.addClass(DFS_CLASS)));
108            il.append(DUP);
109            il.append(new GETSTATIC(cpg.addFieldref(LOCALE_CLASS, "US",
110                                                    LOCALE_SIG)));
111            il.append(new INVOKESPECIAL(init));
112    
113            String tmp = getAttribute("NaN");
114            if ((tmp == null) || (tmp.equals(EMPTYSTRING))) {
115                int nan = cpg.addMethodref(DFS_CLASS,
116                                           "setNaN", "(Ljava/lang/String;)V");
117                il.append(DUP);
118                il.append(new PUSH(cpg, "NaN"));
119                il.append(new INVOKEVIRTUAL(nan));
120            }
121    
122            tmp = getAttribute("infinity");
123            if ((tmp == null) || (tmp.equals(EMPTYSTRING))) {
124                int inf = cpg.addMethodref(DFS_CLASS,
125                                           "setInfinity",
126                                           "(Ljava/lang/String;)V");
127                il.append(DUP);
128                il.append(new PUSH(cpg, "Infinity"));
129                il.append(new INVOKEVIRTUAL(inf));
130            }
131                
132            final int nAttributes = _attributes.getLength();
133            for (int i = 0; i < nAttributes; i++) {
134                final String name = _attributes.getQName(i);
135                final String value = _attributes.getValue(i);
136    
137                boolean valid = true;
138                int method = 0;
139    
140                if (name.equals("decimal-separator")) {
141                    // DecimalFormatSymbols.setDecimalSeparator();
142                    method = cpg.addMethodref(DFS_CLASS,
143                                              "setDecimalSeparator", "(C)V");
144                }
145                else if (name.equals("grouping-separator")) {
146                    method =  cpg.addMethodref(DFS_CLASS,
147                                               "setGroupingSeparator", "(C)V");
148                }
149                else if (name.equals("minus-sign")) {
150                    method = cpg.addMethodref(DFS_CLASS,
151                                              "setMinusSign", "(C)V");
152                }
153                else if (name.equals("percent")) {
154                    method = cpg.addMethodref(DFS_CLASS,
155                                              "setPercent", "(C)V");
156                }
157                else if (name.equals("per-mille")) {
158                    method = cpg.addMethodref(DFS_CLASS,
159                                              "setPerMill", "(C)V");
160                }
161                else if (name.equals("zero-digit")) {
162                    method = cpg.addMethodref(DFS_CLASS,
163                                              "setZeroDigit", "(C)V");
164                }
165                else if (name.equals("digit")) {
166                    method = cpg.addMethodref(DFS_CLASS,
167                                              "setDigit", "(C)V");
168                }
169                else if (name.equals("pattern-separator")) {
170                    method = cpg.addMethodref(DFS_CLASS,
171                                              "setPatternSeparator", "(C)V");
172                }
173                else if (name.equals("NaN")) {
174                    method = cpg.addMethodref(DFS_CLASS,
175                                              "setNaN", "(Ljava/lang/String;)V");
176                    il.append(DUP);
177                    il.append(new PUSH(cpg, value));
178                    il.append(new INVOKEVIRTUAL(method));
179                    valid = false;
180                }
181                else if (name.equals("infinity")) {
182                    method = cpg.addMethodref(DFS_CLASS,
183                                              "setInfinity",
184                                              "(Ljava/lang/String;)V");
185                    il.append(DUP);
186                    il.append(new PUSH(cpg, value));
187                    il.append(new INVOKEVIRTUAL(method));
188                    valid = false;
189                }
190                else {
191                    valid = false;
192                }
193    
194                if (valid) {
195                    il.append(DUP);
196                    il.append(new PUSH(cpg, value.charAt(0)));
197                    il.append(new INVOKEVIRTUAL(method));
198                }
199    
200            }
201    
202            final int put = cpg.addMethodref(TRANSLET_CLASS,
203                                             "addDecimalFormat",
204                                             "("+STRING_SIG+DFS_SIG+")V");
205            il.append(new INVOKEVIRTUAL(put));
206        }
207    
208        /**
209         * Creates the default, nameless, DecimalFormat object in
210         * AbstractTranslet's format_symbols hashtable.
211         * This should be called for every stylesheet, and the entry
212         * may be overridden by later nameless xsl:decimal-format instructions.
213         */
214        public static void translateDefaultDFS(ClassGenerator classGen,
215                                               MethodGenerator methodGen) {
216    
217            ConstantPoolGen cpg = classGen.getConstantPool();
218            InstructionList il = methodGen.getInstructionList();
219            final int init = cpg.addMethodref(DFS_CLASS, "<init>",
220                                              "("+LOCALE_SIG+")V");
221    
222            // Push the format name, which is empty, on the stack
223            // for call to addDecimalFormat()
224            il.append(classGen.loadTranslet());
225            il.append(new PUSH(cpg, EMPTYSTRING));
226    
227            // Manufacture a DecimalFormatSymbols on the stack for
228            // call to addDecimalFormat().  Use the US Locale as the
229            // default, as most of its settings are equivalent to
230            // the default settings required of xsl:decimal-format -
231            // except for the NaN and infinity attributes.
232            il.append(new NEW(cpg.addClass(DFS_CLASS)));
233            il.append(DUP);
234            il.append(new GETSTATIC(cpg.addFieldref(LOCALE_CLASS, "US",
235                                                    LOCALE_SIG)));
236            il.append(new INVOKESPECIAL(init));
237    
238            int nan = cpg.addMethodref(DFS_CLASS,
239                                       "setNaN", "(Ljava/lang/String;)V");
240            il.append(DUP);
241            il.append(new PUSH(cpg, "NaN"));
242            il.append(new INVOKEVIRTUAL(nan));
243    
244            int inf = cpg.addMethodref(DFS_CLASS,
245                                       "setInfinity",
246                                       "(Ljava/lang/String;)V");
247            il.append(DUP);
248            il.append(new PUSH(cpg, "Infinity"));
249            il.append(new INVOKEVIRTUAL(inf));
250    
251            final int put = cpg.addMethodref(TRANSLET_CLASS,
252                                             "addDecimalFormat",
253                                             "("+STRING_SIG+DFS_SIG+")V");
254            il.append(new INVOKEVIRTUAL(put));
255        }
256    }