/******************************************************************************* * Copyright © 2011, 2013 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.mof.serialization.xml; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.eclipse.edt.mof.EClass; import org.eclipse.edt.mof.EDataType; import org.eclipse.edt.mof.EEnum; import org.eclipse.edt.mof.EField; import org.eclipse.edt.mof.EGenericType; import org.eclipse.edt.mof.EObject; import org.eclipse.edt.mof.EType; import org.eclipse.edt.mof.MofFactory; import org.eclipse.edt.mof.impl.Dynamic; import org.eclipse.edt.mof.impl.DynamicEClass; import org.eclipse.edt.mof.impl.DynamicEObject; import org.eclipse.edt.mof.serialization.DeserializationException; import org.eclipse.edt.mof.serialization.Deserializer; import org.eclipse.edt.mof.serialization.IEnvironment; import org.eclipse.edt.mof.serialization.MofObjectNotFoundException; import org.eclipse.edt.mof.serialization.ProxyEObject; import org.eclipse.edt.mof.utils.EList; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; public class XMLDeserializer extends DefaultHandler implements Deserializer { private static final String Attr_eClass = "eClass"; private static final String Attr_ID = "ID"; private static final String Attr_IDREF = "IDREF"; private static final String Attr_href = "href"; private static final String Element_entry = "entry"; XMLReader parser; MofFactory mof = MofFactory.INSTANCE; Object object; Stack<Object> stack = new Stack<Object>(); Map<String, EObject> eobjects = new HashMap<String, EObject>(); InputSource input = null; IEnvironment env; EType datatype; EType listDatatype; boolean newList = true; boolean isPopEntry = false; StringBuilder charBuf; public XMLDeserializer(InputStream input, IEnvironment env) throws UnsupportedEncodingException { this.input = new InputSource(input); this.env = env; } @Override public EObject deserialize() throws DeserializationException { if (input == null) { throw new DeserializationException("No input to process"); } parser = createParser(); parser.setContentHandler(this); parser.setErrorHandler(this); try { parser.parse(input); } catch (Exception e) { throw new DeserializationException(e); } return (EObject)object; } private XMLReader createParser() { try { return XMLReaderFactory.createXMLReader(); } catch (SAXException e) { e.printStackTrace(); } return null; } @Override public void startElement(String nsURI, String localName, String qName, Attributes attrs) throws SAXException { checkCharacters(); EObject result = null; if (localName == null || localName.length() == 0) { localName = qName; } // One of the following must be set String typeSignature = attrs.getValue("", Attr_eClass); String idref = attrs.getValue("", Attr_IDREF); String href = attrs.getValue("", Attr_href); if (href != null) { // We have a stand-alone EClassifier as value reference result = resolveTypeReference(href, true); } else if (idref != null) { // We have a reference to another element in the document result = eobjects.get(idref); if (result == null) { // We are dealing with a forward reference result = new ProxyEObject(); eobjects.put(idref, result); } } else if (localName.equals(Element_entry) && listDatatype != null) { if (newList) { // List object has not been created yet stack.push(new EList()); newList = false; } // Handle nested List of List values EType type = ((EGenericType)listDatatype).getETypeArguments().get(0); if (type instanceof EGenericType) { listDatatype = type; } else { datatype = type; if (datatype instanceof EDataType) { stack.push(""); //push a dummy entry in case the value is "" } isPopEntry = !(datatype instanceof EClass); } } else if (typeSignature == null) { // We are dealing with an element that represents an EDataType value // within a List; otherwise the value would be handled as an attribute EObject obj = (EObject)stack.peek(); if (obj == null) throw new SAXException("No type specified for element: " + localName); else { EClass eClass = obj.getEClass(); if (eClass instanceof DynamicEClass) { stack.push(""); //push a dummy entry in case the value is "" datatype = eClass; } else { EField field = eClass.getEField(localName); if (field == null) { stack.push(null); } if (field != null && field.getEType().getEClassifier().equals(mof.getEListEDataType())) { datatype = ((EGenericType)field.getEType()).getETypeArguments().get(0); if (datatype.getEClassifier().equals(mof.getEListEDataType())) { // We have a List of Lists listDatatype = datatype; datatype = null; } else { if (datatype instanceof EDataType) { stack.push(""); //push a dummy entry in case the value is "" } } } else { throw new SAXException("No type specified for element: " + localName); } } } } else { EClass eClass = (EClass)resolveTypeReference(typeSignature, false); result = eClass.newInstance(true, false); for (int i=0; i<attrs.getLength(); i++) { String attr = attrs.getLocalName(i); if (attr == null || attr.length() == 0) { attr = attrs.getQName(i); } if (attr.equals("ID")) { Object proxy = eobjects.get(attrs.getValue(i)); if (proxy instanceof ProxyEObject) { ((ProxyEObject)proxy).updateReferences(result); } eobjects.put(attrs.getValue(i), result); continue; } if (attr.equals(Attr_eClass) || attr.equals(Attr_ID) || attr.equals(Attr_IDREF)) continue; if (eClass instanceof Dynamic) { result.eSet(attr, attrs.getValue(i)); } else { EField field = eClass.getEField(attr); if (field != null) { Object value = valueFromString(field.getEType(), attrs.getValue(i)); result.eSet(field, value); } } } } if (result != null) stack.push(result); } @SuppressWarnings("unchecked") @Override public void endElement(String nsURI, String localName, String qName) throws SAXException { checkCharacters(); if (localName == null || localName.length() == 0) { localName = qName; } datatype = null; if (localName.equals(Element_entry)) { if (isPopEntry) isPopEntry = false; else return; } Object source = stack.pop(); if (source == null) return; if (stack.isEmpty()) { object = source; } else { Object target = stack.peek(); if (target instanceof List) { ((List)target).add(source); if (localName.equals(Element_entry)) { if (source instanceof List) newList = true; // only occurs if dealing with List of List values } } else if (target instanceof DynamicEObject) { // Only happens if source is part of a List List list = (List)((DynamicEObject)target).eGet(localName); if (list == null) { list = new EList(); ((DynamicEObject)target).eSet(localName, list); } list.add(source); } else { EObject etarget = (EObject)target; EField field = ((EClass)etarget.getEClass()).getEField(localName); if (field.getEType().getEClassifier() == mof.getEListEDataType()) { if ((List)etarget.eGet(field) == null && isNullableListType(field)) { etarget.eSet(field, new EList()); } ((List)etarget.eGet(field)).add(source); if (source instanceof List) newList = true; // only occurs if dealing with List of List values } else { etarget.eSet(field, source); } } } } private boolean isNullableListType(EField field) { if (!field.isNullable()) { return false; } if (field.getEType() != null && field.getEType().getEClassifier() != null) { return field.getEType().getEClassifier().equals(MofFactory.INSTANCE.getEListEDataType()); } return false; } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (charBuf == null) { charBuf = new StringBuilder(length); } charBuf.append(ch, start, length); } private void checkCharacters() throws SAXException { if (charBuf != null) { if (datatype != null && (datatype instanceof EDataType || datatype instanceof DynamicEClass) ) { String string = charBuf.toString(); Object value; stack.pop(); //pop off the dummy entry that was added, because an value of "" will not come here if (datatype instanceof DynamicEClass) { value = ((DynamicEClass)datatype).getEncodedValue(string); } else { value = valueFromString(datatype, string); } stack.push(value); datatype = null; } charBuf = null; } } @SuppressWarnings("unchecked") private Object valueFromString(EType type, String input) throws SAXException { if (type == mof.getEIntEDataType()) { return Integer.parseInt(input); } else if (type == mof.getEBooleanEDataType()) { return Boolean.parseBoolean(input); } else if (type instanceof EEnum) { Class<Enum> enumClass; try { enumClass = (Class<Enum>)Class.forName(type.getETypeSignature()); return Enum.valueOf(enumClass, input); } catch (ClassNotFoundException e) { return ((EEnum)type).getEEnumLiteral(input); } } else if (type instanceof EClass && (type == mof.getMofReferenceTypeClass() || ((EClass)type).isSubClassOf(mof.getMofReferenceTypeClass()))) { try { return resolveTypeReference(input, true); } catch (Exception e) { throw new SAXException(e); } } else { return input; } } private EObject resolveTypeReference(String href, boolean useProxies) throws SAXException { try { EObject obj = env.find(href, useProxies); if (obj == null) throw new SAXException("Object not found: " + href); return obj; } catch (DeserializationException e) { throw new SAXException(e); } catch (MofObjectNotFoundException e1) { throw new SAXException(e1); } } }