Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.thing / src / main / java / thing / ThinletDTD.java @ 309

History | View | Annotate | Download (24 KB)

1
// jEdit settings:
2
// :tabSize=4:indentSize=4:noTabs=true:folding=explicit:collapseFolds=1:
3

    
4
package thing;
5

    
6

    
7
import java.util.Arrays;
8
import java.util.ArrayList;
9
import java.util.HashMap;
10
import java.util.Iterator;
11
import java.util.TreeSet;
12
import java.util.logging.Level;
13
import java.util.logging.Logger;
14

    
15

    
16
/**
17
 * This class encapsulates the DTD for Thinlet DTD.
18
 *
19
 * <p>
20
 * The DTD structure is copied from <code>Thinlet.java</code>. It must be
21
 * adapted whenever the original structure in <code>Thinlet.java</code>
22
 * changes.
23
 * </p>
24
 * <p>
25
 * The other classes and methods in this class provide some handy accessors for
26
 * the contents of the DTD. See {@link ThinletDTD.Widget} and
27
 * {@link ThinletDTD.Property}.
28
 *
29
 *
30
 * Note: 
31
 *         Eliminated attribute i18n to support Thinlet 2005/03/28
32
 * 
33
 *         gvSIG Team
34
 * 
35
 * 
36
 *
37
 *
38
 * @author Dirk Moebius
39
 */
40
public class ThinletDTD
41
{
42
    //{{{ logging
43
    private static final Logger log = Logger.getLogger("thing");
44
    private static final boolean debug() { return log.isLoggable(Level.FINE); }
45
    //}}}
46

    
47

    
48
    //{{{ copied from Thinlet.java
49
    private static final Object[] dtd;
50
    static
51
    {
52
        Integer integer_1 = new Integer(-1);
53
        Integer integer0 = new Integer(0);
54
        Integer integer1 = new Integer(1);
55
        String[] orientation = { "horizontal", "vertical" };
56
        String[] leftcenterright = { "left", "center", "right" };
57
        String[] selections = { "single", "interval", "multiple" }; //+none
58
        dtd = new Object[] {
59
            "component", null, new Object[][] {
60
                { "string", "name", null, null },
61
                { "boolean", "enabled", "paint", Boolean.TRUE },
62
                { "boolean", "visible", "parent", Boolean.TRUE },
63
                //{ "boolean", "i18n", "validate", Boolean.FALSE }, // for I18N
64
                { "string", "tooltip", null, null },
65
                { "font", "font", "validate", null },
66
                { "color", "foreground", "paint", null },
67
                { "color", "background", "paint", null },
68
                { "integer", "width", "validate", integer0 },
69
                { "integer", "height", "validate", integer0 },
70
                { "integer", "colspan", "validate", integer1 },
71
                { "integer", "rowspan", "validate", integer1 },
72
                { "integer", "weightx", "validate", integer0 },
73
                { "integer", "weighty", "validate", integer0 },
74
                { "choice", "halign", "validate",
75
                    new String[] { "fill", "center", "left", "right" } },
76
                { "choice", "valign", "validate",
77
                new String[] { "fill", "center", "top", "bottom" } },
78
                // component class String null*
79
                // parent Object null
80
                // (bounds) Rectangle 0 0 0 0
81
                { "property", "property", null, null },
82
                { "method", "init" },
83
                { "method", "focuslost" },
84
                { "method", "focusgained" } },
85
            "label", "component", new Object[][] {
86
                { "string", "text", "validate", null },
87
                { "icon", "icon", "validate", null },
88
                { "choice", "alignment", "validate", leftcenterright },
89
                { "integer", "mnemonic", "paint", integer_1 },
90
                { "component", "for", null, null } },
91
            "button", "label", new Object[][] {
92
                { "choice", "alignment", "validate", new String[] { "center", "left", "right" } },
93
                { "method", "action" },
94
                { "choice", "type", "paint", new String[] { "normal", "default", "cancel", "link" } } },
95
            "checkbox", "label", new Object[][] {
96
                { "boolean", "selected", "paint", Boolean.FALSE }, //...group
97
                { "string", "group", "paint", null }, //...group
98
                { "method", "action" } },
99
            "togglebutton", "checkbox", null,
100
            "combobox", "textfield", new Object[][] {
101
                { "icon", "icon", "validate", null },
102
                { "integer", "selected", "layout", integer_1 } },
103
            "choice", null, new Object[][] {
104
                { "string", "name", null, null },
105
                { "boolean", "enabled", "paint", Boolean.TRUE },
106
                { "boolean", "i18n", "validate", Boolean.FALSE }, // for I18N
107
                { "string", "text", "parent", null },
108
                { "icon", "icon", "parent", null },
109
                { "choice", "alignment", "parent", leftcenterright },
110
                { "string", "tooltip", null, null },
111
                { "font", "font", "validate", null },
112
                { "color", "foreground", "paint", null },
113
                { "color", "background", "paint", null },
114
                { "property", "property", null, null } },
115
            "textfield", "component", new Object[][] {
116
                { "string", "text", "layout", "" },
117
                { "integer", "columns", "validate", integer0 },
118
                { "boolean", "editable", "paint", Boolean.TRUE },
119
                { "integer", "start", "layout", integer0 },
120
                { "integer", "end", "layout", integer0 },
121
                { "method", "action" },
122
                { "method", "insert" },
123
                { "method", "remove" },
124
                { "method", "caret" },
125
                { "method", "perform" } },
126
            "passwordfield", "textfield", null,
127
            "textarea", "textfield", new Object[][] {
128
                { "integer", "rows", "validate", integer0 },
129
                { "boolean", "border", "validate", Boolean.TRUE },
130
                { "boolean", "wrap", "layout", Boolean.FALSE } },
131
            "tabbedpane", "component", new Object[][] {
132
                { "choice", "placement", "validate",
133
                    new String[] { "top", "left", "bottom", "right", "stacked" } },
134
                { "integer", "selected", "paint", integer0 },
135
                { "method", "action" } }, //...focus
136
            "tab", "choice", new Object[][] {
137
                { "integer", "mnemonic", "paint", integer_1 } },
138
            "panel", "component", new Object[][] {
139
                { "integer", "columns", "validate", integer0 },
140
                { "integer", "top", "validate", integer0 },
141
                { "integer", "left", "validate", integer0 },
142
                { "integer", "bottom", "validate", integer0 },
143
                { "integer", "right", "validate", integer0 },
144
                { "integer", "gap", "validate", integer0 },
145
                { "string", "text", "validate", null },
146
                { "icon", "icon", "validate", null },
147
                { "boolean", "border", "validate", Boolean.FALSE },
148
                { "boolean", "scrollable", "validate", Boolean.FALSE } },
149
            "desktop", "component", null,
150
            "dialog", "panel", new Object[][] {
151
                { "boolean", "modal", null, Boolean.FALSE },
152
                { "boolean", "resizable", null, Boolean.FALSE },
153
                { "method", "close" },
154
                { "boolean", "maximizable", "paint", Boolean.FALSE },
155
                { "boolean", "iconifiable", "paint", Boolean.FALSE },
156
                // closable allowed but deprecated, cannot be edited
157
                { "boolean", "closable", null, Boolean.FALSE } },
158
            "spinbox", "textfield", new Object[][] {
159
                { "integer", "minimum", null, new Integer(Integer.MIN_VALUE) },
160
                { "integer", "maximum", null, new Integer(Integer.MAX_VALUE) },
161
                { "integer", "step", null, integer1 },
162
                { "integer", "value", null, integer0 } }, // == text? deprecated
163
            "progressbar", "component", new Object[][] {
164
                { "choice", "orientation", "validate", orientation },
165
                { "integer", "minimum", "paint", integer0 }, //...checkvalue
166
                { "integer", "maximum", "paint", new Integer(100) },
167
                { "integer", "value", "paint", integer0 } },
168
                // change stringpainted
169
            "slider", "progressbar", new Object[][] {
170
                { "integer", "unit", null, new Integer(5) },
171
                { "integer", "block", null, new Integer(25) },
172
                { "method", "action" } },
173
                // minor/majortickspacing
174
                // inverted
175
                // labelincrement labelstart
176
            "splitpane", "component", new Object[][] {
177
                { "choice", "orientation", "validate", orientation },
178
                { "integer", "divider", "layout", integer_1 } },
179
            "list", "component", new Object[][] {
180
                { "choice", "selection", "paint", selections },
181
                { "method", "action" },
182
                { "method", "perform" },
183
                { "boolean", "line", "validate", Boolean.TRUE } },
184
            "item", "choice", new Object[][] {
185
                { "boolean", "selected", null, Boolean.FALSE } },
186
            "table", "list", new Object[][] {
187
                /*{ "choice", "selection",
188
                    new String[] { "singlerow", "rowinterval", "multiplerow",
189
                        "cell", "cellinterval",
190
                        "singlecolumn", "columninterval", "multiplecolumn" } }*/ },
191
            "header", null, new Object[][] {
192
                { "method", "action" },
193
                { "boolean", "resizable", null, Boolean.TRUE } },
194
                // reordering allowed
195
                // autoresize mode: off next (column boundries) subsequents last all columns
196
                // column row selection
197
                // selection row column cell
198
                // editing row/column
199
            "column", "choice", new Object[][] {
200
                { "integer", "width", null, new Integer(80) },
201
                { "choice", "sort", null, new String[] { "none", "ascent", "descent" } },
202
                { "boolean", "selected", null, Boolean.FALSE } },
203
            "row", null, new Object[][] {
204
                { "boolean", "selected", null, Boolean.FALSE } },
205
            "cell", "choice", null,
206
            "tree", "list", new Object[][] {
207
                { "boolean", "angle", null, Boolean.FALSE },
208
                { "method", "expand" },
209
                { "method", "collapse" } },
210
            "node", "choice", new Object[][] {
211
                { "boolean", "selected", null, Boolean.FALSE },
212
                { "boolean", "expanded", null, Boolean.TRUE } },
213
            "separator", "component", null,
214
            "menubar", "component", new Object[][] {
215
                { "choice", "placement", "validate", new String[] { "top", "bottom" } } },
216
            "menu", "choice", new Object[][] {
217
                { "integer", "mnemonic", "paint", integer_1 } },
218
            "menuitem", "choice", new Object[][] {
219
                { "keystroke", "accelerator", null, null },
220
                { "method", "action" },
221
                { "integer", "mnemonic", "paint", integer_1 } },
222
            "checkboxmenuitem", "menuitem", new Object[][] {
223
                { "boolean", "selected", "paint", Boolean.FALSE }, //...group
224
                { "string", "group", "paint", null } }, //...group
225
            "popupmenu", "component", new Object[][] {
226
                { "method", "menushown" } }, // Post menu: Shift+F10
227
            "bean", "component", new Object[][] {
228
                { "bean", "bean", null, null } }
229
        };
230
    }
231
    //}}}
232

    
233

    
234
    //{{{ class initialization
235
    private static final String[] components =
236
    {
237
        "bean",  "button",  "checkbox",  "combobox",  "desktop",  "dialog",
238
        "label",  "list",  "menubar",  "panel",  "passwordfield",  "popupmenu",
239
        "progressbar",  "separator",  "slider",  "spinbox",  "splitpane",
240
        "tabbedpane",  "table",  "textarea",  "textfield",  "togglebutton",
241
        "tree"
242
    };
243

    
244
    private static final Object[] allowedSubWidgets =
245
    {
246
        // component        add allowed for...
247
        // ---------------- ---------------------------------------------------
248
        "bean",             new String[] { "popupmenu" },
249
        "button",           new String[] { "popupmenu" },
250
        "cell",             new String[] { },
251
        "checkbox",         new String[] { "popupmenu" },
252
        "checkboxmenuitem", new String[] { },
253
        "choice",           new String[] { },
254
        "column",           new String[] { },
255
        "combobox",         new String[] { "choice", "popupmenu" },
256
        "desktop",          components,
257
        "dialog",           components,
258
        "header",           new String[] { "column" },
259
        "item",             new String[] { },
260
        "label",            new String[] { "popupmenu" },
261
        "list",             new String[] { "item", "popupmenu" },
262
        "menu",             new String[] { "menu", "menuitem", "checkboxmenuitem", "separator" },
263
        "menubar",          new String[] { "menu" },
264
        "menuitem",         new String[] { },
265
        "node",             new String[] { "node" },
266
        "panel",            components,
267
        "passwordfield",    new String[] { "popupmenu" },
268
        "popupmenu",        new String[] { "menu", "menuitem", "checkboxmenuitem", "separator" },
269
        "progressbar",      new String[] { "popupmenu" },
270
        "row",              new String[] { "cell" },
271
        "separator",        new String[] { "popupmenu" },
272
        "slider",           new String[] { "popupmenu" },
273
        "spinbox",          new String[] { "popupmenu" },
274
        "splitpane",        components,
275
        "tab",              components,
276
        "tabbedpane",       new String[] { "tab", "popupmenu" },
277
        "table",            new String[] { "row", "header", "popupmenu" },
278
        "textarea",         new String[] { "popupmenu" },
279
        "textfield",        new String[] { "popupmenu" },
280
        "togglebutton",     new String[] { "popupmenu" },
281
        "tree",             new String[] { "node", "popupmenu" }
282
    };
283

    
284

    
285
    private static final HashMap widgets;
286

    
287
    static
288
    {
289
        widgets = new HashMap();
290

    
291
        for(int i = 0; i < dtd.length; i += 3)
292
        {
293
            // create Widget
294
            String classname = (String) dtd[i];
295
            Widget widget = new Widget(classname);
296
            widgets.put(classname, widget);
297
            // add properties
298
            Object[][] propDefs = (Object[][]) dtd[i + 2];
299
            Property[] properties = new Property[0];
300
            if(propDefs != null)
301
            {
302
                TreeSet props = new TreeSet();
303
                for(int j = 0; j < propDefs.length; ++j)
304
                {
305
                    String type = (String) propDefs[j][0];
306
                    String name = (String) propDefs[j][1];
307
                    Object defaultValue = (propDefs[j].length > 3) ? propDefs[j][3] : null;
308
                    props.add(new Property(widget, name, type, defaultValue));
309
                }
310
                properties = (Property[]) props.toArray(new Property[props.size()]);
311
            }
312
            widget.setProperties(properties);
313
        }
314

    
315
        // set parent-child relationship
316
        for(int i = 0; i < dtd.length; i += 3)
317
        {
318
            String classname = (String) dtd[i];
319
            String parentclassname = (String) dtd[i + 1];
320
            Widget widget = (Widget) widgets.get(classname);
321
            Widget parentWidget = (Widget) widgets.get(parentclassname);
322
            widget.setParent(parentWidget);
323
        }
324

    
325
        // set add-allowed relationship
326
        for(int i = 0; i < allowedSubWidgets.length; i += 2)
327
        {
328
            String classname = (String) allowedSubWidgets[i];
329
            String[] allowed = (String[]) allowedSubWidgets[i + 1];
330
            if(allowed.length > 0)
331
            {
332
                Widget widget = (Widget) widgets.get(classname);
333
                for(int j = 0; j < allowed.length; ++j)
334
                {
335
                    Widget allowedWidget = (Widget) widgets.get(allowed[j]);
336
                    widget.addAllowedSubWidget(allowedWidget);
337
                }
338
            }
339
        }
340
    }
341
    //}}}
342

    
343

    
344
    public static Widget getWidget(String classname)
345
    {
346
        Widget widget = (Widget) widgets.get(classname);
347
        if(widget == null)
348
            throw new IllegalArgumentException("unknown classname: " + classname);
349
        else
350
            return widget;
351
    }
352

    
353

    
354
    public static Property[] getProperties(String classname)
355
    {
356
        return getWidget(classname).getProperties();
357
    }
358

    
359

    
360
    public static Property getProperty(String classname, String propName)
361
    {
362
        Property property = getWidget(classname).getProperty(propName);
363
        if(property != null)
364
            return property;
365
        else
366
            throw new IllegalArgumentException("unknown property: " + propName + " in class: " + classname);
367
    }
368

    
369

    
370
    //{{{ class Widget
371
    /**
372
     * A Widget represents a Thinlet component, such as "button" or "label".
373
     *
374
     * <p>
375
     * Note that Widgets are comparable. The default sort order is to sort by
376
     * name.
377
     * </p>
378
     */
379
    public static class Widget implements Comparable
380
    {
381
        private String classname;
382
        private Widget parent;
383
        private Property[] properties;
384
        private ArrayList allowedSubWidgets = new ArrayList();
385
        private boolean allPropertiesCalculated = false;
386
        private HashMap propMap = new HashMap();
387

    
388
        Widget(String classname)
389
        {
390
            this.classname = classname;
391
        }
392

    
393
        void setProperties(Property[] properties)
394
        {
395
            this.properties = properties;
396
        }
397

    
398
        void setParent(Widget parent)
399
        {
400
            this.parent = parent;
401
        }
402

    
403
        void addAllowedSubWidget(Widget w)
404
        {
405
            this.allowedSubWidgets.add(w);
406
        }
407

    
408
        public String getClassname()
409
        {
410
            return classname;
411
        }
412

    
413
        public Widget getParent()
414
        {
415
            return parent;
416
        }
417

    
418
        public String getParentClassname()
419
        {
420
            return getParent() != null ? getParent().getClassname() : null;
421
        }
422

    
423
        public Property getProperty(String propName)
424
        {
425
            calculateAllProperties();
426
            return (Property) this.propMap.get(propName);
427
        }
428

    
429
        public boolean hasProperty(String propName)
430
        {
431
            return getProperty(propName) != null;
432
        }
433

    
434
        public Property[] getProperties()
435
        {
436
            calculateAllProperties();
437
            return properties;
438
        }
439

    
440
        public Widget[] getAllowedSubWidgets()
441
        {
442
            return (Widget[]) allowedSubWidgets.toArray(new Widget[allowedSubWidgets.size()]);
443
        }
444

    
445
        public boolean isSubWidgetAllowed(Widget subWidget)
446
        {
447
            return isSubWidgetAllowed(subWidget.getClassname());
448
        }
449

    
450
        public boolean isSubWidgetAllowed(String classname)
451
        {
452
            for(Iterator it = allowedSubWidgets.iterator(); it.hasNext(); )
453
                if(classname.equals(((Widget)it.next()).getClassname()))
454
                    return true;
455
            return false;
456
        }
457

    
458
        /**
459
         * Checks whether the class of this Widget is derived from the Widget
460
         * with the specified classname, by checking the parents of this
461
         * Widget.
462
         */
463
        public boolean isInstanceOf(String classname)
464
        {
465
            if(classname.equals(getClassname()))
466
                return true;
467
            if(getParent() == null)
468
                return false;
469
            return getParent().isInstanceOf(classname);
470
        }
471

    
472
        //{{{ Comparable interface
473
        public boolean equals(Object obj)
474
        {
475
            if(obj instanceof Widget)
476
                return getClassname().equals(((Widget)obj).getClassname());
477
            else
478
                return false;
479
        }
480

    
481
        public int hashCode()
482
        {
483
            return getClassname().hashCode() + 7;
484
        }
485

    
486
        public int compareTo(Object obj)
487
        {
488
            Widget other = (Widget) obj;
489
            return this.getClassname().compareTo(other.getClassname());
490
        }
491
        //}}}
492

    
493
        public String toString()
494
        {
495
            return getClassname();
496
        }
497

    
498
        private void calculateAllProperties()
499
        {
500
            if(!this.allPropertiesCalculated)
501
            {
502
                // add all parent properties recursively to this.properties
503
                // using a TreeSet, so that the properties are sorted.
504
                TreeSet set = new TreeSet();
505
                Widget widget = this;
506
                while(widget != null)
507
                {
508
                    set.addAll(Arrays.asList(widget.properties));
509
                    widget = widget.getParent();
510
                }
511
                this.properties = (Property[]) set.toArray(new Property[set.size()]);
512

    
513
                // build map for fast access by property name
514
                for(int i=0; i < this.properties.length; ++i)
515
                    this.propMap.put(this.properties[i].getName(), this.properties[i]);
516

    
517
                this.allPropertiesCalculated = true;
518
            }
519
        }
520
    }
521
    //}}}
522

    
523

    
524
    //{{{ class Property
525
    /**
526
     * A Property represents an attribute of a Thinlet component, such as
527
     * "width", "name" or "action".
528
     *
529
     * <p>
530
     * Note that Properties are comparable. The default sort order is to sort
531
     * by its parenting widget first, then by name.
532
     * </p>
533
     */
534
    public static class Property implements Comparable
535
    {
536
        public static final int STRING = 0;
537
        public static final int INTEGER = 1;
538
        public static final int BOOLEAN = 2;
539
        public static final int CHOICE = 3;
540
        public static final int COLOR = 4;
541
        public static final int ICON = 5;
542
        public static final int FONT = 6;
543
        public static final int KEYSTROKE = 7;
544
        public static final int METHOD = 8;
545
        public static final int BEAN = 9;
546
        public static final int PROPERTY = 10;
547
        public static final int COMPONENT = 11; // see "label.for" property
548

    
549
        private static final String[] allTypes = { "string", "integer",
550
            "boolean", "choice", "color", "icon", "font", "keystroke",
551
            "method", "bean", "property", "component" };
552

    
553
        private Widget widget;
554
        private String name;
555
        private int type;
556
        private Object defaultValue;
557

    
558
        Property(Widget widget, String name, String type, Object defaultValue)
559
        {
560
            this.widget = widget;
561
            this.name = name;
562
            this.defaultValue = defaultValue;
563
            this.type = -1;
564

    
565
            for(int i = 0; i < allTypes.length; ++i)
566
            {
567
                if(allTypes[i].equals(type))
568
                {
569
                    this.type = i;
570
                    break;
571
                }
572
            }
573

    
574
            if(this.type == -1)
575
                throw new IllegalArgumentException("unknown property type: " + type);
576
        }
577

    
578
        public Widget getWidget()
579
        {
580
            return widget;
581
        }
582

    
583
        public int getType()
584
        {
585
            return type;
586
        }
587

    
588
        public String getName()
589
        {
590
            return name;
591
        }
592

    
593
        public Object getDefaultValue()
594
        {
595
            return (type == CHOICE) ? getChoices()[0] : defaultValue;
596
        }
597

    
598
        /**
599
         * If this property is of type {@link #CHOICE} then return the possible
600
         * choices, otherwise return null.
601
         */
602
        public String[] getChoices()
603
        {
604
            return (type == CHOICE) ? (String[]) defaultValue : null;
605
        }
606

    
607
        public String getTypeName()
608
        {
609
            return getTypeName(getType());
610
        }
611

    
612
        public static String getTypeName(int type)
613
        {
614
            return allTypes[type];
615
        }
616

    
617
        //{{{ Comparable interface
618
        public boolean equals(Object obj)
619
        {
620
            if(obj instanceof Property)
621
            {
622
                Property prop = (Property)obj;
623
                return this.getWidget().equals(prop.getWidget())
624
                    && this.getName().equals(prop.getName());
625
            }
626
            else
627
                return false;
628
        }
629

    
630
        public int hashCode()
631
        {
632
            return getWidget().hashCode() * 3 + getName().hashCode() + 7;
633
        }
634

    
635
        public int compareTo(Object obj)
636
        {
637
            Property other = (Property) obj;
638
            return this.getName().compareTo(other.getName());
639
        }
640
        //}}}
641

    
642
        public String toString()
643
        {
644
            return getName();
645
        }
646
    }
647
    //}}}
648

    
649
}
650