Version 13.1 by smmccraw on 2007/07/08 09:44

Hide last authors
smmccraw 12.1 1 == Overview ==
David Avendasora 10.1 2
smmccraw 12.1 3 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 Avendasora 10.1 4
smmccraw 12.1 5 == Advantages ==
David Avendasora 10.1 6
7 There are several advantages to using EOGenerator over EOModeler's default Java file generation and merging with FileMerge.
8
9 * 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.
10 * 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.
11 * As David LaBer put it, "all the cool kids use it - and we all know looking cool is the **most** important criteria".
12
smmccraw 12.1 13 == How To Use It ==
David Avendasora 10.1 14
15 Kieran Kelleher has writtten an [[Introduction to EOGenerator>>http://homepage.mac.com/kelleherk/iblog/C1837526061/E1908382110/index.html]] on his blog.
16
17 It's actually very simple to use. The quick start is:
18
19 * Download and untar EOGenerator from the Rubicode site
20 * Run the following command:
21
22 {{panel}}
23
smmccraw 12.1 24 eogenerator -model /path/to/model/YourModel.eomodeld -destination /path/to/source/folder -subclassDestination /path/to/source/folder -templatedir /path/to/EOGenerator/templates -java -packagedirs
David Avendasora 10.1 25
26 {{/panel}}
27
28 Voila. EOGenerator will spit out your Java files for you. Let's break down the commands you can pass in:
29
smmccraw 12.1 30 * --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//
31 * --destination <path>, the folder that //Person.java//--//style java files will be produced in (the non-editable files)//
32 * --java, produce java files--
33 * --javaTemplate <filename>, the name of the Java template to use inside of the template dir (//Person)//--
34 * --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
35 * --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 :) )--
36 * --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
37 * --subclassDestination <path>, the folder that Person.java--style java files will be produced in (the editable files)
38 * --subclassJavaTemplate <filename>, the name of the Java subclass template to use inside of the template dir (Person)--
39 * --templatedir <path>, the path to the folder that contains EOGenerator templates--
40 * --verbose, turn on verbose output--
David Avendasora 10.1 41
smmccraw 12.1 42 == Custom EOGenerator Mods ==
David Avendasora 10.1 43
smmccraw 12.1 44 === Zak Burke ===
David Avendasora 10.1 45
46 Allow setting nulls on a to-one relationship (and turn it into a remove). Note, this is also included in Jonathan Rentzsch's templates.
47
smmccraw 12.1 48 {{panel}}
David Avendasora 10.1 49
smmccraw 12.1 50 public void save<$ToOneRelationship.name.initialCapitalString$>(<$ToOneRelationship.destinationEntity.referenceJavaClassName$> value)
David Avendasora 10.1 51 {
52 if (value == null)
53 {
54 <$ToOneRelationship.destinationEntity.referenceJavaClassName$> object = <$ToOneRelationship.name$>();
55 if (object != null)
56 removeObjectFromBothSidesOfRelationshipWithKey(object, "<$ToOneRelationship.name$>");
57 }
58 else
59 {
60 addObjectToBothSidesOfRelationshipWithKey(value, "<$ToOneRelationship.name$>");
61 }
62 }
63
smmccraw 12.1 64 {{/panel}}
David Avendasora 10.1 65
smmccraw 12.1 66 === Chuck Hill ===
David Avendasora 10.1 67
68 Return the list of changes between the current EO and the last committed version of the EO:
69
smmccraw 12.1 70 {{panel}}
David Avendasora 10.1 71
smmccraw 12.1 72 public NSDictionary changedProperties() {
David Avendasora 10.1 73 NSDictionary commitedValues = editingContext().committedSnapshotForObject(this);
74 return changesFromSnapshot(commitedValues);
75 }
76
smmccraw 12.1 77 {{/panel}}
David Avendasora 10.1 78
smmccraw 12.1 79 === Jonathan Rentzsch ===
David Avendasora 10.1 80
81 Jonathan Rentzsch has provided his base EOGenerator templates, which are a must-have:
82
smmccraw 12.1 83 http:~/~/rentzsch.com/share/eogenerator52templates.zip
David Avendasora 10.1 84
smmccraw 12.1 85 === Markus Ruggiero ===
David Avendasora 10.1 86
87 Constants for all attributes and relationships. This allows compile time error checking in situations like
88 addObjecttoBothSidesOfRelationshipWithKey(myObject, Person.TO//MANY//Children)
89
smmccraw 12.1 90 {{panel}}
David Avendasora 10.1 91
smmccraw 12.1 92 <$foreach attribute classAttributes.@reversedArray do$>
David Avendasora 10.1 93 public static final String ATTRIBUTE_<$attribute.name$> = "<$attribute.name$>";<$endforeach do$>
94
smmccraw 12.1 95 {{/panel}}
96
David Avendasora 10.1 97 <$foreach ToOneRelationship classToOneRelationships.@reversedArray do$>
smmccraw 12.1 98
99 {{panel}}
100
David Avendasora 10.1 101 public static final String TO_ONE_<$ToOneRelationship.name$> = "<$ToOneRelationship.name$>";<$endforeach do$>
102
smmccraw 12.1 103 {{/panel}}
104
David Avendasora 10.1 105 <$foreach ToManyRelationship classToManyRelationships.@reversedArray do$>
smmccraw 12.1 106
107 {{panel}}
108
David Avendasora 10.1 109 public static final String TO_MANY_<$ToManyRelationship.name$> = "<$ToManyRelationship.name$>";<$endforeach do$>
110
smmccraw 12.1 111 {{/panel}}
David Avendasora 10.1 112
113 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".
114
smmccraw 12.1 115 {{panel}}
David Avendasora 10.1 116
smmccraw 12.1 117 <$if attribute.userInfo.usage h1. booleanFlag $> // boolean accessors
David Avendasora 10.1 118 public void <$attribute.userInfo.setterName$>(boolean newBoolean) {
119 set<$attribute.name.initialCapitalString$>(newBoolean ? "true" : "false");
120 }
smmccraw 12.1 121
David Avendasora 10.1 122 public boolean <$attribute.userInfo.getterName$>() {
123 return "true".equals(<$attribute.name$>()) ? true : false;
124 }
smmccraw 12.1 125
David Avendasora 10.1 126 // validation
127 public String validate<$attribute.name.initialCapitalString$>(String newValue) {
128 if ( newValue null ) {
129 return "false";
130 } else if ( !newValue.equals("true") && !newValue.equals("false") ) {
131 String errorMessage = MessageHandler.format("INVALID_BOOLEAN_FLAG <$classNameWithoutPackage$>.<$attribute.name$>", null);
132 throw new NSValidation.ValidationException(errorMessage);
133 }
134 return newValue;
135 }
136 <$endif$>
137
smmccraw 12.1 138 {{/panel}}
David Avendasora 10.1 139
smmccraw 12.1 140 === Mike Schrag ===
David Avendasora 10.1 141
142 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)~://
143
smmccraw 12.1 144 {{panel}}
David Avendasora 10.1 145
smmccraw 12.1 146 public static final String ENTITY_NAME = "<$name$>";
David Avendasora 10.1 147
smmccraw 12.1 148 {{/panel}}
David Avendasora 10.1 149
150 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):
151
smmccraw 12.1 152 {{panel}}
David Avendasora 10.1 153
smmccraw 12.1 154 public static <$classNameWithoutPackage$> create<$classNameWithoutPackage$>(EOEditingContext _editingContext<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$>, <$Attribute.javaValueClassName$> _<$Attribute.name$><$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> _<$ToOneRelationship.name$><$endif$><$endforeach do$>) {
155 <$classNameWithoutPackage$> eoObject = (<$classNameWithoutPackage$>)EOUtilities.createAndInsertInstance(_editingContext, <$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME);<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$>
156 eoObject.set<$Attribute.name.initialCapitalString$>(_<$Attribute.name$>);<$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>
157 eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(_<$ToOneRelationship.name$>);<$endif$><$endforeach do$>
158 return eoObject;
159 }
David Avendasora 10.1 160
smmccraw 12.1 161 {{/panel}}
David Avendasora 10.1 162
163 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):
164
smmccraw 12.1 165 {{panel}}
David Avendasora 10.1 166
smmccraw 12.1 167 public static <$classNameWithoutPackage$> create<$classNameWithoutPackage$>(EOEditingContext editingContext<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$>, <$Attribute.javaValueClassName$> <$Attribute.name$><$endif$><$endforeach do$><$foreach Attribute parentEntity.classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$><$set RestrictingQualifierKey = false$><$foreach QualifierKey restrictingQualifier.allQualifierKeys do$><$if Attribute.name = QualifierKey$><$set RestrictingQualifierKey = true$><$endif$><$endforeach do$><$if RestrictingQualifierKey = false$>, <$Attribute.javaValueClassName$> <$Attribute.name$><$endif$><$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> <$ToOneRelationship.name$><$endif$><$endforeach do$><$foreach ToOneRelationship parentEntity.classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> <$ToOneRelationship.name$><$endif$><$endforeach do$>) {
168 <$classNameWithoutPackage$> eoObject = (<$classNameWithoutPackage$>)EOUtilities.createAndInsertInstance(editingContext, <$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME);<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$>
169 eoObject.set<$Attribute.name.initialCapitalString$>(<$Attribute.name$>);<$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>
170 eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(<$ToOneRelationship.name$>);<$endif$><$endforeach do$><$foreach Attribute parentEntity.classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$><$set RestrictingQualifierKey = false$><$foreach QualifierKey restrictingQualifier.allQualifierKeys do$><$if Attribute.name = QualifierKey$><$set RestrictingQualifierKey = true$><$endif$><$endforeach do$><$if RestrictingQualifierKey = false$>
171 eoObject.set<$Attribute.name.initialCapitalString$>(<$Attribute.name$>);<$endif$><$endif$><$endforeach do$><$foreach ToOneRelationship parentEntity.classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>
172 eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(<$ToOneRelationship.name$>);<$endif$><$endforeach do$>
173 return eoObject;
174 }
David Avendasora 10.1 175
smmccraw 12.1 176 {{/panel}}
David Avendasora 10.1 177
178 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:
179
smmccraw 12.1 180 {{panel}}
David Avendasora 10.1 181
smmccraw 12.1 182 public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext) {
David Avendasora 10.1 183 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchAll<$classNameWithoutPackage$>s(_editingContext, null);
184 }
185
smmccraw 12.1 186 {{/panel}}
David Avendasora 10.1 187
smmccraw 12.1 188 public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext //editingContext, NSArray //sortOrderings) {
David Avendasora 10.1 189
smmccraw 12.1 190 {{panel}}
191
David Avendasora 10.1 192 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, null, _sortOrderings);
193 }
194
smmccraw 12.1 195 {{/panel}}
David Avendasora 10.1 196
smmccraw 12.1 197 public static NSArray fetch<$classNameWithoutPackage$>s(EOEditingContext //editingContext, EOQualifier //qualifier, NSArray //sortOrderings) {//
David Avendasora 10.1 198
smmccraw 12.1 199 {{panel}}
200
David Avendasora 10.1 201 EOFetchSpecification fetchSpec = new EOFetchSpecification(<$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME, _qualifier, _sortOrderings);
202 fetchSpec.setIsDeep(true);
203 NSArray eoObjects = _editingContext.objectsWithFetchSpecification(fetchSpec);
204 return eoObjects;
205 }
206
smmccraw 12.1 207 {{/panel}}
David Avendasora 10.1 208
smmccraw 12.1 209 public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext //editingContext, String //keyName, Object //value) {//
David Avendasora 10.1 210
smmccraw 12.1 211 {{panel}}
212
David Avendasora 10.1 213 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value));
214 }
215
smmccraw 12.1 216 {{/panel}}
David Avendasora 10.1 217
smmccraw 12.1 218 public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext //editingContext, EOQualifier //qualifier) {
David Avendasora 10.1 219
smmccraw 12.1 220 {{panel}}
221
David Avendasora 10.1 222 NSArray eoObjects = <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, _qualifier, null);
223 <$classNameWithoutPackage$> eoObject;
224 int count = eoObjects.count();
225 if (count == 0) {
226 eoObject = null;
227 }
228 else if (count == 1) {
229 eoObject = (<$classNameWithoutPackage$>)eoObjects.objectAtIndex(0);
230 }
231 else {
232 throw new IllegalStateException("There was more than one <$classNameWithoutPackage$> that matched the qualifier '" + _qualifier + "'.");
233 }
234 return eoObject;
235 }
smmccraw 12.1 236
237 public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) {
David Avendasora 10.1 238 return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchRequired<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value));
239 }
240
smmccraw 12.1 241 {{/panel}}
David Avendasora 10.1 242
smmccraw 12.1 243 public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext //editingContext, EOQualifier //qualifier) {
David Avendasora 10.1 244
smmccraw 12.1 245 {{panel}}
246
David Avendasora 10.1 247 <$classNameWithoutPackage$> eoObject = <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, _qualifier);
smmccraw 12.1 248 if (eoObject h1. null) {
David Avendasora 10.1 249 throw new NoSuchElementException("There was no <$classNameWithoutPackage$> that matched the qualifier '" + _qualifier + "'.");
250 }
251 return eoObject;
252 }
253
smmccraw 12.1 254 {{/panel}}
David Avendasora 10.1 255
256 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):
257
smmccraw 12.1 258 {{panel}}
David Avendasora 10.1 259
smmccraw 12.1 260 public <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext) {
David Avendasora 10.1 261 return (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, this);
262 }
263
smmccraw 12.1 264 {{/panel}}
David Avendasora 10.1 265
smmccraw 12.1 266 public static <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext //editingContext, <$classNameWithoutPackage$> //eo) {
David Avendasora 10.1 267
smmccraw 12.1 268 {{panel}}
269
270 return (_eo null) ? null : (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, _eo);
David Avendasora 10.1 271 }
272
smmccraw 12.1 273 {{/panel}}
David Avendasora 10.1 274
275 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:
276
smmccraw 12.1 277 {{panel}}
David Avendasora 10.1 278
smmccraw 12.1 279 <$if !ToManyRelationship.inverseRelationship$>
David Avendasora 10.1 280 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier) {
281 return <$ToManyRelationship.name$>(qualifier, null);
282 }
283 <$endif$>
284 <$if ToManyRelationship.inverseRelationship$>
285 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier) {
286 return <$ToManyRelationship.name$>(qualifier, null, false);
287 }
smmccraw 12.1 288
289 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier, boolean fetch) {
David Avendasora 10.1 290 return <$ToManyRelationship.name$>(qualifier, null, fetch);
291 }
292 <$endif$>
smmccraw 12.1 293
David Avendasora 10.1 294 public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier, NSArray sortOrderings<$if ToManyRelationship.inverseRelationship$>, boolean fetch<$endif$>) {
295 NSArray results;
296 <$if ToManyRelationship.inverseRelationship$>
297 if (fetch) {
298 EOQualifier fullQualifier;
smmccraw 12.1 299 EOQualifier inverseQualifier = new EOKeyValueQualifier(<$ToManyRelationship.destination.className$>.<$ToManyRelationship.inverseRelationship.name.uppercaseUnderbarString$>_KEY, EOQualifier.QualifierOperatorEqual, this);
David Avendasora 10.1 300 if (qualifier == null) {
301 fullQualifier = inverseQualifier;
302 }
303 else {
304 NSMutableArray qualifiers = new NSMutableArray();
305 qualifiers.addObject(qualifier);
306 qualifiers.addObject(inverseQualifier);
307 fullQualifier = new EOAndQualifier(qualifiers);
308 }
309 results = <$ToManyRelationship.destination.className$>.fetch<$ToManyRelationship.destination.name$>s(editingContext(), fullQualifier, sortOrderings);
310 }
311 else {
312 <$endif$>
313 results = <$ToManyRelationship.name$>();
314 if (qualifier != null) {
315 results = EOQualifier.filteredArrayWithQualifier(results, qualifier);
316 }
317 if (sortOrderings != null) {
318 results = EOSortOrdering.sortedArrayUsingKeyOrderArray(results, sortOrderings);
319 }
320 <$if ToManyRelationship.inverseRelationship$>
321 }
322 <$endif$>
323 return results;
324 }
325
smmccraw 12.1 326 {{/panel}}
David Avendasora 10.1 327
smmccraw 12.1 328 === John Huss ===
David Avendasora 10.1 329
smmccraw 12.1 330 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 331
smmccraw 12.1 332 {{panel}}
David Avendasora 10.1 333
smmccraw 12.1 334 @SuppressWarnings("all")
David Avendasora 10.1 335 public class _Invoice extends ERXGenericRecord {
336 }
337
smmccraw 12.1 338 {{/panel}}
David Avendasora 10.1 339
smmccraw 12.1 340 === Guido Neitzer ===
David Avendasora 10.1 341
342 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:
343
smmccraw 12.1 344 {{panel}}
David Avendasora 10.1 345
smmccraw 12.1 346 /**
347 * Initialization of the instance while inserting it into an editing context
348 */
349 public void awakeFromInsertion (EOEditingContext editingContext) {
350 super.awakeFromInsertion (editingContext);
351 // initialize your object here
352 }
David Avendasora 10.1 353
smmccraw 12.1 354 {{/panel}}
David Avendasora 10.1 355
356 This is from my JavaSubclassSourceTemplate.eotemplate
smmccraw 12.1 357
358 Category:WebObjects