/* $Id: $ ***************************************************************************** * Copyright (c) 2010 Contributors - see below * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bob Tarling ***************************************************************************** */ package org.argouml.core.propertypanels.model; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.log4j.Logger; import org.argouml.model.Model; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * The cache of property panel metadata * * @author Bob Tarling */ public class MetaDataCache { private static final Logger LOG = Logger.getLogger(MetaDataCache.class); private Map<Class<?>, PanelData> cache = new HashMap<Class<?>, PanelData>(); private Map<String, Class<?>> metaTypeByName; private Map<Class<?>, String> nameByMetaType; public MetaDataCache() throws Exception { Document doc = getDocument(); NodeList nl = doc.getDocumentElement().getChildNodes(); for (int i = 0; i < nl.getLength(); ++i) { Node n = nl.item(i); if (n.getNodeName().equals("classes")) { final int size = n.getChildNodes().getLength(); nameByMetaType = new HashMap<Class<?>, String>(size); metaTypeByName = new HashMap<String, Class<?>>(size); populateClassMaps((Element) n, nameByMetaType, metaTypeByName); } else if (n.getNodeName().equals("panels")) { cache = getPanels((Element) n); } } } public PanelData get(Class<?> clazz) { Class<?>[] interfaces = clazz.getInterfaces(); for (Class interfaze : interfaces) { PanelData pd = cache.get(interfaze); if (pd != null) { return pd; } } return null; } private Document getDocument() throws IOException, DOMException, ParserConfigurationException, SAXException { final String filename; if (Model.getFacade().getUmlVersion().charAt(0) == '2') { filename = "org/argouml/core/propertypanels/model/metamodel2.xml"; } else { filename = "org/argouml/core/propertypanels/model/metamodel.xml"; } InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filename); InputSource inputSource = new InputSource(inputStream); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); return db.parse(inputSource); } private void populateClassMaps( final Element classesNode, final Map<Class<?>, String> nameByMetaType, final Map<String, Class<?>> metaTypeByName) { final NodeList nl = classesNode.getElementsByTagName("class"); for (int i = 0; i < nl.getLength(); ++i) { Node classNode = nl.item(i); String className = classNode.getTextContent(); try { final String name = classNode.getAttributes().getNamedItem("name").getNodeValue(); Class<?> clazz = Class.forName(className); metaTypeByName.put(name, clazz); nameByMetaType.put(clazz, name); } catch (ClassNotFoundException e) { LOG.error("Class not found " + className, e); } } } private Map<Class<?>, PanelData> getPanels(Element panelsNode) { final Map<Class<?>, PanelData> map = new HashMap<Class<?>, PanelData>(); final NodeList panelNodes = panelsNode.getElementsByTagName("panel"); for (int i = 0; i < panelNodes.getLength(); ++i) { Element panelNode = (Element) panelNodes.item(i); final String name = panelNode.getAttribute("name"); Class<?> clazz = metaTypeByName.get(name); if (clazz == null) { LOG.warn("No class name translation found for panel: " + name); } else { final List<Class<?>> newChildTypes = stringToMetaTypes(panelNode.getAttribute("new-child")); final List<Class<?>> newSiblingTypes = stringToMetaTypes(panelNode.getAttribute("new-sibling")); final boolean siblingNavigation = "true".equals(panelNode.getAttribute("navigate-sibling")); final PanelData pm = new PanelData(clazz, name, newChildTypes, newSiblingTypes, siblingNavigation); map.put(clazz, pm); final NodeList controlNodes = panelNode.getElementsByTagName("*"); for (int j = 0; j < controlNodes.getLength(); ++j) { Element controlNode = (Element) controlNodes.item(j); final String propertyName = controlNode.getAttribute("name"); final String label = controlNode.getAttribute("label"); final ControlData controlData = new ControlData(controlNode.getTagName(), propertyName, label); final List<Class<?>> types = stringToMetaTypes(controlNode.getAttribute("type")); for (Class<?> metaType : types) { controlData.addType(metaType); } if (controlNode.getTagName().equals("checkgroup")) { addCheckboxes(controlData, controlNode); } pm.addControlData(controlData); } } } return map; } /** * Takes as input a string of comma separated metatypes (e.g. * "Class,Interface,Attribute") and converts it to a list of classes of * the appropriate type. * @param typesString * @return */ private List<Class<?>> stringToMetaTypes(String typesString) { List<Class<?>> classes = new ArrayList<Class<?>>(); StringTokenizer st = new StringTokenizer(typesString, ","); while (st.hasMoreTokens()) { classes.add(metaTypeByName.get(st.nextToken())); } return classes; } private void addCheckboxes(ControlData controlData, Element controlElement) { final NodeList checkBoxElements = controlElement.getElementsByTagName("checkbox"); for (int i = 0; i < checkBoxElements.getLength(); ++i) { Element cbNode = (Element) checkBoxElements.item(i); final String checkBoxType = cbNode.getAttributes().getNamedItem("type").getNodeValue(); final String checkBoxName = cbNode.getAttributes().getNamedItem("name").getNodeValue(); CheckBoxData cbd = new CheckBoxData(metaTypeByName.get(checkBoxType), checkBoxName); controlData.addCheckbox(cbd); } } }