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 * @(#) AMConverter.java 1.63 - last change made 06/25/97
34 */
35
36package sunsoft.jws.visual.rt.type;
37
38import sunsoft.jws.visual.rt.base.Global;
39
40import sunsoft.jws.visual.rt.base.*;
41import sunsoft.jws.visual.rt.shadow.java.awt.*;
42import sunsoft.jws.visual.rt.awt.CardPanel;
43import sunsoft.jws.visual.rt.shadow.CardPanelShadow;
44import java.util.*;
45
46/**
47 * This class can convert attribute managment trees to strings and
48 * such strings back again to attribute management trees.  The string
49 * produced is a complete description of an application, and so it can
50 * be used to save a tree to a file.
51 *
52 * @see AttributeManager
53 * @version 1.63, 06/25/97
54 */
55public class AMConverter extends Converter {
56
57    private static AttributeListConverter attrlistconv =
58	new AttributeListConverter();
59
60    public AMConverter() {
61    }
62
63    /**
64     * List of paths (package prefixes) to groups of Shadow classes
65     * also a few individual Shadow classes.  The Hashtables are for
66     * caching the matches after they are discovered.
67     */
68    private static Vector shadows = new Vector();
69    private static Hashtable shortShadowKeyed = new Hashtable();
70    private static Hashtable longShadowKeyed = new Hashtable();
71
72    /**
73     * Caches the matches after they are discovered.
74     */
75    private static void cacheShadowPair(String shortName,
76					String longName) {
77        shortShadowKeyed.put(shortName, longName);
78        longShadowKeyed.put(longName, shortName);
79    }
80
81    /**
82     * Adds the name of a package where custom shadow
83     * classes can be found.
84     * Package names in this list always end with a "."
85     */
86    private static void addShadowPath(String pkgName) {
87        if (pkgName.endsWith(/* NOI18N */"."))
88            shadows.addElement(pkgName);
89        else
90            shadows.addElement(/* NOI18N */(pkgName + "."));
91    }
92
93    /**
94     * Adds a specific shadow class to the list.
95     * Specific shadow classes
96     * in the list never end with a "."
97     */
98    private static void addShadowItem(String className) {
99        cacheShadowPair(Converter.shortClassName(className),
100			className);
101    }
102
103    static {
104        // add packages where shadow classes might be found
105        //
106        // Don't add any more of these or the quick
107        // code generation will
108        // be screwed!
109        addShadowPath(/* NOI18N */"sunsoft.jws.visual.rt.shadow");
110
111        // add individual exceptions
112        addShadowItem
113	    (/* NOI18N */"sunsoft.jws.visual.rt.base.Root");
114    }
115
116    /**
117     * Figures out the short name for a shadow class.  Removes the
118     * initial component of a class name if it's one of the currently
119     * listed paths for finding shadow classes.  The result is the
120     * abbreviated name for the shadow class that can be placed in the
121     * save file.
122     *
123     * Warning: you should not nest shadow packages in the path within
124     * each other, as this routine might return the wrong shortened
125     * form
126     * of the class name.
127     */
128    private static String shortenShadowPath(String longClassName) {
129        // return the cached value if available
130        if (longShadowKeyed.containsKey(longClassName))
131            return ((String) longShadowKeyed.get(longClassName));
132        /* JSTYLED */
133	for (Enumeration e = shadows.elements(); e.hasMoreElements(); ) {
134	    String s = (String) e.nextElement();
135	    if (longClassName.startsWith(s))
136		return (longClassName.substring(s.length()));
137	}
138
139	// the long class name was not found in any of the paths
140	return (longClassName);
141    }
142
143    /**
144     * Searches the currently listed shadow paths for the shadow class
145     * given.  Returns a runtime class reference to the shadow class
146     * once it finds that class under one of the paths (or, in the end,
147     * under the actual name given.)
148     *
149     * This is basically the reverse of shortenShadowPath.
150     */
151    private static Class searchShadowPath(String shortClassName) {
152	// return the cached value if available
153	if (shortShadowKeyed.containsKey(shortClassName)) {
154	    try {
155		return (Global.util.getClassLoader().loadClass
156			((String) shortShadowKeyed.get
157			 (shortClassName)));
158	    }
159	    catch (ClassNotFoundException ex) {
160		// that didn't work, silently try something else...
161            }
162	}
163
164	Class retval = null;
165	/* JSTYLED */
166	for (Enumeration e = shadows.elements(); e.hasMoreElements(); ) {
167	    String path = (String) e.nextElement();
168	    try {
169		retval = Global.util.getClassLoader().loadClass
170		    (path + shortClassName);
171		break;
172	    }
173	    catch (ClassNotFoundException ex) {
174		// that didn't work, silently try again...
175	    }
176	}
177
178	if (retval == null) {
179	    try {
180		retval = Global.util.getClassLoader().loadClass
181		    (shortClassName);
182	    }
183	    catch (ClassNotFoundException ex) {
184		// that didn't work either, how sad
185		throw new ParseException(Global.fmtMsg
186			 ("sunsoft.jws.visual.rt.type.AMConverter.FMT.0",
187					  Global.newline(), /* NOI18N */"\t",
188					  ex.toString()));
189	    }
190	}
191
192	// cache this pairing so it won't have to be looked up again
193	if (retval != null && !shortShadowKeyed.containsKey
194	    (shortClassName))
195	    cacheShadowPair(retval.getName(), shortClassName);
196
197	return (retval);
198    }
199
200    /**
201     * Creates a string from the reference to the root or branch of the
202     * attribute management tree given.  Appends the string to the given
203     * string buffer.
204     *
205     * @param obj attribute management tree reference
206     * @param buf string buffer to append to
207     * @return string that describes the tree
208     */
209    public void convertToString(Object obj, StringBuffer buf) {
210	if (obj == null)
211	    return;
212
213	// Make sure the first card is showing before saving the card panel.
214	if (obj instanceof CardPanelShadow) {
215	    CardPanel cardPanel = (CardPanel)
216		((CardPanelShadow)obj).getCardPanel();
217	    if (cardPanel != null)
218		cardPanel.first();
219	}
220
221	AttributeManager tree = (AttributeManager) obj;
222
223	//
224	// Skip over any windows that are marked as panels.
225	//
226	/* JSTYLED */
227	if ((tree instanceof WindowShadow) && ((WindowShadow)tree).isPanel()) {
228	    AttributeManager child = ((WindowShadow)tree).getPanel();
229	    if (child != null)
230		convertToString(child, buf);
231	    return;
232	}
233
234	// this object's own attributes
235	indent(buf);
236	buf.append(shortenShadowPath(tree.getClass().getName()));
237	buf.append(/* NOI18N */" ");
238
239	ListParser.quote(tree.getName(), buf, false);
240
241	buf.append(/* NOI18N */" {");
242	newline(buf);
243	incrIndent();
244	attrlistconv.convertToString(tree.getAttributeList(), buf);
245	decrIndent();
246
247	// children
248	if (tree instanceof AMContainer) {
249	    Enumeration e = ((AMContainer) tree).getChildList();
250	    AttributeManager child;
251
252	    if (e.hasMoreElements()) {
253		incrIndent();
254		indent(buf);
255		buf.append(/* NOI18N */"child list {");
256		newline(buf);
257
258		incrIndent();
259		while (e.hasMoreElements()) {
260		    child = (AttributeManager) e.nextElement();
261		    convertToString(child, buf);
262		}
263		decrIndent();
264
265		indent(buf);
266		buf.append(/* NOI18N */"}");
267		newline(buf);
268		decrIndent();
269	    }
270	}
271
272	indent(buf);
273	buf.append(/* NOI18N */"}");
274	newline(buf);
275    }
276
277    /**
278     * Call the convertFromString function that takes a version number
279     * instead.  Tree conversion cannot take place without a version
280     * number (for the string description.)
281     *
282     * @exception Error when an attempt is made to call this method
283     */
284    public Object convertFromString(String s) {
285	throw new Error(Global.getMsg(
286		/* JSTYLED */
287				      "sunsoft.jws.visual.rt.type.AMConverter.AMConverter__convertF.0"));
288    }
289
290    /**
291     * Creates a new tree based upon the description string given.
292     * There should only be one object (as the root of the tree) in the
293     * string.  That root object may contain other objects, or children,
294     * as it were.
295     *
296     * @param version the version number for the gui description string
297     * @param s the string to convert to a tree @return new shadow tree
298     * @exception ParseException when there is an error in the string
299     */
300    public Object convertFromString(double version, String s) {
301	if (s == null)
302	    return null;
303
304	// Parse the string
305	Enumeration e = ListParser.getListElements(s, 3);
306	String type = null, name = null, attr = null;
307
308	try {
309	    type = (String)e.nextElement();
310	    name = (String)e.nextElement();
311	    attr = (String)e.nextElement();
312	}
313	catch (NoSuchElementException ex) {
314	    throw new ParseException(Global.newline() +
315				     /* BEGIN JSTYLED */
316				     Global.getMsg("sunsoft.jws.visual.rt.type.AMConverter.________Incomplete__attri.1") +
317				     /* END JSTYLED */
318		     Global.newline() + /* NOI18N */"      type = " + type +
319		     Global.newline() + /* NOI18N */"      name = " + name +
320		     Global.newline() + /* NOI18N */"      attr = " + attr);
321	}
322
323	// Start recording AMRef's made during construction of tree
324	AMRef.startRecording();
325
326	// Create the attribute manager
327	AttributeManager mgr = convertParent(type, name);
328	if (mgr == null)
329	    return null;
330
331	// Parse the attributes and children
332	convertChildren(version, mgr, attr);
333
334	// Stop recording and resolve all AMRef's that were made
335	AMRef.stopRecording(mgr);
336
337	return mgr;
338    }
339
340    private AttributeManager convertParent(String type, String name) {
341	AttributeManager mgr = null;
342
343	// Instantiate a new attribute manager
344	Class onLineType = searchShadowPath(type);
345	if (onLineType == null)
346	    return null;
347
348	try {
349	    mgr = (AttributeManager) onLineType.newInstance();
350	}
351	catch (IllegalAccessException e) {
352	    /* BEGIN JSTYLED */
353	    throw new ParseException(Global.fmtMsg("sunsoft.jws.visual.rt.type.AMConverter.FMT.1", Global.getMsg("sunsoft.jws.visual.rt.type.AMConverter.Could__not__access__"), onLineType.getName()));
354	    /* END JSTYLED */
355	}
356	catch (InstantiationException e) {
357	    /* BEGIN JSTYLED */
358	    throw new ParseException(Global.fmtMsg("sunsoft.jws.visual.rt.type.AMConverter.FMT.2", Global.getMsg("sunsoft.jws.visual.rt.type.AMConverter.Could__not__instantiat.2"), onLineType.getName()));
359	    /* END JSTYLED */
360	}
361
362	if (mgr != null) {
363	    // Assign name of shadow object
364	    mgr.set(/* NOI18N */"name", name);
365	}
366
367	return mgr;
368    }
369
370    private void convertChildren(double version,
371				 AttributeManager parent, String attr) {
372	String type, name;
373	String children = attrlistconv.convertFromString
374	    (version, parent, attr);
375	if (children == null)
376	    return;
377
378	Enumeration e = ListParser.getListElements(children, 3);
379
380	while (e.hasMoreElements()) {
381	    type = null;
382	    name = null;
383	    attr = null;
384
385	    try {
386		type = (String)e.nextElement();
387		name = (String)e.nextElement();
388		attr = (String)e.nextElement();
389	    }
390	    catch (NoSuchElementException ex) {
391		throw new ParseException(Global.newline() +
392					 /* BEGIN JSTYLED */
393					 Global.getMsg("sunsoft.jws.visual.rt.type.AMConverter.________Incomplete__attri.3") +
394					 /* END JSTYLED */
395		 Global.newline() + /* NOI18N */"      type = " + type +
396		 Global.newline() + /* NOI18N */"      name = " + name +
397		 Global.newline() + /* NOI18N */"      attr = " + attr);
398	    }
399
400	    AttributeManager child = convertParent(type, name);
401	    if (child == null)
402		continue;
403
404	    //
405	    // Insert a frame around any panels that are
406	    // immediate children
407	    // of the root, and mark the frame as a panel.
408	    //
409	    if ((parent instanceof Root) &&
410		(child instanceof PanelShadow)) {
411		FrameShadow f = new FrameShadow();
412		f.isPanel(true);
413
414		((AMContainer)parent).add(f);
415		f.add(child);
416	    } else {
417		// REMIND: add error check for non-AMContainer type
418		((AMContainer)parent).add(child);
419	    }
420
421	    convertChildren(version, child, attr);
422	}
423    }
424
425    /**
426     * The conversion of shadow trees into code is performed within the
427     * designer and not implemented here.  This method should never be
428     * called.
429     *
430     * @exception Error when an attempt is made to call this method
431     */
432    public String convertToCode(Object obj) {
433	/* BEGIN JSTYLED */
434	throw new Error(Global.fmtMsg("sunsoft.jws.visual.rt.type.AMConverter.FMT.3", Global.getMsg("sunsoft.jws.visual.rt.type.AMConverter.will__not__generate__co.4"),
435				      Global.getMsg("sunsoft.jws.visual.rt.type.AMConverter.implementation__of__th.5")));
436	/* END JSTYLED */
437    }
438}
439