/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for * visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions Suite #1A 2328 Government Street Victoria BC V8T 5G5 Canada * * (250)385-6040 www.vividsolutions.com */ package com.vividsolutions.jump.util.java2xml; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.apache.log4j.Logger; import org.jdom.Element; import org.jdom.input.SAXBuilder; import com.vividsolutions.jts.util.Assert; import com.vividsolutions.jump.util.StringUtil; public class XML2Java extends XMLBinder { private ArrayList listeners = new ArrayList(); private ClassLoader classLoader = getClass().getClassLoader(); private static Logger LOG = Logger.getLogger(XMLBinder.class); public XML2Java() { } public XML2Java(ClassLoader classLoader) { this.classLoader = classLoader; } public Object read(String xml, Class c) throws Exception { StringReader reader = new StringReader(xml); try { return read(reader, c); } finally { reader.close(); } } public Object read(Reader reader, Class c) throws Exception { return read(new SAXBuilder().build(reader).getRootElement(), c); } public Object read(File file, Class c) throws Exception { FileReader fileReader = new FileReader(file); try { BufferedReader bufferedReader = new BufferedReader(fileReader); try { return new XML2Java().read(bufferedReader, c); } finally { bufferedReader.close(); } } finally { fileReader.close(); } } private void read(final Element tag, final Object object, List specElements) throws Exception { Assert.isTrue(tag != null); visit(specElements, new SpecVisitor() { private void fillerTagSpecFound(String xmlName, List specChildElements) throws Exception { if (tag.getChildren(xmlName).size() == 0) { System.err.println("WARNING: Expected 1 <" + xmlName + "> tag but found None"); return; } if (tag.getChildren(xmlName).size() != 1) { throw new XMLBinderException("Expected 1 <" + xmlName + "> tag but found " + tag.getChildren(xmlName).size()); } read(tag.getChild(xmlName), object, specChildElements); } private void normalTagSpecFound(String xmlName, String javaName, List specChildElements) throws Exception { setValuesFromTags(object, setter(object.getClass(), javaName), tag.getChildren(xmlName)); //The parent may specify additional tags for itself in the // children. [Jon Aquino] for (Iterator i = tag.getChildren(xmlName).iterator(); i .hasNext();) { Element childTag = (Element) i.next(); read(childTag, object, specChildElements); } } public void tagSpecFound(String xmlName, String javaName, List specChildElements) throws Exception { if (javaName == null) { fillerTagSpecFound(xmlName, specChildElements); } else { normalTagSpecFound(xmlName, javaName, specChildElements); } } public void attributeSpecFound(String xmlName, String javaName) throws Exception { if (tag.getAttribute(xmlName) == null) { String msg = ("Expected '" + xmlName + "' attribute but found none. Tag = " + tag.getName() + "; Attributes = " + StringUtil.toCommaDelimitedString(tag .getAttributes())); // [sstein 5April2008] replaced XMLB exception by Log // so when a problem with styling appears data are still loaded if (tag.getName().equalsIgnoreCase("style")){ LOG.warn(msg); System.out.println(msg); return; //return to avoid further messages } // [mmichaud 2011-07-04] Following attributes were introduced in 1.4.1 release. // Skip them to keep compatibility with old project files if (tag.getName().equalsIgnoreCase("layer") && xmlName != null && (xmlName.equalsIgnoreCase("editable") || xmlName.equalsIgnoreCase("selectable") || xmlName.equalsIgnoreCase("read-only") ) ) { LOG.warn(msg); System.out.println(msg); return; //return to avoid further messages } else{ throw new XMLBinderException(msg); } } Method setter = setter(object.getClass(), javaName); setValue(object, setter, toJava(tag.getAttribute(xmlName) .getValue(), setter.getParameterTypes()[0])); } }, object.getClass()); } private Object read(Element tag, Class c) throws Exception { if (tag.getAttribute("null") != null && tag.getAttributeValue("null").equals("true")) { return null; } if (c == QName.class) { return QName.valueOf(tag.getTextTrim()); } if (specifyingTypeExplicitly(c)) { if (tag.getAttribute("class") == null) { throw new XMLBinderException("Expected <" + tag.getName() + "> to have 'class' attribute but found none"); } return read(tag, Class.forName(tag.getAttributeValue("class"), true, classLoader)); } fireCreatingObject(c); if (hasCustomConverter(c)) { return toJava(tag.getTextTrim(), c); } Object object = c.newInstance(); if (object instanceof Map) { for (Iterator i = tag.getChildren().iterator(); i.hasNext();) { Element mappingTag = (Element) i.next(); if (!mappingTag.getName().equals("mapping")) { throw new XMLBinderException("Expected <" + tag.getName() + "> to have <mapping> tag but found none"); } if (mappingTag.getChildren().size() != 2) { throw new XMLBinderException("Expected <" + tag.getName() + "> to have 2 tags under <mapping> but found " + mappingTag.getChildren().size()); } if (mappingTag.getChildren("key").size() != 1) { throw new XMLBinderException( "Expected <" + tag.getName() + "> to have 1 <key> tag under <mapping> but found " + mappingTag.getChildren("key").size()); } if (mappingTag.getChildren("value").size() != 1) { throw new XMLBinderException( "Expected <" + tag.getName() + "> to have 1 <value> tag under <mapping> but found " + mappingTag.getChildren("key").size()); } ((Map) object).put(read(mappingTag.getChild("key"), Object.class), read(mappingTag.getChild("value"), Object.class)); } } else if (object instanceof Collection) { for (Iterator i = tag.getChildren().iterator(); i.hasNext();) { Element itemTag = (Element) i.next(); if (!itemTag.getName().equals("item")) { throw new XMLBinderException("Expected <" + tag.getName() + "> to have <item> tag but found none"); } ((Collection) object).add(read(itemTag, Object.class)); } } else { read(tag, object, specElements(object.getClass())); } return object; } private void fireCreatingObject(Class c) { for (Iterator i = listeners.iterator(); i.hasNext();) { Listener l = (Listener) i.next(); l.creatingObject(c); } } public void addListener(Listener listener) { listeners.add(listener); } private void setValuesFromTags(Object object, Method setter, Collection tags) throws Exception { for (Iterator i = tags.iterator(); i.hasNext();) { Element tag = (Element) i.next(); setValueFromTag(object, setter, tag); } } private void setValueFromTag(Object object, Method setter, Element tag) throws Exception { setValue(object, setter, read(tag, fieldClass(setter))); } private void setValue(Object object, Method setter, Object value) throws IllegalAccessException, InvocationTargetException { //If you get an InvocationTargetException, check the bottom of the // stack //trace -- you should see the stack trace for the underlying exception. //[Jon Aquino] setter.invoke(object, new Object[]{value}); } public static interface Listener { public void creatingObject(Class c); } }