// $Id: XmiReaderEUMLImpl.java 18239 2010-04-12 06:51:13Z tfmorris $ /******************************************************************************* * Copyright (c) 2007,2010 Tom Morris and other contributors * 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: * Tom Morris - initial implementation * Bogdan Pistol - undo support & UUID maps for diagrams * thn *****************************************************************************/ package org.argouml.model.euml; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.argouml.model.NotImplementedException; import org.argouml.model.UmlException; import org.argouml.model.XmiReader; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.impl.EStructuralFeatureImpl.BasicFeatureMapEntry; import org.eclipse.emf.ecore.impl.EStructuralFeatureImpl.SimpleFeatureMapEntry; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.FeatureMap.Entry; import org.eclipse.emf.ecore.xml.type.AnyType; import org.eclipse.uml2.uml.Element; import org.xml.sax.InputSource; /** * The implementation of the XmiReader for EUML2. */ class XmiReaderEUMLImpl implements XmiReader { private static final Logger LOG = Logger.getLogger(XmiReaderEUMLImpl.class); /** * The model implementation. */ private EUMLModelImplementation modelImpl; private static Set<String> searchDirs = new HashSet<String>(); private Resource resource; /** * Constructor. * * @param implementation * The ModelImplementation. */ public XmiReaderEUMLImpl(EUMLModelImplementation implementation) { modelImpl = implementation; } public int getIgnoredElementCount() { return 0; } public String[] getIgnoredElements() { // Not needed currently for UML 2 return new String[0]; } public Map<String, Object> getXMIUUIDToObjectMap() { if (resource == null) { throw new IllegalStateException(); } Map<String, Object> map = new HashMap<String, Object>(); Iterator<EObject> it = resource.getAllContents(); while (it.hasNext()) { EObject o = it.next(); map.put(resource.getURIFragment(o), o); } return map; } public Collection parse(InputSource inputSource, boolean readOnly) throws UmlException { if (inputSource == null) { throw new NullPointerException( "The input source must be non-null."); //$NON-NLS-1$ } InputStream is = null; boolean needsClosing = false; String name = inputSource.getSystemId(); if (name == null) { name = inputSource.getPublicId(); } if (name == null) { name = inputSource.toString(); } LOG.debug("Parsing " + name); //$NON-NLS-1$ if (inputSource.getByteStream() != null) { is = inputSource.getByteStream(); } else if (inputSource.getSystemId() != null) { try { URL url = new URL(inputSource.getSystemId()); if (url != null) { LOG.debug("Parsing URL " + url); //$NON-NLS-1$ is = url.openStream(); if (is != null) { is = new BufferedInputStream(is); needsClosing = true; } } } catch (MalformedURLException e) { // do nothing } catch (IOException e) { // do nothing } } if (is == null) { throw new UnsupportedOperationException(); } // TODO: Review - priority of public ID vs system ID has been reversed // from original implementation String id = inputSource.getPublicId(); if (id == null) { id = inputSource.getSystemId(); if (id != null) { // we only take the filename, not the whole system path int ix = id.lastIndexOf('/'); if (ix != -1) { id = id.substring(ix + 1); } } } Resource r = UMLUtil.getResource(modelImpl, URI.createURI(id), readOnly); try { modelImpl.getModelEventPump().stopPumpingEvents(); r.unload(); r.load(is, null); // TODO: Some import-only UML 2 profiles trigger this - Investigate. // if (!isUML2(r)) { // throw new UmlException("Attempted to load non-UML 2.x file"); // } if (isUML14(r)) { throw new UmlException("Attempted to load UML 1.4 file"); //$NON-NLS-1$ } } catch (IOException e) { throw new UmlException(e); } finally { modelImpl.getModelEventPump().startPumpingEvents(); if (needsClosing) { try { is.close(); } catch (IOException e) { throw new RuntimeException(e); } } } resource = r; LOG.debug("Parsed resource " + resource //$NON-NLS-1$ + " with " + resource.getContents().size() + " elements"); //$NON-NLS-1$ //$NON-NLS-2$ return r.getContents(); } /** * Test whether this is a UML2 file. Returns as soon as the first UML2 * element is seen. * * @param r resource containing loaded UML model * @return true if any of the contained objects are instances of UML2 * Element. */ private boolean isUML2(Resource r) { for (EObject eobj: r.getContents()) { if (eobj instanceof Element) { return true; } } return false; } /** * Attempt to detect ArgoUML/NetBeans MDR style UML 1.4 XMI files. These can * be loaded without complaint by EMF, but they won't do us any good. * * @param r resource containing the loaded file * @return true if XMI.header/XMI.metamodel contains xmi.name="UML" and * xmi.version="1.4" */ private boolean isUML14(Resource r) { for (EObject eobj : r.getContents()) { if ("XMI.header".equals(eobj.eClass().getName())) { //$NON-NLS-1$ for (Entry e1 : ((AnyType) eobj).getMixed()) { if (e1 instanceof BasicFeatureMapEntry) { BasicFeatureMapEntry x1 = (BasicFeatureMapEntry) e1; String n1 = x1.getEStructuralFeature().getName(); if ("XMI.metamodel".equals(n1)) { //$NON-NLS-1$ AnyType v = (AnyType) x1.getValue(); for (Entry e2 : v.getAnyAttribute()) { if (e2 instanceof SimpleFeatureMapEntry) { SimpleFeatureMapEntry x = (SimpleFeatureMapEntry) e2; String n = x.getEStructuralFeature().getName(); if ("xmi.name".equals(n)) { //$NON-NLS-1$ if (!("UML".equals((String) x.getValue()))) { //$NON-NLS-1$ LOG.warn("Tried to parse XMI file with " + "XMI.header/XMI.metamodel/xmi.name = " + (String) x.getValue()); return false; } } else if ("xmi.version".equals(n)) { //$NON-NLS-1$ String version = (String) x.getValue(); if (version != null && version.startsWith("1.4")) { //$NON-NLS-1$ LOG.debug("Tried to parse XMI file with " + "XMI.header/XMI.metamodel/xmi.version = " + version); return true; } } } } } } } } } return false; } public boolean setIgnoredElements(String[] elementNames) { if (elementNames == null) { return true; } throw new NotImplementedException("setIgnoredElements not implemented for UML 2.x"); // TODO: Silently ignore instead? // return false; } public String getTagName() { if (resource == null) { return "uml:Model"; //$NON-NLS-1$ } List l = resource.getContents(); if (!l.isEmpty()) { return "uml:" //$NON-NLS-1$ + modelImpl.getMetaTypes().getName(l.get(0)); } else { return null; } } public void addSearchPath(String path) { searchDirs.add(path); } public List<String> getSearchPath() { return new ArrayList<String>(searchDirs); } public void removeSearchPath(String path) { searchDirs.remove(path); } public String getHeader() { // TODO: Auto-generated method stub throw new NotYetImplementedException(); } }