001 package com.sptci;
002
003 import java.lang.reflect.Field;
004
005 import java.util.Formatter;
006 import java.util.Map;
007 import java.util.HashSet;
008 import java.util.TreeMap;
009
010 /**
011 * An abstract code generator. Generates the code for common methods
012 * that are implemented in source code.
013 *
014 * <p>Copyright 2006 Sans Pareil Technologies, Inc.</p>
015 * @author Rakesh Vidyadharan 2006-02-07
016 * @version $Id: CodeGenerator.java,v 1.2 2006/02/09 16:09:13 rakesh Exp $
017 */
018 public abstract class CodeGenerator
019 {
020 /**
021 * The name of the class that is being generated.
022 */
023 protected String name;
024
025 /**
026 * A <code>Map</code> of <code>fields</code> for the generated class.
027 * The name of the field is stored as the <code>key</code> and the
028 * type of the field as the <code>value</code>.
029 */
030 protected Map<String, String> fields;
031
032 /**
033 * A <code>HashSet</code> containing the primitive types defined
034 * for Java.
035 */
036 protected HashSet<String> primitives;
037
038 /**
039 * Default constructor. Initialises code generator containers.
040 */
041 public CodeGenerator()
042 {
043 fields = new TreeMap<String, String>();
044 primitives = new HashSet<String>();
045 primitives.add( "boolean" );
046 primitives.add( "byte" );
047 primitives.add( "char" );
048 primitives.add( "double" );
049 primitives.add( "float" );
050 primitives.add( "int" );
051 primitives.add( "long" );
052 primitives.add( "short" );
053 }
054
055 /**
056 * The abstract method that will generate the full source code.
057 *
058 * @throws Exception If errors are encountered while generating
059 * the source file.
060 */
061 public abstract void generate() throws Exception;
062
063 /**
064 * Generate the default fields for the class. Sub-classes must
065 * over-ride this method, while usually using the results of this
066 * method to enhance their output.
067 *
068 * @return String The default fields to be added to each source file.
069 */
070 protected String generateFields()
071 {
072 StringBuilder builder = new StringBuilder( 256 );
073 Formatter formatter = new Formatter( builder );
074
075 formatter.format( " /**%n" );
076 formatter.format( " * The default value to use as the base for hashCode.%n" );
077 formatter.format( " */%n" );
078 formatter.format( " protected static final int HASH = 7;%n%n" );
079
080 formatter.format( " /**%n" );
081 formatter.format( " * The field used to store pre-computed hashCode.%n" );
082 formatter.format( " */%n" );
083 formatter.format( " protected int hash = HASH;%n%n" );
084
085 return builder.toString();
086 }
087
088 /**
089 * Generate the <code>toString</code> method for the JavaBean.
090 * Generate an XML representation of the class and its fields.
091 *
092 * @return String The default <code>toString</code> method
093 * implementation.
094 */
095 protected String generateToStringMethod()
096 {
097 StringBuilder builder = new StringBuilder( 2048 );
098 Formatter formatter = new Formatter( builder );
099
100 formatter.format( " /**%n" );
101 formatter.format( " * Return a String representation of the fields of this class.%n" );
102 formatter.format( " * The String representation is returned as an XML document.%n" );
103 formatter.format( " *%n" );
104 formatter.format( " * @return String An XML representation of the class fields.%n" );
105 formatter.format( " */%n" );
106 formatter.format( " @Override%n" );
107 formatter.format( " public String toString()%n" );
108 formatter.format( " {%n" );
109
110 formatter.format( " StringBuilder builder = new StringBuilder( 4096 );%n" );
111 formatter.format( " Formatter formatter = new Formatter( builder );%n" );
112
113 formatter.format( " formatter.format( \"<%s package='%%s'>%%n\", getClass().getPackage().getName() );%n%n", name );
114
115 for ( Map.Entry<String, String> entry : fields.entrySet() )
116 {
117 formatter.format( " formatter.format( \"<%s type='%s'>%%n\" );%n",
118 entry.getKey(),
119 entry.getValue().replaceAll( "<", "<" ).replaceAll( ">", ">" ) );
120
121 if ( entry.getValue().startsWith( "Map<" ) )
122 {
123 formatter.format(
124 " for ( Map.Entry<%s> entry : %s.entrySet() )%n",
125 entry.getValue().substring(
126 entry.getValue().indexOf( "<" ) + 1,
127 entry.getValue().indexOf( ">" ) ), entry.getKey() );
128 formatter.format( " {%n" );
129 formatter.format( " formatter.format( \"<key>%%n%%s%%n</key>%%n\", entry.getKey() );%n" );
130 formatter.format( " formatter.format( \"<value>%%n%%s%%n</value>%%n\", entry.getValue() );%n" );
131 formatter.format( " }%n" );
132 }
133 else if ( entry.getValue().startsWith( "List<" ) )
134 {
135 formatter.format(
136 " for ( %s entry : %s )%n",
137 entry.getValue().substring(
138 entry.getValue().indexOf( "<" ) + 1,
139 entry.getValue().indexOf( ">" ) ), entry.getKey() );
140 formatter.format( " {%n" );
141 formatter.format( " formatter.format( \"<entry>%%n%%s%%n</entry>%%n\", entry );%n" );
142 formatter.format( " }%n" );
143 }
144 else
145 {
146 formatter.format( " formatter.format( \"%%s%%n\", %s );%n",
147 entry.getKey() );
148 }
149
150 formatter.format( " formatter.format( \"</%s>%%n\" );%n%n",
151 entry.getKey() );
152 }
153
154 formatter.format( " formatter.format( \"</%s>%%n\" );%n", name );
155
156 formatter.format( " return builder.toString();%n" );
157 formatter.format( " }%n" );
158
159 return builder.toString();
160 }
161
162 /**
163 * Generate the <code>equals</code> method for the JavaBean.
164 *
165 * @return String The default <code>equals</code> method
166 * implementation.
167 */
168 protected String generateEqualsMethod()
169 {
170 StringBuilder builder = new StringBuilder( 1024 );
171 Formatter formatter = new Formatter( builder );
172
173 formatter.format( " /**%n" );
174 formatter.format( " * Compare the specified object with this object for equality.%n" );
175 formatter.format( " * Returns <code>true</code> if the specified object is of the%n" );
176 formatter.format( " * same type as this object, and the values of the fields%n" );
177 formatter.format( " * are equal.%n" );
178 formatter.format( " *%n" );
179 formatter.format( " * @param object The object that is to compared with this object.%n" );
180 formatter.format( " * @return boolean Returns <code>true</code> if the values of%n" );
181 formatter.format( " * the class fields are the same.%n" );
182 formatter.format( " */%n" );
183 formatter.format( " @Override%n" );
184 formatter.format( " public boolean equals( Object object )%n" );
185 formatter.format( " {%n" );
186 formatter.format( " if ( this == object ) return true;%n%n" );
187 formatter.format( " boolean value = false;%n" );
188 formatter.format( " if ( ( object != null ) &&%n" );
189 formatter.format( " ( getClass() == object.getClass() ) )%n" );
190 formatter.format( " {%n" );
191 formatter.format( " %s obj = (%s) object;%n%n", name, name );
192 formatter.format( " value = ( hashCode() == obj.hashCode() );%n" );
193
194 int count = 0;
195 for ( Map.Entry<String, String> entry : fields.entrySet() )
196 {
197 formatter.format( " value = value && " );
198
199 if ( primitives.contains( entry.getValue() ) )
200 {
201 formatter.format( " ( %s == obj.%s );%n",
202 entry.getKey(), entry.getKey() );
203 }
204 else
205 {
206 formatter.format( " ( ( %s == obj.%s ) ||%n",
207 entry.getKey(), entry.getKey() );
208 formatter.format( " ( %s != null &&%n", entry.getKey() );
209 formatter.format( " %s.equals( obj.%s ) ) );%n",
210 entry.getKey(), entry.getKey() );
211 }
212
213 ++count;
214 }
215
216 formatter.format( " }%n%n" );
217 formatter.format( " return value;%n" );
218 formatter.format( " }%n" );
219
220 return builder.toString();
221 }
222
223 /**
224 * Generate the <code>equals</code> method for the JavaBean.
225 *
226 * @return String The default <code>hashCode</code> method
227 * implementation.
228 */
229 protected String generateHashCodeMethod()
230 {
231 StringBuilder builder = new StringBuilder( 1024 );
232 Formatter formatter = new Formatter( builder );
233
234 formatter.format( " /**%n" );
235 formatter.format( " * Return a hash code for this object. Returns a hash code%n" );
236 formatter.format( " * computed from the class fields.%n" );
237 formatter.format( " *%n" );
238 formatter.format( " * @return int The hashCode value.%n" );
239 formatter.format( " */%n" );
240 formatter.format( " @Override%n" );
241 formatter.format( " public int hashCode()%n" );
242 formatter.format( " {%n" );
243 formatter.format( " if ( hash == HASH )%n" );
244 formatter.format( " {%n" );
245 for ( Map.Entry<String, String> entry : fields.entrySet() )
246 {
247 if ( primitives.contains( entry.getValue() ) )
248 {
249 if ( entry.getValue().equals( "boolean" ) )
250 {
251 formatter.format( " hash = ( 31 * hash ) +%n" );
252 formatter.format( " ( ( %s ) ? 1 : 0;%n",
253 entry.getKey() );
254 }
255 else
256 {
257 formatter.format(
258 " hash = ( 31 * hash ) + ( (int) %s );%n",
259 entry.getKey() );
260 }
261 }
262 else
263 {
264 formatter.format( " hash = ( 31 * hash ) +%n" );
265 formatter.format( " ( ( %s == null ) ? 0 : %s.hashCode() );%n",
266 entry.getKey(), entry.getKey() );
267 }
268 }
269 formatter.format( " }%n" );
270
271 formatter.format( "%n return hash;%n" );
272 formatter.format( " }%n%n" );
273
274 return builder.toString();
275 }
276
277 /**
278 * Generate the <code>compareTo</code> method for the JavaBean.
279 *
280 * @return String The default <code>compareTo</code> method
281 * implementation.
282 */
283 protected String generateCompareToMethod()
284 {
285 StringBuilder builder = new StringBuilder( 1024 );
286 Formatter formatter = new Formatter( builder );
287
288 formatter.format( " /**%n" );
289 formatter.format( " * Implementation of the <code>Comparable</code> interface.%n" );
290 formatter.format( " * Compares this object with the specified object for order. Returns%n" );
291 formatter.format( " * a negative integer, zero, or a positive integer as this object is%n" );
292 formatter.format( " * less than, equal to, or greater than the specified object.%n" );
293 formatter.format( " * The comparison is done by comparing the {@link #hashCode()} values%n" );
294 formatter.format( " * of the objects.%n" );
295 formatter.format( " *%n" );
296 formatter.format( " * @param object - The object with which this class is to%n" );
297 formatter.format( " * be compared. No class type checking is done.%n" );
298 formatter.format( " * @return int - A negative integer, zero, or a positive integer as%n" );
299 formatter.format( " * this object is less than, equal to, or greater than the%n" );
300 formatter.format( " * specified object.%n" );
301 formatter.format( " */%n" );
302 formatter.format( " public int compareTo( %s object )%n", name );
303 formatter.format( " {%n" );
304 formatter.format( " return ( this.hashCode() - object.hashCode() );%n" );
305 formatter.format( " }%n%n" );
306
307 return builder.toString();
308 }
309
310 /**
311 * Generate the clone method for the JavaBean.
312 *
313 * @return String The default <code>clone</code> method implementation.
314 */
315 protected String generateCloneMethod()
316 {
317 StringBuilder builder = new StringBuilder( 1024 );
318 Formatter formatter = new Formatter( builder );
319
320 formatter.format( " /**%n" );
321 formatter.format( " * Creates and returns a copy of this object. Implementation of%n" );
322 formatter.format( " * the <code>Cloneable</code> interface. No special actions are%n" );
323 formatter.format( " * performed. This method simply allows public access to the%n" );
324 formatter.format( " * <code>Object.clone</code> method.%n" );
325 formatter.format( " *%n" );
326 formatter.format( " * @return Object A clone of this instance.%n" );
327 formatter.format( " * @throws CloneNotSupportedException If the super-class implementation%n" );
328 formatter.format( " * throws an error.%n" );
329 formatter.format( " */%n" );
330 formatter.format( " @Override%n" );
331 formatter.format( " public Object clone() throws CloneNotSupportedException%n" );
332 formatter.format( " {%n" );
333 formatter.format( " return super.clone();%n" );
334 formatter.format( " }%n%n" );
335
336 return builder.toString();
337 }
338
339 /**
340 * Generate the accessor and mutator methods for the JavaBean.
341 *
342 * @return String The default accessor and mutator methods for
343 * the {@link #fields}.
344 */
345 protected String generateBeanMethods()
346 {
347 StringBuilder builder = new StringBuilder( 4096 );
348 Formatter formatter = new Formatter( builder );
349 for ( Map.Entry<String, String> entry : fields.entrySet() )
350 {
351 formatter.format( "%n /**%n" );
352 formatter.format(
353 " * Returns the value of {@link #%s}.%n",
354 entry.getKey() );
355 formatter.format( " *%n" );
356 formatter.format( " * @return %s The value of %s.%n",
357 entry.getValue(), entry.getKey() );
358 formatter.format( " */%n" );
359 formatter.format( " public final %s get%s%s()%n",
360 entry.getValue(),
361 entry.getKey().substring( 0, 1 ).toUpperCase(),
362 entry.getKey().substring( 1 ) );
363 formatter.format( " {%n" );
364 formatter.format( " return %s;%n", entry.getKey() );
365 formatter.format( " }%n%n" );
366
367 formatter.format( " /**%n" );
368 formatter.format(
369 " * Sets the value of {@link #%s}. Resets {@link #hash}.%n",
370 entry.getKey() );
371 formatter.format( " *%n" );
372 formatter.format( " * @param %s The value to set.%n",
373 entry.getKey() );
374 formatter.format( " */%n" );
375 formatter.format( " public final void set%s%s( %s %s )%n",
376 entry.getKey().substring( 0, 1 ).toUpperCase(),
377 entry.getKey().substring( 1 ),
378 entry.getValue(), entry.getKey() );
379 formatter.format( " {%n" );
380 formatter.format( " %s oldValue = this.%s;%n",
381 entry.getValue(), entry.getKey() );
382 formatter.format( " this.%s = %s;%n",
383 entry.getKey(), entry.getKey() );
384 formatter.format( " hash = HASH;%n" );
385 formatter.format( " }%n" );
386 }
387
388 return builder.toString();
389 }
390 }