001    // Copyright 2006, 2007, 2008, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.ioc.util;
016    
017    import java.util.Formatter;
018    
019    import org.apache.tapestry5.ioc.services.MethodSignature;
020    
021    /**
022     * Utility class for assembling the <em>body</em> used with Javassist when defining a method or constructor. Basically,
023     * assists with formatting and with indentation. This makes the code that assembles a method body much simpler ... and
024     * it makes the result neater, which will be easier to debug (debugging dynamically generated code is hard enough that
025     * it should be easy to read the input code before worrying about why it doesn't compile or execute properly).
026     * <p/>
027     * This class is not threadsafe.
028     * <p/>
029     * Most of the methods return the BodyBuilder, to form a fluent interface.
030     * 
031     * @deprecated In 5.3/0, to be removed in a later release
032     */
033    public final class BodyBuilder
034    {
035        /**
036         * Feels right for the size of a typical body.
037         */
038        private static final int DEFAULT_LENGTH = 200;
039    
040        private static final String INDENT = "  ";
041    
042        private final StringBuilder buffer = new StringBuilder(DEFAULT_LENGTH);
043    
044        private final Formatter formatter = new Formatter(buffer);
045    
046        // Per level of nesting depth (two spaces).
047    
048        private int nestingDepth = 0;
049    
050        private boolean atNewLine = true;
051    
052        /**
053         * Clears the builder, returning it to its initial, empty state.
054         */
055        public BodyBuilder clear()
056        {
057            nestingDepth = 0;
058            atNewLine = true;
059            buffer.setLength(0);
060    
061            return this;
062        }
063    
064        /**
065         * Adds text to the current line, without ending the line.
066         * 
067         * @param format
068         *            string format, as per {@link java.util.Formatter}
069         * @param args
070         *            arguments referenced by format specifiers
071         */
072        public BodyBuilder add(String format, Object... args)
073        {
074            add(format, args, false);
075    
076            return this;
077        }
078    
079        /**
080         * Adds text to the current line and ends the line.
081         * 
082         * @param format
083         *            string format, as per {@link java.util.Formatter}
084         * @param args
085         *            arguments referenced by format specifiers
086         */
087        public BodyBuilder addln(String format, Object... args)
088        {
089            add(format, args, true);
090    
091            return this;
092        }
093    
094        private BodyBuilder add(String format, Object[] args, boolean newLine)
095        {
096            indent();
097    
098            // Format output, send to buffer
099    
100            formatter.format(format, args);
101    
102            if (newLine)
103                newline();
104    
105            return this;
106        }
107    
108        private void newline()
109        {
110            buffer.append("\n");
111            atNewLine = true;
112        }
113    
114        /**
115         * Begins a new block. Emits a "{", properly indented, on a new line.
116         */
117        public BodyBuilder begin()
118        {
119            if (!atNewLine)
120                newline();
121    
122            indent();
123            buffer.append("{");
124            newline();
125    
126            nestingDepth++;
127    
128            return this;
129        }
130    
131        /**
132         * Ends the current block. Emits a "}", propertly indented, on a new line.
133         */
134        public BodyBuilder end()
135        {
136            if (!atNewLine)
137                newline();
138    
139            // TODO: Could check here if nesting depth goes below zero.
140    
141            nestingDepth--;
142    
143            indent();
144            buffer.append("}");
145    
146            newline();
147    
148            return this;
149        }
150    
151        private void indent()
152        {
153            if (atNewLine)
154            {
155                for (int i = 0; i < nestingDepth; i++)
156                    buffer.append(INDENT);
157    
158                atNewLine = false;
159            }
160        }
161    
162        /**
163         * Returns the current contents of the buffer. This value is often passed to methods such as
164         * {@link org.apache.tapestry5.ioc.services.ClassFab#addConstructor(Class[], Class[], String)} or
165         * {@link org.apache.tapestry5.ioc.services.ClassFab#addMethod(int, MethodSignature, String)}.
166         * <p/>
167         * A BodyBuilder can be used again after invoking toString(), typically by invoking {@link #clear()}.
168         */
169        @Override
170        public String toString()
171        {
172            return buffer.toString();
173        }
174    }