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
015package org.apache.tapestry5.ioc.util;
016
017import java.util.Formatter;
018
019import 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 */
033public 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}