001/*
002 * Copyright (c) 2004-2007 by Michael Connor. All Rights Reserved.
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 *  o Redistributions of source code must retain the above copyright notice,
008 *    this list of conditions and the following disclaimer.
009 *
010 *  o Redistributions in binary form must reproduce the above copyright notice,
011 *    this list of conditions and the following disclaimer in the documentation
012 *    and/or other materials provided with the distribution.
013 *
014 *  o Neither the name of FormLayoutBuilder or Michael Connor nor the names of
015 *    its contributors may be used to endorse or promote products derived
016 *    from this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/*
031 * ComponentDef.java
032 *
033 * Created on September 30, 2004, 5:44 PM
034 */
035
036package org.mlc.swing.layout;
037
038import java.io.IOException;
039import java.io.InputStream;
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.Enumeration;
043import java.util.HashMap;
044import java.util.List;
045import java.util.Map;
046import java.util.jar.JarEntry;
047import java.util.jar.JarFile;
048
049import javax.swing.Icon;
050import javax.xml.parsers.DocumentBuilder;
051import javax.xml.parsers.DocumentBuilderFactory;
052
053import org.w3c.dom.Document;
054import org.w3c.dom.NamedNodeMap;
055import org.w3c.dom.Node;
056import org.w3c.dom.NodeList;
057
058/**
059 * A container class for all the definition data about a Component.
060 * Instances of this class make up the component palette and are
061 * used when creating and editing component data.
062 * @author Michael Connor
063@version $Id$
064@since Ptolemy II 8.0
065 */
066public class ComponentDef implements Comparable<Object> {
067    //private static final long serialVersionUID = 1L;
068
069    public String name = "";
070
071    public String iconClass = "";
072
073    public Icon icon = null;
074
075    public String declarations = "";
076
077    public String configure = "";
078
079    public String add = "";
080
081    public String remove = "";
082
083    public String imports = "";
084
085    public String preview = "";
086
087    public boolean isContainer = false;
088
089    private String description = "";
090
091    public String getDescription() {
092        return description;
093    }
094
095    public ComponentDef() {
096    }
097
098    // Stolen from Xerces 2.5.0 code - need to replace java 1.5 getTextContent method
099    final boolean hasTextContent(Node child) {
100        return child.getNodeType() != Node.COMMENT_NODE
101                && child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE;
102        //          &&
103        //            (child.getNodeType() != Node.TEXT_NODE );
104        //          ||
105        //             ((TextImpl) child).isIgnorableWhitespace() == false);
106    }
107
108    // Stolen from Xerces 2.5.0 code - need to replace java 1.5 getTextContent method
109    void getTextContent(Node anode, StringBuffer buf) {
110        Node child = anode.getFirstChild();
111        while (child != null) {
112            if (hasTextContent(child)) {
113                buf.append(child.getNodeValue());
114            }
115            child = child.getNextSibling();
116        }
117    }
118
119    // Stolen from Xerces 2.5.0 code - need to replace java 1.5 getTextContent method
120    private String getTextContent(Node anode) //throws DOMException
121    {
122        Node child = anode.getFirstChild();
123        if (child != null) {
124            Node next = child.getNextSibling();
125            if (next == null) {
126                return hasTextContent(child) ? child.getNodeValue() : "";
127            }
128            StringBuffer buf = new StringBuffer();
129            getTextContent(anode, buf);
130            return buf.toString();
131        }
132        return "";
133    }
134
135    private String doNode(Node parent, String nodeName) {
136        String temp = "";
137        Node[] nodes = getNodesNamed(parent, nodeName);
138        for (Node node : nodes) {
139            // Java 1.5 library function
140            //      temp += nodes[i].getTextContent();
141            temp += getTextContent(node);
142        }
143        return temp;
144    }
145
146    public ComponentDef(Node componentNode) {
147        Map<String, String> attributes = getAttributeMap(componentNode);
148        name = attributes.get("name");
149        iconClass = attributes.get("iconClass");
150        description = attributes.get("desc");
151        // Java 1.5 library function.
152        //    isContainer = Boolean.parseBoolean(attributes.get("container"));
153        String str = attributes.get("container");
154        if (str != null) {
155            isContainer = str.compareToIgnoreCase("true") == 0;
156        }
157
158        imports = doNode(componentNode, "imports");
159        declarations = doNode(componentNode, "declarations");
160        configure = doNode(componentNode, "configure");
161        remove = doNode(componentNode, "remove");
162        add = doNode(componentNode, "add");
163        preview = doNode(componentNode, "preview");
164    }
165
166    // KBR 12/31/05 Add a ctor for use by the "old-style" new button
167    public ComponentDef(String name, String imp, String decl, String add) {
168        this.name = name;
169        this.imports = imp;
170        this.declarations = decl;
171        this.add = add;
172    }
173
174    private static InputStream getCompFile() {
175        // pull the components.xml file out of the root of the jar file
176        String jarFileName = "formLayoutMakerx.jar";
177        JarFile jf = null;
178        try {
179            jf = new JarFile(jarFileName);
180            JarEntry je = null;
181            Enumeration entries = jf.entries();
182            while (entries.hasMoreElements()) {
183                je = (JarEntry) entries.nextElement();
184                if (je.getName().equals("components.xml")) {
185                    return jf.getInputStream(je);
186                }
187            }
188        } catch (IOException e) {
189            return null;
190        } finally {
191            if (jf != null) {
192                try {
193                    jf.close();
194                } catch (IOException ex) {
195                    System.out.println(
196                            "Failed to close \"" + jarFileName + "\": " + ex);
197                }
198            }
199        }
200        return null;
201    }
202
203    /** Creates a new instance of Component Palette. All component configurations
204    are pulled out of components.xml
205     */
206    @SuppressWarnings("unchecked")
207    public static List<ComponentDef> createComponentDefs() {
208        List<ComponentDef> components = new ArrayList<ComponentDef>();
209        InputStream paletteStream = null;
210        try {
211            paletteStream = ComponentDef.class
212                    .getResourceAsStream("components.xml");
213            if (paletteStream == null) {
214                paletteStream = getCompFile();
215            }
216            if (paletteStream == null) {
217                return components;
218            }
219
220            Document dataDocument = null;
221
222            try {
223                DocumentBuilder documentBuilder = DocumentBuilderFactory
224                        .newInstance().newDocumentBuilder();
225                dataDocument = documentBuilder.parse(paletteStream);
226                Node paletteNode = dataDocument.getDocumentElement();
227                Node[] componentNodes = getNodesNamed(paletteNode, "component");
228
229                for (Node componentNode : componentNodes) {
230                    components.add(new ComponentDef(componentNode));
231                }
232            } catch (Exception e) {
233                throw new RuntimeException("Unable to create DocumentBuilder",
234                        e);
235            }
236
237            Collections.sort(components);
238        } finally {
239            if (paletteStream != null) {
240                try {
241                    paletteStream.close();
242                } catch (Exception ex) {
243                    throw new RuntimeException(
244                            "Failed to close paletteStream " + paletteStream,
245                            ex);
246                }
247            }
248        }
249        return components;
250    }
251
252    private static Map<String, String> getAttributeMap(Node node) {
253
254        Map<String, String> attributeMap = new HashMap<String, String>();
255
256        NamedNodeMap attributes = node.getAttributes();
257        if (attributes != null) {
258            for (int index = 0; index < attributes.getLength(); index++) {
259                Node attribute = attributes.item(index);
260                attributeMap.put(attribute.getNodeName(),
261                        attribute.getNodeValue());
262            }
263        }
264
265        return attributeMap;
266    }
267
268    private static Node[] getNodesNamed(Node parent, String nodeName) {
269        NodeList children = parent.getChildNodes();
270        List<Node> childList = new ArrayList<Node>();
271        for (int i = 0; i < children.getLength(); i++) {
272            String childname = children.item(i).getNodeName();
273            if (childname != null) {
274                if (nodeName.equals(childname)) {
275                    childList.add(children.item(i));
276                }
277            }
278        }
279        Node[] result = new Node[childList.size()];
280        return childList.toArray(result);
281    }
282
283    public String getConfigure(String name) {
284        return configure.replaceAll("\\$\\{name\\}", name);
285    }
286
287    public String getImports(String name) {
288        return imports.replaceAll("\\$\\{name\\}", name);
289    }
290
291    public String getDeclarations(String name) {
292        return declarations.replaceAll("\\$\\{name\\}", name);
293    }
294
295    public String getAdd(String name) {
296        return add.replaceAll("\\$\\{name\\}", name);
297    }
298
299    /** When dragging from the palette we need a clone rather than modify
300        the original.
301     */
302    @Override
303    public ComponentDef clone() {
304        ComponentDef newone = new ComponentDef();
305        newone.name = name;
306        newone.iconClass = iconClass;
307        newone.declarations = declarations;
308        newone.configure = configure;
309        newone.add = add;
310        newone.imports = imports;
311        newone.preview = preview;
312        newone.isContainer = isContainer;
313        return newone;
314    }
315
316    /** Make it sortable on name so the palette is ordered.
317     */
318    @Override
319    public int compareTo(Object o) {
320        return name.compareTo(((ComponentDef) o).name);
321    }
322
323    public static void main(String[] args) {
324        List<ComponentDef> components = ComponentDef.createComponentDefs();
325        System.out.println(components);
326    }
327}