();
+ for (String version : verFileMap.keySet()) {
+ Version cur = new Version(version);
+ if (base.compareTo(cur) <= 0) {
+ versions.add(cur);
+ }
+ }
+
+ // sort the list of versions and create the return list
+ Collections.sort(versions, new ReverseVersionComparator());
+ for (Version version : versions) {
+ rval.add(verFileMap.get(version.toString()));
+ }
+ }
+
+ return rval;
+ }
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureException.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureException.java
new file mode 100644
index 0000000000..484a0a8a28
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureException.java
@@ -0,0 +1,52 @@
+package com.raytheon.uf.featureexplorer;
+
+/**
+ * Base exception for Feature Explorer
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * 9/7/2008 SP#15 bclement Initial Creation.
+ *
+ *
+ *
+ * @author bclement
+ *
+ */
+public class FeatureException extends Exception {
+
+ private static final long serialVersionUID = 3812733413272988454L;
+
+ /**
+ * Default Constructor
+ *
+ */
+ public FeatureException() {
+ }
+
+ /**
+ * @param message
+ */
+ public FeatureException(String message) {
+ super(message);
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public FeatureException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * @param cause
+ */
+ public FeatureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureExplorer.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureExplorer.java
new file mode 100644
index 0000000000..752cc109ca
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureExplorer.java
@@ -0,0 +1,287 @@
+package com.raytheon.uf.featureexplorer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import com.raytheon.uf.featureexplorer.jaxb.Feature;
+import com.raytheon.uf.featureexplorer.jaxb.Includes;
+import com.raytheon.uf.featureexplorer.jaxb.Plugin;
+import com.raytheon.uf.featureexplorer.search.IFeatureSearch;
+import com.raytheon.uf.featureexplorer.search.IPluginSearch;
+
+/**
+ * This represents an interface for a user to search through a feature for
+ * plugins. This class utilizes other interfaces to do the actual searches, but
+ * handles the bundling of the final list as well as the recursion into included
+ * features.
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 3, 2008 dglazesk Initial creation
+ * Oct 7, 2008 SP#15 bclement Added FeatureException code
+ * Oct 7, 2008 SP#15 bclement Changed ArrayList references to List
+ * Oct 10, 2008 SP#15 bclement Added static functions for reading manifests
+ * Feb 4, 2013 #1577 bkowal Verify that a plugin has not been included in more than one feature.
+ *
+ *
+ * @author dglazesk
+ * @version 1.0
+ */
+public class FeatureExplorer {
+ /**
+ * This holds the feature searching object for the class.
+ */
+ protected IFeatureSearch featureSearch;
+
+ /**
+ * This holds the plugin searching object for the class.
+ */
+ protected IPluginSearch pluginSearch;
+
+ /**
+ * This holds the feature file object should the user choose to set it.
+ */
+ protected File feature = null;
+
+ private Map pluginLookupMap = new HashMap();
+
+ /**
+ * This constructor allows a user to setup what the feature for this
+ * instance is. This allows the user to use getPlugins(), though the other
+ * methods are still available.
+ *
+ * @param aFeature
+ * File object for the feature for this instance
+ * @param aFeatureSearch
+ * The object that can search for features
+ * @param aPluginSearh
+ * The object that can search for plugins
+ */
+ public FeatureExplorer(File aFeature, IFeatureSearch aFeatureSearch,
+ IPluginSearch aPluginSearh) {
+ featureSearch = aFeatureSearch;
+ pluginSearch = aPluginSearh;
+ feature = aFeature;
+ }
+
+ /**
+ * This constructor sets up the classes that will be used for searching for
+ * plugins and features. It is expected that a user will use
+ * getPlugins(File) or getPlugins(String) later when getting the plugins.
+ *
+ * @param aFeatureSearch
+ * The object that can search for features
+ * @param aPluginSearh
+ * The object that can search for plugins
+ */
+ public FeatureExplorer(IFeatureSearch aFeatureSearch,
+ IPluginSearch aPluginSearh) {
+ featureSearch = aFeatureSearch;
+ pluginSearch = aPluginSearh;
+ }
+
+ /**
+ * This is a convenience method for when the feature file object is set in a
+ * constructor.
+ *
+ * @return The list of files in the feature for this instance
+ * @throws FeatureException
+ */
+ public ArrayList getPlugins() throws FeatureException {
+ return getPlugins(feature);
+ }
+
+ /**
+ * This is just a convenience method for getting plugins from a feature.
+ * This is equivalent to doing getPlugins(new File(aPath)).
+ *
+ * @param aPath
+ * Path to the feature.xml file to be scanned
+ * @return The list of file objects for all of the plugins in the feature
+ * @throws FeatureException
+ */
+ public ArrayList getPlugins(String aPath) throws FeatureException {
+ File feat = new File(aPath);
+ return getPlugins(feat);
+ }
+
+ /**
+ * This function attempts to find all of the plugins associated with the
+ * feature. This includes recursing into any included features and grabbing
+ * their plugins.
+ *
+ * @param aFeature
+ * The file object for the feature to be scanned
+ * @return The list of file objects for the located plugins
+ * @throws FeatureException
+ */
+ public ArrayList getPlugins(File aFeature) throws FeatureException {
+ ArrayList rval = new ArrayList();
+
+ HashMap plugins = getFeaturePlugins(aFeature);
+ rval = new ArrayList(plugins.values());
+
+ return rval;
+ }
+
+ /**
+ * This finds all of the plugins listed in a feature and maps their ids to
+ * their file system locations as file objects. This is the function that
+ * does the brunt of the work in locating the plugins.
+ *
+ * @param aFile
+ * The feature file that is being scanned
+ * @return A hash map that links the plugin id to its file system location
+ * @throws FeatureException
+ * If there are any problems with JAXB, a feature cannot be
+ * found, or a plugin cannot be found
+ */
+ protected HashMap getFeaturePlugins(File aFile)
+ throws FeatureException {
+ HashMap rval = new HashMap();
+ if (aFile == null || !aFile.exists() || !aFile.canRead())
+ return rval;
+
+ Feature feat = null;
+ try {
+ JAXBContext jc = JAXBContext.newInstance(Feature.class);
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ feat = (Feature) unmarshaller.unmarshal(aFile);
+ } catch (Exception e) {
+ throw new FeatureException("Unable to unmarshal file " + aFile, e);
+ }
+
+ for (Includes include : feat.getIncludes()) {
+ // go through all of the included features and try to find them
+ List features = featureSearch.findFeature(include.getId(),
+ include.getVersion());
+ try {
+ // get all of the plugin id to file objects and add them
+ rval.putAll(getFeaturePlugins(features.get(0)));
+ } catch (IndexOutOfBoundsException e) {
+ if (!include.getOptional()) {
+ // this means we received an empty list, no feature found
+ throw new FeatureException("Could not find feature "
+ + include.getId() + " with version greater than "
+ + include.getVersion());
+ }
+ }
+ }
+
+ for (Plugin plugin : feat.getPlugins()) {
+ // go through all of the mentioned plugins
+ List plugs = pluginSearch.findPlugin(plugin.getId(),
+ plugin.getVersion());
+ try {
+ if (this.pluginLookupMap.containsKey(plugin.getId())
+ && this.pluginLookupMap.get(plugin.getId()) != aFile) {
+ StringBuilder stringBuilder = new StringBuilder("Plugin ");
+ stringBuilder.append(plugin.getId());
+ stringBuilder.append(" is in Feature ");
+ stringBuilder.append(this.generateFeatureFileName(
+ aFile.getParent(), aFile.getName()));
+ stringBuilder.append(" and Feature ");
+ stringBuilder
+ .append(this.generateFeatureFileName(
+ this.pluginLookupMap.get(plugin.getId())
+ .getParent(), this.pluginLookupMap
+ .get(plugin.getId()).getName()));
+ stringBuilder.append("!");
+ throw new FeatureException(stringBuilder.toString());
+ }
+
+ // add the plugin id and its file object to the map
+ rval.put(plugin.getId(), plugs.get(0));
+ this.pluginLookupMap.put(plugin.getId(), aFile);
+ } catch (IndexOutOfBoundsException e) {
+ // this means we received an empty list, no plugin found
+ throw new FeatureException("Could not find plugin "
+ + plugin.getId() + " with version greater than "
+ + plugin.getVersion());
+ }
+ }
+
+ return rval;
+ }
+
+ private String generateFeatureFileName(String parentPath, String fileName) {
+ String[] pathElements = parentPath.split(File.separator);
+ return pathElements[pathElements.length - 1] + File.separator
+ + fileName;
+ }
+
+ /**
+ * Searches a project's manifest for a specific attribute. The returned list
+ * will contain all values for the attribute or empty if not found.
+ *
+ * @param projectRoot
+ * @param attrib
+ * @return a list of a values for the attribute or an empty list if not
+ * found
+ * @throws IOException
+ */
+ public static List readManifest(File projectRoot, String attrib)
+ throws IOException {
+ File maniFile = new File(projectRoot, "/META-INF/MANIFEST.MF");
+ Manifest m = null;
+ List rval = new ArrayList();
+
+ try {
+ m = new Manifest();
+ // we only care if the manifest actually exists
+ InputStream is = new FileInputStream(maniFile);
+ m.read(is);
+ is.close();
+
+ } catch (IOException e) {
+ throw new IOException(
+ "IO Error while reading manifest for project: "
+ + projectRoot.getName());
+ }
+
+ // if we get this far, m shouldn't be null
+ if (m != null) {
+ Attributes attribs = m.getMainAttributes();
+ String deploys = attribs.getValue(attrib);
+
+ // manifests that do not have a deploy entry will return a wildcard
+ if (deploys != null) {
+ for (String s : deploys.split(",")) {
+ rval.add(s.trim());
+ }
+ }
+ }
+ return rval;
+ }
+
+ /**
+ * Reads the manifest for the project and returns all values for the
+ * "Edex-Deploy" attribute. If the attribute could not be found, default to
+ * returning a wildcard for all jars.
+ *
+ * @param projectRoot
+ * @return a list of jar names or a wildcard for all jars if attribute not
+ * found
+ * @throws IOException
+ */
+ public static List getJars(File projectRoot) throws IOException {
+ List rval = readManifest(projectRoot, "Edex-Deploy");
+ if (rval.isEmpty()) {
+ rval.add("*.jar");
+ }
+ return rval;
+ }
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureIndex.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureIndex.java
new file mode 100644
index 0000000000..41f59f6225
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/FeatureIndex.java
@@ -0,0 +1,81 @@
+package com.raytheon.uf.featureexplorer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import com.raytheon.uf.featureexplorer.jaxb.Feature;
+
+/**
+ * This class represents an index of feature.xml files in a certain directory.
+ * This also has querying abilities thanks to the abstract class.
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 6, 2008 bclement Initial creation
+ *
+ *
+ * @author bclement
+ * @version 1.0
+ */
+public class FeatureIndex extends AbstractResourceIndex {
+
+ /**
+ * Needed constructor that sets the base directory for the index.
+ *
+ * @param aBaseDirectory
+ * File object for the base directory to start the index in
+ * @throws IOException
+ */
+ public FeatureIndex(Collection aBaseDirectories) throws IOException {
+ super(aBaseDirectories);
+ }
+
+ /**
+ * This function does all of the work. It looks for a feature in the project
+ * directory and creates entries in the map for the IDs and the version
+ * numbers for the project into the map.
+ *
+ * @param projectDirectory
+ * Directory for the project to look into
+ * @see AbstractResourceIndex#catalog(File)
+ */
+ protected void catalog(File projectDirectory) {
+ File featureFile = new File(projectDirectory, "feature.xml");
+
+ if (featureFile.exists()) {
+ try {
+ // using JAXB to do the unmarshalling of feature.xml
+ JAXBContext jc = JAXBContext.newInstance(Feature.class);
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ Feature feat = (Feature) unmarshaller.unmarshal(featureFile);
+
+ String id = feat.getId();
+ String version = feat.getVersion();
+
+ // ignore features that do not have a bundle name and version
+ if (id != null && version != null) {
+ if (index.containsValue(id)) {
+ index.get(id).put(version, projectDirectory);
+ } else {
+ Map versionMap = new HashMap();
+ versionMap.put(version, featureFile);
+ index.put(id, versionMap);
+ }
+ }
+ } catch (JAXBException e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/PluginManifestIndex.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/PluginManifestIndex.java
new file mode 100644
index 0000000000..94e6aa5023
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/PluginManifestIndex.java
@@ -0,0 +1,118 @@
+package com.raytheon.uf.featureexplorer;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * This class is to be used for indexing sets of manifests that are found in a
+ * plug-in project for Eclipse.
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 3, 2008 bclement Initial creation
+ * Oct 6, 2008 dglazesk Added support for indexing the jar files.
+ * This just helps with Eclipse plugin indexing.
+ *
+ *
+ * @author bclement
+ * @version 1.0
+ */
+public class PluginManifestIndex extends AbstractResourceIndex {
+
+ /**
+ * Create the index based on a base directory. The base directory must
+ * exist.
+ *
+ * @param aBaseDirectory
+ * The base directory where all the plugins are subdirectories
+ * @throws IOException
+ * Thrown if the directory does not exist or is not a directory.
+ */
+ public PluginManifestIndex(Collection aBaseDirectories)
+ throws IOException {
+ super(aBaseDirectories);
+ }
+
+ /**
+ * This function does all of the work. It looks for a manifest in the
+ * "META-INF" folder in the project directory and creates entries in the map
+ * for the IDs and the version numbers for the project into the map.
+ *
+ * @param projectDirectory
+ * Directory for the project to look into
+ */
+ protected void catalog(File projectDirectory) {
+ File maniFile = new File(projectDirectory, "META-INF/MANIFEST.MF");
+ Manifest m = null;
+
+ try {
+ if (projectDirectory.isFile()) {
+ // regular file means it may be a jar, so try it
+ JarFile proj = new JarFile(projectDirectory);
+ m = proj.getManifest();
+ } else if (maniFile.exists()) {
+ m = new Manifest();
+ // we only care if the manifest actually exists
+ InputStream is = new FileInputStream(maniFile);
+ m.read(is);
+ is.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // if m is null, we either didn't have the manifest or there was an
+ // exception, skip
+ if (m != null) {
+ Attributes attribs = m.getMainAttributes();
+ String id = attribs.getValue("Bundle-SymbolicName");
+ String version = attribs.getValue("Bundle-Version");
+
+ // ignore manifests that do not have a bundle name and version
+ if (id != null && version != null) {
+ if (index.containsValue(id)) {
+ index.get(id).put(version, projectDirectory);
+ } else {
+ Map versionMap = new HashMap();
+ versionMap.put(version, projectDirectory);
+ index.put(id, versionMap);
+ }
+ }
+ }
+ }
+
+ /**
+ * Finds the sub-directories and any jar files in the base directory. The
+ * plugin manifest index needs to support indexing the plug-in jars as well.
+ *
+ * @return The array of sub-directories or jars in the base directory
+ */
+ @Override
+ public File[] getSubDirectories() {
+ return baseDir.listFiles(new FileFilter() {
+ /**
+ * This function accepts if the file is a directory or is a jar
+ * file.
+ *
+ * @param f
+ * The file being checked for acceptance
+ * @return True if the file is a directory or a jar
+ */
+ @Override
+ public boolean accept(File f) {
+ return f.isDirectory() || f.getName().endsWith(".jar");
+ }
+ });
+ }
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/ReverseVersionComparator.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/ReverseVersionComparator.java
new file mode 100644
index 0000000000..832bf84b26
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/ReverseVersionComparator.java
@@ -0,0 +1,38 @@
+package com.raytheon.uf.featureexplorer;
+
+import java.util.Comparator;
+
+/**
+ * This class is available for the sort function of Collections. The idea is
+ * that it compares the Versions backwards so that the sort creates a descending
+ * order Collection.
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 3, 2008 dglazesk Initial creation
+ *
+ *
+ * @author dglazesk
+ * @version 1.0
+ */
+public class ReverseVersionComparator implements Comparator {
+
+ /**
+ * This comparator is purposefully backwards so that the highest version
+ * number can be sorted to the front of an array.
+ *
+ * @param left
+ * The left hand side of the version comparison
+ * @param right
+ * The right hand side of the version comparison
+ * @return -1 if left is bigger than right, 1 if right is bigger, and 0 if
+ * equal
+ */
+ @Override
+ public int compare(Version left, Version right) {
+ return right.compareTo(left);
+ }
+
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/Version.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/Version.java
new file mode 100644
index 0000000000..cc22163583
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/Version.java
@@ -0,0 +1,111 @@
+package com.raytheon.uf.featureexplorer;
+
+/**
+ * This class represents a version number that is separated by periods(.). The
+ * length doesn't matter because the array sizes based on a string split. This
+ * is also capable of comparing two version numbers of any length and
+ * determining which is greater. TODO Add Description
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 3, 2008 dglazesk Initial creation
+ *
+ *
+ * @author dglazesk
+ * @version 1.0
+ */
+public class Version implements Comparable {
+
+ /**
+ * This is meant to hold the string version as passed in to the constructor.
+ */
+ private String versionString;
+
+ /**
+ * This holds the split up version of the string version with all split
+ * parts interpreted as ints.
+ */
+ private int[] intVersion;
+
+ /**
+ * Create a version and set the version string as version. This also sets up
+ * the intVersion for comparison purposes.
+ *
+ * @param version
+ * The string for the version number
+ */
+ public Version(String version) {
+ versionString = version;
+
+ String[] nums = version.split("[.]");
+ intVersion = new int[nums.length];
+ for (int i = 0; i < nums.length; ++i) {
+ try {
+ intVersion[i] = Integer.parseInt(nums[i]);
+ } catch (NumberFormatException e) {
+ intVersion[i] = 0;
+ }
+ }
+ }
+
+ /**
+ * Get the integer array version of the version number. Good for comparison.
+ *
+ * @return The integer array for the version as parsed in constructor
+ */
+ public int[] getIntVersion() {
+ return intVersion;
+ }
+
+ /**
+ * Returns the passed in version string.
+ *
+ * @return The string version as passed in to the constructor
+ */
+ public String toString() {
+ return versionString;
+ }
+
+ /**
+ * Comparing function for the comparable interface. Returns -1 if the other
+ * version number is greater. If the version numbers differ in the number of
+ * integers, zeros will be added to the number that is shorter.
+ *
+ * @param otherVersion
+ * The version object we are comparing against.
+ * @return -1 if otherVersion is bigger, 1 if this is bigger, and 0 if equal
+ */
+ @Override
+ public int compareTo(Version otherVersion) {
+ int[] otherIntVersion = otherVersion.getIntVersion();
+
+ // set the limit for the loop as bigger length
+ int limit = intVersion.length;
+ if (otherIntVersion.length > intVersion.length)
+ limit = otherIntVersion.length;
+
+ int me = 0;
+ int you = 0;
+ for (int i = 0; i < limit; ++i) {
+ // me looking at you looking at me
+ me = you = me = 0;
+
+ // me still have ints
+ if (intVersion.length > i)
+ me = intVersion[i];
+ // you still have ints
+ if (otherIntVersion.length > i)
+ you = otherIntVersion[i];
+
+ // first difference in number tells us the greater one
+ if (me > you)
+ return 1;
+ else if (me < you)
+ return -1;
+ }
+
+ return 0;
+ }
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Feature.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Feature.java
new file mode 100644
index 0000000000..cc1aada7e6
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Feature.java
@@ -0,0 +1,95 @@
+package com.raytheon.uf.featureexplorer.jaxb;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class Feature {
+
+ @XmlAttribute
+ private String id;
+
+ @XmlAttribute
+ private String label;
+
+ @XmlAttribute
+ private String version;
+
+ @XmlAttribute(name = "provider-name")
+ private String providerName;
+
+ @XmlElements( { @XmlElement(name = "includes", type = Includes.class) })
+ private List includes;
+
+ @XmlElements( { @XmlElement(name = "plugin", type = Plugin.class) })
+ private List plugins;
+
+ public Feature() {
+ this.includes = new ArrayList();
+ this.plugins = new ArrayList();
+ }
+
+ public Feature(String anId, String aLabel, String aVersion,
+ String aProviderName, List includesList,
+ List pluginsList) {
+ this.includes = new ArrayList(includesList);
+ this.plugins = new ArrayList(pluginsList);
+ this.id = anId;
+ this.label = aLabel;
+ this.version = aVersion;
+ this.providerName = aProviderName;
+ }
+
+ public List getIncludes() {
+ return this.includes;
+ }
+
+ public List getPlugins() {
+ return this.plugins;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getLabel() {
+ return this.label;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public String getProviderName() {
+ return this.providerName;
+ }
+
+ public void setId(String anId) {
+ this.id = anId;
+ }
+
+ public void setLabel(String aLabel) {
+ this.label = aLabel;
+ }
+
+ public void setVersion(String aVersion) {
+ this.version = aVersion;
+ }
+
+ public void setProviderName(String aProviderName) {
+ this.providerName = aProviderName;
+ }
+
+ public void setPlugins(List pluginsList) {
+ this.plugins = new ArrayList(pluginsList);
+ }
+
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Includes.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Includes.java
new file mode 100644
index 0000000000..4b457437bb
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Includes.java
@@ -0,0 +1,51 @@
+package com.raytheon.uf.featureexplorer.jaxb;
+
+import javax.xml.bind.annotation.XmlAccessOrder;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+
+@XmlAccessorType(XmlAccessType.NONE)
+@XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
+public class Includes {
+
+ @XmlAttribute
+ private String id;
+
+ @XmlAttribute
+ private String version;
+
+ @XmlAttribute
+ private boolean optional = false;
+
+ public Includes() {
+
+ }
+
+ public Includes(String anId, String aVersion, boolean isOptional) {
+ this.id = anId;
+ this.version = aVersion;
+ this.optional = isOptional;
+ }
+
+ public void setId(String anId) {
+ this.id = anId;
+ }
+
+ public void setVersion(String aVersion) {
+ this.version = aVersion;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public boolean getOptional() {
+ return this.optional;
+ }
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Plugin.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Plugin.java
new file mode 100644
index 0000000000..4ed570a749
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/jaxb/Plugin.java
@@ -0,0 +1,79 @@
+package com.raytheon.uf.featureexplorer.jaxb;
+
+import javax.xml.bind.annotation.XmlAccessOrder;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+
+@XmlAccessorType(XmlAccessType.NONE)
+@XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
+public class Plugin {
+
+ @XmlAttribute
+ private String id;
+
+ @XmlAttribute(name = "download-size")
+ private double downloadSize;
+
+ @XmlAttribute
+ private String version;
+
+ @XmlAttribute(name = "install-size")
+ private double installSize;
+
+ @XmlAttribute
+ private boolean unpack;
+
+ public Plugin() {
+ }
+
+ public Plugin(String anId, double aDownloadSize, double anInstallSize,
+ String aVersion, boolean doUnpack) {
+ this.id = anId;
+ this.downloadSize = aDownloadSize;
+ this.version = aVersion;
+ this.installSize = anInstallSize;
+ this.unpack = doUnpack;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public double getDownloadSize() {
+ return this.downloadSize;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public double getInstallSize() {
+ return this.installSize;
+ }
+
+ public boolean getUnpack() {
+ return this.unpack;
+ }
+
+ public void setId(String anId) {
+ this.id = anId;
+ }
+
+ public void setDownloadSize(double aDownloadSize) {
+ this.downloadSize = aDownloadSize;
+ }
+
+ public void setVersion(String aVersion) {
+ this.version = aVersion;
+ }
+
+ public void setInstallSize(double anInstallSize) {
+ this.installSize = anInstallSize;
+ }
+
+ public void setUnpack(boolean doUnpack) {
+ this.unpack = doUnpack;
+ }
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/search/IFeatureSearch.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/search/IFeatureSearch.java
new file mode 100644
index 0000000000..7399fa6db3
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/search/IFeatureSearch.java
@@ -0,0 +1,35 @@
+package com.raytheon.uf.featureexplorer.search;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * This interface is used for creating objects that are capable of searching for
+ * a feature.
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 3, 2008 dglazesk Initial creation
+ * Oct 7, 2008 SP#15 bclement changed return value to List
+ *
+ *
+ * @author dglazesk
+ * @version 1.0
+ */
+public interface IFeatureSearch {
+ /**
+ * This will search for a feature that has anId as its ID and has a version
+ * greater than or equal to aVersion. The final list will be ordered by
+ * version number with the highest version number at element 0.
+ *
+ * @param anId
+ * ID of the feature being searched for
+ * @param aVersion
+ * Version minimum for the feature
+ * @return List of file objects for the feature.xml that match the search
+ * criteria
+ */
+ public List findFeature(String anId, String aVersion);
+}
diff --git a/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/search/IPluginSearch.java b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/search/IPluginSearch.java
new file mode 100644
index 0000000000..84a158f232
--- /dev/null
+++ b/javaUtilities/com.raytheon.uf.featureexplorer/src/com/raytheon/uf/featureexplorer/search/IPluginSearch.java
@@ -0,0 +1,35 @@
+package com.raytheon.uf.featureexplorer.search;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * This interface is used for creating objects that are capable of searching for
+ * a plugins.
+ *
+ *
+ * SOFTWARE HISTORY
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 3, 2008 dglazesk Initial creation
+ * Oct 7, 2008 SP#15 bclement changed return value to List
+ *
+ *
+ * @author dglazesk
+ * @version 1.0
+ */
+public interface IPluginSearch {
+ /**
+ * This will search for a plugin with anId as its ID and has a version
+ * number greater than or equal to aVersion. The final list will be ordered
+ * by version number with the highest version at element 0.
+ *
+ * @param anId
+ * The ID of the plugin being searched for
+ * @param aVersion
+ * The minimum version of the plugin
+ * @return List of file locations for the plugins matching the search
+ * criteria
+ */
+ public List findPlugin(String anId, String aVersion);
+}
diff --git a/ncep/com.raytheon.uf.edex.ncep.feature/feature.xml b/ncep/com.raytheon.uf.edex.ncep.feature/feature.xml
index d2d1940b9a..ba17c96f33 100644
--- a/ncep/com.raytheon.uf.edex.ncep.feature/feature.xml
+++ b/ncep/com.raytheon.uf.edex.ncep.feature/feature.xml
@@ -24,14 +24,6 @@
version="0.0.0"
unpack="false"/>
-
-
-