1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * ident	"%Z%%M%	%I%	%E% SMI"
24 *
25 * Copyright (c) 2000 by Sun Microsystems, Inc.
26 * All rights reserved.
27 */
28
29/*
30 *        Copyright (C) 1996  Active Software, Inc.
31 *                  All rights reserved.
32 *
33 * @(#) Converter.java 1.65 - last change made 08/20/97
34 */
35
36package sunsoft.jws.visual.rt.type;
37
38import sunsoft.jws.visual.rt.base.Global;
39
40import sunsoft.jws.visual.rt.base.*;
41
42import java.util.*;
43
44/**
45* Base class for all converters.  Converts a type of
46* object to a string
47* and back again.
48*
49* @version 1.65, 08/20/97
50*/
51
52public abstract class Converter {
53    /**
54     * Table of names for each registered converter.
55     */
56    private static Hashtable converterNameTable = new Hashtable();
57
58    /**
59     * Table of instances for each converter that has
60     * been instantiated.
61    */
62    private static Hashtable converterInstanceTable = new Hashtable();
63
64    /**
65     * Adds a new type converter to the global table of converters.  A
66     * converter must be listed for this table in order for the search
67     * for a converter for that particular type to be successful.
68     *
69     * @param typeName the name of the type (what is returned by a
70     * call to getClass().getType() for an instance of that type)
71     * @param converterClassName the full name of the converter class
72     */
73    public static void addConverter(String typeName,
74				    String converterClassName) {
75        converterNameTable.put(typeName, converterClassName);
76    }
77
78    /**
79     * Initialize the type converters for the types we know about.
80     */
81    static {
82        addConverter(/* NOI18N */"[I", /* NOI18N */
83		     "sunsoft.jws.visual.rt.type.IntArrayConverter");
84        addConverter(/* NOI18N */"[D", /* NOI18N */
85		     "sunsoft.jws.visual.rt.type.DoubleArrayConverter");
86        addConverter(/* NOI18N */"java.lang.String",
87		     /* NOI18N */"sunsoft.jws.visual.rt.type.StringConverter");
88        addConverter(/* NOI18N */"[Ljava.lang.String;",
89	     /* NOI18N */"sunsoft.jws.visual.rt.type.StringArrayConverter");
90        addConverter(/* NOI18N */"java.lang.Boolean",
91	     /* NOI18N */"sunsoft.jws.visual.rt.type.BooleanConverter");
92        addConverter(/* NOI18N */"java.lang.Character",
93	     /* NOI18N */"sunsoft.jws.visual.rt.type.CharacterConverter");
94        addConverter(/* NOI18N */"java.lang.Integer",
95	     /* NOI18N */"sunsoft.jws.visual.rt.type.IntegerConverter");
96        addConverter(/* NOI18N */"java.awt.Color",
97		     /* NOI18N */"sunsoft.jws.visual.rt.type.ColorConverter");
98        addConverter(/* NOI18N */"java.awt.SystemColor",
99		     /* NOI18N */"sunsoft.jws.visual.rt.type.ColorConverter");
100        addConverter(/* NOI18N */"java.awt.Font",
101		     /* NOI18N */"sunsoft.jws.visual.rt.type.FontConverter");
102        addConverter(/* NOI18N */"java.awt.Point",
103		     /* NOI18N */"sunsoft.jws.visual.rt.type.PointConverter");
104        addConverter(/* NOI18N */"java.awt.Dimension",
105	     /* NOI18N */"sunsoft.jws.visual.rt.type.DimensionConverter");
106        addConverter(/* NOI18N */"java.awt.Insets",
107		     /* NOI18N */"sunsoft.jws.visual.rt.type.InsetsConverter");
108        addConverter(/* NOI18N */
109		     "sunsoft.jws.visual.rt.awt.GBConstraints",
110	     /* NOI18N */"sunsoft.jws.visual.rt.type.GBConstraintsConverter");
111        addConverter(/* NOI18N */
112		     "sunsoft.jws.visual.rt.base.AttributeManager",
113		     /* NOI18N */"sunsoft.jws.visual.rt.type.AMConverter");
114        addConverter(/* NOI18N */"sunsoft.jws.visual.rt.type.AMRef",
115		     /* NOI18N */"sunsoft.jws.visual.rt.type.AMRefConverter");
116        addConverter(/* NOI18N */
117		     "sunsoft.jws.visual.rt.base.Attribute",
118	     /* NOI18N */"sunsoft.jws.visual.rt.type.AttributeConverter");
119        addConverter(/* NOI18N */
120		     "sunsoft.jws.visual.rt.base.AttributeList",
121	     /* NOI18N */"sunsoft.jws.visual.rt.type.AttributeListConverter");
122        addConverter(/* NOI18N */"sunsoft.jws.visual.rt.type.ImageRef",
123	     /* NOI18N */"sunsoft.jws.visual.rt.type.ImageRefConverter");
124        addConverter(/* NOI18N */
125		     "sunsoft.jws.visual.rt.type.AlignmentEnum",
126	     /* NOI18N */"sunsoft.jws.visual.rt.type.BaseEnumConverter");
127        addConverter(/* NOI18N */
128		     "sunsoft.jws.visual.rt.type.AnchorEnum",
129	     /* NOI18N */"sunsoft.jws.visual.rt.type.BaseEnumConverter");
130        addConverter(/* NOI18N */
131		     "sunsoft.jws.visual.rt.type.OrientationEnum",
132	     /* NOI18N */"sunsoft.jws.visual.rt.type.BaseEnumConverter");
133        addConverter(/* NOI18N */
134		     "sunsoft.jws.visual.rt.type.ReliefEnum",
135	     /* NOI18N */"sunsoft.jws.visual.rt.type.BaseEnumConverter");
136        addConverter(/* NOI18N */"sunsoft.jws.visual.rt.type.ModeEnum",
137	     /* NOI18N */"sunsoft.jws.visual.rt.type.BaseEnumConverter");
138        addConverter(/* NOI18N */"unknown", /* NOI18N */
139		     "sunsoft.jws.visual.rt.type.UnknownTypeConverter");
140    }
141
142    /**
143     * Returns an existing converter for the given type.  Creates a new
144     * converter only if necessary (typically the first
145     * time one is asked for.)
146    */
147    public static Converter getConverter(String typeName) {
148        Converter converter;
149
150        converter = (Converter)converterInstanceTable.get(typeName);
151        if (converter != null)
152            return converter;
153
154        String converterType = (String) converterNameTable.get
155	    (typeName);
156        if (converterType == null) {
157            /* JSTYLED */
158	    // Load the class for the type and try again. Some types have
159            // static initializers that register their converters.
160            loadType(typeName);
161            converterType = (String) converterNameTable.get(typeName);
162        }
163
164        if (converterType == null) {
165            converterType = (String) converterNameTable.get
166		(/* NOI18N */"unknown");
167            if (converterType == null)
168                /* JSTYLED */
169		throw new Error(Global.getMsg("sunsoft.jws.visual.rt.type.Converter.No__converter__defined.20"));
170        }
171        try {
172            Class c = Class.forName(converterType);
173            converter = (Converter) c.newInstance();
174            converter.setConverterType(typeName);
175            converterInstanceTable.put(typeName, converter);
176            return converter;
177        }
178        catch (Exception e) {
179            throw new Error(e.getMessage());
180        }
181    }
182
183    private static void loadType(String typeName) {
184        // For arrays, use the array type
185        if (typeName.charAt(0) == /* NOI18N */ '[') {
186            int i;
187            int len = typeName.length();
188            for (i = 0; i < len; i++) {
189                if (typeName.charAt(i) != /* NOI18N */ '[')
190                    break;
191            }
192            i++;
193            if (i < len)
194                typeName = typeName.substring(i, len-1);
195        }
196
197        try {
198            Class.forName(typeName);
199        }
200        catch (ClassNotFoundException ex) {
201            /* JSTYLED */
202	    System.out.println(Global.getMsg("sunsoft.jws.visual.rt.type.Converter.Class__not__found__for__.21") + typeName + /* NOI18N */"\".");
203        }
204    }
205
206    /**
207     * Returns true if there is a converter for the given type.
208     */
209    public static boolean hasConverter(String typeName) {
210        return (converterNameTable.containsKey(typeName));
211    }
212
213    /**
214     * The type editors (for more complex types.)
215     */
216    private static Hashtable typeEditorNameTable = new Hashtable();
217
218    /**
219     * Registers a type editor for a type.  At run-time (in generated
220     * applications) there will typically be no editors, but they are
221     * needed for the attribute editor in the designer.  The designer
222     * will set up all the standard ones.
223     *
224     * @see TypeEditor
225     */
226    public static void addTypeEditor(String typeName,
227				     String editorClassName) {
228        typeEditorNameTable.put(typeName, editorClassName);
229    }
230
231    /**
232     * Returns true if there is an editor for the given type.
233     *
234     * @see TypeEditor
235     */
236    public static boolean hasTypeEditor(String typeName) {
237        return (typeEditorNameTable.containsKey(typeName));
238    }
239
240    /* BEGIN JSTYLED */
241    /**
242     * Returns a new instance of a type editor.
243     * The caller (typically the
244     * Designer) gets a new one of these every time, one for each
245     * attribute being edited, even if they are the same type.  Caching
246     * instances of these type editors is up to the caller.
247     */
248    /* END JSTYLED */
249
250    public static TypeEditor newTypeEditor(String typeName) {
251        String editorType = (String) typeEditorNameTable.get(typeName);
252
253        if (editorType != null) {
254            try {
255                // instances of type editors are NOT cached
256                Class c = Class.forName(editorType);
257                return ((TypeEditor) c.newInstance());
258            }
259            catch (Exception ex) {
260                /* JSTYLED */
261		throw new VJException(Global.newline() + /* NOI18N */"    " + ex.toString());
262            }
263        }
264
265        return null;
266    }
267
268    /**
269     * Returns whether a converter instance has an
270     * associated type editor.
271     *
272     * @see TypeEditor
273     */
274    public boolean hasTypeEditor() {
275        return (hasTypeEditor(getConverterType()));
276    }
277
278    /**
279     * Returns a new instance of the type editor associated with this
280     * converter.
281     */
282    public TypeEditor newTypeEditor() {
283        return (newTypeEditor(getConverterType()));
284    }
285    /* JSTYLED */
286    // ------ Interfaces for Sub-Classers -----------------------------------
287
288    /**
289     * The name of the type being edited.
290     */
291    protected String converterType;
292
293    /**
294     * An interface that can be overridden in sub-classes
295     * to whom the type
296     * converted is important.
297     *
298     * @see BaseEnumConverter
299     */
300    protected void setConverterType(String type) {
301        converterType = type;
302    }
303
304    /**
305     * Returns the type of object converted by this converter.
306     */
307    public String getConverterType() {
308        return (converterType);
309    }
310
311    /* BEGIN JSTYLED */
312    /**
313     * Returns the string representation for an instance of
314     * the type this
315     * converter converts.  Must be declared in subclasses
316     * to convert an
317     * object of the type specific to that subclass of Converter.
318     * <p>
319     * One of the two "convertToString" methods must be overridden in
320     * the converter sub-class.  The overridden "convertToString"
321     * method
322     * should NOT call "super.convertToString".  It is preferrable to
323     * override the StringBuffer version (the other one) because this
324     * will result in better performance.
325     */
326    /* END JSTYLED */
327    public String convertToString(Object obj) {
328        enterConvert(TOSTRING, false);
329        StringBuffer buf = new StringBuffer();
330        convertToString(obj, buf);
331        exitConvert(TOSTRING, false);
332
333        return buf.toString();
334    }
335
336    /**
337     * Places a string representation of an instance of the type this
338     * converter converts into a string buffer.
339     */
340    public void convertToString(Object obj, StringBuffer buf) {
341        enterConvert(TOSTRING, true);
342        buf.append(convertToString(obj));
343        exitConvert(TOSTRING, true);
344    }
345
346    /**
347     * Returns a new instance of the type this converter converts, as
348     * specified by the string given.  Must be declared
349     * in subclasses of
350     * Converter to convert a string representation into an object of
351     * the type converted by the subclass.
352     */
353    public abstract Object convertFromString(String s);
354
355    /**
356     * Converts an instance of the type into a block of code.
357     */
358    public void convertToCodeBlock(String amName,
359				   Attribute a, int indent, StringBuffer buf) {
360
361        Converter c = getConverter(a.getType());
362        String attr_name;
363
364        indent(buf, indent);
365        buf.append(amName);
366        buf.append(/* NOI18N */".set(\"");
367        attr_name = a.getName();
368        buf.append(attr_name);
369        buf.append(/* NOI18N */"\", ");
370        buf.append(c.convertToCode(a.getValue()));
371        buf.append(/* NOI18N */");");
372        newline(buf);
373    }
374
375    /**
376     * Converts an instance of the type converted into a line of code.
377     * This method provides a default way for any type to get a
378     * convertToCode method into it.  It generates code that will feed
379     * the string representation of the object into the
380     * appropriate type
381     * converter.  The performance isn't as good as customized
382     * convertToCode functions in subclasses since more classes have to
383     * be loaded at runtime.
384     */
385    public String convertToCode(Object obj) {
386        if (obj != null)
387            return (/* NOI18N */"convert(\"" +
388		    obj.getClass().getName() + /* NOI18N */"\", \""
389		    + convertToString(obj) + /* NOI18N */"\")");
390        else
391            return (/* NOI18N */"null");
392    }
393
394    /**
395     * Returns the string that should be displayed in the attribute
396     * editor.  Subclassers that want something displayed other than
397     * what is returned from convertToString should override this
398     * method to return that.
399     */
400    public String displayString(Object obj) {
401        return (convertToString(obj));
402    }
403
404    /**
405     * Returns true if this type should be displayed in an editor.
406     *
407     * For the attribute editor, a return value of false means that the
408     * the textfield will be hidden.
409     *
410     * @return true
411     */
412    public boolean viewableAsString() {
413        return true;
414    }
415
416    /**
417     * Returns true if this type is simple enough to be
418     * edited as a string
419     * in an editor.
420     *
421     * Sub-classers that represent type too complex for
422     * this should override
423     * this function to return false.  For the attribute editor,
424     * this means
425     * that the textfield will be read-only.
426     *
427     * @see #viewableAsString
428     * @return same as viewableAsString
429     */
430    public boolean editableAsString() {
431        return viewableAsString();
432    }
433
434    /**
435     * These weird looking enter/exit methods ensure that the converter
436     * sub-class is overriding at least one of the "convertToString"
437     * methods, and at least one of the "convertToCode" methods.
438     * An error will be thrown at runtime if this in not the case.
439     * If this check wasn't done here , then the failure to
440     * override one
441     * of the methods would result in an infinite loop.
442     */
443    private static final int TOSTRING = 0;
444    private static final int TOCODE = 1;
445
446    private boolean converting[] = {false, false};
447    private boolean isBuffered[] = {false, false};
448    private int convertRecurse[] = {0, 0};
449
450    private void enterConvert(int c, boolean isBuffered) {
451        if (converting[c] && this.isBuffered[c] != isBuffered)
452            throw new Error(Global.getMsg(
453	  "sunsoft.jws.visual.rt.type.Converter.Sub-classes__of__Conve.22"));
454
455        this.isBuffered[c] = isBuffered;
456        converting[c] = true;
457        convertRecurse[c]++;
458    }
459
460    private void exitConvert(int c, boolean isBuffered) {
461        if (!converting[c])
462	    /* BEGIN JSTYLED */
463	    throw new Error(Global.getMsg("sunsoft.jws.visual.rt.type.Converter.Convert__exit__without.25"));
464
465	if (this.isBuffered[c] != isBuffered)
466	    throw new Error(Global.getMsg("sunsoft.jws.visual.rt.type.Converter.isBuffered__mismatch__.26"));
467
468	/* END JSTYLED */
469	convertRecurse[c]--;
470        if (convertRecurse[c] == 0)
471            converting[c] = false;
472    }
473    /* BEGIN JSTYLED */
474    // ------ Utility Functions ----------------------------------------------
475
476    /**
477     * Returns a string that can be used as a newline.
478     * This string includes
479     * a carriage return if we are running on Windows.
480     */
481    /* END JSTYLED */
482    public static String newline() {
483        return Global.newline();
484    }
485
486    /**
487     * Appends a newline to buf.  This also appends a carriage return
488     * if we are running on Windows.
489     */
490    public static void newline(StringBuffer buf) {
491        Global.newline(buf);
492    }
493
494    private static final String indentString = /* NOI18N */"  ";
495    private static int indentLevel = 0;
496
497    /**
498     * Appends spaces to "buf" based on the current indent level.
499     */
500    protected static void indent(StringBuffer buf) {
501        for (int i = 0; i < indentLevel; i++)
502            buf.append(indentString);
503    }
504
505    /**
506     * Appends spaces to "buf" based on the given indent level.
507     */
508    protected static void indent(StringBuffer buf, int indentLevel) {
509        for (int i = 0; i < indentLevel; i++)
510            buf.append(/* NOI18N */ ' ');
511    }
512
513    /**
514     * Increments the indent level.
515     */
516    protected static void incrIndent() {
517        indentLevel++;
518    }
519
520    /**
521     * Decrements the indent level.
522     */
523    protected static void decrIndent() {
524        indentLevel--;
525    }
526
527    /**
528     * Returns the current indent level.
529     */
530    protected static int indentLevel() {
531        return indentLevel;
532    }
533
534    /**
535     * Returns the last token in a class name.  i.e. the name that you
536     * can use for a class when you've imported the class already.
537     */
538    public static String shortClassName(String className) {
539        int index = className.lastIndexOf(/* NOI18N */ '.');
540        if (index == -1)
541            return (className);
542        else
543            return (className.substring(index + 1));
544    }
545
546}
547