View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software 
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License.
16   */
17  
18  package org.apache.jdo.impl.enhancer.generator;
19  
20  import java.lang.reflect.Modifier;
21  
22  import java.util.Iterator;
23  import java.util.Collection;
24  import java.util.List;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.Properties;
28  
29  import java.io.Serializable;
30  import java.io.File;
31  import java.io.Writer;
32  import java.io.PrintWriter;
33  import java.io.FileWriter;
34  import java.io.BufferedWriter;
35  import java.io.InputStream;
36  import java.io.BufferedInputStream;
37  import java.io.FileInputStream;
38  import java.io.ObjectOutputStream;
39  import java.io.IOException;
40  import java.io.FileNotFoundException;
41  
42  import org.apache.jdo.impl.enhancer.meta.ExtendedMetaData;
43  import org.apache.jdo.impl.enhancer.meta.prop.EnhancerMetaDataPropertyImpl;
44  import org.apache.jdo.impl.enhancer.util.Support;
45  
46  
47  
48  
49  /***
50   *
51   */
52  public final class Main
53      extends Support
54  {
55      /***
56       *  The stream to write messages to.
57       */
58      private final PrintWriter out = new PrintWriter(System.out, true);
59  
60      /***
61       *  The stream to write error messages to.
62       */
63      private final PrintWriter err = new PrintWriter(System.err, true);
64  
65      /***
66       *  The command line options.
67       */
68      private final CmdLineOptions opts = new CmdLineOptions();
69  
70      /***
71       *
72       */
73      private final CodeWriter writer = new CodeWriter();
74  
75      /***
76       * The MetaData for generating classes.
77       */
78      private ExtendedMetaData meta = null;
79  
80      /***
81       *
82       */
83      public Main()
84      {}
85  
86      /***
87       *
88       */
89      public static final void main(String[] argv)
90      {
91          final Main gen = new Main();
92          try {
93              gen.opts.processArgs(argv);
94              gen.init();
95              gen.generate();
96          } catch(Exception ex) {
97              gen.printError(null, ex);
98          }
99      }
100 
101     /***
102      *  A class for holding the command line options.
103      */
104     private class CmdLineOptions
105     {
106         // final Collection inputFileNames = new ArrayList();
107         String destinationDirectory = null;
108         String jdoXMLModelFileName = null;
109         String jdoPropertiesFileName = null;
110         boolean verbose = false;
111         boolean quiet = false;
112         boolean forceWrite = false;
113         boolean noWrite = false;
114 
115         /***
116          * Print a usage message to System.err
117          */
118         public void usage() {
119             err.println("Usage: Main <options> <arguments>...");
120             err.println("Options:");
121             err.println("  -v, --verbose            print verbose output");
122 /*
123             err.println("  -q, --quiet              supress warnings");
124             err.println("  -n, --nowrite            never write classfiles");
125             err.println("  -f, --force              ever write classfiles");
126 */
127             err.println("  -d, --dest <dir>         destination directory for output files");
128             err.println("  -p, --properties <file>  use property file for meta data");
129 /*
130             err.println("  -x, --xmlmodel <file>    use JDO XML model file for meta data");
131 */
132             err.println();
133             err.println("Arguments:");
134             err.println();
135             err.println("Returns a non-zero value in case of errors.");
136             System.exit(1);
137         }
138 
139         /***
140          * Process command line options
141          */
142         protected int processArgs(String[] argv)
143         {
144             for (int i = 0; i < argv.length; i++) {
145                 final String arg = argv[i];
146                 if (arg.equals("-v")
147                     || arg.equals("--verbose")) {
148                     verbose = true;
149                     quiet = false;
150                     continue;
151                 }
152 /*
153                 if (arg.equals("-q")
154                     || arg.equals("--quiet")) {
155                     quiet = true;
156                     verbose = false;
157                     continue;
158                 }
159                 if (arg.equals("-f")
160                     || arg.equals("--force")) {
161                     forceWrite = true;
162                     continue;
163                 }
164                 if (arg.equals("-n")
165                     || arg.equals("--nowrite")) {
166                     noWrite = true;
167                     continue;
168                 }
169 */
170                 if (arg.equals("-d")
171                     || arg.equals("--dest")) {
172                     if (argv.length - i < 2) {
173                         printError("Missing argument to the -d/-dest option", null);
174                         usage();
175                     }
176                     destinationDirectory = argv[++i];
177                     continue;
178                 }
179                 if (arg.equals("-p") ||
180                     arg.equals("--properties")) {
181                     if (argv.length - i < 2) {
182                         printError("Missing argument to the -p/--properties option", null);
183                         usage();
184                     }
185                     jdoPropertiesFileName = argv[++i];
186                     continue;
187                 }
188 /*
189                 if (arg.equals("-x") ||
190                     arg.equals("--xmlmodel")) {
191                     if (argv.length - i < 2) {
192                         printError("Missing argument to the -p/--properties option", null);
193                         usage();
194                     }
195                     jdoXMLModelFileName = argv[++i];
196                     continue;
197                 }
198 */
199                 if (arg.length() > 0 && arg.charAt(0) == '-') {
200                     printError("Unrecognized option:" + arg, null);
201                     usage();
202                 }
203                 if (arg.length() == 0) {
204                     printMessage("Ignoring empty command line argument.");
205                     continue;
206                 }
207 
208                 //inputFileNames.add(arg);
209             }
210 
211             // The user must specify a destination directory
212             if (jdoPropertiesFileName == null) {
213                 printError("No destination directory specified", null);
214                 usage();
215             }
216 
217             // The user must specify a destination directory
218             if (destinationDirectory == null) {
219                 printError("No destination directory specified", null);
220                 usage();
221             }
222 
223             return 0;
224         }
225     }
226 
227     private void init()
228         throws FileNotFoundException, IOException
229     {
230         // load the properties
231         affirm(opts.jdoPropertiesFileName != null);
232         meta = new EnhancerMetaDataPropertyImpl(out, opts.verbose,
233                                                 opts.jdoPropertiesFileName);
234 
235         // create the destination directory
236         affirm(opts.destinationDirectory != null);
237         final File destinationDir = new File(opts.destinationDirectory);
238         boolean res = destinationDir.mkdirs();
239         if (!res) {
240             throw new IOException("unable to create destination directory: "
241                                   + "'" + destinationDir + "'");
242         }
243     }
244 
245     private void generate()
246     {
247         final String[] classes = meta.getKnownClasses();
248         for (int i = 0; i < classes.length; i++) {
249             final String classname = classes[i];
250             try {
251                 if (classname.indexOf('$') != -1) {
252                     printMessage("Skipping generation of nested class " + classname + "." +
253                                  " Note, a nested ObjectId class is generated with its pc class.");
254                     continue;
255                 }
256                 final Writer writer = createFileWriter(classname);
257                 this.writer.setWriter(writer);
258                 generateClass(classname);
259                 writer.close();
260             } catch(IOException ex) {
261                 printError("Error generating class '" + classname + "'.", ex);
262             }
263         }
264     }
265 
266     private void generateClass(final String classname)
267         throws IOException
268     {
269         affirm(classname);
270         
271         final String normClassName = NameHelper.normalizeClassName(classname);
272         printMessage("generating '" + normClassName + "'...");
273 
274         final</strong> String packageName = NameHelper.getPackageName(classname);
275         writer.writePackage(
276             packageName,
277             null);
278 
279         writer.writeImports(
280             null,
281             null);
282         
283         // write the class header and key class
284         final String oidClassName = meta.getKeyClass(classname);
285         if (oidClassName == null) {
286             writeClassHeader(classname);
287         } else {
288             final String oidPackageName
289                 = NameHelper.getPackageName(oidClassName);
290             affirm(packageName.equals(oidPackageName),
291                    "PC class and key class must be in same package.");
292 
293             final boolean enclosedOid
294                 = oidClassName.startsWith(classname + "$");
295             if (enclosedOid) {
296                 writeClassHeader(classname);
297                 writeOidClass(classname, oidClassName, enclosedOid);
298             } else {
299                 writeOidClass(classname, oidClassName, enclosedOid);
300                 writeClassHeader(classname);
301             }
302         }
303         
304         writeClassMembers(classname);
305 
306         // write the augmentation
307         final boolean isPC = meta.isPersistenceCapableClass(classname);
308         if (isPC) {
309             final boolean isPCRoot
310                 = meta.isPersistenceCapableRootClass(classname);
311             if (isPCRoot) {
312                 writePCRootMembers(classname);
313             }
314             writePCMembers(classname);
315             
316             writeClassMemberAccessors(classname);
317         }
318 
319         writer.writeClassEnd();
320     }
321 
322     private Writer createFileWriter(String classname)
323         throws IOException
324     {
325         final File file = new File(opts.destinationDirectory,
326                                    classname + ".java");
327         file.getAbsoluteFile().getParentFile().mkdirs();
328         return new BufferedWriter(new FileWriter(file));
329     }
330 
331     private void writeClassHeader(final String classname)
332         throws IOException
333     {
334         final boolean isPCRoot = meta.isPersistenceCapableRootClass(classname);
335         final String superclass = meta.getSuperClass(classname);
336 
337         String[] interfaces = null;
338         String[] comments = null;
339         interfaces
340             = new String[]{ ImplHelper.CLASSNAME_JDO_PERSISTENCE_CAPABLE };
341         writer.writeClassHeader(meta.getClassModifiers(classname),
342                                 ImplHelper.getClassName(classname),
343                                 superclass,
344                                 interfaces,
345                                 comments);
346     }
347 
348     private void writeClassMembers(final String classname)
349         throws IOException
350     {
351         writer.writeComments(1, new String[]{
352             "----------------------------------------------------------------------",
353             "Class Members:",
354             "----------------------------------------------------------------------"
355         });
356         writer.writeln();
357         
358         // write default constructor
359         writer.writeConstructor(
360             ImplHelper.getClassName(classname),
361             Modifier.PUBLIC,
362             null, null, null,
363             ImplHelper.getDefaultConstructorImpl(),
364             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
365 
366         // write default constructor
367         writer.writeConstructor(
368             ImplHelper.getClassName(classname),
369             Modifier.PUBLIC,
370             new String[]{ "str" },
371             new String[]{ "String" },
372             null,
373             ImplHelper.getDummyConstructorImpl(),
374             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
375 
376         final String[] fieldnames = meta.getKnownFields(classname);
377         final int n = (fieldnames != null ? fieldnames.length : 0);
378         
379         // write the fields and with their bean getters/setters
380         for (int i = 0; i < n; i++) {
381             final String fieldname = (String)fieldnames[i];
382             writeFieldMember(classname, fieldname);
383         }
384     }
385     
386     private void writeFieldMember(final String classname,
387                                   final String fieldname)
388         throws IOException
389     {
390         final String fieldtype = meta.getFieldType(classname, fieldname);
391         final int access = meta.getFieldModifiers(classname, fieldname);
392         final String normClassName = NameHelper.normalizeClassName(classname);
393         final List impl = new ArrayList();
394 
395         // the field
396         writer.writeField(
397             fieldname,
398             access,
399             fieldtype,
400             null, null);
401 
402         // do not write bean getters and setters for static fields
403         if ((access & Modifier.STATIC) != 0) {
404             return;
405         }
406 
407         // write bean getter (calling accessor)
408         impl.clear();
409         impl.add("//return this." + fieldname + ';');
410         final String accessor
411             = ImplHelper.createJDOFieldAccessorName(classname, fieldname);
412         impl.add("return " + normClassName + "." + accessor + "(this);");
413         writer.writeMethod(
414             createMethodName("get", fieldname),
415             Modifier.PUBLIC,
416             fieldtype,
417             null,
418             null,
419             null,
420             impl,
421             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
422 
423         // write bean setter (calling mutator)
424         impl.clear();
425         impl.add("//this." + fieldname + " = " + fieldname + ';');
426         final String mutator
427             = ImplHelper.createJDOFieldMutatorName(classname, fieldname);
428         impl.add(normClassName + "." + mutator + "(this, " + fieldname + ");");
429         writer.writeMethod(
430             createMethodName("set", fieldname),
431             Modifier.PUBLIC,
432             "void",
433             new String[]{ fieldname },
434             new String[]{ fieldtype },
435             null,
436             impl,
437             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
438     }
439 
440     private void writeClassMemberAccessors(final String classname)
441         throws IOException
442     {
443         writer.writeComments(1, new String[]{
444             "----------------------------------------------------------------------",
445             "Augmentation for Field Accessors and Mutators (added by enhancer):",
446             "----------------------------------------------------------------------"
447         });
448         writer.writeln();
449 
450         // write the fields and their access methods
451         final String[] fields = meta.getManagedFields(classname);
452         final int n = (fields != null ? fields.length : 0);
453         for (int i = 0; i < n; i++) {
454             final String fieldname = (String)fields[i];
455             writeFieldAccessors(classname, fieldname);
456         }
457     }
458     
459     private void writeFieldAccessors(final String classname,
460                                      final String fieldname)
461         throws IOException
462     {
463         final String fieldtype
464             = meta.getFieldType(classname, fieldname);
465         final int fieldnumber
466             = meta.getFieldNumber(classname, fieldname);
467         final boolean dfg
468             = meta.isDefaultFetchGroupField(classname, fieldname);
469         final int access
470             = meta.getFieldModifiers(classname, fieldname);
471         final int flags
472             = meta.getFieldFlags(classname, fieldname);
473 
474         final String accessor
475             = ImplHelper.createJDOFieldAccessorName(classname, fieldname);
476         final String mutator
477             = ImplHelper.createJDOFieldMutatorName(classname, fieldname);
478 
479         final String instancename
480             = "instance";
481         
482         // jdo accessor
483         {
484             affirm(((flags & meta.CHECK_READ) == 0)
485                    | (flags & meta.MEDIATE_READ) == 0);
486             final List impl;
487             if ((flags & meta.CHECK_READ) != 0) {
488                 impl = ImplHelper.getJDOFieldCheckReadImpl(fieldname,
489                                                            fieldtype,
490                                                            fieldnumber,
491                                                            instancename);
492             } else if ((flags & meta.MEDIATE_READ) != 0) {
493                 impl = ImplHelper.getJDOFieldMediateReadImpl(fieldname,
494                                                              fieldtype,
495                                                              fieldnumber,
496                                                              instancename);
497             } else {
498                 impl = ImplHelper.getJDOFieldDirectReadImpl(fieldname,
499                                                             fieldtype,
500                                                             fieldnumber,
501                                                             instancename);
502             }
503             writer.writeMethod(
504                 accessor,
505                 access | Modifier.STATIC | Modifier.FINAL,
506                 fieldtype,
507                 new String[]{ instancename },
508                 new String[]{ classname },
509                 null,
510                 impl,
511                 ImplHelper.COMMENT_ENHANCER_ADDED);
512         }
513         
514         // jdo mutator
515         {
516             affirm(((flags & meta.CHECK_WRITE) == 0)
517                    | (flags & meta.MEDIATE_WRITE) == 0);
518             final List impl;
519             if ((flags & meta.CHECK_WRITE) != 0) {
520                 impl = ImplHelper.getJDOFieldCheckWriteImpl(fieldname,
521                                                             fieldtype,
522                                                             fieldnumber,
523                                                             instancename,
524                                                             fieldname);
525             } else if ((flags & meta.MEDIATE_WRITE) != 0) {
526                 impl = ImplHelper.getJDOFieldMediateWriteImpl(fieldname,
527                                                               fieldtype,
528                                                               fieldnumber,
529                                                               instancename,
530                                                               fieldname);
531             } else {
532                 impl = ImplHelper.getJDOFieldDirectWriteImpl(fieldname,
533                                                              fieldtype,
534                                                              fieldnumber,
535                                                              instancename,
536                                                              fieldname);
537             }
538             writer.writeMethod(
539                 mutator,
540                 access | Modifier.STATIC | Modifier.FINAL,
541                 "void",
542                 new String[]{ instancename, fieldname },
543                 new String[]{ classname, fieldtype },
544                 null,
545                 impl,
546                 ImplHelper.COMMENT_ENHANCER_ADDED);
547         }
548     }
549     
550     private void writePCRootMembers(final String classname)
551         throws IOException
552     {
553         writer.writeComments(1, new String[]{
554             "----------------------------------------------------------------------",
555             "Augmentation for Persistence-Capable Root Classes (added by enhancer):",
556             "----------------------------------------------------------------------"
557         });
558         writer.writeln();
559 
560         // jdoStateManager
561         writer.writeField(
562             ImplHelper.FIELDNAME_JDO_STATE_MANAGER,
563             Modifier.PROTECTED | Modifier.TRANSIENT,
564             ImplHelper.CLASSNAME_JDO_STATE_MANAGER,
565             "null",
566             ImplHelper.COMMENT_ENHANCER_ADDED);
567 
568         // jdoFlags
569         writer.writeField(
570             ImplHelper.FIELDNAME_JDO_FLAGS,
571             Modifier.PROTECTED | Modifier.TRANSIENT,
572             "byte",
573             "0", // (ImplHelper.CLASSNAME_JDO_PERSISTENCE_CAPABLE
574             //       + "." + "READ_WRITE_OK"),
575             ImplHelper.COMMENT_ENHANCER_ADDED);
576 
577         // jdoReplaceStateManager
578         writer.writeMethod(
579             ImplHelper.METHODNAME_JDO_REPLACE_STATE_MANAGER,
580             Modifier.PUBLIC | Modifier.FINAL | Modifier.SYNCHRONIZED,
581             "void",
582             new String[]{ "sm" },
583             new String[]{ ImplHelper.CLASSNAME_JDO_STATE_MANAGER },
584             null,
585             ImplHelper.getJDOReplaceStateManagerImpl("sm"),
586             ImplHelper.COMMENT_ENHANCER_ADDED);
587         
588         // jdoReplaceFlags
589         writer.writeMethod(
590             ImplHelper.METHODNAME_JDO_REPLACE_FLAGS,
591             Modifier.PUBLIC | Modifier.FINAL,
592             "void", null, null, null,
593             ImplHelper.getJDOReplaceFlagsImpl(),
594             ImplHelper.COMMENT_ENHANCER_ADDED);
595 
596         // getPersistenceManager
597         writer.writeMethod(
598             ImplHelper.METHODNAME_JDO_GET_PERSISTENCE_MANAGER,
599             Modifier.PUBLIC | Modifier.FINAL,
600             ImplHelper.CLASSNAME_JDO_PERSISTENCE_MANAGER, null, null, null,
601             ImplHelper.getJDOStateManagerObjectDelegationImpl("getPersistenceManager(this)"),
602             ImplHelper.COMMENT_ENHANCER_ADDED);
603         
604         // getObjectId
605         writer.writeMethod(
606             ImplHelper.METHODNAME_JDO_GET_OBJECT_ID,
607             Modifier.PUBLIC | Modifier.FINAL,
608             Object.class.getName(), null, null, null,
609             ImplHelper.getJDOStateManagerObjectDelegationImpl("getObjectId(this)"),
610             ImplHelper.COMMENT_ENHANCER_ADDED);
611 
612         writer.writeMethod(
613             ImplHelper.METHODNAME_JDO_GET_TRANSACTIONAL_OBJECT_ID,
614             Modifier.PUBLIC | Modifier.FINAL,
615             Object.class.getName(), null, null, null,
616             ImplHelper.getJDOStateManagerObjectDelegationImpl("getTransactionalObjectId(this)"),
617             ImplHelper.COMMENT_ENHANCER_ADDED);
618 
619         // jdoGetVersion
620         writer.writeMethod(
621             ImplHelper.METHODNAME_JDO_GET_VERSION,
622             Modifier.PUBLIC | Modifier.FINAL,
623             Object.class.getName(), null, null, null,
624             // TODO: generate real method body
625             ImplHelper.getNotYetImplemented(
626                 ImplHelper.METHODNAME_JDO_GET_VERSION),
627             ImplHelper.COMMENT_ENHANCER_ADDED);
628 
629         // is-methods
630         writer.writeMethod(
631             ImplHelper.METHODNAME_JDO_IS_PERSISTENT,
632             Modifier.PUBLIC | Modifier.FINAL,
633             "boolean", null, null, null,
634             ImplHelper.getJDOStateManagerBooleanDelegationImpl("isPersistent(this)"),
635             ImplHelper.COMMENT_ENHANCER_ADDED);
636 
637         writer.writeMethod(
638             ImplHelper.METHODNAME_JDO_IS_TRANSACTIONAL,
639             Modifier.PUBLIC | Modifier.FINAL,
640             "boolean", null, null, null,
641             ImplHelper.getJDOStateManagerBooleanDelegationImpl("isTransactional(this)"),
642             ImplHelper.COMMENT_ENHANCER_ADDED);
643 
644         writer.writeMethod(
645             ImplHelper.METHODNAME_JDO_IS_NEW,
646             Modifier.PUBLIC | Modifier.FINAL,
647             "boolean", null, null, null,
648             ImplHelper.getJDOStateManagerBooleanDelegationImpl("isNew(this)"),
649             ImplHelper.COMMENT_ENHANCER_ADDED);
650 
651         writer.writeMethod(
652             ImplHelper.METHODNAME_JDO_IS_DELETED,
653             Modifier.PUBLIC | Modifier.FINAL,
654             "boolean", null, null, null,
655             ImplHelper.getJDOStateManagerBooleanDelegationImpl("isDeleted(this)"),
656             ImplHelper.COMMENT_ENHANCER_ADDED);
657 
658         writer.writeMethod(
659             ImplHelper.METHODNAME_JDO_IS_DIRTY,
660             Modifier.PUBLIC | Modifier.FINAL,
661             "boolean", null, null, null,
662             ImplHelper.getJDOStateManagerBooleanDelegationImpl("isDirty(this)"),
663             ImplHelper.COMMENT_ENHANCER_ADDED);
664 
665         writer.writeMethod(
666             ImplHelper.METHODNAME_JDO_IS_DETACHED,
667             Modifier.PUBLIC | Modifier.FINAL,
668             "boolean", null, null, null,
669             // TODO: generate real method body
670             ImplHelper.getNotYetImplemented(
671                 ImplHelper.METHODNAME_JDO_IS_DETACHED),
672             ImplHelper.COMMENT_ENHANCER_ADDED);
673 
674         // makeDirty
675         writer.writeMethod(
676             ImplHelper.METHODNAME_JDO_MAKE_DIRTY,
677             Modifier.PUBLIC | Modifier.FINAL,
678             "void",
679             new String[]{ "fieldname" },
680             new String[]{ String.class.getName() },
681             null,
682             ImplHelper.getJDOStateManagerVoidDelegationImpl("makeDirty(this, fieldname)"),
683             ImplHelper.COMMENT_ENHANCER_ADDED);
684 
685         // replaceFields
686         writer.writeMethod(
687             ImplHelper.METHODNAME_JDO_REPLACE_FIELDS,
688             Modifier.PUBLIC | Modifier.FINAL,
689             "void",
690             new String[]{ "fieldnumbers" },
691             new String[]{ "int[]" },
692             null,
693             ImplHelper.getJDOFieldIterationImpl("fieldnumbers",
694                                                 ImplHelper.METHODNAME_JDO_REPLACE_FIELD),
695             ImplHelper.COMMENT_ENHANCER_ADDED);
696 
697         // provideFields
698         writer.writeMethod(
699             ImplHelper.METHODNAME_JDO_PROVIDE_FIELDS,
700             Modifier.PUBLIC | Modifier.FINAL,
701             "void",
702             new String[]{ "fieldnumbers" },
703             new String[]{ "int[]" },
704             null,
705             ImplHelper.getJDOFieldIterationImpl("fieldnumbers",
706                                                 ImplHelper.METHODNAME_JDO_PROVIDE_FIELD),
707             ImplHelper.COMMENT_ENHANCER_ADDED);
708 
709         // preSerialize
710         writer.writeMethod(
711             ImplHelper.METHODNAME_JDO_PRE_SERIALIZE,
712             Modifier.PROTECTED | Modifier.FINAL,
713             "void", null, null, null,
714             ImplHelper.getJDOStateManagerVoidDelegationImpl("preSerialize(this)"),
715             ImplHelper.COMMENT_ENHANCER_ADDED);
716 
717         // write method clone()
718         writer.writeMethod(
719             "clone",
720             Modifier.PUBLIC,
721             "Object",
722             null,
723             null,
724             new String[]{ "java.lang.CloneNotSupportedException" },
725             ImplHelper.getCloneImpl(classname),
726             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
727 
728     }
729 
730     private void writePCMembers(final String classname)
731         throws IOException
732     {
733         writer.writeComments(1, new String[]{
734             "----------------------------------------------------------------------",
735             "Augmentation for Persistence-Capable Classes (added by enhancer):",
736             "----------------------------------------------------------------------"
737         });
738         writer.writeln();
739         
740         final String[] managedFieldNames
741             = meta.getManagedFields(classname);
742         final String[] managedFieldTypes
743             = meta.getFieldType(classname, managedFieldNames);
744         final boolean isPCRoot
745             = meta.isPersistenceCapableRootClass(classname);
746 
747         writePCStaticMembers(classname);
748 
749         // jdoNewInstance
750         writer.writeMethod(
751             ImplHelper.METHODNAME_JDO_NEW_INSTANCE,
752             Modifier.PUBLIC,
753             ImplHelper.CLASSNAME_JDO_PERSISTENCE_CAPABLE,
754             new String[]{ "sm" },
755             new String[]{ ImplHelper.CLASSNAME_JDO_STATE_MANAGER },
756             null,
757             ImplHelper.getJDONewInstanceImpl(classname,
758                                              "sm"),
759             ImplHelper.COMMENT_ENHANCER_ADDED);
760 
761         // jdoNewInstance
762         writer.writeMethod(
763             ImplHelper.METHODNAME_JDO_NEW_INSTANCE,
764             Modifier.PUBLIC,
765             ImplHelper.CLASSNAME_JDO_PERSISTENCE_CAPABLE,
766             new String[]{ "sm", "oid" },
767             new String[]{ ImplHelper.CLASSNAME_JDO_STATE_MANAGER, "Object" },
768             null,
769             ImplHelper.getJDONewInstanceKeyImpl(classname,
770                                                 //oidClassName,
771                                                 "sm",
772                                                 "oid"),
773                                                 //keyFieldNames),
774             ImplHelper.COMMENT_ENHANCER_ADDED);
775 
776         // jdoReplaceField
777         writer.writeMethod(
778             ImplHelper.METHODNAME_JDO_REPLACE_FIELD,
779             Modifier.PUBLIC,
780             "void",
781             new String[]{ "fieldnumber" },
782             new String[]{ "int" },
783             null,
784             ImplHelper.getJDOReplaceFieldImpl("fieldnumber",
785                                               isPCRoot,
786                                               managedFieldNames,
787                                               managedFieldTypes),
788             ImplHelper.COMMENT_ENHANCER_ADDED);
789 
790         // jdoProvideField(s)
791         writer.writeMethod(
792             ImplHelper.METHODNAME_JDO_PROVIDE_FIELD,
793                            Modifier.PUBLIC,
794             "void",
795             new String[]{ "fieldnumber" },
796             new String[]{ "int" },
797             null,
798             ImplHelper.getJDOProvideFieldImpl("fieldnumber",
799                                               isPCRoot,
800                                               managedFieldNames,
801                                               managedFieldTypes),
802             ImplHelper.COMMENT_ENHANCER_ADDED);
803 
804         // jdoCopyFields
805         writer.writeMethod(
806             ImplHelper.METHODNAME_JDO_COPY_FIELDS,
807             Modifier.PUBLIC,
808             "void",
809             new String[]{ "pc", "fieldnumbers" },
810             new String[]{ Object.class.getName(), "int[]" },
811             null,
812             ImplHelper.getJDOCopyFieldsImpl(classname,
813                                             "pc",
814                                             "fieldnumbers"),
815             ImplHelper.COMMENT_ENHANCER_ADDED);
816 
817         // jdoCopyField
818         writer.writeMethod(
819             ImplHelper.METHODNAME_JDO_COPY_FIELD,
820             Modifier.PROTECTED | Modifier.FINAL,
821             "void",
822             new String[]{ "pc", "fieldnumber" },
823             new String[]{ classname, "int" },
824             null,
825             ImplHelper.getJDOCopyFieldImpl(classname,
826                                            "pc",
827                                            "fieldnumber",
828                                            managedFieldNames,
829                                            isPCRoot),
830             ImplHelper.COMMENT_ENHANCER_ADDED);
831 
832         writePCKeyHandlingMembers(classname);
833 
834         writePCSerializationMembers(classname);
835     }
836 
837     private void writePCStaticMembers(final String classname)
838         throws IOException
839     {
840         final String[] managedFieldNames
841             = meta.getManagedFields(classname);
842         final String superPC
843             = meta.getPersistenceCapableSuperClass(classname);
844         final String[] managedFieldTypes
845             = meta.getFieldType(classname, managedFieldNames);
846         final int[] managedFieldFlags
847             = meta.getFieldFlags(classname, managedFieldNames);
848         final boolean isPCRoot 
849             = meta.isPersistenceCapableRootClass(classname);
850 
851         // inheritedFieldCount
852         writer.writeField(
853             ImplHelper.FIELDNAME_JDO_INHERITED_FIELD_COUNT,
854             Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
855             "int",
856             null,
857             ImplHelper.COMMENT_ENHANCER_ADDED);
858 
859         // fieldNames
860         writer.writeField(
861             ImplHelper.FIELDNAME_JDO_FIELD_NAMES,
862             Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
863             "String[]",
864             null,
865             ImplHelper.COMMENT_ENHANCER_ADDED);
866 
867         // fieldTypes
868         writer.writeField(
869             ImplHelper.FIELDNAME_JDO_FIELD_TYPES,
870             Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
871             "Class[]",
872             null,
873             ImplHelper.COMMENT_ENHANCER_ADDED);
874 
875         // fieldFlags
876         writer.writeField(
877             ImplHelper.FIELDNAME_JDO_FIELD_FLAGS,
878             Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
879             "byte[]",
880             null,
881             ImplHelper.COMMENT_ENHANCER_ADDED);
882 
883         // PC superclass
884         writer.writeField(
885             ImplHelper.FIELDNAME_JDO_PC_SUPERCLASS,
886             Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
887             "Class",
888             null,
889             ImplHelper.COMMENT_ENHANCER_ADDED);
890 
891         // static initializer
892         writer.writeStaticInitializer(
893             ImplHelper.getStaticInitializerImpl(classname,
894                                                 superPC,
895                                                 managedFieldNames,
896                                                 managedFieldTypes,
897                                                 managedFieldFlags),
898             ImplHelper.COMMENT_ENHANCER_ADDED);
899 
900         // jdoGetManagedFieldCount
901         writer.writeMethod(
902             ImplHelper.METHODNAME_JDO_GET_MANAGED_FIELD_COUNT,
903             Modifier.PROTECTED | Modifier.STATIC,
904             "int", null, null, null,
905             ImplHelper.getJDOGetManagedFieldCountImpl(
906                 isPCRoot, superPC, managedFieldNames.length),
907             ImplHelper.COMMENT_ENHANCER_ADDED);
908     }
909 
910     private void writePCKeyHandlingMembers(final String classname)
911         throws IOException
912     {
913         final boolean isPCRoot
914             = meta.isPersistenceCapableRootClass(classname);
915         final String oidClassName
916             = NameHelper.normalizeClassName(meta.getKeyClass(classname));
917 
918         // generate these methods if this is the PC root class or if
919         // there's a key class definition
920         if (!isPCRoot && oidClassName == null) {
921             return;
922         }
923 
924         final String superOidClassName
925             = NameHelper.normalizeClassName(meta.getSuperKeyClass(classname));
926         final String[] keyFieldNames
927             = meta.getKeyFields(classname);
928         final String[] keyFieldTypes
929             = meta.getFieldType(classname, keyFieldNames);
930         final int[] keyFieldNumbers
931             = meta.getFieldNumber(classname, keyFieldNames);
932 
933         // jdoNewOidInstance
934         writer.writeMethod(
935             ImplHelper.METHODNAME_JDO_NEW_OID_INSTANCE,
936             Modifier.PUBLIC,
937             Object.class.getName(), null, null, null,
938             ImplHelper.getJDONewOidInstanceImpl(oidClassName),
939             ImplHelper.COMMENT_ENHANCER_ADDED);
940 
941         writer.writeMethod(
942             ImplHelper.METHODNAME_JDO_NEW_OID_INSTANCE,
943             Modifier.PUBLIC,
944             Object.class.getName(),
945             new String[]{ "o" },
946             new String[]{ "Object" },
947             null,
948             ImplHelper.getJDONewOidInstanceImpl(oidClassName,
949                                                 "o"),
950             ImplHelper.COMMENT_ENHANCER_ADDED);
951 
952         // jdoCopyKeyFieldsTo/FromOid
953         writer.writeMethod(
954             ImplHelper.METHODNAME_JDO_COPY_KEY_FIELDS_TO_OID,
955             Modifier.PUBLIC,
956             "void",
957             new String[]{ "oid" },
958             new String[]{ "Object" },
959             null,
960             ImplHelper.getJDOCopyKeyFieldsToOid(oidClassName,
961                                                 superOidClassName,
962                                                 "oid",
963                                                 keyFieldNames),
964             ImplHelper.COMMENT_ENHANCER_ADDED);
965 
966         writer.writeMethod(
967             ImplHelper.METHODNAME_JDO_COPY_KEY_FIELDS_FROM_OID,
968             Modifier.PROTECTED,
969             "void",
970             new String[]{ "oid" },
971             new String[]{ "Object" },
972             null,
973             ImplHelper.getJDOCopyKeyFieldsFromOid(oidClassName,
974                                                   superOidClassName,
975                                                   "oid",
976                                                   keyFieldNames),
977             ImplHelper.COMMENT_ENHANCER_ADDED);
978 
979         writer.writeMethod(
980             ImplHelper.METHODNAME_JDO_COPY_KEY_FIELDS_TO_OID,
981             Modifier.PUBLIC,
982             "void",
983             new String[]{ "ofs", "oid" },
984             new String[]{ ImplHelper.CLASSNAME_JDO_OBJECT_ID_FIELD_SUPPLIER,
985                           "Object" },
986             null,
987             ImplHelper.getJDOCopyKeyFieldsToOid(oidClassName,
988                                                 superOidClassName,
989                                                 "ofs",
990                                                 "oid",
991                                                 keyFieldNames,
992                                                 keyFieldTypes,
993                                                 keyFieldNumbers),
994             ImplHelper.COMMENT_ENHANCER_ADDED);
995 
996         writer.writeMethod(
997             ImplHelper.METHODNAME_JDO_COPY_KEY_FIELDS_FROM_OID,
998             Modifier.PUBLIC,
999             "void",
1000             new String[]{ "ofc", "oid" },
1001             new String[]{ ImplHelper.CLASSNAME_JDO_OBJECT_ID_FIELD_CONSUMER,
1002                           "Object" },
1003             null,
1004             ImplHelper.getJDOCopyKeyFieldsFromOid(oidClassName,
1005                                                   superOidClassName,
1006                                                   "ofc",
1007                                                   "oid",
1008                                                   keyFieldNames,
1009                                                   keyFieldTypes,
1010                                                   keyFieldNumbers),
1011             ImplHelper.COMMENT_ENHANCER_ADDED);
1012     }
1013 
1014     private void writePCSerializationMembers(final String classname)
1015         throws IOException
1016     {
1017         final long serialUID
1018             = createJDOVersionUID(classname);
1019 
1020         //^olsen: to adapt
1021         writer.writeField(
1022             ImplHelper.FIELDNAME_SERIAL_VERSION_UID,
1023             Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
1024             "long",
1025             ImplHelper.getSerialVersionUIDInitValue(serialUID),
1026             new String[]{ "only a dummy value yet"});
1027         //ImplHelper.COMMENT_ENHANCER_ADDED);
1028 
1029         writer.writeMethod(
1030             ImplHelper.METHODNAME_WRITE_OBJECT,
1031             Modifier.PRIVATE,
1032             "void",
1033             new String[]{ "out" },
1034             new String[]{ ObjectOutputStream.class.getName() },
1035             new String[]{ IOException.class.getName() },
1036             ImplHelper.getWriteObjectImpl("out"),
1037             ImplHelper.COMMENT_ENHANCER_ADDED);
1038     }
1039 
1040     private void writeOidClass(final String classname,
1041                                final String oidClassName,
1042                                final boolean enclosedOid)
1043         throws IOException
1044     {
1045         final int indent = (enclosedOid ? 1 : 0);
1046         writer.writeComments(indent, new String[]{
1047             "----------------------------------------------------------------------",
1048             "Key Class:",
1049             "----------------------------------------------------------------------"
1050         });
1051         writer.writeln();
1052 
1053         writer.setInitialIndents(indent);
1054 
1055         final String superOidClassName
1056             = NameHelper.normalizeClassName(meta.getSuperKeyClass(classname));
1057 
1058         writer.writeClassHeader(
1059             (enclosedOid ? Modifier.PUBLIC | Modifier.STATIC : 0),
1060             oidClassName,
1061             superOidClassName,
1062             new String[]{ Serializable.class.getName() },
1063             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
1064 
1065         final boolean isPCRoot
1066             = meta.isPersistenceCapableRootClass(classname);
1067 
1068         final String[] pknames = meta.getKeyFields(classname);
1069         final String[] pktypes = meta.getFieldType(classname, pknames);
1070 
1071         // write the PK-fields
1072         for (int i = 0; i < pknames.length; i++) {
1073             writer.writeField(
1074                 pknames[i],
1075                 Modifier.PUBLIC,
1076                 pktypes[i],
1077                 null,
1078                 null);
1079         }
1080 
1081         // write default constructor
1082         writer.writeConstructor(
1083             NameHelper.getClassName(oidClassName),
1084             Modifier.PUBLIC,
1085             null, null, null,
1086             ImplHelper.getDefaultConstructorImpl(),
1087             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
1088 
1089         // write string argument constructor
1090         writer.writeConstructor(
1091             NameHelper.getClassName(oidClassName),
1092             Modifier.PUBLIC,
1093             new String[]{ "str" },
1094             new String[]{ "String" },
1095             null,
1096             ImplHelper.getOidStringArgConstructorImpl(superOidClassName,
1097                                                       "str"),
1098             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
1099 
1100         // hashCode
1101         writer.writeMethod(
1102             "hashCode",
1103             Modifier.PUBLIC,
1104             "int",
1105             null,
1106             null,
1107             null,
1108             ImplHelper.getOidHashCodeImpl(pknames,
1109                                           pktypes,
1110                                           isPCRoot),
1111             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
1112 
1113         // equals
1114         writer.writeMethod(
1115             "equals", Modifier.PUBLIC, "boolean",
1116             new String[]{ "pk" },
1117             new String[]{ Object.class.getName() },
1118             null,
1119             ImplHelper.getOidEqualsImpl(oidClassName,
1120                                         pknames,
1121                                         pktypes,
1122                                         "pk",
1123                                         isPCRoot),
1124             ImplHelper.COMMENT_NOT_ENHANCER_ADDED);
1125 
1126         writer.writeClassEnd();
1127         writer.setInitialIndents(0);
1128     }
1129 
1130     //^olsen to adapt
1131     static private long createJDOVersionUID(final String classname)
1132     {
1133         return classname.hashCode();
1134     }
1135 
1136     static private String createMethodName(final String prefix,
1137                                            final String fieldname)
1138     {
1139         return (prefix + Character.toUpperCase(fieldname.charAt(0))
1140                 + fieldname.substring(1));
1141     }
1142 
1143     private void printMessage(String msg)
1144     {
1145         out.println(msg);
1146     }
1147 
1148     private void printError(String    msg,
1149                                    Throwable ex)
1150     {
1151         if (msg != null) {
1152             err.println(msg + (ex != null ? ": " + ex.getMessage() : ""));
1153         }
1154         if (ex != null) {
1155             ex.printStackTrace(err);
1156         }
1157     }
1158 }