View Javadoc

1   /*
2    * $Source: /usr/cvsroot/melati/maven-dsd-plugin/src/main/java/org/melati/poem/prepro/FieldDef.java,v $
3    * $Revision: 1.45 $
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.poem.prepro;
47  
48  import java.util.Vector;
49  import java.io.StreamTokenizer;
50  import java.io.Writer;
51  import java.io.IOException;
52  
53  /**
54   * An abstract definition of a <tt>Field</tt> from which all other
55   * <tt>FieldDef</tt>s are derived.
56   *
57   */
58  public abstract class FieldDef {
59  
60    protected final TableDef table;
61  
62    protected final String name;
63  
64    protected final String suffix;
65  
66    protected int displayOrder;
67  
68    String displayName;
69  
70    String description;
71  
72    protected final String type;
73  
74    protected final String rawType;
75  
76    protected final Vector qualifiers;
77  
78    final String mainClass;
79  
80    final String tableAccessorMethod;
81  
82    protected String displayLevel = null;
83  
84    protected String searchability = null;
85  
86    private boolean sortDescending = false;
87  
88    int displayOrderPriority = -1;
89  
90    private boolean isNullable = false;
91  
92    private boolean isTroidColumn = false;
93  
94    private boolean isDeletedColumn = false;
95  
96    private boolean isEditable = true;
97  
98    private boolean isCreateable = true;
99  
100   private boolean isIndexed = false;
101 
102   private boolean isUnique = false;
103 
104   boolean isCompareOnly = false;
105 
106   private int width = -1, height = -1;
107 
108   String renderinfo = null;
109 
110   protected int lineNumber;
111 
112   /**
113    * Constructor.
114    *
115    * @param table
116    *          the {@link TableDef} that this <code>Field</code> is part of
117    * @param name
118    *          the name of this field
119    * @param type
120    *          the POEM type of this field
121    * @param rawType
122    *          the underlying java type of this field
123    * @param displayOrder
124    *          where to place this field in a list
125    * @param qualifiers
126    *          all the qualifiers to be applied to this field
127    *
128    * @throws IllegalityException
129    *           if a semantic inconsistency is detected
130    */
131   public FieldDef(int lineNo, TableDef table, String name, String type,
132       String rawType, int displayOrder, Vector qualifiers)
133       throws IllegalityException {
134     this.lineNumber = lineNo;
135     this.table = table;
136     this.name = name;
137     this.displayOrder = displayOrder;
138     this.suffix = StringUtils.capitalised(name);
139     this.type = type;
140     this.rawType = rawType;
141     this.qualifiers = qualifiers;
142 
143     this.mainClass = table.naming.mainClassUnambiguous();
144     this.tableAccessorMethod = table.naming.tableAccessorMethod();
145 
146     for (int q = 0; q < qualifiers.size(); ++q) {
147       ((FieldQualifier)qualifiers.elementAt(q)).apply(this);
148     }
149 
150   }
151 
152   /** @return a name for this class */
153   public String toString() {
154     return table.name + "." + name + " (" + (isNullable ? "nullable " : "")
155         + type + ")";
156   }
157 
158   private static void fieldQualifiers(Vector qualifiers, StreamTokenizer tokens)
159       throws ParsingDSDException, IOException {
160     while (tokens.ttype == '(') {
161       tokens.nextToken();
162       qualifiers.addElement(FieldQualifier.from(tokens));
163       DSD.expect(tokens, ')');
164       tokens.nextToken();
165     }
166   }
167 
168   /**
169    * Creates the appropriate type of <code>FieldDef</code> from the input
170    * stream.
171    *
172    * @param table
173    *          the {@link TableDef} we are dealing with
174    * @param tokens
175    *          the <code>StreamTokenizer</code> to get tokens from
176    * @param displayOrder
177    *          the ranking of this <code>Field</code>
178    *
179    * @throws ParsingDSDException
180    *           if an unexpected token is encountered
181    * @throws IOException
182    *           if something goes wrong with the file system
183    * @throws IllegalityException
184    *           if a semantic incoherence is detected
185    * @return a new <code>FieldDef</code> of the appropriate type
186    */
187   public static FieldDef from(TableDef table, StreamTokenizer tokens,
188       int displayOrder) throws ParsingDSDException, IOException,
189       IllegalityException {
190     table.addImport("org.melati.poem.AccessPoemException", "both");
191     table.addImport("org.melati.poem.ValidationPoemException", "table");
192     table.addImport("org.melati.poem.Persistent", "table");
193 
194     table.definesColumns = true;
195     Vector qualifiers = new Vector();
196     fieldQualifiers(qualifiers, tokens);
197     if (tokens.ttype != StreamTokenizer.TT_WORD)
198       throw new ParsingDSDException("<field type>", tokens);
199     String type = tokens.sval;
200     // HACK we allow "byte[]" for binary data
201     if (type.equals("byte")) {
202       if (tokens.nextToken() != '[' || tokens.nextToken() != ']')
203         throw new ParsingDSDException("[", tokens);
204       type = "byte[]";
205     }
206 
207     if (tokens.nextToken() != StreamTokenizer.TT_WORD)
208       throw new ParsingDSDException("<field name>", tokens);
209     String name = tokens.sval;
210     tokens.nextToken();
211     fieldQualifiers(qualifiers, tokens);
212     DSD.expect(tokens, ';');
213     int lineNo = tokens.lineno();
214     if (type.equals("Integer"))
215       return new IntegerFieldDef(lineNo, table, name, displayOrder, qualifiers);
216     if (type.equals("Long"))
217       return new LongFieldDef(lineNo, table, name, displayOrder, qualifiers);
218     else if (type.equals("Double"))
219       return new DoubleFieldDef(lineNo, table, name, displayOrder, qualifiers);
220     else if (type.equals("Boolean"))
221       return new BooleanFieldDef(lineNo, table, name, displayOrder, qualifiers);
222     else if (type.equals("String"))
223       return new StringFieldDef(lineNo, table, name, displayOrder, qualifiers);
224     else if (type.equals("Password"))
225       return new PasswordFieldDef(lineNo, table, name, displayOrder, qualifiers);
226     else if (type.equals("Date"))
227       return new DateFieldDef(lineNo, table, name, displayOrder, qualifiers);
228     else if (type.equals("Timestamp"))
229       return new TimestampFieldDef(lineNo, table, name, displayOrder,
230           qualifiers);
231     else if (type.equals("ColumnType"))
232       return new ColumnTypeFieldDef(lineNo, table, name, displayOrder,
233           qualifiers);
234     else if (type.equals("DisplayLevel"))
235       return new DisplayLevelFieldDef(lineNo, table, name, displayOrder,
236           qualifiers);
237     else if (type.equals("Searchability"))
238       return new SearchabilityFieldDef(lineNo, table, name, displayOrder,
239           qualifiers);
240     else if (type.equals("IntegrityFix"))
241       return new IntegrityFixFieldDef(lineNo, table, name, displayOrder,
242           qualifiers);
243     else if (type.equals("BigDecimal"))
244       return new BigDecimalFieldDef(lineNo, table, name, displayOrder,
245           qualifiers);
246     else if (type.equals("byte[]"))
247       return new BinaryFieldDef(lineNo, table, name, displayOrder, qualifiers);
248     else
249       return new ReferenceFieldDef(lineNo, table, name, displayOrder, type,
250           qualifiers);
251   }
252 
253   /**
254    * Write out this <code>Column</code>'s base methods.
255    *
256    * @param w
257    *          Persistent Base
258    *
259    * @throws IOException
260    *           if something goes wrong with the file system
261    */
262   public void generateBaseMethods(Writer w) throws IOException {
263     w.write("\n /**\n" + "  * Retrieves the <code>" + suffix
264         + "</code> value, without locking, \n" + "  * for this <code>"
265         + table.suffix + "</code> <code>Persistent</code>.\n" + "  *\n"
266         + "  * @see " + "org.melati.poem.prepro.FieldDef"
267         + "#generateBaseMethods \n" + "  * @return the " + rawType + " " + name
268         + "\n" + "  */\n");
269     w.write("  public " + rawType + " get" + suffix + "_unsafe() {\n"
270         + "    return " + name + ";\n" + "  }\n" + "\n");
271     w.write("\n /**\n" + "  * Sets the <code>" + suffix
272         + "</code> value directly, without checking, \n" + "  * for this "
273         + table.suffix + " <code>Persistent</code>.\n" + "  * \n"
274         + "  * @see " + "org.melati.poem.prepro.FieldDef"
275         + "#generateBaseMethods \n"
276         + "  * @param cooked  the pre-validated value to set\n" + "  */\n");
277     w.write("  public void set" + suffix + "_unsafe(" + rawType
278         + " cooked) {\n" + "    " + name + " = cooked;\n" + "  }\n");
279   }
280 
281   /**
282    * Write out this <code>Column</code>'s field creators.
283    *
284    * @param w
285    *          Persistent Base
286    * @throws IOException
287    *           if something goes wrong with the file system
288    */
289   public void generateFieldCreator(Writer w) throws IOException {
290     w.write("\n /**\n" + "  * Retrieves the <code>" + suffix
291         + "</code> value as a <code>Field</code>\n" + "  * from this <code>"
292         + table.suffix + "</code> <code>Persistent</code>.\n" + "  * \n"
293         + "  * @see " + "org.melati.poem.prepro.FieldDef"
294         + "#generateFieldCreator \n" + "  * @throws AccessPoemException \n"
295         + "  *         if the current <code>AccessToken</code> \n"
296         + "  *         does not confer write access rights\n"
297         + "  * @return the " + rawType + " " + name + "\n" + "  */\n");
298     w.write("  public Field get" + suffix + "Field() "
299         + "throws AccessPoemException {\n" + "    Column c = _"
300         + tableAccessorMethod + "()." + "get" + suffix + "Column();\n"
301         + "    return new Field(c.getRaw(this), c);\n" + "  }\n");
302   }
303 
304   /**
305    * Write out this <code>Field</code>'s java declaration string.
306    *
307    * @param w
308    *          PersistentBase
309    * @throws IOException
310    *           if something goes wrong with the file system
311    */
312   public abstract void generateJavaDeclaration(Writer w) throws IOException;
313 
314   /**
315    * Write out this <code>Column</code>'s java declaration string.
316    *
317    * @param w
318    *          TableBase
319    * @throws IOException
320    *           if something goes wrong with the file system
321    */
322   public void generateColDecl(Writer w) throws IOException {
323     w.write("Column col_" + name);
324   }
325 
326   /**
327    * Write out this <code>Column</code>'s accessors.
328    *
329    * @param w
330    *          TableBase
331    * @throws IOException
332    *           if something goes wrong with the file system
333    */
334   public void generateColAccessor(Writer w) throws IOException {
335     w.write("\n /**\n" + "  * Retrieves the <code>" + suffix
336         + "</code> <code>Column</code> for this \n" + "  * <code>"
337         + table.suffix + "</code> <code>Table</code>.\n" + "  * \n"
338         + "  * @see " + "org.melati.poem.prepro.FieldDef"
339         + "#generateColAccessor \n" + "  * @return the " + name
340         + " <code>Column</code>\n" + "  */\n");
341     w.write("  public final Column get" + suffix + "Column() {\n"
342         + "    return col_" + name + ";\n" + "  }\n");
343   }
344 
345   /**
346    * Write out this <code>Column</code>'s field accessors as part of the
347    * anonymous definition of the <code>Column</code>.
348    *
349    * @param w
350    *          TableBase
351    * @throws IOException
352    *           if something goes wrong with the file system
353    */
354   protected void generateColRawAccessors(Writer w) throws IOException {
355     w.write("          public Object getRaw_unsafe(Persistent g)\n"
356         + "              throws AccessPoemException {\n"
357         + "            return ((" + mainClass + ")g)." + "get" + suffix
358         + "_unsafe();\n" + "          }\n" + "\n");
359 
360     w.write("          public void setRaw_unsafe(Persistent g, Object raw)\n"
361         + "              throws AccessPoemException {\n" + "            (("
362         + mainClass + ")g).set" + suffix + "_unsafe((" + rawType + ")raw);\n"
363         + "          }\n");
364   }
365 
366   /**
367    * Write out this <code>Column</code>'s definition using an anonymous
368    * class.
369    *
370    * @param w
371    *          TableBase
372    * @throws IOException
373    *           if something goes wrong with the file system
374    */
375   public void generateColDefinition(Writer w) throws IOException {
376     w
377         .write("    defineColumn(col_"
378             + name
379             + " =\n"
380             + "        new Column(this, \""
381             + name
382             + "\",\n"
383             + "                   "
384             + poemTypeJava()
385             + ",\n"
386             + "                   DefinitionSource.dsd) { \n"
387             + "          public Object getCooked(Persistent g)\n"
388             + "              throws AccessPoemException, PoemException {\n"
389             + "            return (("
390             + mainClass
391             + ")g).get"
392             + suffix
393             + "();\n"
394             + "          }\n"
395             + "\n"
396             + "          public void setCooked(Persistent g, Object cooked)\n"
397             + "              throws AccessPoemException, ValidationPoemException {\n"
398             + "            ((" + mainClass + ")g).set" + suffix + "((" + type
399             + ")cooked);\n" + "          }\n" + "\n"
400             + "          public Field asField(Persistent g) {\n"
401             + "            return ((" + mainClass + ")g).get" + suffix
402             + "Field();\n" + "          }\n" + "\n");
403 
404     if (isTroidColumn || !isEditable)
405       w.write("          protected boolean defaultUserEditable() {\n"
406           + "            return false;\n" + "          }\n" + "\n");
407 
408     if (isTroidColumn || !isCreateable)
409       w.write("          protected boolean defaultUserCreateable() {\n"
410           + "            return false;\n" + "          }\n" + "\n");
411 
412     if (displayLevel != null)
413       w.write("          protected DisplayLevel defaultDisplayLevel() {\n"
414           + "            return DisplayLevel." + displayLevel + ";\n"
415           + "          }\n" + "\n");
416 
417     if (searchability != null)
418       w.write("          protected Searchability defaultSearchability() {\n"
419           + "            return Searchability." + searchability + ";\n"
420           + "          }\n" + "\n");
421 
422     if (displayOrderPriority != -1)
423       w.write("          protected Integer defaultDisplayOrderPriority() {\n"
424           + "            return new Integer(" + displayOrderPriority + ");\n"
425           + "          }\n" + "\n");
426 
427     if (sortDescending)
428       w.write("          protected boolean defaultSortDescending() {\n"
429           + "            return true;\n" + "          }\n" + "\n");
430 
431     if (displayName != null)
432       w.write("          protected String defaultDisplayName() {\n"
433           + "            return " + StringUtils.quoted(displayName, '"')
434           + ";\n" + "          }\n" + "\n");
435 
436     w
437         .write("          protected int defaultDisplayOrder() {\n"
438             + "            return " + displayOrder + ";\n" + "          }\n"
439             + "\n");
440 
441     if (description != null)
442       w.write("          protected String defaultDescription() {\n"
443           + "            return " + StringUtils.quoted(description, '"')
444           + ";\n" + "          }\n" + "\n");
445 
446     if (isIndexed)
447       w.write("          protected boolean defaultIndexed() {\n"
448           + "            return true;\n" + "          }\n" + "\n");
449 
450     if (isUnique)
451       w.write("          protected boolean defaultUnique() {\n"
452           + "            return true;\n" + "          }\n" + "\n");
453 
454     if (width != -1)
455       w.write("          protected int defaultWidth() {\n"
456           + "            return " + width + ";\n" + "          }\n" + "\n");
457 
458     if (height != -1)
459       w.write("          protected int defaultHeight() {\n"
460           + "            return " + height + ";\n" + "          }\n" + "\n");
461 
462     if (renderinfo != null)
463       w.write("          protected String defaultRenderinfo() {\n"
464           + "            return " + StringUtils.quoted(renderinfo, '"') + ";\n"
465           + "          }\n" + "\n");
466 
467     generateColRawAccessors(w);
468 
469     w.write("        });\n");
470   }
471 
472   /** @return the Java string for this <code>PoemType</code>. */
473   public abstract String poemTypeJava();
474 
475   /**
476    * @return whether this column is a deleted marker
477    */
478   public boolean isDeletedColumn() {
479     return isDeletedColumn;
480   }
481 
482   /**
483    * Set whether this field represents a deleted marker.
484    *
485    * @param isDeletedColumn boolean
486    */
487   public void setDeletedColumn(boolean isDeletedColumn) {
488     if (this.isDeletedColumn)
489       throw new IllegalityException(lineNumber,
490           "Deleted qualifier already set true.");
491     this.isDeletedColumn = isDeletedColumn;
492   }
493 
494   /**
495    * @return whether this field represents a troid column.
496    */
497   public boolean isTroidColumn() {
498     return isTroidColumn;
499   }
500 
501   /**
502    * Set the isTroidColumn property.
503    *
504    * @param isTroidColumn boolean
505    */
506   public void setTroidColumn(boolean isTroidColumn) {
507     if (this.isTroidColumn)
508       throw new IllegalityException(lineNumber,
509           "Troid qualifier  already set true.");
510     this.isTroidColumn = isTroidColumn;
511   }
512 
513   /**
514    * @return whether this column is nullable.
515    */
516   public boolean isNullable() {
517     return isNullable;
518   }
519 
520   /**
521    * Set the nullable property.
522    *
523    * @param isNullable boolean
524    */
525   public void setNullable(boolean isNullable) {
526     if (this.isNullable)
527       throw new IllegalityException(lineNumber,
528           "Nullable qualifier  already set true.");
529     this.isNullable = isNullable;
530   }
531 
532   /**
533    * @return whether this field is editable
534    */
535   public boolean isEditable() {
536     return isEditable;
537   }
538 
539   /**
540    * Set the isEditable property.
541    *
542    * @param isEditable boolean
543    */
544   public void setEditable(boolean isEditable) {
545     if (!this.isEditable)
546       throw new IllegalityException(lineNumber,
547           "Editable qualifier  already set true.");
548     this.isEditable = isEditable;
549   }
550 
551   /**
552    * @return whether this column shoudl be sorted in descending order
553    */
554   public boolean isSortDescending() {
555     return sortDescending;
556   }
557 
558   /**
559    * Set the sortDescending property.
560    *
561    * @param sortDescending
562    */
563   public void setSortDescending(boolean sortDescending) {
564     if (this.sortDescending)
565       throw new IllegalityException(lineNumber,
566           "Sort descending qualifier  already set true.");
567     this.sortDescending = sortDescending;
568   }
569 
570   /**
571    * @return whether this column is user creatable
572    */
573   public boolean isCreateable() {
574     return isCreateable;
575   }
576 
577   /**
578    * Set the isCreatable property.
579    *
580    * @param isCreateable boolean
581    */
582   public void setCreateable(boolean isCreateable) {
583     if (!this.isCreateable)
584       throw new IllegalityException(lineNumber,
585           "Creatable qualifier  already set true.");
586     this.isCreateable = isCreateable;
587   }
588 
589   /**
590    * @return whether this column is indexed.
591    */
592   public boolean isIndexed() {
593     return isIndexed;
594   }
595 
596   /**
597    * Set the isIndexed property.
598    * @param isIndexed boolean
599    */
600   public void setIndexed(boolean isIndexed) {
601     if (this.isIndexed)
602       throw new IllegalityException(lineNumber,
603           "Indexed qualifier  already set true.");
604     this.isIndexed = isIndexed;
605   }
606 
607   /**
608    * @return whether this column is unique
609    */
610   public boolean isUnique() {
611     return isUnique;
612   }
613 
614   /**
615    * Set the isUnique property.
616    *
617    * @param isUnique boolean
618    */
619   public void setUnique(boolean isUnique) {
620     if (this.isUnique)
621       throw new IllegalityException(lineNumber,
622           "Unique qualifier  already set true.");
623     this.isUnique = isUnique;
624   }
625 
626   /**
627    * @return the width
628    */
629   public int getWidth() {
630     return width;
631   }
632 
633   /**
634    * Set the width property.
635    *
636    * @param width the width to set
637    */
638   public void setWidth(int width) {
639     if (this.width != -1)
640       throw new IllegalityException(lineNumber, "Size already set to "
641           + this.width + " cannot overwrite with " + width);
642     this.width = width;
643   }
644 
645   /**
646    * @return the height
647    */
648   public int getHeight() {
649     return height;
650   }
651 
652   /**
653    * Set the heigth property.
654    *
655    * @param height the height to set
656    */
657   public void setHeight(int height) {
658     if (this.height != -1)
659       throw new IllegalityException(lineNumber, "Height already set to "
660           + this.width + " cannot overwrite with " + width);
661     this.height = height;
662   }
663 }