Version 27.1 by Quinton Dolan on 2007/07/09 06:52

Hide last authors
Quinton Dolan 24.1 1 |=Contents
2 |
Quinton Dolan 14.1 3
Quinton Dolan 24.1 4 {{toc style="disc" minLevel="2"}}{{/toc}}
Quinton Dolan 14.1 5
Quinton Dolan 24.1 6 == Overview ==
David Avendasora 10.1 7
Quinton Dolan 24.1 8 If you've ever used EOModeler's Java source code generator, you know how much of a pain it can be when you make changes to your model objects and have to merge changes in later. One solution for this is to use [[EOGenerator>>http://www.rubicode.com/Software/EOGenerator/]], an application developed by Rubicode Software, which uses the Generation Gap pattern to create your Java files from your EOModels. EOGenerator produces TWO java files for each Entity rather than one. Take the example of a Person entity. The first java file is //Person.java, which contains all of the autogenerated methods. The second java file is Person.java, and Person extends //Person. The second file is where you place all of your customizations. Any time your model changes, only your //Xxx.java files are updated, and your customizations are left untouched. Additionally, EOGenerator allows for the creation of extensive custom templates for your files, which provides the ability to place convenience methods in your //Xxx.java files.
David LeBer 18.1 9
Quinton Dolan 24.1 10 == Advantages ==
11
David Avendasora 10.1 12 There are several advantages to using EOGenerator over EOModeler's default Java file generation and merging with FileMerge.
13
14 * EOGenerator uses the Generation Gap pattern, which provides a much cleaner separation of autogenerated vs customized code with no need to deal with merging at all. There are border cases with FileMerge that can cause you to deal with annoying conflicts.
15 * EOGenerator uses the MiscMerge language for its templates. This allows you to extend the core templates with extensive customizations (see the EOGenerator Mods section below), better supporting your own custom development process and workflow.
16 * As David LaBer put it, "all the cool kids use it - and we all know looking cool is the **most** important criteria".
17
Quinton Dolan 24.1 18 == How To Use It ==
David Avendasora 10.1 19
20 Kieran Kelleher has writtten an [[Introduction to EOGenerator>>http://homepage.mac.com/kelleherk/iblog/C1837526061/E1908382110/index.html]] on his blog.
21
22 It's actually very simple to use. The quick start is:
23
24 * Download and untar EOGenerator from the Rubicode site
25 * Run the following command:
26
27 {{panel}}
28
Quinton Dolan 24.1 29 eogenerator -model /path/to/model/YourModel.eomodeld -destination /path/to/source/folder
30 -subclassDestination /path/to/source/folder -templatedir /path/to/EOGenerator/templates -java -packagedirs
David Avendasora 10.1 31
32 {{/panel}}
33
34 Voila. EOGenerator will spit out your Java files for you. Let's break down the commands you can pass in:
35
Quinton Dolan 24.1 36 * --define--EOGenericRecord <class>, allows you to specify the //Person class's superclass. For instance, if you use Project Wonder, you would specify --define--EOGenericRecord er.extensions.ERXGenericRecord//
37 * --destination <path>, the folder that //Person.java//--//style java files will be produced in (the non-editable files)//
38 * --java, produce java files--
39 * --javaTemplate <filename>, the name of the Java template to use inside of the template dir (//Person)//--
40 * --model <path>, Passes in the path of a .eomodeld you would like to generate Java files for. You can actually include multiple --model commands on the commandline
41 * --packagedirs, produce package directory for any package statements defined in your Java files (not necessary if you don't specify package names on your entities. By the way, you should specify packages on your entities :) )--
42 * --refmodel <path>, Passes in the path of an .eomodeld that is required for generating Java files, but that won't actually have Java files generated for it. For instance, you should --refmodel any prototypes, or any models in other frameworks that you depend on
43 * --subclassDestination <path>, the folder that Person.java--style java files will be produced in (the editable files)
44 * --subclassJavaTemplate <filename>, the name of the Java subclass template to use inside of the template dir (Person)--
45 * --templatedir <path>, the path to the folder that contains EOGenerator templates--
46 * --verbose, turn on verbose output--
David Avendasora 10.1 47
Quinton Dolan 24.1 48 == Custom EOGenerator Mods ==
David Avendasora 10.1 49
Quinton Dolan 24.1 50 === Zak Burke ===
David Avendasora 10.1 51
52 Allow setting nulls on a to-one relationship (and turn it into a remove). Note, this is also included in Jonathan Rentzsch's templates.
53
Quinton Dolan 14.1 54 {{code}}
David Avendasora 10.1 55
Quinton Dolan 24.1 56 public void save<$ToOneRelationship.name.initialCapitalString$>(<$ToOneRelationship.destinationEntity.referenceJavaClassName$> value)
David Avendasora 10.1 57 {
58 if (value == null)
59 {
60 <$ToOneRelationship.destinationEntity.referenceJavaClassName$> object = <$ToOneRelationship.name$>();
61 if (object != null)
62 removeObjectFromBothSidesOfRelationshipWithKey(object, "<$ToOneRelationship.name$>");
63 }
64 else
65 {
66 addObjectToBothSidesOfRelationshipWithKey(value, "<$ToOneRelationship.name$>");
67 }
68 }
69
Quinton Dolan 14.1 70 {{/code}}
David Avendasora 10.1 71
Quinton Dolan 24.1 72 === Chuck Hill ===
David Avendasora 10.1 73
74 Return the list of changes between the current EO and the last committed version of the EO:
75
Quinton Dolan 14.1 76 {{code}}
David Avendasora 10.1 77
Quinton Dolan 24.1 78 public NSDictionary changedProperties() {
David Avendasora 10.1 79 NSDictionary commitedValues = editingContext().committedSnapshotForObject(this);
80 return changesFromSnapshot(commitedValues);
81 }
82
Quinton Dolan 14.1 83 {{/code}}
David Avendasora 10.1 84
Quinton Dolan 24.1 85 === Jonathan Rentzsch ===
David Avendasora 10.1 86
87 Jonathan Rentzsch has provided his base EOGenerator templates, which are a must-have:
88
Quinton Dolan 24.1 89 http:~/~/rentzsch.com/share/eogenerator52templates.zip
David Avendasora 10.1 90
Quinton Dolan 24.1 91 === Markus Ruggiero ===
David Avendasora 10.1 92
93 Constants for all attributes and relationships. This allows compile time error checking in situations like
94 addObjecttoBothSidesOfRelationshipWithKey(myObject, Person.TO//MANY//Children)
95
Quinton Dolan 14.1 96 {{code}}
David Avendasora 10.1 97
Quinton Dolan 24.1 98 <$foreach attribute classAttributes.@reversedArray do$>
David Avendasora 10.1 99 public static final String ATTRIBUTE_<$attribute.name$> = "<$attribute.name$>";<$endforeach do$>
100
101 <$foreach ToOneRelationship classToOneRelationships.@reversedArray do$>
102 public static final String TO_ONE_<$ToOneRelationship.name$> = "<$ToOneRelationship.name$>";<$endforeach do$>
103
104 <$foreach ToManyRelationship classToManyRelationships.@reversedArray do$>
105 public static final String TO_MANY_<$ToManyRelationship.name$> = "<$ToManyRelationship.name$>";<$endforeach do$>
106
Quinton Dolan 14.1 107 {{/code}}
David Avendasora 10.1 108
109 We also make heavy use of the user info dictionary on entity and attribute level. Allows to generate customized methods and what not. One example is booleans that are stored in the DB as strings with values "true" and "false".
110
Quinton Dolan 14.1 111 {{code}}
David Avendasora 10.1 112
Quinton Dolan 24.1 113 <$if attribute.userInfo.usage h1. booleanFlag $> // boolean accessors
David Avendasora 10.1 114 public void <$attribute.userInfo.setterName$>(boolean newBoolean) {
115 set<$attribute.name.initialCapitalString$>(newBoolean ? "true" : "false");
116 }
Quinton Dolan 24.1 117
David Avendasora 10.1 118 public boolean <$attribute.userInfo.getterName$>() {
119 return "true".equals(<$attribute.name$>()) ? true : false;
120 }
Quinton Dolan 24.1 121
David Avendasora 10.1 122 // validation
123 public String validate<$attribute.name.initialCapitalString$>(String newValue) {
124 if ( newValue null ) {
125 return "false";
126 } else if ( !newValue.equals("true") && !newValue.equals("false") ) {
127 String errorMessage = MessageHandler.format("INVALID_BOOLEAN_FLAG <$classNameWithoutPackage$>.<$attribute.name$>", null);
128 throw new NSValidation.ValidationException(errorMessage);
129 }
130 return newValue;
131 }
132 <$endif$>
133
Quinton Dolan 14.1 134 {{/code}}
David Avendasora 10.1 135
Quinton Dolan 24.1 136 === Mike Schrag ===
David Avendasora 10.1 137
138 Add a constant that represents the name of the entity so that you can refer to Person.ENTITY//NAME in fetches rather than the String (allows refactoring support in Eclipse)~://
139
Quinton Dolan 14.1 140 {{code}}
David Avendasora 10.1 141
Quinton Dolan 24.1 142 public static final String ENTITY_NAME = "<$name$>";
David Avendasora 10.1 143
Quinton Dolan 14.1 144 {{/code}}
David Avendasora 10.1 145
146 Add a static factory method to your EO's ( Person createPerson(...) ) that shows you what required attributes and relationships are configured for you entity (attempts to provide a replacement "constructor" since EO constructors are empty):
147
Quinton Dolan 14.1 148 {{code}}
David Avendasora 10.1 149
Quinton Dolan 26.1 150 public static <$classNameWithoutPackage$> create<$classNameWithoutPackage$>(EOEditingContext _editingContext<
151 $foreach Attribute classAttributes.@sortedNameArray do$><
152 $if !Attribute.allowsNull$>, <$Attribute.javaValueClassName$> _<$Attribute.name$><$endif$><
153 $endforeach do$><
154 $foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><
155 $if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> _<$ToOneRelationship.name$><$endif$><
156 $endforeach do$>) {
157 <$classNameWithoutPackage$> eoObject = (<$classNameWithoutPackage$>)EOUtilities.createAndInsertInstance(_editingContext,
158 <$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME);<
159 $foreach Attribute classAttributes.@sortedNameArray do$><
160 $if !Attribute.allowsNull$>
161 eoObject.set<$Attribute.name.initialCapitalString$>(_<$Attribute.name$>);<
162 $endif$><
163 $endforeach do$><
164 $foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><
165 $if ToOneRelationship.isMandatory$>
166 eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(_<$ToOneRelationship.name$>);<
167 $endif$><
168 $endforeach do$>
169 return eoObject;
170 }
David Avendasora 10.1 171
Quinton Dolan 14.1 172 {{/code}}
David Avendasora 10.1 173
174 Here's a little bitty fancier (read: nastier) version that also handles superclass mandatory attributes and fields (one level). It skips any attribute that is referenced in the restricting qualifier of your subclass (since you are probably going to set that in your awakeFromInsertion):
175
Quinton Dolan 14.1 176 {{code}}
David Avendasora 10.1 177
Quinton Dolan 26.1 178 public static <$classNameWithoutPackage$> create<$classNameWithoutPackage$>(EOEditingContext editingContext<
179 $foreach Attribute classAttributes.@sortedNameArray do$><
180 $if !Attribute.allowsNull$>, <$Attribute.javaValueClassName$> <$Attribute.name$><$endif$><
181 $endforeach do$><
182 $foreach Attribute parentEntity.classAttributes.@sortedNameArray do$><
183 $if !Attribute.allowsNull$><$set RestrictingQualifierKey = false$><
184 $foreach QualifierKey restrictingQualifier.allQualifierKeys do$><
185 $if Attribute.name = QualifierKey$><$set RestrictingQualifierKey = true$><$endif$><
186 $endforeach do$><$if RestrictingQualifierKey = false$>, <$Attribute.javaValueClassName$> <$Attribute.name$><$endif$><
187 $endif$><
188 $endforeach do$><
189 $foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><
190 $if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> <$ToOneRelationship.name$><$endif$><
191 $endforeach do$><
192 $foreach ToOneRelationship parentEntity.classToOneRelationships.@sortedNameArray do$><
193 $if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> <$ToOneRelationship.name$><$endif$><
194 $endforeach do$>) {
195 <$classNameWithoutPackage$> eoObject = (<$classNameWithoutPackage$>)EOUtilities.createAndInsertInstance(editingContext, <$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME);<
196 $foreach Attribute classAttributes.@sortedNameArray do$><
197 $if !Attribute.allowsNull$>
198 eoObject.set<$Attribute.name.initialCapitalString$>(<$Attribute.name$>);<
199 $endif$><
200 $endforeach do$><
201 $foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><
202 $if ToOneRelationship.isMandatory$>
203 eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(<$ToOneRelationship.name$>);<
204 $endif$><
205 $endforeach do$><
206 $foreach Attribute parentEntity.classAttributes.@sortedNameArray do$><
207 $if !Attribute.allowsNull$><$set RestrictingQualifierKey = false$><
208 $foreach QualifierKey restrictingQualifier.allQualifierKeys do$><
209 $if Attribute.name = QualifierKey$><$set RestrictingQualifierKey = true$><$endif$><
210 $endforeach do$><
211 $if RestrictingQualifierKey = false$>
212 eoObject.set<$Attribute.name.initialCapitalString$>(<$Attribute.name$>);<
213 $endif$><
214 $endif$><
215 $endforeach do$><
216 $foreach ToOneRelationship parentEntity.classToOneRelationships.@sortedNameArray do$><
217 $if ToOneRelationship.isMandatory$>
218 eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(<$ToOneRelationship.name$>);<
219 $endif$><
220 $endforeach do$>
Quinton Dolan 14.1 221 return eoObject;
222 }
David Avendasora 10.1 223
Quinton Dolan 14.1 224 {{/code}}
David Avendasora 10.1 225
226 Add a bunch of convience fetch methods (fetchAllPersons, fetchRequiredPerson, and other variants). It's not smart about pluralization, so it's just going to put an "s" on the end of the entity name:
227
Quinton Dolan 14.1 228 {{code}}
David Avendasora 10.1 229
Quinton Dolan 24.1 230 public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext) {
David Avendasora 10.1 231 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchAll<$classNameWithoutPackage$>s(_editingContext, null);
232 }
233
Quinton Dolan 24.1 234 public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext, NSArray _sortOrderings) {
David Avendasora 10.1 235 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, null, _sortOrderings);
236 }
237
Quinton Dolan 24.1 238 public static NSArray fetch<$classNameWithoutPackage$>s(EOEditingContext _editingContext, EOQualifier _qualifier, NSArray _sortOrderings) {
David Avendasora 10.1 239 EOFetchSpecification fetchSpec = new EOFetchSpecification(<$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME, _qualifier, _sortOrderings);
240 fetchSpec.setIsDeep(true);
241 NSArray eoObjects = _editingContext.objectsWithFetchSpecification(fetchSpec);
242 return eoObjects;
243 }
244
Quinton Dolan 24.1 245 public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) {
David Avendasora 10.1 246 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value));
247 }
248
Quinton Dolan 24.1 249 public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext _editingContext, EOQualifier _qualifier) {
David Avendasora 10.1 250 NSArray eoObjects = <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, _qualifier, null);
251 <$classNameWithoutPackage$> eoObject;
252 int count = eoObjects.count();
253 if (count == 0) {
254 eoObject = null;
255 }
256 else if (count == 1) {
257 eoObject = (<$classNameWithoutPackage$>)eoObjects.objectAtIndex(0);
258 }
259 else {
260 throw new IllegalStateException("There was more than one <$classNameWithoutPackage$> that matched the qualifier '" + _qualifier + "'.");
261 }
262 return eoObject;
263 }
Quinton Dolan 24.1 264
265 public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) {
David Avendasora 10.1 266 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchRequired<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value));
267 }
268
Quinton Dolan 24.1 269 public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext _editingContext, EOQualifier _qualifier) {
David Avendasora 10.1 270 <$classNameWithoutPackage$> eoObject = <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, _qualifier);
Quinton Dolan 24.1 271 if (eoObject h1. null) {
David Avendasora 10.1 272 throw new NoSuchElementException("There was no <$classNameWithoutPackage$> that matched the qualifier '" + _qualifier + "'.");
273 }
274 return eoObject;
275 }
276
Quinton Dolan 14.1 277 {{/code}}
David Avendasora 10.1 278
279 Add methods for getting local instances of EO's. The static one is handy if you have a reference to an EO that might be null (it does a null check first):
280
Quinton Dolan 14.1 281 {{code}}
David Avendasora 10.1 282
Quinton Dolan 24.1 283 public <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext) {
David Avendasora 10.1 284 return (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, this);
285 }
286
Quinton Dolan 24.1 287 public static <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext, <$classNameWithoutPackage$> _eo) {
288 return (_eo null) ? null : (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, _eo);
David Avendasora 10.1 289 }
290
Quinton Dolan 14.1 291 {{/code}}
David Avendasora 10.1 292
293 If you've ever wanted to be able to qualify a toMany relationship on your EO's, but sometimes you want to fetch them w/ a fetch spec, and sometimes you want to filter in-memory, you can use the following:
294
Quinton Dolan 14.1 295 {{code}}
David Avendasora 10.1 296
Quinton Dolan 24.1 297 <$if !ToManyRelationship.inverseRelationship$>
David Avendasora 10.1 298 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier) {
299 return <$ToManyRelationship.name$>(qualifier, null);
300 }
301 <$endif$>
302 <$if ToManyRelationship.inverseRelationship$>
303 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier) {
304 return <$ToManyRelationship.name$>(qualifier, null, false);
305 }
Quinton Dolan 24.1 306
307 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier, boolean fetch) {
David Avendasora 10.1 308 return <$ToManyRelationship.name$>(qualifier, null, fetch);
309 }
310 <$endif$>
Quinton Dolan 24.1 311
David Avendasora 10.1 312 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier, NSArray sortOrderings<$if ToManyRelationship.inverseRelationship$>, boolean fetch<$endif$>) {
313 NSArray results;
314 <$if ToManyRelationship.inverseRelationship$>
315 if (fetch) {
316 EOQualifier fullQualifier;
Quinton Dolan 14.1 317 EOQualifier inverseQualifier = new EOKeyValueQualifier(<$ToManyRelationship.destination.className$>.
318 <$ToManyRelationship.inverseRelationship.name.uppercaseUnderbarString$>_KEY, EOQualifier.QualifierOperatorEqual, this);
David Avendasora 10.1 319 if (qualifier == null) {
320 fullQualifier = inverseQualifier;
321 }
322 else {
323 NSMutableArray qualifiers = new NSMutableArray();
324 qualifiers.addObject(qualifier);
325 qualifiers.addObject(inverseQualifier);
326 fullQualifier = new EOAndQualifier(qualifiers);
327 }
328 results = <$ToManyRelationship.destination.className$>.fetch<$ToManyRelationship.destination.name$>s(editingContext(), fullQualifier, sortOrderings);
329 }
330 else {
331 <$endif$>
332 results = <$ToManyRelationship.name$>();
333 if (qualifier != null) {
334 results = EOQualifier.filteredArrayWithQualifier(results, qualifier);
335 }
336 if (sortOrderings != null) {
337 results = EOSortOrdering.sortedArrayUsingKeyOrderArray(results, sortOrderings);
338 }
339 <$if ToManyRelationship.inverseRelationship$>
340 }
341 <$endif$>
342 return results;
343 }
344
Quinton Dolan 14.1 345 {{/code}}
David Avendasora 10.1 346
Quinton Dolan 24.1 347 === John Huss ===
David Avendasora 10.1 348
Quinton Dolan 24.1 349 I wanted to share a wonderful bit of knowledge I learned today. If you're using Java 1.5 you can add @SuppressWarnings("all") to the template for your //EO base classes and eliminate annoying compiler messages (usually uneeded import statements).//
David Avendasora 10.1 350
Quinton Dolan 14.1 351 {{code}}
David Avendasora 10.1 352
Quinton Dolan 24.1 353 @SuppressWarnings("all")
David Avendasora 10.1 354 public class _Invoice extends ERXGenericRecord {
355 }
356
Quinton Dolan 14.1 357 {{/code}}
David Avendasora 10.1 358
Quinton Dolan 24.1 359 === Guido Neitzer ===
David Avendasora 10.1 360
361 Create awakeFromInsertion() and awakeFromFetch() in your EOGenerator template as a method stub that only calls super() and has a comment for "initialize your object here ...". You only have to put in the code at that place and can't possibly forget to call super(). Here is an example:
362
Quinton Dolan 14.1 363 {{code}}
David Avendasora 10.1 364
Quinton Dolan 24.1 365 /**
Quinton Dolan 14.1 366 * Initialization of the instance while inserting it into an editing context
367 */
368 public void awakeFromInsertion (EOEditingContext editingContext) {
369 super.awakeFromInsertion (editingContext);
370 // initialize your object here
371 }
David Avendasora 10.1 372
Quinton Dolan 14.1 373 {{/code}}
David Avendasora 10.1 374
375 This is from my JavaSubclassSourceTemplate.eotemplate
Quinton Dolan 24.1 376
377 Category:WebObjects