View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * This file was originally developed by Keith Visco during the
44   * course of employment at Intalio Inc.
45   * All portions of this file developed by Keith Visco after Jan 19 2005 are
46   * Copyright (C) 2005 Keith Visco. All Rights Reserved.
47   *
48   * $Id: $
49   */
50  package org.exolab.castor.builder;
51  
52  import java.io.File;
53  import java.io.FileInputStream;
54  import java.io.IOException;
55  import java.util.Enumeration;
56  import java.util.Properties;
57  
58  import org.castor.core.constants.cpa.JDOConstants;
59  import org.exolab.castor.builder.conflictresolution.ClassNameCRStrategy;
60  import org.exolab.castor.builder.conflictresolution.ClassNameCRStrategyRegistry;
61  import org.exolab.castor.builder.descriptors.DescriptorSourceFactory;
62  import org.exolab.castor.builder.descriptors.JDOClassDescriptorFactory;
63  import org.exolab.castor.builder.factory.MappingFileSourceFactory;
64  import org.exolab.castor.builder.info.ClassInfo;
65  import org.exolab.castor.builder.info.nature.JDOClassInfoNature;
66  import org.exolab.castor.builder.info.nature.XMLInfoNature;
67  import org.exolab.castor.builder.printing.JClassPrinter;
68  import org.exolab.castor.builder.printing.JClassPrinterFactoryRegistry;
69  import org.exolab.castor.mapping.xml.MappingRoot;
70  import org.exolab.castor.util.dialog.ConsoleDialog;
71  import org.exolab.javasource.JClass;
72  import org.exolab.javasource.JComment;
73  import org.exolab.javasource.JNaming;
74  
75  /**
76   * Writes a single class (and any associated inner classes) to a file.
77   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> - Main author.
78   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a> - Contributions.
79   * @author <a href="mailto:nsgreen@thazar.com">Nathan Green</a> - Contributions.
80   * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a> - Separated from SourceGenerator
81   * @version $Revision: 0000 $ $Date:  $
82   */
83  public final class SingleClassGenerator {
84      /**
85       * The default code header. Please leave "$" and "Id" separated with "+" so
86       * that the CVS server does not expand it here. */
87      private static final String DEFAULT_HEADER = "This class was automatically generated with \n"
88                                                   + "<a href=\"" + SourceGenerator.APP_URI + "\">"
89                                                   + SourceGenerator.APP_NAME + " "
90                                                   + SourceGenerator.VERSION
91                                                   + "</a>, using an XML Schema.\n$" + "Id" + "$";
92  
93      /** Name of the CDR (Class Descriptor Resolver) file. */
94      private static final String CDR_FILE = ".castor.cdr";
95      /** True if the user should be prompted to overwrite when a file already exists. */
96      private boolean _promptForOverwrite = true;
97      /** Destination directory where all our output goes. */
98      private String _destDir;
99      /**
100      * Destination directory for all resource files (e.g. .castor.cdr files).
101      */
102     private String _resourceDestinationDirectory;
103     /** The line separator to use for output. */
104     private String _lineSeparator = null;
105     /** A flag indicating whether or not to create descriptors for the generated classes. */
106     private boolean _createDescriptors = true;
107     
108     /** 
109      * A flag indicating whether or not to create JDO descriptors for 
110      * the generated classes.
111      */
112     private boolean _createJdoDescriptors = false;
113 
114     /** The header at the top of each generated file. */
115     private final JComment _header;
116     /** Console dialog used to prompt the user when something is wrong. */
117     private final ConsoleDialog _dialog;
118     
119     /** 
120      * The DescriptorSourceFactory instance.
121      */
122     private final DescriptorSourceFactory _descriptorSourceFactory;
123     
124     /** 
125      * The JDOClassDescriptorFactory instance. 
126      */
127     private JDOClassDescriptorFactory _jdoDescriptorSourceFactory;
128     
129     /** The MappingFileSourceFactory instance. */
130     private final MappingFileSourceFactory _mappingSourceFactory;
131     /** The SourceGenerator instance that created us. */
132     private final SourceGenerator _sourceGenerator;
133 
134     /**
135      * The class name conflict error handling strategy to use for
136      * resolving class name conflicts.
137      */
138     private ClassNameCRStrategy _conflictStrategy;
139     
140     /**
141      * The implementation of {@link JClassPrinter} to use for generating the 
142      * Java classes and writing them to the file system.
143      */
144     private JClassPrinter _jClassPrinter;
145 
146     /**
147      * The registry for {@link ClassNameCRStrategy} implementations.
148      */
149     private ClassNameCRStrategyRegistry _classNameConflictResolutionStrategyRegistry;
150 
151     
152     /**
153      * Creates an instance of this class.
154      * @param dialog A ConsoleDialog instance
155      * @param sourceGenerator A SourceGenerator instance
156      * @param conflictStrategyType Type of the {@link ClassNameCRStrategy} instance to be used.
157      * @param jClassPrinterType The string representation of the printer to be used,
158      */
159     public SingleClassGenerator(final ConsoleDialog dialog,
160             final SourceGenerator sourceGenerator,
161             final String conflictStrategyType,
162             final String jClassPrinterType) {
163         this._dialog = dialog;
164         this._sourceGenerator = sourceGenerator;
165         this._header = new JComment(JComment.HEADER_STYLE);
166         this._descriptorSourceFactory = new DescriptorSourceFactory(_sourceGenerator);
167         this._jdoDescriptorSourceFactory = new JDOClassDescriptorFactory(_sourceGenerator);
168         this._mappingSourceFactory = new MappingFileSourceFactory(_sourceGenerator);
169 
170         final String strategy = sourceGenerator.getProperty(
171                 BuilderConfiguration.Property.NAME_CONFLICT_STRATEGIES, "");
172         this._classNameConflictResolutionStrategyRegistry
173                 = new ClassNameCRStrategyRegistry(strategy);
174         createNameConflictStrategy(conflictStrategyType);
175         createJClassPrinter(jClassPrinterType);
176     }
177     
178     /**
179      * Creates a JClassPrinter instance from the given string key. 
180      * @param classPrinterType The string identifier if the printer,
181      */
182     private void createJClassPrinter(final String classPrinterType) {
183         JClassPrinterFactoryRegistry registry = _sourceGenerator.getJClassPrinterFactoryRegistry();
184         this._jClassPrinter = 
185             registry.getJClassPrinterFactory(classPrinterType).getJClassPrinter();        
186     }
187     
188     /**
189      * Sets the type of the {@link JClassPrinter} instance to be used for {@link JClass} writing. 
190      * @param jclassPrinterType The string identifier if the printer,
191      */
192     public void setJClassPrinterType(final String jclassPrinterType) {
193         this.createJClassPrinter(jclassPrinterType);
194     }
195     
196     /**
197      * Sets the destination directory.
198      *
199      * @param destDir the destination directory.
200      */
201     public void setDestDir(final String destDir) {
202        _destDir = destDir;
203        if (_resourceDestinationDirectory == null) {
204            _resourceDestinationDirectory = destDir;
205        }
206     }
207 
208     /**
209      * Sets the destination directory for generated resources.
210      *
211      * @param destDir the destination directory.
212      */
213     public void setResourceDestinationDirectory(final String destinationDirectory) {
214        _resourceDestinationDirectory = destinationDirectory;
215     }
216 
217     /**
218      * Sets the line separator to use when printing the source code.
219      *
220      * @param lineSeparator
221      *            the line separator to use when printing the source code. This
222      *            method is useful if you are generating source on one platform,
223      *            but will be compiling the source on a different platform.
224      *            <B>Note:</B>This can be any string, so be careful. I
225      *            recommend either using the default or using one of the
226      *            following:
227      * <PRE>
228      * windows systems use: "\r\n"
229      * unix systems use: "\n"
230      * mac systems use: "\r"
231      * </PRE>
232      */
233     public void setLineSeparator(final String lineSeparator) {
234         _lineSeparator = lineSeparator;
235     } //-- setLineSeparator
236 
237     /**
238      * Sets whether or not to create ClassDescriptors for the generated classes.
239      * By default, descriptors are generated.
240      *
241      * @param createDescriptors
242      *            a boolean, when true indicates to generated ClassDescriptors
243      */
244     public void setDescriptorCreation(final boolean createDescriptors) {
245         _createDescriptors = createDescriptors;
246     } //-- setDescriptorCreation
247     
248     
249     /**
250      * Sets whether or not to create JDOClassDescriptors for the generated classes.
251      * By default, descriptors are generated.
252      * 
253      * @param createJdoDescriptors if true, JDOClassDescriptors are generated.
254      */
255     public void setJdoDescriptorCreation(final boolean createJdoDescriptors) {
256         _createJdoDescriptors = createJdoDescriptors;
257     }
258     
259     /**
260      * Sets whether or not to prompt when we would otherwise overwrite an
261      * existing JClass.  If set to false, then it is always OK to overwrite
262      * an existing class.  If set to true, the user will be prompted.
263      *
264      * @param promptForOverwrite the new value
265      */
266     public void setPromptForOverwrite(final boolean promptForOverwrite) {
267         this._promptForOverwrite = promptForOverwrite;
268     } //-- setPromptForOverwrite
269 
270     /**
271      * Processes the JClass mapped by the provided key unless the JClass has
272      * already been processed.
273      *
274      * @param state SourceGenerator state
275      * @param classKeys Enumeration over a collection of keys to ClassInfos
276      *
277      * @return true if processing is allowed to continue, false if the
278      *         SourceGenerator state is STOP_STATUS,
279      * @throws IOException If an already existing '.castor.cdr' file can not be
280      *         loaded or found
281      */
282     boolean processIfNotAlreadyProcessed(final Enumeration<?> classKeys,
283                                          final SGStateInfo state) throws IOException {
284         while (classKeys.hasMoreElements()) {
285             ClassInfo classInfo = state.resolve(classKeys.nextElement());
286             JClass jClass = classInfo.getJClass();
287             if (!state.processed(jClass) 
288                     && (inCurrentSchema(state, classInfo) 
289                             || _sourceGenerator.getGenerateImportedSchemas())) {
290                 process(jClass, state);
291                 if (state.getStatusCode() == SGStateInfo.STOP_STATUS) {
292                     return false;
293                 }
294             }
295         }
296         return true;
297     }
298 
299     /**
300      * Indicates whether {@link ClassInfo} instance is defined within target namespace.
301      * @param state The Sourcegenerator state.
302      * @param classInfo The {@link ClassInfo} instance to be analyzed.
303      * @return True if it's within the targetNamespace
304      */
305     private boolean inCurrentSchema(final SGStateInfo state, final ClassInfo classInfo) {
306         final String targetNamespace = state.getSchema().getTargetNamespace();
307         boolean inCurrentSchema = true;
308         if (targetNamespace != null) {
309             if (classInfo.hasNature(XMLInfoNature.class.getName())) {
310                 XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
311                 inCurrentSchema = targetNamespace.equals(xmlNature.getNamespaceURI());
312             }
313         }
314         return inCurrentSchema;
315     }
316 
317     /**
318      * Processes the given JClasses, one by one, stopping if the SourceGenerator
319      * state indicates STOP after processing one class.
320      *
321      * @param classes Array of classes to process
322      * @param state SourceGenerator state
323      * @return true if processing is allowed to continue, false if the
324      *         SourceGenerator state is STOP_STATUS,
325      * @throws IOException If an already existing '.castor.cdr' file can not be
326      *         loaded or found
327      */
328     boolean process(final JClass[] classes, final SGStateInfo state) throws IOException {
329         for (int i = 0; i < classes.length; i++) {
330             process(classes[i], state);
331             if (state.getStatusCode() == SGStateInfo.STOP_STATUS) {
332                 return false;
333             }
334         }
335         return true;
336     }
337 
338     /**
339      * Processes the given JClass by checking for class name conflicts, and if
340      * there are none, making the class as processed and then printing the class
341      * and, if appropriate, its class descriptors.
342      * <p>
343      * If there is a class name conflict, at best the user stops the source
344      * generation and at worst the user continues, skipping this class.
345      *
346      * @param jClass the class to process
347      * @param state SourceGenerator state
348      * @return true if processing is allowed to continue, false if the
349      *         SourceGenerator state is STOP_STATUS,
350      * @throws IOException If an already existing '.castor.cdr' file can not be
351      *         loaded or found
352      */
353     boolean process(final JClass jClass, final SGStateInfo state) throws IOException {
354         if (state.getStatusCode() == SGStateInfo.STOP_STATUS) {
355             return false;
356         }
357 
358         if (state.processed(jClass)) {
359             return true;
360         }
361 
362         //--Make sure this class's name doesn't conflict with a java.lang.* class
363         checkNameNotReserved(jClass.getName(), state);
364 
365         ClassInfo classInfo = state.resolve(jClass);
366 
367         //-- Have we already processed a class with this name?
368         JClass conflict = state.getProcessed(jClass.getName());
369         if (conflict != null && !state.getSuppressNonFatalWarnings()) {
370             SGStateInfo stateAfterResolution =
371                 _conflictStrategy.dealWithClassNameConflict(state, classInfo, conflict);
372             return stateAfterResolution.getStatusCode() != SGStateInfo.STOP_STATUS;
373         }
374 
375         //-- Mark the current class as processed
376         state.markAsProcessed(jClass);
377 
378         //-- Print the class
379         if (checkAllowPrinting(jClass)) {
380             //hack for the moment
381             //to avoid the compiler complaining with java.util.Date
382             jClass.removeImport("org.exolab.castor.types.Date");
383             jClass.setHeader(_header);
384             if (_lineSeparator == null) {
385                 _lineSeparator = System.getProperty("line.separator");
386             }
387             _jClassPrinter.printClass(jClass, _destDir, _lineSeparator, DEFAULT_HEADER);
388         }
389 
390         //-- Process and print the class descriptors
391         if (classInfo != null) {
392             processClassDescriptor(jClass, state, classInfo);
393             if (classInfo.hasNature(JDOClassInfoNature.class.getName())) {
394                 processJDOClassDescriptor(jClass, state, classInfo);
395             }
396         }
397 
398         return state.getStatusCode() != SGStateInfo.STOP_STATUS;
399     } //-- processJClass
400 
401     /**
402      * Processes the Class Descriptor for the provided JClass.
403      *
404      * @param jClass the classInfo to process
405      * @param state SourceGenerator state
406      * @param classInfo the XML Schema element declaration
407      * @throws IOException If an already existing '.castor.cdr' file can not be
408      *         loaded or found
409      */
410     private void processClassDescriptor(final JClass jClass, final SGStateInfo state,
411                                         final ClassInfo classInfo) throws IOException {
412         if (_createDescriptors) {
413             JClass desc = _descriptorSourceFactory.createSource(classInfo);
414             if (checkAllowPrinting(desc)) {
415                 updateCDRFile(jClass, desc, state, CDR_FILE);
416                 desc.setHeader(_header);
417                 if (_lineSeparator == null) {
418                     _lineSeparator = System.getProperty("line.separator");
419                 }
420                 _jClassPrinter.printClass(desc, _destDir, _lineSeparator, DEFAULT_HEADER);
421             }
422         } else {
423             // TODO cleanup mapping file integration (what does this TODO mean?)
424             // create a class mapping
425             String pkg = state.getPackageName();
426             if (pkg == null) {
427                 pkg = "";
428             }
429             MappingRoot mapping = state.getMapping(pkg);
430             if (mapping == null) {
431                 mapping = new MappingRoot();
432                 state.setMapping(pkg, mapping);
433             }
434             mapping.addClassMapping(_mappingSourceFactory.createMapping(classInfo));
435         }
436     }
437     
438     /**
439      * Process/generate JDOClassDescriptors for the given {@link ClassInfo}.
440      * 
441      * @param jClass
442      *            a structure to represent Java Source Files. See {@link JClass}
443      *            for details.
444      * @param state
445      *            the state of the SourceGenerator.
446      * @param classInfo
447      *            the object holding all necessary information to generate the
448      *            source code for the JDOClassDescriptor.
449      * @throws IOException
450      *             If an already existing '.castor.cdr' file can not be loaded
451      *             or found
452      */
453     private void processJDOClassDescriptor(final JClass jClass, final SGStateInfo state,
454             final ClassInfo classInfo) throws IOException {
455         
456         if (_createJdoDescriptors) {
457             JClass desc = _jdoDescriptorSourceFactory.createSource(classInfo);
458             if (checkAllowPrinting(desc)) {
459                 updateCDRFile(jClass, desc, state, JDOConstants.PKG_CDR_LIST_FILE);
460                 desc.setHeader(_header);
461                 if (_lineSeparator == null) {
462                     _lineSeparator = System.getProperty("line.separator");
463                 }
464                 _jClassPrinter.printClass(desc, _destDir, _lineSeparator, DEFAULT_HEADER);
465             }
466         }  
467     }
468 
469     /**
470      * Checks to see if we will write the provided JClass to disk. If we have
471      * been configured to not prompt for overwrite, then it is assumed and
472      * overwriting an existing file is always OK. If the file does not exist, it
473      * is always OK to write it. Only if we are configured to prompt for
474      * overwrite and the file already exists do we need to issue a dialog and
475      * get the user's permission.
476      *
477      * @param jClass a JClass to check to see if we can write
478      * @return true if we can write out the provided jClass
479      */
480     private boolean checkAllowPrinting(final JClass jClass) {
481         if (!_promptForOverwrite) {
482             return true;
483         }
484 
485         // Check whether there exists already a file with the same name;
486         // if not, it is OK to write (aka create) the (new) file
487         String filename = jClass.getFilename(_destDir);
488         File file = new File(filename);
489 
490         if (!file.exists()) {
491             return true;
492         }
493 
494         return _conflictStrategy.dealWithFileOverwrite(filename);
495     }
496 
497     /**
498      * Checks the given name against various naming conflicts.  If a conflict is
499      * found, then this method generates an appropriate error message and throws
500      * an IllegalArgumentException.
501      * @param elementName element name to check against lists of reserved names
502      * @param sInfo source generator state
503      */
504     private void checkNameNotReserved(final String elementName, final SGStateInfo sInfo) {
505         if (elementName == null) {
506             return;
507         }
508 
509         String nameToCompare = elementName.substring(0, 1).toUpperCase() + elementName.substring(1);
510         if (JNaming.isInJavaLang(nameToCompare)) {
511             String err = "'" + nameToCompare
512                       + "' conflicts with a class in java.lang.* and may cause a conflict during\n"
513                       + " compilation. If you get this complaint during compilation, you need to\n"
514                       + " use a mapping file or change the name of the schema element.";
515             sInfo.getDialog().notify(err);
516         }
517 
518         if (JNaming.isReservedByCastor(nameToCompare)) {
519             String warn = "'" + nameToCompare + "' might conflict with a field name used"
520                     + " by Castor.  If you get a complaint\nabout a duplicate name, you will"
521                     + " need to use a mapping file or change\nthe name of the conflicting"
522                     + " schema element.";
523             sInfo.getDialog().notify(warn);
524         }
525 
526         final String withoutPackage = nameToCompare.substring(nameToCompare.lastIndexOf('.') + 1); 
527         if (JNaming.isReservedByWindows(nameToCompare) 
528                 || JNaming.isReservedByWindows(withoutPackage)) {
529             // FIXME  We should fail under Windows and warn under other OSes
530             String warn = "'" + nameToCompare + "' is reserved by the Windows filesystem and"
531                     + " cannot be\nused as a class name.  Windows will not allow you to create"
532                     + " a file with this\nname.  You will have to use a binding file or change"
533                     + " the name of the conflicting\nschema element.  For more information,"
534                     + " see\nhttp://msdn.microsoft.com/library/default.asp?"
535                     + "url=/library/en-us/fileio/fs/naming_a_file.asp";
536             sInfo.getDialog().notify(warn);
537         }
538     }
539 
540     /**
541      * Updates the CDR (ClassDescriptorResolver) file with the
542      * classname->descriptor mapping.
543      *
544      * @param jClass JClass instance describing the entity class
545      * @param jDesc JClass instance describing is *Descriptor class
546      * @param sInfo state info
547      * @param cdrFileName the filename of the class descriptor resolver (cdr) file
548      * @throws IOException If an already existing '.castor.cdr' file can not be
549      *         found or loaded
550      */
551     private void updateCDRFile(final JClass jClass, final JClass jDesc,
552             final SGStateInfo sInfo, final String cdrFileName) throws IOException {
553         String entityFilename = jClass.getFilename(_resourceDestinationDirectory);
554         File file = new File(entityFilename);
555         File parentDirectory = file.getParentFile();
556         File cdrFile = new File(parentDirectory, cdrFileName);
557         String cdrFilename = cdrFile.getAbsolutePath();
558 
559         Properties props = sInfo.getCDRFile(cdrFilename);
560 
561         if (props == null) {
562             // check for existing .castor.xml file
563             props = new Properties();
564             if (cdrFile.exists()) {
565                 FileInputStream fileStream = new FileInputStream(cdrFile);
566                 props.load(fileStream);
567                 fileStream.close();
568             }
569             sInfo.setCDRFile(cdrFilename, props);
570         }
571         props.setProperty(jClass.getName(), jDesc.getName());
572     } //-- updateCDRFile
573 
574     /**
575      * Sets the desired {@link ClassNameCRStrategy} instance type to be used
576      * for name conflict resolution.
577      * @param nameConflictStrategy the desired {@link ClassNameCRStrategy} instance type
578      */
579     public void setNameConflictStrategy(final String nameConflictStrategy) {
580         createNameConflictStrategy(nameConflictStrategy);
581     }
582 
583     /**
584      * Creates a new {@link ClassNameCRStrategy} instance by calling the
585      * {@link ClassNameConflictResolutionStrategyFactory}.
586      * @param nameConflictStrategy The desired {@link ClassNameCRStrategy} type.
587      */
588     private void createNameConflictStrategy(final String nameConflictStrategy) {
589         this._conflictStrategy = _classNameConflictResolutionStrategyRegistry
590                 .getClassNameConflictResolutionStrategy(nameConflictStrategy);
591         this._conflictStrategy.setConsoleDialog(_dialog);
592         this._conflictStrategy.setSingleClassGenerator(this);
593     }
594 
595     /**
596      * Returns the {@link SourceGenerator} instance that created this class.
597      * @return the {@link SourceGenerator} instance that created this class.
598      */
599     public SourceGenerator getSourceGenerator() {
600         return _sourceGenerator;
601     }
602 
603 }