Wiki source code of EOF-Using EOF-EOGenerator

Version 30.1 by Pascal Robert on 2012/01/21 22:03

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