com.g2inc.scap.library.domain.SCAPContentManager.java Source code

Java tutorial

Introduction

Here is the source code for com.g2inc.scap.library.domain.SCAPContentManager.java

Source

package com.g2inc.scap.library.domain;
/* Copyright (c) 2016 - 2016. VMware, Inc. All rights reserved.
* 
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3.0 
* as published by the FreeSoftware Foundation 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 version 3.0 
* for more details. You should have received a copy of the GNU General Public License version 3.0 along with this program; if not, write to
* the Free Software Foundation, Inc., 675 Mass Avenue, Cambridge, MA 02139, USA.
*/

/* ESCAPE Software Copyright 2010 G2, Inc. - All rights reserved.
*
* ESCAPE is open source software distributed under GNU General Public License Version 3.  ESCAPE is not in the public domain 
* and G2, Inc. holds its copyright.  Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
    
* 1. Redistributions of ESCAPE source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the ESCAPE Software distribution. 
* 3. Neither the name of G2, Inc. nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
    
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL G2, INC., THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
    
* You should have received a copy of the GNU General Public License Version 3 along with this program. 
* If not, see http://www.gnu.org/licenses/ for a copy.
*/

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
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 javax.swing.JOptionPane;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.g2inc.scap.library.domain.bundle.SCAPBundleType;
import com.g2inc.scap.library.domain.bundle.SCAPDocumentBundle;
import com.g2inc.scap.library.domain.cpe.CPEDictionaryDocument;
import com.g2inc.scap.library.domain.cpe.CPEItem;
import com.g2inc.scap.library.domain.cpe.CPEItemTitle;
import com.g2inc.scap.library.domain.oval.AvailableObjectBehavior;
import com.g2inc.scap.library.domain.oval.DefinitionClassEnum;
import com.g2inc.scap.library.domain.oval.OvalDatatype;
import com.g2inc.scap.library.domain.oval.OvalDefinition;
import com.g2inc.scap.library.domain.oval.OvalDefinitionsDocument;
import com.g2inc.scap.library.domain.oval.OvalEntity;
import com.g2inc.scap.library.domain.oval.OvalObject;
import com.g2inc.scap.library.domain.oval.OvalReference;
import com.g2inc.scap.library.domain.oval.OvalState;
import com.g2inc.scap.library.domain.oval.OvalTest;
import com.g2inc.scap.library.domain.oval.OvalVariable;
import com.g2inc.scap.library.parsers.ParserAbstract;
import com.g2inc.scap.library.schema.NameDoc;
import com.g2inc.scap.library.schema.PlatformNameKey;
import com.g2inc.scap.library.schema.SchemaLocator;

/**
 * This class is the primary way to load, access, and save content.
 * It is defined as a singleton so that access to the content can be synchronized.
 *
 * @author ssill2
 */
public class SCAPContentManager {
    private static final Logger LOG = Logger.getLogger(SCAPContentManager.class.getName());
    private static SCAPContentManager instance = null;

    private SCAPDocumentBundle officalCPEBundle = null;

    private List<String> officalCPEProductNames = null;
    private List<String> officalCPEProductTitles = null;
    private List<String> officalCPEPlatformNames = null;
    private List<String> officalCPEPlatformTitles = null;

    private final String cpeHold = "CPEHOLD";

    private static final Map<SCAPDocumentTypeEnum, List<String>> platformTypesForOvalVersion = new HashMap<SCAPDocumentTypeEnum, List<String>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>> testTypesForOvalVersionAndPlatformType = new HashMap<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>> objectTypesForOvalVersionAndPlatformType = new HashMap<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>> stateTypesForOvalVersionAndPlatformType = new HashMap<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, Map<String, List<OvalEntity>>>> objectEntityTypesForOvalVersionAndObjectType = new HashMap<SCAPDocumentTypeEnum, Map<String, Map<String, List<OvalEntity>>>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, Map<String, List<OvalEntity>>>> stateEntityTypesForOvalVersionAndStateType = new HashMap<SCAPDocumentTypeEnum, Map<String, Map<String, List<OvalEntity>>>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, NameDoc>> enumTypesForOvalVersion = new HashMap<SCAPDocumentTypeEnum, Map<String, NameDoc>>();
    private final Map<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>> enumValuesForOvalVersion = new HashMap<SCAPDocumentTypeEnum, Map<String, List<NameDoc>>>();
    private final Map<SCAPDocumentTypeEnum, Map<PlatformNameKey, List<AvailableObjectBehavior>>> behaviorValues = new HashMap<SCAPDocumentTypeEnum, Map<PlatformNameKey, List<AvailableObjectBehavior>>>();
    private Map<String, SCAPDocument> content = new HashMap<String, SCAPDocument>();

    private void parseDocs(File fileOrDir) throws IOException, FileNotFoundException, Exception {
        if (fileOrDir.exists() && fileOrDir.canRead()) {
            if (fileOrDir.isDirectory()) {
                FilenameFilter fnf = new FilenameFilter() {
                    @Override
                    public boolean accept(File dir, String name) {
                        if (name.endsWith(".xml") || name.endsWith(".XML")) {
                            return true;
                        }
                        return false;
                    }
                };

                File[] dircontents = fileOrDir.listFiles(fnf);
                int len = dircontents.length;
                for (int x = 0; x < len; x++) {
                    parseDocs(dircontents[x]);
                }
            } else if (fileOrDir.isFile()) {
                Document doc = null;

                try {
                    if (LOG.isDebugEnabled()) {
                        LOG.trace("Beginning parse of " + fileOrDir.getAbsolutePath());
                    }

                    SCAPDocument sdoc = SCAPDocumentFactory.loadDocument(fileOrDir);
                    content.put(fileOrDir.getAbsolutePath(), sdoc);

                } catch (JDOMException je) {
                    throw new IllegalArgumentException("JDOMException occured trying to parse file " + fileOrDir,
                            je);
                } catch (IOException e) {
                    throw e;
                } catch (Exception e) {
                    throw e;
                }

            }
        } else {
            throw new FileNotFoundException(fileOrDir.getAbsolutePath() + " doesn't exist or is not readable!");
        }
    }

    /**
     * This getInstance method allows you to return the singleton instance of this
     * class for a given file or directory
     *
     * @param fileOrDir A File object representing a file or directory where content is stored
     * @return SCAPContentManager
    */
    public synchronized static SCAPContentManager getInstance(File fileOrDir) throws IOException, Exception {
        return getInstance(fileOrDir, null);
    }

    /**
     * This function should be called to create a new oval definitions document in the given filename.
     * The type(aka version) of oval document is also specified.
     *
     * @param fn Path of new oval file you would like created.
     * @param docType The type of document you want created
    */
    public OvalDefinitionsDocument newDefinitionsDocument(String fn, SCAPDocumentTypeEnum docType) {
        String newFileName = fn;
        LOG.trace("Creating new Oval file:" + newFileName);
        File newFile = new File(newFileName);
        SCAPDocument sdoc = null;

        try {
            sdoc = SCAPDocumentFactory.createNewDocument(docType);
            sdoc.setFilename(newFile.getAbsolutePath());
        } catch (Exception e) {
            LOG.error("IO Error creating new OVAL XML file", e);
            return null;
        }

        OvalDefinitionsDocument doc = (OvalDefinitionsDocument) sdoc;
        doc.setGeneratorDate(new Date());
        try {
            doc.save();
        } catch (Exception e) {
            LOG.error("Error writing new OVAL XML file", e);
            return null;
        }

        return doc;
    }

    /**
     * This getInstance method allows you to return the singleton instance of this
     * class for a given file or directory, along with specifying the path
     * where the schemas can be located.
     *
     * @param fileOrDir A File object representing a file or directory where content is stored
     * @param schemaroot Path to schemas
    */
    public synchronized static SCAPContentManager getInstance(File fileOrDir, String schemaroot)
            throws IOException, Exception {
        if (instance == null) {
            instance = new SCAPContentManager();
            instance.populateOVALSchemaInformation();
        }

        if (fileOrDir == null) {
            return instance;
        }

        if (fileOrDir.isFile()) {
            // when called with a file, see if that one file is already parsed.
            String absPath = fileOrDir.getAbsolutePath();
            if (instance.content.containsKey(absPath)) {
                return instance;
            }

            long start = System.nanoTime();
            instance.parseDocs(fileOrDir);
            long finish = System.nanoTime();
            long timetakenNS = finish - start;
            float timeTakenSec = (float) timetakenNS / (float) 1000000000;
            if (LOG.isDebugEnabled()) {
                LOG.trace("parseDocs time taken(sec): " + timeTakenSec);
            }
        } else if (fileOrDir.isDirectory()) {
            List<File> files = new ArrayList<File>();
            gatherFiles(fileOrDir, files);

            Iterator<File> fi = files.iterator();

            while (fi.hasNext()) {
                File f = fi.next();

                String absPath = f.getAbsolutePath();
                if (instance.content.containsKey(absPath)) {
                    continue;
                }

                long start = System.nanoTime();
                instance.parseDocs(fileOrDir);
                long finish = System.nanoTime();
                long timetakenNS = finish - start;
                float timeTakenSec = (float) timetakenNS / (float) 1000000000;

                if (LOG.isDebugEnabled()) {
                    LOG.trace("parseDocs time taken(sec): " + timeTakenSec);
                }
            }
        }

        return instance;
    }

    private static void gatherFiles(File fileOrDir, List<File> files) {
        if (fileOrDir.isFile()) {
            if (fileOrDir.getName().toLowerCase().endsWith(".xml")) {
                files.add(fileOrDir);
            }
        } else if (fileOrDir.isDirectory()) {
            File[] children = fileOrDir.listFiles();

            if (children == null) {
                return;
            }

            for (int x = 0; x < children.length; x++) {
                gatherFiles(children[x], files);
            }
        }
    }

    /**
     * This getInstance method allows you to return the singleton instance of this
     * class.  If the instance hasn't been initialized, it will be initialized without
     * any documents.
     * 
     * @return SCAPContentManager
    */
    public synchronized static SCAPContentManager getInstance() {

        if (instance == null) {
            try {
                return getInstance(null, null);
            } catch (Exception e) {
                throw new IllegalStateException("Got really unexpected exception", e);
            }
        }

        return instance;
    }

    /**
     * Get an already loaded document by name.
     *
     * @param absFilePath String path of a loaded document.
     * @return SCAPDocument
    */
    public synchronized SCAPDocument getDocument(String absFilePath) {
        return content.get(absFilePath);
    }

    /**
     * Get the definitions from an already loaded document.
     *
     * @param absFilePath String path of a loaded document.
     * @return List<OvalDefinition>
    */
    public synchronized List<OvalDefinition> getDefinitions(String absFilePath) {
        List<OvalDefinition> defs = null;

        SCAPDocument d = content.get(absFilePath);

        if (d == null) {
            return null;
        }

        if (!(d instanceof OvalDefinitionsDocument)) {
            return null;
        }

        OvalDefinitionsDocument odd = (OvalDefinitionsDocument) d;

        defs = odd.getOvalDefinitions();

        return defs;
    }

    public synchronized List<SCAPDocument> getLoadedSCAPDocuments() {
        List<SCAPDocument> list = new ArrayList<SCAPDocument>();
        Iterator<String> contentIter = content.keySet().iterator();
        while (contentIter.hasNext()) {
            String filename = contentIter.next();
            SCAPDocument sd = content.get(filename);
            list.add(sd);
        }
        return list;
    }

    public synchronized List<SCAPDocument> getLoadedSCAPDocuments(SCAPDocumentTypeEnum type) {
        List<SCAPDocument> list = new ArrayList<SCAPDocument>();
        Iterator<String> contentIter = content.keySet().iterator();
        while (contentIter.hasNext()) {
            String filename = contentIter.next();
            SCAPDocument sd = content.get(filename);
            if (type == null)
                list.add(sd);
            else if (sd.getDocumentType() == type) {
                list.add(sd);
            }
        }
        return list;
    }

    /**
     * Get any loaded oval definitions documents
     *
     * 
    */
    public synchronized List<OvalDefinitionsDocument> getOvalDefinitionsDocuments() {
        return getOvalDefinitionsDocuments(null);
    }

    /**
     * Get any loaded oval definitions documents in a certain directory
     *
     * 
    */
    public synchronized List<OvalDefinitionsDocument> getOvalDefinitionsDocuments(String absParent) {
        List<OvalDefinitionsDocument> defDocs = new ArrayList<OvalDefinitionsDocument>();

        Iterator<String> itr = content.keySet().iterator();

        while (itr.hasNext()) {
            String filename = itr.next();

            if (filename == null || filename.startsWith(absParent)) {
                SCAPDocument sd = content.get(filename);

                if (sd instanceof OvalDefinitionsDocument) {
                    defDocs.add((OvalDefinitionsDocument) sd);
                }
            }
        }

        return defDocs;
    }

    /**
     * Returns true if any of the loaded OvalDefinitionsDocuments contain
     * a definition with the given id
     *
     * @param defId The id of an oval definition.
     * 
    */
    public synchronized boolean haveDefinition(String defId) {
        boolean ret = false;

        Iterator<String> itr = content.keySet().iterator();

        while (itr.hasNext()) {
            String filename = itr.next();

            SCAPDocument sd = content.get(filename);

            if (sd instanceof OvalDefinitionsDocument) {
                OvalDefinitionsDocument odd = (OvalDefinitionsDocument) sd;

                // ask about defId
                ret = odd.containsDefinition(defId);

                if (ret) {
                    break;
                }
            }
        }

        return ret;
    }

    private void addDependentDefs(OvalDefinitionsDocument existingDoc, OvalDefinitionsDocument newDoc,
            List<OvalDefinition> referencedDefs) {
        if (referencedDefs != null) {
            for (OvalDefinition od : referencedDefs) {
                if (!newDoc.containsDefinition(od.getId())) {
                    newDoc.add(od);

                    List<OvalDefinition> rdefs = od.getReferencedDefinitions();
                    if (rdefs != null && rdefs.size() > 0) {
                        addDependentDefs(existingDoc, newDoc, rdefs);
                    }

                    List<OvalTest> rtests = od.getReferencedTests();
                    if (rtests != null && rtests.size() > 0) {
                        addDependentTests(existingDoc, newDoc, rtests);
                    }
                }
            }
        }
    }

    private void addDependentTests(OvalDefinitionsDocument existingDoc, OvalDefinitionsDocument newDoc,
            List<OvalTest> referencedTests) {
        if (referencedTests != null) {
            for (OvalTest ot : referencedTests) {
                if (!newDoc.containsTest(ot.getId())) {
                    //               newDocTestsElement.addContent((Element) ot.getElement().clone());
                    newDoc.add(ot);

                    OvalObject oo = ot.getObject();
                    if (oo != null) {
                        List<OvalObject> robjs = new ArrayList<OvalObject>();
                        robjs.add(oo);
                        addDependentObjects(existingDoc, newDoc, robjs);
                    }

                    OvalState os = ot.getState();
                    if (os != null) {
                        List<OvalState> rstates = new ArrayList<OvalState>();
                        rstates.add(os);
                        addDependentStates(existingDoc, newDoc, rstates);
                    }
                }
            }
        }
    }

    private void addDependentObjects(OvalDefinitionsDocument existingDoc, OvalDefinitionsDocument newDoc,
            List<OvalObject> referencedObjs) {
        if (referencedObjs != null) {
            for (OvalObject oo : referencedObjs) {
                if (!newDoc.containsObject(oo.getId())) {
                    //               newDocObjectsElement.addContent((Element) oo.getElement().clone());
                    newDoc.add(oo);

                    List<OvalObject> robjs = oo.getReferencedObjects();
                    if (robjs != null && robjs.size() > 0) {
                        addDependentObjects(existingDoc, newDoc, robjs);
                    }

                    List<OvalState> rstates = oo.getReferencedStates();
                    if (rstates != null && rstates.size() > 0) {
                        addDependentStates(existingDoc, newDoc, rstates);
                    }

                    List<OvalVariable> rvars = oo.getReferencedVariables();
                    if (rvars != null && rvars.size() > 0) {
                        addDependentVariables(existingDoc, newDoc, rvars);
                    }
                }
            }
        }
    }

    private void addDependentStates(OvalDefinitionsDocument existingDoc, OvalDefinitionsDocument newDoc,
            List<OvalState> referencedStates) {
        if (referencedStates != null) {
            for (OvalState os : referencedStates) {
                if (!newDoc.containsState(os.getId())) {
                    //               newDocStatesElement.addContent((Element) os.getElement().clone());
                    newDoc.add(os);

                    List<OvalVariable> rvars = os.getReferencedVariables();

                    if (rvars != null && rvars.size() > 0) {
                        addDependentVariables(existingDoc, newDoc, rvars);
                    }
                }
            }
        }
    }

    private void addDependentVariables(OvalDefinitionsDocument existingDoc, OvalDefinitionsDocument newDoc,
            List<OvalVariable> referencedVars) {
        if (referencedVars != null) {
            for (OvalVariable ov : referencedVars) {
                if (!newDoc.containsVariable(ov.getId())) {
                    newDoc.add(ov);

                    List<OvalVariable> rvars = ov.getReferencedVariables();
                    if (rvars != null && rvars.size() > 0) {
                        addDependentVariables(existingDoc, newDoc, rvars);
                    }

                    List<OvalObject> robjs = ov.getReferencedObjects();
                    if (robjs != null && robjs.size() > 0) {
                        addDependentObjects(existingDoc, newDoc, robjs);
                    }
                }
            }
        }
    }

    /**
     * Returns a new OvalDefinitionsDocument with definitions that references the given set of reference ids. Dependent
     * tests, objects, states, and variables are also added.
     *
     * @param refsToFind A set of id strings we are looking for.
     * @param type Produce a document of this type and stick to this type when looking at documents for definitions.
     * 
    */
    public synchronized OvalDefinitionsDocument newDefinitionsDocumentForRefs(Set<String> refsToFind,
            SCAPDocumentTypeEnum type) {
        OvalDefinitionsDocument ret = null;

        ret = (OvalDefinitionsDocument) SCAPDocumentFactory.createNewDocument(type);

        Iterator<String> keyItr = content.keySet().iterator();

        while (keyItr.hasNext()) {
            String documentFilename = keyItr.next();

            SCAPDocument sd = content.get(documentFilename);

            if (sd instanceof OvalDefinitionsDocument) {
                OvalDefinitionsDocument odd = (OvalDefinitionsDocument) sd;

                if (odd.getDocumentType().equals(type)) {
                    for (String refId : refsToFind) {
                        List<OvalDefinition> defs = odd.getDefsForReferenceId(refId);

                        if (defs != null && defs.size() > 0) {
                            addDependentDefs(odd, ret, defs);
                        }
                    }
                }
            }
        }

        return ret;
    }

    /**
     * Returns a new OvalDefinitionsDocument with definitions that references the given set of reference ids. Dependent
     * tests, objects, states, and variables are also added.
     *
     * @param refsToFind A set of id strings we are looking for.
     * 
    */

    public synchronized OvalDefinitionsDocument newDefinitionsDocumentForRefs(Set<String> refsToFind) {
        return newDefinitionsDocumentForRefs(refsToFind, SCAPDocumentTypeEnum.OVAL_511);

    }

    /**
     * Returns a new OvalDefinitionsDocument with definitions that references the given set of reference ids. Dependent
     * tests, objects, states, and variables are also added.  Also adds a definition to the resulting document that has
     * an extend definition reference to the definitions that were added because they matched the reference ids supplied.
     *
     * @param refsToFind A set of id strings we are looking for.
     * @param type Produce a document of this type and stick to this type when looking at documents for definitions.
     * @param di numeric portion of resulting defintion id
     * @param dn namespace portion of resulting definition id
     * 
    */
    public synchronized OvalDefinitionsDocument newDefinitionsDocumentForRefs(Set<String> refsToFind,
            SCAPDocumentTypeEnum type, String dn, String di) {
        OvalDefinitionsDocument odd = newDefinitionsDocumentForRefs(refsToFind, type);

        odd.setBaseId(dn);

        // add a definition to the document that includes the other definitions in the document as extendDefinition entries
        OvalDefinition newDef = odd.createDefinition(DefinitionClassEnum.VULNERABILITY);
        newDef.setId("oval:" + dn + ":def:" + di);

        odd.addDefinition(newDef, 0);

        Element criteriaElement = newDef.getElement().getChild("criteria", newDef.getElement().getNamespace());

        if (criteriaElement == null) {
            criteriaElement = new Element("criteria", newDef.getElement().getNamespace());
            newDef.getElement().addContent(criteriaElement);
        }

        List<OvalDefinition> defList = odd.getOvalDefinitions();

        for (OvalDefinition od : defList) {
            List<OvalReference> refs = od.getReferences();

            if (criteriaElement != null && !od.getId().equals(newDef.getId())) {
                boolean foundRef = false;

                for (Iterator<String> i = refsToFind.iterator(); i.hasNext();) {
                    if (od.getMetadata().containsReference(i.next())) {
                        foundRef = true;
                        break;
                    }
                }
                if (foundRef) {
                    newDef.addReferences(refs);

                    Element edrElement = new Element("extend_definition", criteriaElement.getNamespace());
                    edrElement.setAttribute("comment", "generated comment");
                    edrElement.setAttribute("definition_ref", od.getId());
                    criteriaElement.addContent(edrElement);
                }
            }
        }

        return odd;
    }

    /**
     * Returns a new OvalDefinitionsDocument with definitions that references the given set of reference ids. Dependent
     * tests, objects, states, and variables are also added.  Also adds a definition to the resulting document that has
     * an extend definition reference to the definitions that were added because they matched the reference ids supplied.
     *
     * @param refsToFind A set of id strings we are looking for.
     * @param di numeric portion of resulting defintion id
     * @param dn namespace portion of resulting definition id
     * 
    */

    public synchronized OvalDefinitionsDocument newDefinitionsDocumentForRefs(Set<String> refsToFind, String dn,
            String di) {
        return newDefinitionsDocumentForRefs(refsToFind, SCAPDocumentTypeEnum.OVAL_511, dn, di);
    }

    /**
     * Returns list of valid test types on a given set of platforms for a specific version of oval
     *
     * @param ovalVersion Enum of oval version we want
     * @param platforms String array of platforms we are interested in
     * 
    */
    public List<NameDoc> getValidTestTypes(SCAPDocumentTypeEnum ovalVersion, String[] platforms) {
        List<NameDoc> allTestTypes = new ArrayList<NameDoc>();
        for (int i = 0; i < platforms.length; i++) {
            Map<String, List<NameDoc>> testTypesByPlatform = testTypesForOvalVersionAndPlatformType
                    .get(ovalVersion);
            List<NameDoc> testTypes = testTypesByPlatform.get(platforms[i]);
            allTestTypes.addAll(testTypes);

        }
        Collections.sort(allTestTypes);
        return allTestTypes;
    }

    /**
     * Returns list of valid object types on a given set of platforms for a specific version of oval
     *
     * @param ovalVersion Enum of oval version we want
     * @param platforms String array of platforms we are interested in
     * 
    */
    public List<NameDoc> getValidObjectTypes(SCAPDocumentTypeEnum ovalVersion, String[] platforms) {
        List<NameDoc> allObjectTypes = new ArrayList<NameDoc>();
        for (int i = 0; i < platforms.length; i++) {
            Map<String, List<NameDoc>> objectTypesByPlatform = objectTypesForOvalVersionAndPlatformType
                    .get(ovalVersion);
            List<NameDoc> objectTypes = objectTypesByPlatform.get(platforms[i]);
            allObjectTypes.addAll(objectTypes);
        }
        Collections.sort(allObjectTypes);
        return allObjectTypes;
    }

    /**
     * Returns list of valid state types on a given set of platforms for a specific version of oval
     *
     * @param ovalVersion Enum of oval version we want
     * @param platforms String array of platforms we are interested in
     * 
    */
    public List<NameDoc> getValidStateTypes(SCAPDocumentTypeEnum ovalVersion, String[] platforms) {
        List<NameDoc> allStateTypes = new ArrayList<NameDoc>();
        for (int i = 0; i < platforms.length; i++) {
            Map<String, List<NameDoc>> stateTypesByPlatform = stateTypesForOvalVersionAndPlatformType
                    .get(ovalVersion);
            List<NameDoc> stateTypes = stateTypesByPlatform.get(platforms[i]);
            allStateTypes.addAll(stateTypes);
        }
        Collections.sort(allStateTypes);
        return allStateTypes;
    }

    /**
     * Returns list of valid object entity types on a given set of platforms for a specific version of oval and
     * a specific type of oval object
     *
     * @param ovalVersion Enum of oval version we want
     * @param platform The platform we are interested in
     * @param objectType String name of an oval object type
     * 
    */
    public List<OvalEntity> getValidObjectEntityTypes(SCAPDocumentTypeEnum ovalVersion, String platform,
            String objectType) {
        //      LOG.debug("ovalVersion=" + ovalVersion + ", platform=" + platform + ", objectType=" + objectType);
        Map<String, Map<String, List<OvalEntity>>> platformEntityMap = objectEntityTypesForOvalVersionAndObjectType
                .get(ovalVersion);
        Map<String, List<OvalEntity>> objectEntityMap = platformEntityMap.get(platform);
        List<OvalEntity> objectEntityTypes = null;

        if (objectEntityMap != null) {
            objectEntityTypes = objectEntityMap.get(objectType);
        }

        return objectEntityTypes;
    }

    /**
     * Returns list of valid state entity types on a given set of platforms for a specific version of oval and
     * a specific type of oval state
     *
     * @param ovalVersion Enum of oval version we want
     * @param platform The platform we are interested in
     * @param stateType String name of an oval state type
     * 
    */
    public List<OvalEntity> getValidStateEntityTypes(SCAPDocumentTypeEnum ovalVersion, String platform,
            String stateType) {
        Map<String, Map<String, List<OvalEntity>>> platformEntityMap = stateEntityTypesForOvalVersionAndStateType
                .get(ovalVersion);
        Map<String, List<OvalEntity>> stateEntityMap = platformEntityMap.get(platform);
        List<OvalEntity> stateEntityTypes = stateEntityMap.get(stateType);
        return stateEntityTypes;
    }

    /**
     * Returns list of valid platforms for a specific version of oval.
     *
     * @param ovalVersion Enum of oval version we want
     * 
    */
    public List<String> getValidPlatforms(SCAPDocumentTypeEnum ovalVersion) {
        return platformTypesForOvalVersion.get(ovalVersion);
    }

    /**
     * Returns list of valid enum types for a specific version of oval.
     *
     * @param ovalVersion Enum of oval version we want
     * 
    */
    public Map<String, NameDoc> getEnumTypesForOvalVersion(SCAPDocumentTypeEnum ovalVersion) {
        return enumTypesForOvalVersion.get(ovalVersion);
    }

    /**
     * Returns list of valid enum values for a specific version of oval.
     *
     * @param ovalVersion Enum of oval version we want
     * 
    */
    public Map<String, List<NameDoc>> getEnumValuesForOvalVersion(SCAPDocumentTypeEnum ovalVersion) {
        return enumValuesForOvalVersion.get(ovalVersion);
    }

    /**
     * Returns list of valid object behavior values for a specific version of oval.
     *
     * @param ovalVersion Enum of oval version we want
     * 
    */
    public Map<PlatformNameKey, List<AvailableObjectBehavior>> getBehaviorValues(SCAPDocumentTypeEnum ovalVersion) {
        return behaviorValues.get(ovalVersion);
    }

    private void populateOVALSchemaInformation() {
        SchemaLocator slocator = SchemaLocator.getInstance();

        LOG.trace("Populating schema information");
        // Read xsd files from all OVAL schemas, one xsd
        // for each supported platform (eg, "aix", "linux", "solaris", "windows", etc)
        // For each schema/platform combination, record the valid OVAL test types,
        // object types, and state types. Then for each valid object or state type, record
        // the valid "entity" types, for example the registry_object can have "hive",
        // "key", and "name" as valid entity types.
        for (SCAPDocumentTypeEnum docType : SCAPDocumentTypeEnum.values()) {
            //   LOG.debug("Processing docType " + docType);
            if (docType.name().startsWith("OVAL")) {
                Map<String, List<NameDoc>> testMap = new HashMap<String, List<NameDoc>>();
                Map<String, List<NameDoc>> objectMap = new HashMap<String, List<NameDoc>>();
                Map<String, List<NameDoc>> stateMap = new HashMap<String, List<NameDoc>>();
                Map<String, Map<String, List<OvalEntity>>> objectEntityMap = new HashMap<String, Map<String, List<OvalEntity>>>();
                Map<String, Map<String, List<OvalEntity>>> stateEntityMap = new HashMap<String, Map<String, List<OvalEntity>>>();

                List<String> ovalSchemaResourceNames = slocator.getOvalSchemaResourceFileNames(docType);

                List<String> platformNames = new ArrayList<String>();

                if (ovalSchemaResourceNames != null && ovalSchemaResourceNames.size() > 0) {
                    for (int i = 0; i < ovalSchemaResourceNames.size(); i++) {
                        String schemaResourceName = ovalSchemaResourceNames.get(i);
                        String schemaFileName = schemaResourceName
                                .substring(schemaResourceName.lastIndexOf("/") + 1);
                        //  LOG.debug("Processing docType " + docType + ", schema file name " + schemaFileName);
                        String platform = null;
                        if (schemaFileName.startsWith("oval")) {
                            //    LOG.debug("Found oval xsd file name " + schemaFileName);
                            // handle both "oval-common" and "oval-definition"; all we want here are Enumerations
                            SchemaDOMParser parser = new SchemaDOMParser();
                            InputStream inputStream = slocator.getInputStream(ovalSchemaResourceNames.get(i));
                            parser.parseSchema(inputStream);
                            updateEnumData(parser, docType, "oval");
                        } else if (schemaFileName.endsWith(".xsd")) {
                            LOG.trace("Found xsd file name " + schemaFileName);
                            // xsd file names look like:
                            //    "solaris-definitions-schema.xsd"
                            // we want to find corresponding schema namespace, eg:
                            //    "http://oval.mitre.org/XMLSchema/oval-definitions-5#solaris"
                            int dashIndex = schemaFileName.indexOf("-definitions-schema");
                            if (dashIndex == -1) {
                                continue;
                            }
                            platform = schemaFileName.substring(0, dashIndex);
                            LOG.trace("Found platform " + platform);
                            platformNames.add(platform);

                            SchemaDOMParser parser = new SchemaDOMParser();
                            parser.setPlatform(platform);
                            InputStream inputStream = slocator.getInputStream(ovalSchemaResourceNames.get(i));
                            parser.parseSchema(inputStream);

                            updateEnumData(parser, docType, platform);

                            List<NameDoc> list = parser.getValidTestsForPlatform();
                            testMap.put(platform, list);

                            list = parser.getValidObjectsForPlatform();
                            objectMap.put(platform, list);

                            list = parser.getValidStatesForPlatform();
                            stateMap.put(platform, list);

                            Map<String, List<OvalEntity>> validObjectEntities = parser
                                    .getValidObjectEntitiesByObject();
                            objectEntityMap.put(platform, validObjectEntities);

                            Map<String, List<OvalEntity>> validStateEntities = parser
                                    .getValidStateEntitiesByState();
                            stateEntityMap.put(platform, validStateEntities);

                            Map<PlatformNameKey, List<AvailableObjectBehavior>> behaviorsMap = behaviorValues
                                    .get(docType);
                            if (behaviorsMap == null) {
                                behaviorsMap = new HashMap<PlatformNameKey, List<AvailableObjectBehavior>>();
                                behaviorValues.put(docType, behaviorsMap);
                            }
                            behaviorsMap.putAll(parser.getBehaviorsValuesMap());
                        }
                    }
                    LOG.trace("Found " + platformNames.size() + " platforms for version " + docType);
                    platformTypesForOvalVersion.put(docType, platformNames);
                    testTypesForOvalVersionAndPlatformType.put(docType, testMap);
                    objectTypesForOvalVersionAndPlatformType.put(docType, objectMap);
                    stateTypesForOvalVersionAndPlatformType.put(docType, stateMap);
                    objectEntityTypesForOvalVersionAndObjectType.put(docType, objectEntityMap);
                    fixEntityDatatypes(docType, objectEntityMap);
                    stateEntityTypesForOvalVersionAndStateType.put(docType, stateEntityMap);
                    fixEntityDatatypes(docType, stateEntityMap);
                }
            }
        }
    }

    private void updateEnumData(SchemaDOMParser parser, SCAPDocumentTypeEnum docType, String platform) {
        // first update List of NameDocs for the Enumeration names themselves
        Map<String, NameDoc> parsedEnumNameMap = parser.getEnumerationNameMap();
        Map<String, NameDoc> enumNamesForDocType = enumTypesForOvalVersion.get(docType);
        if (enumNamesForDocType == null) {
            enumNamesForDocType = new HashMap<String, NameDoc>();
            enumTypesForOvalVersion.put(docType, enumNamesForDocType);
        }
        enumNamesForDocType.putAll(parsedEnumNameMap);
        Map<String, List<NameDoc>> parsedEnumValueMap = parser.getEnumerationValueMap();
        Map<String, List<NameDoc>> enumValuesForDocType = enumValuesForOvalVersion.get(docType);
        if (enumValuesForDocType == null) {
            enumValuesForDocType = new HashMap<String, List<NameDoc>>();
            enumValuesForOvalVersion.put(docType, enumValuesForDocType);
        }
        enumValuesForDocType.putAll(parsedEnumValueMap);
    }

    private void fixEntityDatatypes(SCAPDocumentTypeEnum docType,
            Map<String, Map<String, List<OvalEntity>>> platformEntityMap) {
        Set<String> platformKeys = platformEntityMap.keySet();
        Iterator<String> platformKeyIter = platformKeys.iterator();
        while (platformKeyIter.hasNext()) {
            String key = platformKeyIter.next();
            Map<String, List<OvalEntity>> entityValueMap = platformEntityMap.get(key);
            Set<String> entityKeys = entityValueMap.keySet();
            Iterator<String> entityKeyIter = entityKeys.iterator();
            while (entityKeyIter.hasNext()) {
                String entityKey = entityKeyIter.next();
                List<OvalEntity> entityList = entityValueMap.get(entityKey);
                for (int iEntity = 0; iEntity < entityList.size(); iEntity++) {
                    OvalEntity entity = entityList.get(iEntity);
                    String schemaType = entity.getDatatypeString();
                    int colonOffset = schemaType.indexOf(":");
                    if (colonOffset != -1) {
                        schemaType = schemaType.substring(colonOffset + 1);
                    }
                    Map<String, List<NameDoc>> map = getEnumValuesForOvalVersion(docType);
                    OvalDatatype dataType = new OvalDatatype(schemaType, map);
                    entity.setDatatype(dataType);
                }
            }
        }
    }

    /**
     * A main method only used for testing
     *
     * @param args Command-line arguments
     * 
    */
    public static void main(String[] args) throws Exception {
        BasicConfigurator.configure();
        LOG.trace("SchemaParser starting, schemaVersion =" + args[0] + ", xsd File=" + args[1]);
        File xsdFile = new File(args[1]);
        if (!xsdFile.exists()) {
            LOG.error("Usage: java SchemaParser <name of xsd file>");
        }
        SCAPDocumentTypeEnum version = SCAPDocumentTypeEnum.valueOf(args[0]);
        SCAPContentManager mgr = SCAPContentManager.getInstance(new File(args[1]));

        // schema-related data should now be populated
        List<String> platformTypes = mgr.getValidPlatforms(version);
        LOG.trace("STARTING PLATFORMS");
        for (int i = 0; i < platformTypes.size(); i++) {
            LOG.trace(platformTypes.get(i));
        }

        String[] platforms = { "windows", "independent" };

        List<NameDoc> list = null;
        Iterator<NameDoc> iter = null;
        LOG.trace("STARTING TESTS");
        list = mgr.getValidTestTypes(version, platforms);
        iter = list.iterator();
        while (iter.hasNext()) {
            NameDoc testName = iter.next();
            LOG.trace("\t" + testName);
        }
        LOG.trace("STARTING OBJECTS");
        list = mgr.getValidObjectTypes(version, platforms);
        iter = list.iterator();
        while (iter.hasNext()) {
            NameDoc objName = iter.next();
            LOG.trace("\t" + objName);
            LOG.trace("STARTING OBJECT ENTITIES FOR " + objName.getName());
            List<OvalEntity> entityList = mgr.getValidObjectEntityTypes(version, "windows", objName.getName());
            if (entityList != null && entityList.size() > 0) {
                for (int i = 0; i < entityList.size(); i++) {
                    LOG.trace("\t\t" + entityList.get(i).toString());
                }
            }
        }

        LOG.trace("STARTING STATES");
        list = mgr.getValidStateTypes(version, platforms);
        iter = list.iterator();
        while (iter.hasNext()) {
            NameDoc stateName = iter.next();
            LOG.trace("\t" + stateName);
            LOG.trace("STARTING STATE ENTITIES FOR " + stateName.getName());
            List<OvalEntity> entityList = mgr.getValidStateEntityTypes(version, "windows", stateName.getName());
            if (entityList != null && entityList.size() > 0) {
                for (int i = 0; i < entityList.size(); i++) {
                    LOG.trace("\t\t" + entityList.get(i).toString());
                }
            }
        }
    }

    /**
     * Removes a loaded document from the in-memory map.
     * File could still exist on disk.
     *
     * @param absFilename Path of file
     * 
    */
    public synchronized void removeDocument(String absFilename) {
        SCAPDocument doc = content.get(absFilename);
        if (doc != null) {
            doc.tearDown();

            content.remove(absFilename);
        }
    }

    /**
     * Removes all loaded documents from the in-memory map.
     */
    public synchronized void removeAllDocuments() {
        if (content == null || content.size() == 0) {
            return;
        }

        Set<String> filenames = new HashSet<String>();
        filenames.addAll(content.keySet());

        Iterator<String> keyItr = filenames.iterator();

        while (keyItr.hasNext()) {
            String filename = keyItr.next();
            removeDocument(filename);
        }
    }

    /**
     * Adds document to the in-memory map.
     *
     * @param absFilename Path of file
     * @param doc The loaded document object
     * 
    */
    public synchronized void addDocument(String absFilename, SCAPDocument doc) {
        content.put(absFilename, doc);
    }

    /**
     * Use to copy the file on disk for a given scap document.  The new file can
     * be in a different directory all together.  This is mainly intended to be used
     * by the SCAPDocumentBundle code.  Any new directories specified in the new file's 
     * path will be created. 
     * @param doc
     * @param newFilename
     */
    public void copy(SCAPDocument doc, File newFilename) throws IOException {
        String oldFilename = doc.getFilename();

        File newParent = newFilename.getParentFile();

        if (!newParent.exists()) {
            try {
                newParent.mkdirs();
            } catch (Exception e) {
                throw new IllegalStateException(
                        "Unable to rename SCAP document " + oldFilename + " to " + newFilename.getAbsolutePath()
                                + ": Unable to create distination parent directory or directories",
                        e);
            }
        }

        // remove the document from the content map
        removeDocument(oldFilename);

        // set the new name in the document
        doc.setFilename(newFilename.getAbsolutePath());

        // add the document to the content map under the new name;
        addDocument(doc.getFilename(), doc);

        // save the document in the new location
        doc.save();
    }

    public List<File> getOvalFiles(File dir) {
        ArrayList<File> ovalFileList = new ArrayList<File>();
        // get list of all xml files in dir
        File[] xmlFiles = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.toLowerCase().endsWith(".xml");
            }
        });
        if (xmlFiles != null && xmlFiles.length > 0) {
            try {
                SAXParserFactory saxfactory = SAXParserFactory.newInstance();
                saxfactory.setValidating(false);
                saxfactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
                saxfactory.setFeature("http://xml.org/sax/features/validation", false);

                for (int i = 0; i < xmlFiles.length; i++) {
                    SAXParser saxparser = saxfactory.newSAXParser();
                    XMLReader xmlReader = saxparser.getXMLReader();
                    OvalParser ovalParser = new OvalParser();
                    xmlReader.setContentHandler(ovalParser);
                    FileInputStream inStream = new FileInputStream(xmlFiles[i]);
                    xmlReader.parse(new InputSource(inStream));
                    if (ovalParser.isOval()) {
                        ovalFileList.add(xmlFiles[i]);
                    }
                }
            } catch (Exception e) {
                throw new IllegalStateException(
                        "Caught an error trying list oval files in directory " + dir.getAbsolutePath(), e);
            }
        }
        return ovalFileList;
    }

    private class OvalParser extends ParserAbstract {
        boolean oval = false;

        @Override
        public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException {
            if (name.equals("oval_definitions")) {
                oval = true;
            }
        }

        public boolean isOval() {
            return oval;
        }
    }

    /**
     * Set instance to null and clear any maps.  Be careful where you use this!
     */
    public void reset() {
        content.clear();
        content = null;

        instance = null;
    }

    /**
     * Go find and load the offical CPE dictionary from the filesystem.  This also loads the oval file
     * that contains the checks for the dictionary entries.
     *
     * @return SCAPDocumentBundle
     */

    public SCAPDocumentBundle getOffcialCPEDictionary() {
        File f = new File("logs/official-cpe-dictionary_v2.3.xml");
        f.deleteOnExit();
        synchronized (cpeHold) {
            if (officalCPEBundle != null) {
                return officalCPEBundle;
            }

            CPEDictionaryDocument cpedoc = null;
            OvalDefinitionsDocument ovaldoc = null;

            String dictFilename = LibraryConfiguration.getInstance().getOfficialCPEDictionaryPath();
            // String ovalFilename = LibraryConfiguration.getInstance().getOfficialCPEOvalPath();
            InputStream is = this.getClass().getClassLoader()
                    .getResourceAsStream("official-cpe-dictionary_v2.3.xml");
            File dictFile = new File("logs/official-cpe-dictionary_v2.3.xml");
            OutputStream outputStream;
            try {
                outputStream = new FileOutputStream(dictFile);
                IOUtils.copy(is, outputStream);
                outputStream.close();
                is.close();
            } catch (FileNotFoundException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            // File ovalFile = new File(ovalFilename);
            //  System.out.println(ovalFile.exists());         
            // System.out.println(ovalFile.isFile());

            try {
                cpedoc = (CPEDictionaryDocument) SCAPDocumentFactory.loadDocument(dictFile);
                //     ovaldoc = (OvalDefinitionsDocument) SCAPDocumentFactory.loadDocument(ovalFile);

                officalCPEBundle = new SCAPDocumentBundle();
                officalCPEBundle.setBundleType(SCAPBundleType.STREAM);
                officalCPEBundle.setReadOnly(true);

                officalCPEBundle.setFilename(dictFile.getAbsolutePath());

                officalCPEBundle.addDocument(cpedoc);
                //   officalCPEBundle.addDocument(ovaldoc);
                dictFile.delete();
            } catch (Exception e) {
                LOG.error("Error occurred opening offical CPE dictionary SCAP Data Stream", e);
                dictFile.delete();

            }
            dictFile.delete();

        }
        return officalCPEBundle;
    }

    public List<String> getOfficalCPEPlatformNames() {
        synchronized (cpeHold) {
            if (officalCPEPlatformNames == null) {
                initOfficialCPENamesAndTitles();
            }
            return officalCPEPlatformNames;
        }
    }

    public List<String> getOfficalCPEPlatformTitles() {
        synchronized (cpeHold) {
            if (officalCPEPlatformTitles == null) {
                initOfficialCPENamesAndTitles();
            }
            return officalCPEPlatformTitles;
        }
    }

    public List<String> getOfficalCPEProductNames() {
        synchronized (cpeHold) {
            if (officalCPEProductNames == null) {
                initOfficialCPENamesAndTitles();
            }
            return officalCPEProductNames;
        }
    }

    public List<String> getOfficalCPEProductTitles() {
        synchronized (cpeHold) {
            if (officalCPEProductTitles == null) {
                initOfficialCPENamesAndTitles();
            }
            return officalCPEProductTitles;
        }
    }

    private void initOfficialCPENamesAndTitles() {
        getOffcialCPEDictionary();
        officalCPEPlatformNames = new ArrayList<String>();
        officalCPEPlatformTitles = new ArrayList<String>();
        officalCPEProductNames = new ArrayList<String>();
        officalCPEProductTitles = new ArrayList<String>();
        List<CPEDictionaryDocument> docList = officalCPEBundle.getCPEDictionaryDocs();
        CPEDictionaryDocument doc = docList.get(0);
        List<CPEItem> allItems = doc.getItems();
        for (CPEItem item : allItems) {
            String itemName = item.getName(); // this is "cpe:something"
            List<CPEItemTitle> itemTitleList = item.getTitles();
            String itemTitle = getTitle(itemTitleList);
            if (itemName.startsWith("cpe:/a")) {
                officalCPEProductNames.add(itemName);
                officalCPEProductTitles.add(itemTitle);
            } else if (itemName.startsWith("cpe:/o")) {
                officalCPEPlatformNames.add(itemName);
                officalCPEPlatformTitles.add(itemTitle);
            }
        }
    }

    private String getTitle(List<CPEItemTitle> itemList) {
        String title = null;
        for (CPEItemTitle itemTitle : itemList) {
            if (title == null) {
                title = itemTitle.getText();
            }
            String lang = itemTitle.getLang();
            if (lang.equalsIgnoreCase("en-US")) {
                // if more than one title, they should disagree only in lang; prefer the en-us version
                title = itemTitle.getText();
            }
        }
        return title;
    }
}