1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
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
22package com.sun.org.apache.xalan.internal.xsltc.compiler;
23
24import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
25import com.sun.org.apache.bcel.internal.generic.GETSTATIC;
26import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
27import com.sun.org.apache.bcel.internal.generic.InstructionList;
28import com.sun.org.apache.bcel.internal.generic.PUSH;
29import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
30import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
31import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
32
33/**
34 * @author Jacek Ambroziak
35 * @author Santiago Pericas-Geertsen
36 * @author Morten Jorgensen
37 */
38final class Text extends Instruction {
39
40    private String _text;
41    private boolean _escaping = true;
42    private boolean _ignore = false;
43    private boolean _textElement = false;
44
45    /**
46     * Create a blank Text syntax tree node.
47     */
48    public Text() {
49        _textElement = true;
50    }
51
52    /**
53     * Create text syntax tree node.
54     * @param text is the text to put in the node.
55     */
56    public Text(String text) {
57        _text = text;
58    }
59
60    /**
61     * Returns the text wrapped inside this node
62     * @return The text wrapped inside this node
63     */
64    protected String getText() {
65        return _text;
66    }
67
68    /**
69     * Set the text for this node. Appends the given text to any already
70     * existing text (using string concatenation, so use only when needed).
71     * @param text is the text to wrap inside this node.
72     */
73    protected void setText(String text) {
74        if (_text == null)
75            _text = text;
76        else
77            _text = _text + text;
78    }
79
80    public void display(int indent) {
81        indent(indent);
82        Util.println("Text");
83        indent(indent + IndentIncrement);
84        Util.println(_text);
85    }
86
87    public void parseContents(Parser parser) {
88        final String str = getAttribute("disable-output-escaping");
89        if ((str != null) && (str.equals("yes"))) _escaping = false;
90
91        parseChildren(parser);
92
93        if (_text == null) {
94            if (_textElement) {
95                _text = EMPTYSTRING;
96            }
97            else {
98                _ignore = true;
99            }
100        }
101        else if (_textElement) {
102            if (_text.length() == 0) _ignore = true;
103        }
104        else if (getParent() instanceof LiteralElement) {
105            LiteralElement element = (LiteralElement)getParent();
106            String space = element.getAttribute("xml:space");
107            if ((space == null) || (!space.equals("preserve")))
108        {
109            int i;
110            final int textLength = _text.length();
111            for (i = 0; i < textLength; i++) {
112                char c = _text.charAt(i);
113                if (!isWhitespace(c))
114                    break;
115            }
116            if (i == textLength)
117                _ignore = true;
118        }
119        }
120        else {
121        int i;
122        final int textLength = _text.length();
123        for (i = 0; i < textLength; i++)
124        {
125            char c = _text.charAt(i);
126            if (!isWhitespace(c))
127                break;
128        }
129        if (i == textLength)
130            _ignore = true;
131        }
132    }
133
134    public void ignore() {
135        _ignore = true;
136    }
137
138    public boolean isIgnore() {
139        return _ignore;
140    }
141
142    public boolean isTextElement() {
143        return _textElement;
144    }
145
146    protected boolean contextDependent() {
147        return false;
148    }
149
150    private static boolean isWhitespace(char c)
151    {
152        return (c == 0x20 || c == 0x09 || c == 0x0A || c == 0x0D);
153    }
154
155    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
156        final ConstantPoolGen cpg = classGen.getConstantPool();
157        final InstructionList il = methodGen.getInstructionList();
158
159        if (!_ignore) {
160            // Turn off character escaping if so is wanted.
161            final int esc = cpg.addInterfaceMethodref(OUTPUT_HANDLER,
162                                                      "setEscaping", "(Z)Z");
163            if (!_escaping) {
164                il.append(methodGen.loadHandler());
165                il.append(new PUSH(cpg, false));
166                il.append(new INVOKEINTERFACE(esc, 2));
167            }
168
169            il.append(methodGen.loadHandler());
170
171            // Call characters(String) or characters(char[],int,int), as
172            // appropriate.
173            if (!canLoadAsArrayOffsetLength()) {
174                final int characters = cpg.addInterfaceMethodref(OUTPUT_HANDLER,
175                                                           "characters",
176                                                           "("+STRING_SIG+")V");
177                il.append(new PUSH(cpg, _text));
178                il.append(new INVOKEINTERFACE(characters, 2));
179            } else {
180                final int characters = cpg.addInterfaceMethodref(OUTPUT_HANDLER,
181                                                                 "characters",
182                                                                 "([CII)V");
183                loadAsArrayOffsetLength(classGen, methodGen);
184                il.append(new INVOKEINTERFACE(characters, 4));
185            }
186
187            // Restore character escaping setting to whatever it was.
188            // Note: setEscaping(bool) returns the original (old) value
189            if (!_escaping) {
190                il.append(methodGen.loadHandler());
191                il.append(SWAP);
192                il.append(new INVOKEINTERFACE(esc, 2));
193                il.append(POP);
194            }
195        }
196        translateContents(classGen, methodGen);
197    }
198
199    /**
200     * Check whether this Text node can be stored in a char[] in the translet.
201     * Calling this is precondition to calling loadAsArrayOffsetLength.
202     * @see #loadAsArrayOffsetLength(ClassGenerator,MethodGenerator)
203     * @return true if this Text node can be
204     */
205    public boolean canLoadAsArrayOffsetLength() {
206        // Magic number!  21845*3 == 65535.  BCEL uses a DataOutputStream to
207        // serialize class files.  The Java run-time places a limit on the size
208        // of String data written using a DataOutputStream - it cannot require
209        // more than 64KB when represented as UTF-8.  The number of bytes
210        // required to represent a Java string as UTF-8 cannot be greater
211        // than three times the number of char's in the string, hence the
212        // check for 21845.
213
214        return (_text.length() <= 21845);
215    }
216
217    /**
218     * Generates code that loads the array that will contain the character
219     * data represented by this Text node, followed by the offset of the
220     * data from the start of the array, and then the length of the data.
221     *
222     * The pre-condition to calling this method is that
223     * canLoadAsArrayOffsetLength() returns true.
224     * @see #canLoadArrayOffsetLength()
225     */
226    public void loadAsArrayOffsetLength(ClassGenerator classGen,
227                                        MethodGenerator methodGen) {
228        final ConstantPoolGen cpg = classGen.getConstantPool();
229        final InstructionList il = methodGen.getInstructionList();
230        final XSLTC xsltc = classGen.getParser().getXSLTC();
231
232        // The XSLTC object keeps track of character data
233        // that is to be stored in char arrays.
234        final int offset = xsltc.addCharacterData(_text);
235        final int length = _text.length();
236        String charDataFieldName =
237            STATIC_CHAR_DATA_FIELD + (xsltc.getCharacterDataCount()-1);
238
239        il.append(new GETSTATIC(cpg.addFieldref(xsltc.getClassName(),
240                                       charDataFieldName,
241                                       STATIC_CHAR_DATA_FIELD_SIG)));
242        il.append(new PUSH(cpg, offset));
243        il.append(new PUSH(cpg, _text.length()));
244    }
245}
246