KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > JSX > DTDGenerator


1 /** (c) Brendan Macmillan 2001, licensed under GNU's GPL 2.
2         (If your project is not GPL, then a fee is due - see license on website)
3     Website: http://www.csse.monash.edu.au/~bren/JSX
4     Website: http://freshmeat.net/projects/jsx
5     List (can read without subscribing, need to join to post):
6       http://groups.yahoo.com/group/JSX-ideas/messages
7         Commercial licensing enquiries only:
8             bren@mail.csse.monash.edu.au **/

9
10 /* Example usage code:
11
12 import JSX.*;
13 class DTDTest {
14   static public void main(String[] args) throws Exception {
15     new ObjOut( //we are writing
16       new Config().setDTDGenerator( //Config object
17         new DTDGenerator(new java.io.FileWriter("my.dtd")) //set Writer
18       )
19     ).writeObject(new DTDTest()); // <-------- put your object here.
20   }
21 }
22 */

23
24
25 /** NOTE: each ref-type within the class is recorded. Note that this recording
26     * also includes any subtypes eg: (a | suba | b | subb1 | subb2)*
27     **/

28
29 package JSX;
30
31 import java.io.*;
32 import java.util.*;
33 import java.lang.reflect.*;
34
35
36 //Problem: "DTD" confuses convention of class's first letter lowecase...
37
public class DTDGenerator {
38     //static final boolean DEBUG = true;
39
static final boolean DEBUG = false;
40
41     PrintWriter out;
42     public DTDGenerator(Writer writer) {
43         this.out = new PrintWriter(writer);
44     }
45
46
47 /**
48     * populate().
49     * ----------
50     * Called from putAlias() in XMLSerialize.
51     * It must be called before the alias is depopulated (eg if alias-ref is off)
52     **/

53     HashSet classList = new HashSet();
54     void populate(Object JavaDoc obj) { //what's a better name?
55
classList.add(obj.getClass());
56     }
57
58
59 /**
60     * generate().
61     * -----------
62     * Programmer: called from end of commonSer() in XMLSerialize.
63     * Note that it must be called after the end of serialization, and not
64     * after a custom serialization call. This is why we don't call it from
65     * ObjOut.writeObjectOverride() nor XMLSerialize.serialize().
66     *
67     * API dicussion:
68     * Every object graph configures us, populating the class
69     * list, but the user needs to explicitly call writeDTD. Thus, we can
70     * collect over several object graphs, and it is clearer than writing both
71     * XML and DTD at the same time (hidden and unexpected).
72     * We could have a switch to control this behaviour (background vs explicit),
73     * but better to choose just one or the other.
74     **/

75
76     HashSet entitiesDone = new HashSet();
77     void generate() {
78         if (DEBUG) System.err.println(classList);
79         classList.add(NULL); //special cases
80
classList.add(ALIASREF);
81         Object JavaDoc[] c = (Object JavaDoc[]) classList.toArray(new Object JavaDoc[0]);
82         classList.clear(); //to prevent garbled results, on subsequent calls
83
entitiesDone.clear();
84         writeDTD(c);
85         //out.close();
86
}
87
88
89 /** KEY METHOD **/
90     public void writeDTD(Object JavaDoc[] classList) {
91         for (int i=0; i<classList.length; i++) {
92         //LOGIC
93
SepFields r;
94             Object JavaDoc obj = classList[i];
95             if (obj instanceof Class JavaDoc) {
96                 Class JavaDoc clazz = (Class JavaDoc) obj;
97                 r = custom(clazz);
98                 if (r==null) r = sepFields(clazz);
99                 //add "JSX format" fields
100
r.primList.add(XMLSerialize.ALIAS_ID_TOKEN);
101             } else if (obj instanceof SyntheticClass) {
102                 r = ((SyntheticClass) obj).sepFields; //neither can have ID
103
} else throw new InternalError JavaDoc("expected Class or SyntheticClass");
104
105             r.primList.add(XMLSerialize.NAME_TOKEN); //problem: not *required*
106

107             ArrayList allRefList = getSubclasses(r.refList, classList);
108
109         //PRESENTATION
110
String JavaDoc className;
111             if (obj instanceof Class JavaDoc) { //use polymorphism, in a wrapper
112
Class JavaDoc clazz = (Class JavaDoc) obj;
113                 className = getClassName(clazz);
114             } else if (obj instanceof SyntheticClass) {
115                 className = ((SyntheticClass) obj).className;
116             } else throw new InternalError JavaDoc("expected Class or SyntheticClass");
117             writeElement(className, allRefList); //ESCAPE used here, too
118
writeAttList(className, r.primList);
119         }
120         out.flush();
121     }
122
123
124
125     static final SyntheticClass NULL;
126     static final SyntheticClass ALIASREF;
127     static {
128         //NULL
129
NULL = new SyntheticClass(XMLSerialize.NULL_TOKEN);
130         //ALIASREF
131
ALIASREF = new SyntheticClass(XMLSerialize.ALIAS_TAG_TOKEN);
132         ALIASREF.sepFields.primList.add(XMLSerialize.ALIAS_ATTR_TOKEN);
133     }
134
135     String JavaDoc getClassName(Class JavaDoc clazz) {
136         String JavaDoc className;
137         if (clazz.isArray()) {
138             if (clazz.getComponentType()==byte.class)
139                 className = XMLSerialize.BINARY_DATA_TOKEN;
140             else
141                 className = XMLSerialize.getArrayType(clazz);
142         }
143         else className = clazz.getName();
144         return ParseUtilities.escapeDollar(className); //escape both
145
}
146
147     SepFields sepFields(Class JavaDoc clazz) {
148         //"ref" includes all reference types: classes, arrays and interfaces
149
Field[] f = clazz.getDeclaredFields(); //need INHERITED ONES
150
ArrayList primCache = new ArrayList();
151         ArrayList refCache = new ArrayList();
152         for (int i=0; i<f.length; i++) {
153             Field field = f[i];
154             Class JavaDoc type = field.getType();
155             if (type.isPrimitive() || type==String JavaDoc.class) primCache.add(field);
156             else refCache.add(field);
157         }
158         return new SepFields(refCache, primCache);
159     }
160
161     SepFields custom(Class JavaDoc clazz) {
162         SepFields r = null;
163         if (clazz.isArray()) {
164             r = new SepFields();
165             r.primList.add(XMLSerialize.LENGTH_TOKEN); //just need the String
166
Class JavaDoc component = clazz.getComponentType(); //array "subclass" handled?
167
if (!component.isPrimitive())
168                 r.refList.add(clazz.getComponentType()); //Strings are treated as ref
169
else if (component==byte.class) {
170                 r.primList.add(XMLSerialize.VALUE_TOKEN); //same as wrapper
171
} else { //array of primitives: problem
172
//for (int i=0; ....) {
173
// r.primList.add("a"+i); //but... depends on specific length!!
174
//array of primitives requires *runtime* length number of attr
175
//}
176
}
177         } else if (clazz==Boolean JavaDoc.class || //all wrappers are the same
178
clazz==Byte JavaDoc.class ||
179                 clazz==Short JavaDoc.class ||
180                 clazz==Integer JavaDoc.class ||
181                 clazz==Long JavaDoc.class ||
182                 clazz==Float JavaDoc.class ||
183                 clazz==Double JavaDoc.class ||
184                 clazz==String JavaDoc.class || //char data - may differ
185
clazz==Character JavaDoc.class) { //char data - may differ
186
r = new SepFields();
187             r.primList.add(XMLSerialize.VALUE_TOKEN); //just need the String
188
//would be nicer if Class was the same...
189
} else if (clazz==Vector.class) {
190             r = new SepFields();
191             r.refList.add(Object JavaDoc.class); //need the class (for subclasses)
192
} else if (clazz==Hashtable.class) { //not even numbers; but little point
193
r = new SepFields();
194             r.refList.add(Object JavaDoc.class); //need the class (for subclasses)
195
} else if (clazz==Class JavaDoc.class) {
196             r = new SepFields();
197             r.primList.add(XMLSerialize.CLASSNAME_TOKEN); //just need the String
198
}
199         return r;
200     }
201
202
203 //LOGIC CODE
204
//----------
205
//input: list of Fields
206
//output: list of Strings
207
ArrayList getSubclasses(ArrayList refList, Object JavaDoc[] classList) {
208         if (DEBUG) System.err.println("Starting Subclasses");
209         ArrayList result = new ArrayList();
210         for (Iterator i=refList.listIterator(); i.hasNext(); ) {
211             Class JavaDoc type;
212             Object JavaDoc ref = i.next();
213             if (ref instanceof Field) //use polymorphism instead
214
type = ((Field)ref).getType();
215             else if (ref instanceof Class JavaDoc)
216                 type = (Class JavaDoc) ref;
217             else throw new InternalError JavaDoc("Expected a Field or Class; got a "+ref.getClass());
218         if (DEBUG) System.err.println("\tField of type: "+type);
219
220     result.add(type); //no presentation code here
221

222     //ENTITY def
223
if (!entitiesDone.contains(type)) {
224             entitiesDone.add(type);
225             out.print("<!ENTITY % "+getClassName(type)+" \"(");
226                 boolean firstOne = true;
227                 for (int j=0; j<classList.length; j++) {
228                     Object JavaDoc obj = classList[j];
229                     if (obj instanceof Class JavaDoc) { //use polymorphism, in a wrapper
230
if (type.isAssignableFrom((Class JavaDoc)obj)) {
231                             //superclass subclass
232
//result.add(obj); //no presentation code here
233
if (firstOne) firstOne = false;
234                             else out.print("|");
235                             out.print(getClassName((Class JavaDoc)obj));
236                         }
237                     } else if (obj instanceof SyntheticClass) {
238                         //result.add(obj); //null and aliasref always assignable
239
if (firstOne) firstOne = false;
240                         else out.print("|");
241                         out.print(((SyntheticClass)obj).className);
242                     } else throw new InternalError JavaDoc("expected Class or SyntheticClass");
243                 }
244             out.println(")\">");
245             if (DEBUG) System.err.println("\t\tresult so far: "+result);
246             }
247         }
248
249
250         return result;
251     }
252
253
254
255 //PRESENTATION CODE
256
//-----------------
257
//REF: present ref fields as Element content. eg:
258
// <!ELEMENT clazz (x|y|z)*>
259
void writeElement(String JavaDoc elementName, ArrayList refList) {
260         out.print("<!ELEMENT "+elementName+" ");
261         // S -> e //if empty
262
// S -> ( F E //Start
263
// F -> | F //keep on adding
264
// F -> e //end of list
265
// E -> ) //End
266
//if (refList.size()==0) out.print("EMPTY");
267
boolean firstOne = true;
268         for (Iterator i=refList.listIterator(); i.hasNext(); ) {
269             if (firstOne) {
270                 firstOne = false;
271                 out.print("("); //now we know non-empty
272
} else {
273                 out.print("|");
274             }
275             Object JavaDoc obj = i.next();
276             String JavaDoc className; //exact same code as above
277
if (obj instanceof Class JavaDoc) { //use polymorphism, in a wrapper
278
Class JavaDoc clazz = (Class JavaDoc) obj;
279                 className = getClassName(clazz);
280             } else if (obj instanceof SyntheticClass) {
281                 className = ((SyntheticClass) obj).className;
282             } else throw new InternalError JavaDoc("expected Class or SyntheticClass");
283             out.print("%"+className+";"); //entities...
284
}
285         //NB: (x|y|z)* syntax: we parse by knowing the first one
286
if (firstOne) out.print("EMPTY"); //if empty
287
else out.print(")*");
288         out.println(">"); //close "<!ELEMENT "
289
}
290
291     //PRIM: present primitive fields as an attribute list. eg:
292
// <!ATTLIST clazz
293
// i CDATA #IMPLIED
294
// j CDATA #IMPLIED
295
// k CDATA #IMPLIED
296
// >
297
void writeAttList(String JavaDoc className, ArrayList attList) {
298         boolean firstPrim = true;
299         if (DEBUG) System.err.println(attList);
300         for (Iterator i=attList.listIterator(); i.hasNext(); ) {
301             if (firstPrim) {
302                 out.println("<!ATTLIST " + className); //ln eases layout
303
firstPrim = false; //in case it has none
304
}
305             Object JavaDoc f = i.next();
306             if (f instanceof Field)
307                 out.println(" " + ((Field)f).getName() + " CDATA #IMPLIED");
308             else if (f instanceof String JavaDoc)
309                 out.println(" " + f + " CDATA #IMPLIED");
310
311         }
312         if (!firstPrim) { //only if it had at least one
313
//what about JSX stuff, like obj-name and alias-ID?
314
out.println(">");
315         }
316     }
317
318
319
320 /** some rudimentary test code **/
321     int a;
322     int b;
323     int c;
324     Object JavaDoc o;
325     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
326         Class JavaDoc[] c = {String JavaDoc.class, DTDGenerator.class, XMLSerialize.class};
327         DTDGenerator generator=new DTDGenerator(new OutputStreamWriter(System.out));
328         generator.writeDTD(c);
329     }
330
331 }
332
333
334     class SyntheticClass {
335         String JavaDoc className;
336         SepFields sepFields = new SepFields();
337         SyntheticClass(String JavaDoc className) {
338             this.className = className;
339         }
340     }
341
342     //for return values and class content
343
class SepFields {
344         ArrayList refList;
345         ArrayList primList;
346         SepFields() {
347             this.refList = new ArrayList();
348             this.primList = new ArrayList();
349         }
350         SepFields(ArrayList refList, ArrayList primList) {
351             this.refList = refList;
352             this.primList = primList;
353         }
354     }
355
356 /** FUTURE COOL.
357     * Use entities for subclasses, instead of listing them all explicitly.
358     * More readable (one entity per ref field) and shorter DTD.
359     * It shifts work to the validator
360     * In implementation, repeated content disappears.
361     * And we can do (&Object; &Object;)* for Hashtable easily.
362     **/

363
364 /** IMPLEMENTATION/ARCHECTURE: to do.
365     * Use polymorphism transparently for SyntheticClass and Field.
366     * - enable specification of optional or not (eg alias-ID)
367     **/

368
369 /** MINOR BUGS: to do.
370     * Strings can appear as "aliases" as an attribute.
371     * Switch for "aliasID": whether String occur as attributes or tags.
372     **/

373
374 /** MINOR FEATURES: to do.
375     * change JSX format: "ArrayOf-binary-data", not "ArrayOf-ArrayOf-byte"
376     **/

377
378 /** Limitations.
379     * Custom serialized ref types.
380             Soln: use "Object" catch-all, when writeObject
381     * Custom serialized prim types.
382             Soln: JSX format to have a tag per primitive
383     * Arrays of primitives.
384             Soln: JSX format to have a tag per primitive
385
386     * NB: "ref" = reference type, including objects, arrays and interfaces.
387     * NB: "prim" = primtive types, including Strings in some cases.
388     **/

389
Free Books   Free Magazines  
Popular Tags