package se.cambio.cds.gdl.parser; import java.io.InputStream; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeSet; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.openehr.rm.datatypes.text.CodePhrase; import se.cambio.cds.gdl.model.ArchetypeBinding; import se.cambio.cds.gdl.model.Binding; import se.cambio.cds.gdl.model.ElementBinding; import se.cambio.cds.gdl.model.ResourceDescriptionItem; import se.cambio.cds.gdl.model.Rule; import se.cambio.cds.gdl.model.Term; import se.cambio.cds.gdl.model.TermBinding; import se.cambio.cds.gdl.model.TermDefinition; import se.cambio.cds.gdl.model.TranslationDetails; public class DADLSerializer { public DADLSerializer() { loadProfile("gdl.properties"); } /** * Serialize an object into DADL format using reflection * * @param obj * @return List of serialized strings * @throws Exception */ public List<String> toDADL(Object obj) throws Exception { List<String> lines = new ArrayList<String>(); return toDADL(obj, 1, lines); } private List<String> toDADL(Object obj, int indent, List<String> lines) throws Exception { log.debug("toDADL on obj.getClass: " + obj.getClass().getCanonicalName() + ", indent: " + indent + ", line.size: " + lines.size()); Class klass = obj.getClass(); String className = klass.getSimpleName(); String rmName = toUnderscoreSeparated(className).toUpperCase(); String typeHeader = "(" + rmName + ") <"; int size = lines.size(); if (size == 0) { lines.add(typeHeader); } else { String l = lines.get(size - 1); l += typeHeader; lines.set(size - 1, l); } Collection<String> attributes = attributeList(obj.getClass()); if(profile.containsKey(className)) { attributes = sortByPreferredOrder(attributes, profile.get(className)); } String name = null; Object value = null; StringBuffer buf = null; for (Iterator<String> names = attributes.iterator(); names.hasNext();) { name = names.next(); // suppress Id output in GDL!! if ("id".equals(name) && (obj instanceof ArchetypeBinding || obj instanceof Rule || obj instanceof ElementBinding || obj instanceof Binding || obj instanceof TermBinding || obj instanceof Term || obj instanceof TermDefinition || obj instanceof ResourceDescriptionItem || obj instanceof TranslationDetails)) { continue; } // skip objects generated in 2nd phase parsing if ("preConditionExpressions".equals(name) || "whenStatements".equals(name) || "thenStatements".equals(name) || "predicateStatements".equals(name)) { continue; } Method getter = getter(name, obj.getClass()); if (getter != null) { value = getter.invoke(obj, null); buf = new StringBuffer(); if (value != null) { for (int i = 0; i < indent; i++) { buf.append("\t"); } buf.append(toUnderscoreSeparated(name)); buf.append(" = "); if (value instanceof List) { if (!((List) value).isEmpty()) { buf.append("<"); List list = (List) value; Object member = null; if (list.size() > 0 && isSupportedPrimitiveType(list.get(0))) { if (list.get(0) instanceof String) { for (int i = 0, j = list.size(); i < j; i++) { buf.append("\""); buf.append(list.get(i)); buf.append("\""); if (i != j - 1) { buf.append(", "); } } } else if (list.get(0) instanceof CodePhrase) { for (int i = 0, j = list.size(); i < j; i++) { buf.append("["); buf.append(list.get(i)); buf.append("]"); if (i != j - 1) { buf.append(", "); } } } if (list.size() == 1) { buf.append(",..."); } buf.append(">"); lines.add(buf.toString()); } else { for (int i = 0, j = list.size(); i < j; i++) { member = list.get(i); lines.add(buf.toString()); buf = new StringBuffer(); for (int k = 0; k < indent + 1; k++) { buf.append("\t"); } lines.add(buf.toString() + "[" + (i + 1) + "] = "); toDADL(member, indent + 2, lines); } buf = new StringBuffer(); for (int i = 0; i < indent; i++) { buf.append("\t"); } buf.append(">"); lines.add(buf.toString()); } } } else if (value instanceof Map) { if (!((Map) value).isEmpty()) { buf.append("<"); lines.add(buf.toString()); Map map = (Map) value; SortedSet sorted = new TreeSet(map.keySet()); for (Object key : sorted) { buf = new StringBuffer(); for (int k = 0; k < indent + 1; k++) { buf.append("\t"); } String keystr; if (key instanceof CodePhrase) { keystr = "[" + key + "]"; } else { keystr = "\"" + key.toString() + "\""; } Object member = map.get(key); if (member instanceof String) { buf.append("["); buf.append(keystr); buf.append("] = <\""); buf.append(member); buf.append("\">"); lines.add(buf.toString()); } else { lines.add(buf.toString() + "[" + keystr + "] = "); toDADL(member, indent + 2, lines); } } buf = new StringBuffer(); for (int i = 0; i < indent; i++) { buf.append("\t"); } buf.append(">"); lines.add(buf.toString()); } } else if (isSupportedPrimitiveType(value)) { log.debug("value.type: " + value.getClass()); buf.append("<"); if (value instanceof String) { buf.append("\""); buf.append(value); buf.append("\""); } else if (value instanceof CodePhrase) { buf.append("["); buf.append(value); buf.append("]"); } else { buf.append(value.toString()); } buf.append(">"); lines.add(buf.toString()); } else { // complex block log.debug("complex block.."); lines.add(buf.toString()); toDADL(value, indent + 1, lines); } } } } buf = new StringBuffer(); for (int i = 0; i < indent - 1; i++) { buf.append("\t"); } buf.append(">"); lines.add(buf.toString()); return lines; } private boolean isSupportedPrimitiveType(Object obj) { return (obj instanceof String) || (obj instanceof Integer) || (obj instanceof Double) || (obj instanceof CodePhrase); } private Method getter(String attributeName, Class klass) { Method[] methods = klass.getMethods(); String name = "get" + attributeName.substring(0, 1).toUpperCase() + attributeName.substring(1); log.debug("search getter method of name '" + name + "'"); for (Method method : methods) { if (method.getName().equals(name)) { Type[] paras = method.getParameterTypes(); if (paras.length == 0) { log.debug("found '" + name + "'"); return method; } } } return null; } private SortedSet<String> attributeList(Class klass) { SortedSet<String> set = new TreeSet<String>(); Method[] methods = klass.getMethods(); for (Method method : methods) { String name = method.getName(); if (name.startsWith("set")) { name = name.substring(3); name = name.substring(0, 1).toLowerCase() + name.substring(1); set.add(name); } } log.debug("attribute list: " + set); return set; } private List<String> sortByPreferredOrder(Collection<String> attributes, List<String> preferred) { List<String> ret = new ArrayList<String>(); for(String attr : preferred) { if(attributes.contains(attr)) { ret.add(attr); // add preferred attributes } } for(String attr : attributes) { if( ! ret.contains(attr)) { ret.add(attr); // add the rest } } return ret; } private String toUnderscoreSeparated(String camelCase) { String[] array = StringUtils.splitByCharacterTypeCamelCase(camelCase); StringBuffer buf = new StringBuffer(); for (int i = 0; i < array.length; i++) { String s = array[i]; buf.append(s.substring(0, 1).toLowerCase()); buf.append(s.substring(1)); if (i != array.length - 1) { buf.append("_"); } } return buf.toString(); } private void loadProfile(String name) { profile = new HashMap<String, List<String>>(); try { InputStream input = this.getClass().getClassLoader().getResourceAsStream(name); Properties props = new Properties(); props.load(input); Enumeration keys = props.propertyNames(); StringTokenizer tokens = null; List<String> attributes; while(keys.hasMoreElements()) { Object key = keys.nextElement(); String line = props.getProperty( (String) key); log.debug(line); tokens = new StringTokenizer(line, ","); attributes = new ArrayList<String>(); while(tokens.hasMoreTokens()) { attributes.add(tokens.nextToken()); } profile.put((String) key, attributes); log.debug(key.toString() + " with attributes: " + attributes); } log.debug(profile.size() + " class(es) loaded.."); } catch(Exception e) { log.error("failed to load " + name); } } private Map<String, List<String>> profile; private static Logger log = Logger.getLogger(DADLSerializer.class); } /* * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 2.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 2.0 (the 'License'); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an 'AS IS' basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * * The Initial Developers of the Original Code are Iago Corbal and Rong Chen. * Portions created by the Initial Developer are Copyright (C) 2012-2013 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Software distributed under the License is distributed on an 'AS IS' basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * ***** END LICENSE BLOCK ***** */