DBVIEW
src/org/dbview/addons/AddOnCatalog.java
00001 /*
00002         DbView - Graph Visualization
00003     Copyright (C) 2012  Denis BEURIVE
00004 
00005     This program is free software: you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation, either version 3 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 */
00018 
00019 /**
00020  * @author Denis Beurive
00021  */
00022 
00023 package org.dbview.addons;
00024 
00025 import java.util.ArrayList;
00026 import java.util.Collections;
00027 import java.util.Enumeration;
00028 import java.util.Hashtable;
00029 import java.util.Iterator;
00030 import java.util.regex.Matcher;
00031 import java.util.regex.Pattern;
00032 import org.dbview.conf.Conf;
00033 import org.dbview.utils.JavaVm;
00034 
00035 /**
00036  * @class AddOnCatalog
00037  * The class is a base class for all add-on catalogues.
00038  * <ul>
00039  *    <li>An add-on is represented by a package.</li>
00040  *    <li>An add-on exports adaptors that are interfaces to the add-on's environment.</li>
00041  *    <li>An add-on's adaptors are located in the add-on's package.</li>
00042  *    <li>Add-ons are regrouped into JAR files.</li>
00043  *    <li>A catalogue lists all add-ons located in a given JAR file.</li>
00044  * </ul>
00045  * 
00046  * @author Denis Beurive
00047  */
00048 public class AddOnCatalog
00049 {
00050     /**
00051      * This hash table lists all add-ons declared in the given resource. Each
00052      * add-on is associated to its adaptors.
00053      * <ul>
00054      *    <li>Key: Name of the add-on.</li>
00055      *    <li>Value: See class AddDon. It defines the following data:
00056      *        <ul>
00057      *            <li>The name of the package in which the add-on is declared.</li>
00058      *            <li>The list of adaptors declared for the add-on.</li>
00059      *        </ul>
00060      *    </li>
00061      * </ul>
00062      */
00063     private Hashtable<String, AddOn> __addons = null;
00064 
00065     /**
00066      * This array contains the expected list of adaptors' names.
00067      * This array is used to check that all add-ons present all the mandatory adaptors.
00068      */
00069     private ArrayList<String> __adaptors_list = null;
00070 
00071     /**
00072      * Create a catalogue of add-ons.
00073      *
00074      * @param in_resource_name
00075      *            Name of the resource that contains the add-ons (this is a JAR).
00076      * @param in_adaptors_list
00077      *            List of adaptors that must be part of the add-on's package.
00078      * @param in_pattern
00079      *            Regular expression that is used to recognize an add-on.
00080      * @throws Exception
00081      */
00082     public AddOnCatalog(String in_resource_name,
00083             ArrayList<String> in_adaptors_list,
00084             Pattern in_pattern) throws Exception
00085     {
00086         this.__addons        = new Hashtable<String, AddOn>();
00087         this.__adaptors_list = in_adaptors_list;
00088 
00089         // Load the JAR file that contains all adapters.
00090         ArrayList<String> jar_content = JavaVm.listJar(Conf.get(in_resource_name));
00091 
00092         // Locate targets.
00093         // System.out.println(in_resource_name + " > " + jar_content.size());
00094         Iterator<String> i = jar_content.iterator();
00095         while (i.hasNext())
00096         {
00097             String entry = i.next().replaceAll("/|\\\\", ".");
00098 
00099             Matcher m_adaptor = in_pattern.matcher(entry);
00100 
00101             // Did we find an adaptor?
00102             if (m_adaptor.matches())
00103             {
00104                 AddOn target = null;
00105                 String target_package = m_adaptor.group(1);
00106                 String target_name = m_adaptor.group(2);
00107                 String target_adaptor = m_adaptor.group(3);
00108 
00109                 if (! this.__adaptors_list.contains(target_adaptor)) { continue; }
00110 
00111                 if (!this.__addons.containsKey(target_name))
00112                 {
00113                     target = new AddOn();
00114                     target.package_name = target_package;
00115                     this.__addons.put(target_name, target);
00116                 }
00117                 else
00118                 {
00119                     target = this.__addons.get(target_name);
00120                 }
00121 
00122                 target.adaptors_list.add(target_adaptor);
00123             }
00124 
00125             // The entry is not related to targets... skip it.
00126         }
00127 
00128         // Check that the target has all the mandatory adaptors.
00129         Enumeration<String> targets = this.__addons.keys();
00130         while (targets.hasMoreElements())
00131         {
00132             String target_name = targets.nextElement();
00133             if (!this.__isOk())
00134             {
00135                 throw new Exception("Unexpected error. The add-on \"" + target_name + "\" is missing an adaptor! This error should not happen unless you modify the software.");
00136             }
00137         }
00138 
00139         // At this point, the catalog is built.
00140     }
00141 
00142     /**
00143      * The method produces a string that represents the catalogue.
00144      * @note This method is used to debug the application.
00145      * @return The method returns a string that represents the catalogue.
00146      */
00147     @Override
00148     public String toString()
00149     {
00150         try
00151         {
00152             String r = "";
00153             Enumeration<String> targets = this.getTargets();
00154 
00155             while (targets.hasMoreElements())
00156             {
00157                 String target_name = targets.nextElement();
00158                 AddOn target = this.__addons.get(target_name);
00159                 String package_name = target.package_name;
00160 
00161                 r = r.concat("[Target] " + target_name + System.getProperty("line.separator"));
00162                 r = r.concat("\tPackage: " + package_name + System.getProperty("line.separator"));
00163 
00164                 for (int i = 0; i < target.adaptors_list.size(); i++)
00165                 {
00166                     String adaptator_name = target.adaptors_list.get(i);
00167                     r = r.concat("\t         - " + adaptator_name + System.getProperty("line.separator"));
00168                 }
00169             }
00170 
00171             return r;
00172         }
00173         catch (Exception e)
00174         {
00175             return "Could not generate textual representation of the catalogue: " + e.getMessage();
00176         }
00177     }
00178 
00179     /**
00180      * This method returns the list of all add-ons found in the catalog.
00181      * @return This method returns the list of all add-ons found in the catalog.
00182      * @throws Exception
00183      */
00184     public Enumeration<String> getTargets() throws Exception
00185     {
00186         Enumeration<String> e = this.__addons.keys();
00187         ArrayList<String> r   = new ArrayList<String>();
00188 
00189         while (e.hasMoreElements()) { r.add(e.nextElement()); }
00190         return Collections.enumeration(r);
00191     }
00192 
00193     /**
00194      * Check the list of detected adaptors, for all add-ons in the catalogue.
00195      * @return If the list of detected adaptors is valid, then the function
00196      *         returns the value TRUE. Otherwise the function returns the value
00197      *         fALSE.
00198      */
00199     private Boolean __isOk()
00200     {
00201         for (String adaptor : this.__adaptors_list)
00202         {
00203             if (-1 == this.__adaptors_list.indexOf(adaptor))
00204             {
00205                 return Boolean.FALSE;
00206             }
00207         }
00208         return Boolean.TRUE;
00209     }
00210 
00211     /**
00212      * This method returns the name of the package that contains adaptors'
00213      * implementations for a given add-on (that is: the add-on's package).
00214      *
00215      * @param in_add_on_name
00216      *            Name of the add-on.
00217      * @return This method returns the name of the package that contains
00218      *         adaptors' implementations for the given add-on.
00219      * @throws Exception
00220      */
00221     public String getPackage(String in_add_on_name) throws AddOnCatalogException
00222     {
00223         if (!this.__addons.containsKey(in_add_on_name))
00224         {
00225             throw new AddOnCatalogException("You try to get the name of a package (out of the catalog) for an add-on (\"" + in_add_on_name + "\") that does not exist!");
00226         }
00227         return this.__addons.get(in_add_on_name).package_name;
00228     }
00229 
00230     /**
00231      * This method returns an instance of a given adaptor's, for a given add-on.
00232      *
00233      * @param in_target_name
00234      *            Name of the add-on to which the adaptor belongs.
00235      *            Please note that this name may contain dashes.
00236      *            This string will be converted into "Lower Camel Case".
00237      * @param in_adaptor_name
00238      *            Name of the adaptor to create.
00239      * @return The method returns a new instance of the required adaptor.
00240      * @throws AddOnCatalogException
00241      */
00242     public Object getAdaptor(String in_target_name,
00243                              String in_adaptor_name) throws AddOnCatalogException
00244     {
00245         // Convert into Lower Camel Case ("a-bc-de" -> "aBcDe").
00246         String real_name = AddOnCatalog.fromCli(in_target_name);
00247 
00248         AddOn target = this.__addons.get(real_name);
00249         if (null == target)
00250         {
00251             throw new AddOnCatalogException("The requested add-on (\"" + in_target_name + "\") does not exist.");
00252         }
00253         String package_name = target.package_name;
00254         String class_name = package_name + "." + in_adaptor_name;
00255 
00256         try
00257         {
00258             // System.out.println("Name = " + class_name);
00259             return Class.forName(class_name).newInstance();
00260         }
00261         catch (ExceptionInInitializerError e)
00262         {
00263             throw new AddOnCatalogException("Unexpected error. This error should not happen, unless you modify the software (ExceptionInInitializerError). " + e.getClass().getName() + ": " + e.getMessage());
00264         }
00265         catch (ClassNotFoundException e)
00266         {
00267             throw new AddOnCatalogException("Unexpected error. This error should not happen, unless you modify the software (ClassNotFoundException). " + e.getClass().getName() + ": " + e.getMessage());
00268         }
00269         catch (LinkageError e)
00270         {
00271             throw new AddOnCatalogException("Unexpected error. This error should not happen, unless you modify the software (LinkageError). " + e.getClass().getName() + ": " + e.getMessage());
00272         }
00273         catch (IllegalAccessException e)
00274         {
00275             throw new AddOnCatalogException("Unexpected error. This error should not happen, unless you modify the software (IllegalAccessException). " + e.getClass().getName() + ": " + e.getMessage());
00276         }
00277         catch (InstantiationException e)
00278         {
00279             throw new AddOnCatalogException("Unexpected error. This error should not happen, unless you modify the software (InstantiationException). " + e.getClass().getName() + ": " + e.getMessage());
00280         }
00281         catch (SecurityException e)
00282         {
00283             throw new AddOnCatalogException("Unexpected error. This error should not happen, unless you modify the software (SecurityException). " + e.getClass().getName() + ": " + e.getMessage());
00284         }
00285     }
00286 
00287     /**
00288      * This method converts an add-on name, given from the command line interface, into its internal representation.
00289      * @param in_cli_name Name of the add-on, as given by the command line.
00290      * @return The method returns the internal name of the add-on.
00291      */
00292 
00293     public static String fromCli(String in_cli_name)
00294     {
00295         return org.dbview.utils.Strings.dashToLowerCamelCase(in_cli_name);
00296     }
00297 
00298     /**
00299      * This method converts an add-on name into its CLI name.
00300      * @param in_real_name "Internal" name of the add-on.
00301      * @return The method returns the CLI name of the add-on.
00302      */
00303     public static String toCli(String in_real_name)
00304     {
00305         return org.dbview.utils.Strings.lowerCamelCaseToDash(in_real_name);
00306     }
00307 }