XmiReaderImpl.java

/* $Id$
 *****************************************************************************
 * Copyright (c) 2005-2012 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
 *    Tom Morris
 *    Luis Sergio Oliveira (euluis)
 *    Laurent Braud
 *****************************************************************************
 *
 * Some portions of this file was previously release using the BSD License:
 */

// Copyright (c) 1996-2009 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies.  This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason.  IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

package org.argouml.model.mdr;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jmi.reflect.InvalidObjectException;
import javax.jmi.reflect.RefObject;
import javax.jmi.reflect.RefPackage;
import javax.jmi.xmi.MalformedXMIException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.argouml.model.UmlException;
import org.argouml.model.XmiException;
import org.argouml.model.XmiReader;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.api.xmi.XMIReader;
import org.netbeans.api.xmi.XMIReaderFactory;
import org.netbeans.lib.jmi.xmi.InputConfig;
import org.netbeans.lib.jmi.xmi.UnknownElementsListener;
import org.netbeans.lib.jmi.xmi.XMIHeaderConsumer;
import org.omg.uml.UmlPackage;
import org.openide.ErrorManager;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;

/**
 * A wrapper around the genuine XmiReader that provides public access with no
 * knowledge of actual UML implementation.
 *
 * @author Bob Tarling
 */
class XmiReaderImpl implements XmiReader, UnknownElementsListener,
        XMIHeaderConsumer {

    static final String TEMP_XMI_FILE_PREFIX = "zargo_model_";

    /**
     * Logger.
     */
    private static final Logger LOG =
        Logger.getLogger(XmiReaderImpl.class.getName());

    private static String tempXMIFileURIPrefix;

    private MDRModelImplementation modelImpl;

    private XmiReferenceResolverImpl resolver;

    /**
     * Flag indicating unknown element was found in XMI file.
     */
    private boolean unknownElement;

    /**
     * Name of first unknown element found (if not a UML 1.3 name).
     */
    private String unknownElementName;

    /**
     * Flag indicating that we think unknown element was due to a UML 1.3 file.
     */
    private boolean uml13;

    /**
     * Elements to ignore errors on if they aren't found in the metamodel.
     */
    private String[] ignoredElements = new String[] {};

    /**
     * Flag indicating that we stripped at least one diagram during the import.
     */
    private int ignoredElementCount;

    /**
     * String that we pulled from the header of the XMI file
     */
    private String xmiHeader;


    /**
     * Constructor for XMIReader.
     * @param parentModelImplementation The ModelImplementation
     */
    XmiReaderImpl(MDRModelImplementation parentModelImplementation) {
        modelImpl = parentModelImplementation;
    }

    public Collection parse(InputSource inputSource, boolean readOnly)
        throws UmlException {

        System.setProperty("javax.xml.transform.TransformerFactory",
        "net.sf.saxon.TransformerFactoryImpl");

        Collection<RefObject> newElements = Collections.emptyList();

        String extentBase = inputSource.getPublicId();
        if (extentBase == null) {
            extentBase = inputSource.getSystemId();
        }
        if (extentBase == null) {
            extentBase = MDRModelImplementation.MODEL_EXTENT_NAME;
        }
        String extentName = extentBase;
        UmlPackage extent =
            (UmlPackage) modelImpl.getRepository().getExtent(extentName);
        int serial = 1;
        while (extent != null) {
            extentName = extentBase + " " + serial;
            serial++;
            extent = (UmlPackage) modelImpl.getRepository().getExtent(
                    extentName);
        }

        extent = (UmlPackage) modelImpl.createExtent(extentName, readOnly);
        if (extent == null) {
            LOG.log(Level.SEVERE, "Failed to create extent " + extentName);
        }

        try {
            LOG.log(Level.INFO, "Loading to extent {0} {1}", new Object[]{extentName, extent});

            InputConfig config = new InputConfig();
            config.setUnknownElementsListener(this);
            config.setUnknownElementsIgnored(true);

            String pId = inputSource.getPublicId();
            String sId = modelImpl.getPublic2SystemIds().get(pId);
            if (sId != null) {
                if (sId.equals(inputSource.getSystemId())) {
                    LOG.log(Level.INFO, "Attempt to reread profile - ignoring - "
                            + "publicId = \"" + pId + "\";  systemId = \""
                            + sId + "\".");
                    return Collections.emptySet();
                } else {
                    throw new UmlException("Profile with the duplicate publicId "
                            + "is being loaded! publicId = \"" + pId
                            + "\"; existing systemId = \""
                            + modelImpl.getPublic2SystemIds().get(pId)
                            + "\"; new systemId = \"" + sId + "\".");
                }
            }
            resolver = new XmiReferenceResolverImpl(new RefPackage[] {extent},
                    config, modelImpl.getObjectToId(),
                    modelImpl.getPublic2SystemIds(), modelImpl.getIdToObject(),
                    modelImpl.getSearchPath(),
                    readOnly,
                    inputSource.getPublicId(), inputSource.getSystemId(),
                    modelImpl);
            config.setReferenceResolver(resolver);
            config.setHeaderConsumer(this);

            XMIReader xmiReader =
                    XMIReaderFactory.getDefault().createXMIReader(config);

            /*
             * MDR has a hardcoded printStackTrace on all exceptions,
             * even if they're caught, which is unsightly, so we handle
             * unknown elements ourselves rather than letting MDR throw
             * an exception for us to catch.
             *
             * org/netbeans/lib/jmi/util/Logger.java
             *
             * This can be uses to disable logging.  Default output is
             * System.err
             * setProperty("org.netbeans.lib.jmi.Logger.fileName", "")
             *              org.netbeans.mdr.Logger
             *
             * The property org.netbeans.lib.jmi.Logger controls the minimum
             * severity level for logging
             */
            // Turn off NetBeans logging to System.err
//            System.setProperty("org.netbeans.lib.jmi.Logger.fileName", "");
            // Set minimum severity level for MDR
//            System.setProperty("org.netbeans.lib.jmi.Logger",
//                    Integer.toString(ErrorManager.INFORMATIONAL));
            InputConfig config2 = (InputConfig) xmiReader.getConfiguration();
            config2.setUnknownElementsListener(this);
            config2.setUnknownElementsIgnored(true);
            unknownElement = false;
            uml13 = false;
            ignoredElementCount = 0;

            // Disable event delivery during model load
            modelImpl.getModelEventPump().stopPumpingEvents();

            try {
                String systemId = inputSource.getSystemId();
                // If we've got a streaming input, copy it to make sure we'll
                // be able to rewind it if necessary
                if (inputSource.getByteStream() != null
                        || inputSource.getCharacterStream() != null) {
                    File file = copySource(inputSource);
                    systemId = file.toURI().toURL().toExternalForm();
                    String publicId = inputSource.getPublicId();
                    inputSource = new InputSource(systemId);
                    inputSource.setPublicId(publicId);
                }
                MDRepository repository = modelImpl.getRepository();

                // Use a transaction to avoid the performance penalty (3x) of
                // MDR's autocommit mode
                repository.beginTrans(true);

                // Issue 5816 : invalid XMI
                try {
                    newElements = xmiReader.read(inputSource.getByteStream(),
                            systemId, extent);
                } catch (MalformedXMIException e) {
                    repository.endTrans(true);
                    repository.beginTrans(true);
                    resolver.clearIdMaps();
                    inputSource = convertFromInvalidXMI(inputSource);
                    newElements = xmiReader.read(inputSource.getByteStream(),
                            systemId, extent);

                }

                // If a UML 1.3 file, attempt to upgrade it to UML 1.4
                if (uml13) {
                    // Roll back transaction from first attempt & start new one
                    repository.endTrans(true);
                    repository.beginTrans(true);

                    // Clear the associated ID maps & reset starting collection
                    resolver.clearIdMaps();

                    newElements = convertAndLoadUml13(inputSource.getSystemId(),
                            extent, xmiReader, inputSource);
                }

                // Commit our transaction
                repository.endTrans();
            } catch (Throwable e) {
                // Roll back transaction to remove any partial results read
                try {
                    modelImpl.getRepository().endTrans(true);
                } catch (Throwable e2) {
                    // Ignore any error.  The transaction may already have
                    // been unwound as part of exception processing by MDR
                }
                if (e instanceof MalformedXMIException) {
                    throw (MalformedXMIException) e;
                } else if (e instanceof IOException) {
                    throw (IOException) e;
                } else {
                    // We shouldn't get here, but just in case...
                    // We want a wide exception catcher to make sure our
                    // transaction always gets ended
                    e.printStackTrace();
                    throw new MalformedXMIException();
                }
            } finally {
                modelImpl.getModelEventPump().startPumpingEvents();
            }

            if (unknownElement) {
                modelImpl.deleteExtent(extent);
                throw new XmiException("Unknown element in XMI file : "
                        + unknownElementName);
            }

            if (ignoredElementCount > 0) {
                LOG.log(Level.WARNING, "Ignored one or more elements from list "
                        + ignoredElements);
            }

        } catch (MalformedXMIException e) {
            // If we can find a nested SAX exception, it will have information
            // on the line number, etc.
            ErrorManager.Annotation[] annotations =
                ErrorManager.getDefault().findAnnotations(e);
            for (ErrorManager.Annotation annotation : annotations) {
                Throwable throwable = annotation.getStackTrace();
                if (throwable instanceof SAXParseException) {
                    SAXParseException spe = (SAXParseException) throwable;
                    throw new XmiException(spe.getMessage(), spe.getPublicId(),
                            spe.getSystemId(), spe.getLineNumber(),
                            spe.getColumnNumber(), e);
                } else if (throwable instanceof SAXException) {
                    SAXException se = (SAXException) throwable;
                    Exception e1 = se.getException();
                    if (e1 instanceof org.argouml.model.XmiReferenceRuntimeException) {
                        String href =
                            ((org.argouml.model.XmiReferenceRuntimeException) e1)
                                .getReference();
                        throw new org.argouml.model.XmiReferenceException(href,
                                e);
                    }
                    throw new XmiException(se.getMessage(), se);
                }
            }
            modelImpl.deleteExtent(extent);
            throw new XmiException(e);
        } catch (IOException e) {
            try {
                modelImpl.deleteExtent(extent);
            } catch (InvalidObjectException e2) {
                // Ignore if the extent never got created or has been deleted
            }
            throw new XmiException(e);
        }

        return newElements;
    }


    private Collection<RefObject> convertAndLoadUml13(String systemId,
            RefPackage extent, XMIReader xmiReader, InputSource input)
        throws FileNotFoundException, UmlException, IOException,
            MalformedXMIException {

        LOG.log(Level.INFO, "XMI file doesn't appear to be UML 1.4 - "
                + "attempting UML 1.3->UML 1.4 conversion");
        final String[] transformFiles = new String[] {
            "NormalizeNSUML.xsl",
            "uml13touml14.xsl", };

        unknownElement = false;
        // InputSource xformedInput = chainedTransform(transformFiles, pIs);
        InputSource xformedInput = serialTransform(transformFiles,
                input);
        xformedInput.setPublicId(input.getPublicId());
        return xmiReader.read(xformedInput.getByteStream(), xformedInput
                .getSystemId(), extent);
    }

    /**
     *
     * @param input
     * @return InputSource : a new XML to test
     * @throws UmlException
     */
    private InputSource convertFromInvalidXMI(InputSource input)
        throws UmlException {

        LOG.log(Level.INFO, "XMI file doesn't appear to be a valid XMI");

        final String[] transformFiles = new String[] {
            "umbrello.xsl",
            };

        unknownElement = false;
        // InputSource xformedInput = chainedTransform(transformFiles, pIs);
        InputSource xformedInput = serialTransform(transformFiles,
                input);
        xformedInput.setPublicId(input.getPublicId());
        return xformedInput;
    }

    /**
     * Defines the URI prefix of the temporary XMI file that is being read.
     *
     * @return the URI prefix of the temporary XMI file that is being read.
     */
    static String getTempXMIFileURIPrefix() {
        if (tempXMIFileURIPrefix == null) {
            tempXMIFileURIPrefix =
                new File(System.getProperty("java.io.tmpdir")).toURI()
                + TEMP_XMI_FILE_PREFIX;
        }
        return tempXMIFileURIPrefix;
    }

    /*
     * @see org.argouml.model.XmiReader#getXMIUUIDToObjectMap()
     */
    public Map<String, Object> getXMIUUIDToObjectMap() {
        if (resolver != null) {
            // Give the resolver.getIdToObjectMap() entries
            // priority over entries with the same UUID from
            // resolver.getIdToObjectMaps() because entries
            // in resolver.getIdToObjectMaps() are historic.
            HashMap<String, Object> globalXmiIdToObjectMap =
                new HashMap<String, Object>(resolver.getIdToObjectMap());

            Map<String, Map<String, Object>> idToObjectMaps =
                resolver.getIdToObjectMaps();
            Set<Entry<String,Map<String,Object>>> entrySet = null;
            // I think that the synchronized access to idToObjectMaps is
            // required in order to respect the thread safe nature of the
            // object.
            // FIXME: maybe this should be moved into XmiReferenceResolverImpl,
            // because it depends on internal implementation details of it.
            synchronized (idToObjectMaps) {
                entrySet =
                    new HashSet<Entry<String,Map<String,Object>>>(
                            idToObjectMaps.entrySet());
                for (Entry<String, Map<String, Object>> entry : entrySet) {
                    entry.setValue(new HashMap<String,Object>(entry.getValue()));
                }
            }
            for (Entry<String, Map<String, Object>> entry : entrySet) {
                String xmiIdPrefix =
                    entry.getKey().startsWith(getTempXMIFileURIPrefix()) ? "" :
                        entry.getKey() + "#";
                for (Entry<String, Object> innerMapEntry :
                        entry.getValue().entrySet()) {

                    String id = xmiIdPrefix + innerMapEntry.getKey();
                    if (!globalXmiIdToObjectMap.containsKey(id)) {
                        globalXmiIdToObjectMap.put(
                                id,
                                innerMapEntry.getValue());
                    }
                }
            }
            return globalXmiIdToObjectMap;
        }
        return null;
    }

    private static final String STYLE_PATH =
        "/org/argouml/model/mdr/conversions/";

    /*
     * A near clone of this code works fine outside of ArgoUML, but throws a
     * null pointer exception during the transform when run within ArgoUML I
     * think it's something to do with the class libraries being used, but I
     * can't figure out what, so I've done a simpler, less efficient stepwise
     * translation below in serialTransform
     */
    private InputSource chainedTransform(String[] styles, InputSource input)
        throws XmiException {
        SAXTransformerFactory stf =
            (SAXTransformerFactory) TransformerFactory.newInstance();

        // TODO: Reconfigure exception handling to distinguish between errors
        // that are possible due to bad input data and those that represent
        // unexpected processing errors.
        try {
            // Set up reader to be first filter in chain
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser parser = spf.newSAXParser();
            XMLReader last = parser.getXMLReader();

            // Create filter for each style sheet and chain to previous
            // filter/reader
            for (int i = 0; i < styles.length; i++) {
                String xsltFileName = STYLE_PATH + styles[i];
                URL xsltUrl = getClass().getResource(xsltFileName);
                if (xsltUrl == null) {
                    throw new IOException("Error opening XSLT style sheet : "
                            + xsltFileName);
                }
                StreamSource xsltStreamSource =
                    new StreamSource(xsltUrl.openStream());
                xsltStreamSource.setSystemId(xsltUrl.toExternalForm());
                XMLFilter filter = stf.newXMLFilter(xsltStreamSource);

                filter.setParent(last);
                last = filter;
            }

            SAXSource transformSource = new SAXSource(last, input);

            // Create temporary file for output
            // TODO: we should be able to chain this directly to XMI reader
            File tmpFile = File.createTempFile(TEMP_XMI_FILE_PREFIX, ".xmi");
            tmpFile.deleteOnExit();
            StreamResult result =
                new StreamResult(
                    new FileOutputStream(tmpFile));

            Transformer transformer = stf.newTransformer();
            transformer.transform(transformSource, result);

            return new InputSource(new FileInputStream(tmpFile));

        } catch (SAXException e) {
            throw new XmiException(e);
        } catch (ParserConfigurationException e) {
            throw new XmiException(e);
        } catch (IOException e) {
            throw new XmiException(e);
        } catch (TransformerConfigurationException e) {
            throw new XmiException(e);
        } catch (TransformerException e) {
            throw new XmiException(e);
        }

    }

    private InputSource serialTransform(String[] styles, InputSource input)
        throws UmlException {
        SAXSource myInput = new SAXSource(input);
        SAXTransformerFactory stf =
            (SAXTransformerFactory) TransformerFactory.newInstance();
        try {

            for (int i = 0; i < styles.length; i++) {
                // Set up source for style sheet
                String xsltFileName = STYLE_PATH + styles[i];

                LOG.log(Level.INFO, "Transforming with {0}", xsltFileName);

                URL xsltUrl = getClass().getResource(xsltFileName);
                if (xsltUrl == null) {
                    throw new UmlException("Error opening XSLT style sheet : "
                            + xsltFileName);
                }
                StreamSource xsltStreamSource =
                    new StreamSource(xsltUrl.openStream());
                xsltStreamSource.setSystemId(xsltUrl.toExternalForm());

                // Create & set up temporary output file
                File tmpOutFile = File.createTempFile(TEMP_XMI_FILE_PREFIX, ".xmi");
                tmpOutFile.deleteOnExit();
                StreamResult result =
                    new StreamResult(new FileOutputStream(
                        tmpOutFile));

                // Create transformer and do transformation
                Transformer transformer = stf.newTransformer(xsltStreamSource);
                transformer.transform(myInput, result);

                LOG.log(Level.INFO, "Wrote converted XMI file - {0} converted using : {1}",
                        new Object[]{tmpOutFile, xsltFileName});

                // Set up for next iteration
                myInput =
                    new SAXSource(new InputSource(new FileInputStream(
                        tmpOutFile)));
                myInput.setSystemId(tmpOutFile.toURI().toURL().toExternalForm());
            }
            return myInput.getInputSource();
        } catch (IOException e) {
            throw new UmlException(e);
        } catch (TransformerConfigurationException e) {
            throw new UmlException(e);
        } catch (TransformerException e) {
            throw new UmlException(e);
        }

    }

    private File copySource(InputSource input) throws IOException {
        byte[] buf = new byte[2048];
        int len;

        // Create & set up temporary output file
        File tmpOutFile = File.createTempFile(TEMP_XMI_FILE_PREFIX, ".xmi");
        tmpOutFile.deleteOnExit();
        FileOutputStream out = new FileOutputStream(tmpOutFile);

        // TODO: Bob says - Coding by use of side effect here.
        // Maybe this should be done in a clearer way but it fixes
        // http://argouml.tigris.org/issues/show_bug.cgi?id=4978
        // It seems that when loading an XMI that is not contained in a zip
        // file then the InputStream given as the argument to this method
        // can't be reused as it is at the end of the stream. In that case
        // systemId appears to be none-null at this stage.
        // So if systemId is not null we recreate the InputSource.
        String systemId = input.getSystemId();
        if (systemId != null) {
            input = new InputSource(new URL(systemId).openStream());
        }

        InputStream in = input.getByteStream();

        while ((len = in.read(buf)) >= 0) {
            out.write(buf, 0, len);
        }
        out.close();

        LOG.log(Level.FINE, "Wrote copied XMI file to {0}", tmpOutFile);
        return tmpOutFile;
    }

    private static final String UML_13_ELEMENTS[] =
    {
        "TaggedValue.value",
        "TaggedValue.tag",
        "ModelElement.templateParameter2",
        "ModelElement.templateParameter3",
        "Classifier.structuralFeature",
        "Classifier.parameter",
        "AssociationEnd.type",
        "Node.resident",
        "ElementResidence.implementationLocation",
        "TemplateParameter.modelElement",
        "TemplateParameter.modelElement2",
        "Constraint.constrainedElement2",
        "UseCase.include2",
        "StateMachine.subMachineState",
        "ClassifierRole.message1",
        "ClassifierRole.message2",
        "Message.message3",
        "Message.message4",
        "ElementImport.modelElement",

        "ModelElement.elementResidence",
        "ModelElement.presentation",
        "ModelElement.supplierDependency",
        "ModelElement.templateParameter2",
        "ModelElement.templateParameter3",
        "ModelElement.binding",
        "GeneralizableElement.specialization",
        "Classifier.associationEnd",
        "Classifier.participant",
        "Operation.method",
        "Stereotype.extendedElement",
        "Stereotype.requiredTag",
        "TaggedValue.stereotype",
        "Signal.context",
        "Signal.reception",
        "Signal.sendAction",

        "UseCase.include2",
        "UseCase.extend2",
        "ExtensionPoint.extend",
        "Link.stimulus",
        "Instance.attributeLink",
        "Action.stimulus",
        "Event.state",
        "Event.transition",
        "Transition.state",

        "ClassifierRole.message1",
        "ClassifierRole.message2",
        "Message.message3",
        "Message.message4",

        "Action.state1",
        "Action.state2",
        "Action.state3",
        "Instance.stimulus1",
        "Instance.stimulus2",
        "Instance.stimulus3",

    };


    public void elementFound(String name) {
        // Silently ignore anything specified by caller attempt to continue
        if (ignoredElements != null) {
            for (int i = 0; i < ignoredElements.length; i++) {
                if (name.equals(ignoredElements[i])) {
                    ignoredElementCount++;
                    return;
                }
            }
        }

        if (name.startsWith("Foundation.")) {
            uml13 = true;
            return;
        }

        for (int i = 0; i < UML_13_ELEMENTS.length; i++) {
            if (name.endsWith(UML_13_ELEMENTS[i])) {
                uml13 = true;
                return;
            }
        }

        unknownElement = true;
        if (unknownElementName == null) {
            unknownElementName = name;
        }
        LOG.log(Level.SEVERE, "Unknown XMI element named : " + name);

    }


    public boolean setIgnoredElements(String[] elementNames) {
        if (elementNames == null) {
            ignoredElements = new String[] {};
        } else {
            ignoredElements = elementNames;
        }
        return true;
    }


    public String[] getIgnoredElements() {
        return ignoredElements;
    }


    public int getIgnoredElementCount() {
        return ignoredElementCount;
    }


    public String getTagName() {
        return "XMI";
    }

    public void addSearchPath(String path) {
        modelImpl.addSearchPath(path);
    }

    public void removeSearchPath(String path) {
        modelImpl.removeSearchPath(path);
    }

    public List<String> getSearchPath() {
        return modelImpl.getSearchPath();
    }

    public void consumeHeader(InputStream stream) {
        try {
            int length = stream.available();
            byte[] bytes = new byte[length];
            stream.read(bytes, 0, length);
            // we presume the stream is encoded using the default char encoding
            xmiHeader = new String(bytes);
        } catch (IOException e) {
            LOG.log(Level.SEVERE, "Exception reading XMI file header", e);
        }
    }


    public String getHeader() {
        return xmiHeader;
    }

}