001/**
002 *  '$RCSfile$'
003 *  '$Author: crawl $'
004 *  '$Date: 2012-11-26 22:21:34 +0000 (Mon, 26 Nov 2012) $'
005 *  '$Revision: 31119 $'
006 *
007 *  For Details:
008 *  http://www.kepler-project.org
009 *
010 *  Copyright (c) 2010 The Regents of the
011 *  University of California. All rights reserved. Permission is hereby granted,
012 *  without written agreement and without license or royalty fees, to use, copy,
013 *  modify, and distribute this software and its documentation for any purpose,
014 *  provided that the above copyright notice and the following two paragraphs
015 *  appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF
016 *  CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
017 *  OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
018 *  DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
019 *  POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY
020 *  DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
022 *  SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
023 *  CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
024 *  ENHANCEMENTS, OR MODIFICATIONS.
025 */
026package org.kepler.kar;
027
028import java.util.LinkedHashMap;
029import java.util.List;
030import java.util.StringTokenizer;
031import java.util.Vector;
032
033import org.kepler.build.modules.Module;
034import org.kepler.build.modules.ModuleTree;
035import org.kepler.build.modules.OSRegistryTxt;
036import org.kepler.build.util.Version;
037import org.kepler.configuration.ConfigurationManager;
038import org.kepler.configuration.ConfigurationManagerException;
039import org.kepler.configuration.ConfigurationProperty;
040import org.kepler.configuration.NamespaceException;
041
042
043/**
044 * @author Aaron Schultz 
045 */
046public class ModuleDependencyUtil {
047
048        /**
049         * Return a Vector<String> that contains the module dependencies found in
050         * the given formatted String list (of the type created by
051         * generateDependencyString(Vector<String>). This method can be reversed
052         * using generateDependencyString(Vector<String>).
053         * 
054         * @param depStr
055         * @return
056         */
057        public static Vector<String> parseDependencyString(String depStr) {
058                Vector<String> dependencies = new Vector<String>();
059                if (depStr == null || depStr.trim().equals("")) {
060                        return dependencies;
061                }
062                StringTokenizer st = new StringTokenizer(depStr, KARFile.DEP_SEPARATOR);
063                while (st.hasMoreTokens()) {
064                        String dep = st.nextToken();
065                        if (!dependencies.contains(dep)) {
066                                dependencies.add(dep);
067                        }
068                }
069                return dependencies;
070        }
071
072        /**
073         * Return a delimited string containing the given list of module
074         * dependencies. This method can be reversed using
075         * parseDependencyString(String).
076         * 
077         * @param dependencies
078         * @return String formatted list of Module dependencies.
079         */
080        public static String generateDependencyString(Vector<String> dependencies) {
081                String dependencyStr = "";
082                for (String dep : dependencies) {
083                        dependencyStr += dep + KARFile.DEP_SEPARATOR;
084                }
085                // remove end separator
086                if (dependencyStr.endsWith(KARFile.DEP_SEPARATOR)) {
087                        dependencyStr = dependencyStr.substring(0, dependencyStr.length()
088                                        - KARFile.DEP_SEPARATOR.length());
089                }
090                return dependencyStr;
091        }
092
093        /**
094         * @return currently active modules list as 
095         * delimited string
096         */
097        public static String buildModuleDependenciesString() {
098                String dependenciesStr = "";
099                Vector<String> dependencies = new Vector<String>();
100                
101                ModuleTree moduleTree = ModuleTree.instance();
102                for( Module m : moduleTree ){
103                        dependencies.add(m.toString());
104                }
105                for (String dependency : dependencies) {
106                        dependenciesStr += dependency + KARFile.DEP_SEPARATOR;
107                }
108                if (dependenciesStr.endsWith(KARFile.DEP_SEPARATOR)) {
109                        // remove the last semicolon
110                        dependenciesStr = dependenciesStr.substring(0, dependenciesStr
111                                        .length()
112                                        - KARFile.DEP_SEPARATOR.length());
113                }
114                return dependenciesStr;
115        }
116        
117        /**
118         * 
119         * @param moduleDependencies
120         * @return
121         */
122        public static boolean checkIfModuleDependenciesSatisfied(Vector<String> moduleDependencies) {
123                
124                //first fetch KAR compliance level
125                ConfigurationManager cman = ConfigurationManager.getInstance();
126                ConfigurationProperty coreProperty = cman.getProperty(KARFile.KARFILE_CONFIG_PROP_MODULE);
127                ConfigurationProperty KARComplianceProp = coreProperty.getProperty(KARFile.KAR_COMPLIANCE_PROPERTY_NAME);
128                // user has a core configuration.xml from before KARComplianceProp existed, need to add it
129                // if KARPreferencesTab.initializeTab hasn't already done so.
130                if (KARComplianceProp == null){
131                        KARComplianceProp = new ConfigurationProperty(KARFile.KARFILE_CONFIG_PROP_MODULE, KARFile.KAR_COMPLIANCE_PROPERTY_NAME, KARFile.KAR_COMPLIANCE_DEFAULT);
132                        try {
133                                coreProperty.addProperty(KARComplianceProp);
134                        } catch (NamespaceException e) {
135                                // TODO Auto-generated catch block
136                                e.printStackTrace();
137                        } catch (ConfigurationManagerException e) {
138                                // TODO Auto-generated catch block
139                                e.printStackTrace();
140                        }
141                }
142                String KARCompliance = KARComplianceProp.getValue();
143                
144                
145                int moduleDepsSize = moduleDependencies.size();
146                int currentModuleListSize = ModuleTree.instance().getModuleList().size();
147        
148                if (moduleDepsSize == 0){
149                        return true;
150                }
151                
152                //if strict, and different mod dep lengths, we can just return false
153                if (KARCompliance.equals(KARFile.KAR_COMPLIANCE_STRICT) && 
154                                moduleDepsSize != currentModuleListSize){
155                        return false;
156                }
157
158                
159                boolean allDependenciesSatisfied = true;
160                for (String dep : moduleDependencies) {
161                        // NOTE after r26418 ignoring dependencies that are incompatible
162                        // with OS. See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5232
163                        // for suggestions for a better solution.
164            if( !OSRegistryTxt.isCompatibleWithCurrentOS(Module.make(dep)) ){
165                continue;
166            }
167                        boolean depIsInModuleConfiguration = false;
168                        for (Module m : ModuleTree.instance()) {
169                                
170                                //TODO augment if additional compliance levels added
171                                if (KARCompliance.equals(KARFile.KAR_COMPLIANCE_STRICT)){
172                                        if (m.toString().trim().equals(dep.trim())) {
173                                                //System.out.println("KARFile areAllModuleDependenciesSatisfied() KARCompliance is:"+KARCompliance +
174                                                //              " "+m.toString().trim() + " equals:"+dep.trim());
175                                                depIsInModuleConfiguration = true;
176                                                break;
177                                        }
178                                }
179                                else if (KARCompliance.equals(KARFile.KAR_COMPLIANCE_RELAXED)){
180                                        
181                                        // fix for http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5419.
182                                        // deal with special situation introduced in kepler 2.2: ptolemy-kepler-* > ptolemy-*
183                                        // e.g. we should consider ptolemy-kepler-2.2 newer than ptolemy-8.0. Except if user 
184                                        // is running from trunk, in which case module is called ptolemy, and we assume it's most recent
185                                        if (m.getStemName().trim().contains(Module.PTOLEMY) && Version.stem(dep).contains(Module.PTOLEMY)) {
186                                        
187                                                // this section is for letting trunk users slide, and for letting old unversioned
188                                                // kars be opened.
189                                                try{
190                                                        Version moduleVersion = Version.fromVersionString(m.getName());
191                                                        Version depVersion = Version.fromVersionString(dep);
192                                                        if (!moduleVersion.hasMajor() || !moduleVersion.hasMinor() || !moduleVersion.hasMicro()
193                                                                        || !depVersion.hasMajor() || !depVersion.hasMinor() || !depVersion.hasMicro()){
194                                                                //module or dep might not have version info, just be ok with this
195                                                                //System.out.println("ModuleDependencyUtil.checkIfModuleDependenciesSatisfied KARCompliance is:"+KARCompliance +
196                                                                //              " "+m.getName() +" or dep:"+ dep + " does not specify proper version info, will just let this slide - ok.");
197                                                                depIsInModuleConfiguration = true;
198                                                                break;
199                                                        }
200                                                }
201                                                catch(IllegalArgumentException e){
202                                                        //module or dep might not have version info, just be ok with this
203                                                        //System.out.println("caught exception. ModuleDependencyUtil.checkIfModuleDependenciesSatisfied KARCompliance is:"+KARCompliance +
204                                                        //              " "+m.getName() +" or dep:"+ dep + " does not specify proper version info, will just let this slide - ok.");
205                                                        depIsInModuleConfiguration = true;
206                                                        break;
207                                                }
208                                                
209                                                // this section is for official release users
210                                                if (m.getStemName().trim().contains(Module.PTOLEMY_KEPLER) 
211                                                                && !Version.stem(dep).contains(Module.PTOLEMY_KEPLER)){
212                                                        //System.out.println("ModuleDependencyUtil.checkIfModuleDependenciesSatisfied KARCompliance is:"+KARCompliance +
213                                                        //              " "+m.getName() + " >= " + dep + " - ok.");
214                                                        depIsInModuleConfiguration = true;
215                                                        break;
216                                                }
217                                                else if (!m.getStemName().trim().contains(Module.PTOLEMY_KEPLER) 
218                                                                && Version.stem(dep).contains(Module.PTOLEMY_KEPLER)){
219                                                        //System.out.println("ModuleDependencyUtil.checkIfModuleDependenciesSatisfied KARCompliance is:"+KARCompliance +
220                                                        //              " "+m.getName() + " < "+ dep + " so THIS DEP NOT SATISFIED! - fail.");
221                                                        depIsInModuleConfiguration = false;
222                                                        break;
223                                                }
224                                        }
225                                        
226                                        
227                                        if (m.getStemName().trim().equals(Version.stem(dep))) {
228                                                try{
229                                                        Version moduleVersion = Version.fromVersionString(m.getName());
230                                                        Version depVersion = Version.fromVersionString(dep);
231                                                        if (moduleVersion.hasMajor() && moduleVersion.hasMinor() && moduleVersion.hasMicro()
232                                                                        && depVersion.hasMajor() && depVersion.hasMinor() && depVersion.hasMicro()){
233                                                                
234                                                                if (moduleVersion.getMajor() > depVersion.getMajor() ||
235                                                                (moduleVersion.getMajor() == depVersion.getMajor() 
236                                                                                && moduleVersion.getMinor() > depVersion.getMinor()) ||
237                                                                (moduleVersion.getMajor() == depVersion.getMajor() 
238                                                                                && moduleVersion.getMinor() == depVersion.getMinor() 
239                                                                                && moduleVersion.getMicro() > depVersion.getMicro()) ||
240                                                                (moduleVersion.getMajor() == depVersion.getMajor() 
241                                                                                && moduleVersion.getMinor() == depVersion.getMinor() 
242                                                                                && moduleVersion.getMicro() == depVersion.getMicro())){
243                                                                
244                                                                        //System.out.println("KARFile areAllModuleDependenciesSatisfied() KARCompliance is:"+KARCompliance +
245                                                                        //              " "+m.getName() + " >= " + dep + " - ok.");
246                                                                        depIsInModuleConfiguration = true;
247                                                                        break;
248                                                                }
249                                                                else{
250                                                                        //System.out.println("ModuleDependencyUtil.checkIfModuleDependenciesSatisfied KARCompliance is:"+KARCompliance +
251                                                                        //              " "+m.getName() + " < "+ dep + " so THIS DEP NOT SATISFIED! - fail.");
252                                                                        depIsInModuleConfiguration = false;
253                                                                        break;
254                                                                }
255                                                        }
256                                                        else{
257                                                                //module or dep might not have version info, just be ok with this
258                                                                //System.out.println("KARFile areAllModuleDependenciesSatisfied() KARCompliance is:"+KARCompliance +
259                                                                //              " "+m.getName() +" or dep:"+ dep + " does not specificy proper version info, will just let this slide - ok.");
260                                                                depIsInModuleConfiguration = true;
261                                                                break;
262                                                        }
263                                                }
264                                                catch(IllegalArgumentException e){
265                                                        //module or dep might not have version info, just be ok with this
266                                                        //System.out.println("caught exception. KARFile areAllModuleDependenciesSatisfied() KARCompliance is:"+KARCompliance +
267                                                        //              " "+m.getName() +" or dep:"+ dep + " does not specificy proper version info, will just let this slide - ok.");
268                                                        depIsInModuleConfiguration = true;
269                                                        break;
270                                                }
271                                        }
272                                }
273                        }
274                        if (!depIsInModuleConfiguration) {
275                                // Specified module dependency was not found in the
276                                // current set of modules
277                                //System.out.println("KARFile module dependency not found:" + dep);
278                                allDependenciesSatisfied = false;
279                        }
280                }
281                
282                //System.out.println("ModuleDependencyUtil.checkIfModuleDependenciesSatisfied. returning allDependenciesSatisfied:"+allDependenciesSatisfied);
283                return allDependenciesSatisfied;
284        }
285
286        /**
287         * Check if versions of dependencies are all complete, defined as having specified a Major,
288         * Minor, and Micro number.
289         * @param dependencies
290         * @return
291         */
292        public static boolean isDependencyVersioningInfoComplete(List<String> dependencies) {
293                
294                for (String dependency : dependencies) {                        
295                        Version v = null;
296                        try{
297                                v = Version.fromVersionString(dependency);
298                                if (!v.hasMajor() || !v.hasMinor() || !v.hasMicro()){
299                                        //System.out.println("ModuleDependencyUtil isDependencyVersioningInfoComplete dependency:"
300                                        //              +dependency+ " missing full version info. Returning false");
301                                        return false;
302                                }
303                        }
304                        catch(IllegalArgumentException iae){
305                                //System.out.println("ModuleDependencyUtil isDependencyVersioningInfoComplete dependency:"
306                                //              +dependency+ " missing full version info. Returning false");
307                                return false;
308                        }
309                }
310                
311                //System.out.println("ModuleDependencyUtil.isDependencyVersioningInfoComplete Returning true");
312                return true;
313        }
314        
315        /**
316         * Check dependencies against currently running modules to see if they're satisfied.
317         * Return a LinkedHashMap of any unsatisfied dependencies and their versions
318         * @param dependencies
319         * @return
320         */
321        public static LinkedHashMap<String, Version> getUnsatisfiedDependencies(List<String> dependencies){
322                
323                LinkedHashMap<String, Version> unsatisfiedDependencies = new LinkedHashMap<String, Version>();
324                ModuleTree moduleTree = ModuleTree.instance();
325        
326                for (String dependency : dependencies) {        
327                        Version v = null;
328                        try{
329                                v = Version.fromVersionString(dependency);
330                        }
331                        catch(IllegalArgumentException iae){
332                        }
333                        
334                        if (!moduleTree.contains(dependency)) { 
335                                //System.out.println("ModuleDependencyUtil getUnsatisfiedDependencies adding unsat dependency:"+dependency);
336                                unsatisfiedDependencies.put(dependency, v);
337                        }
338                        else{
339                                //System.out.println("ModuleDependencyUtil getUnsatisfiedDependencies moduleTree.contains("+dependency+") no need to add this as unsat dep");
340                        }
341                }
342                
343                return unsatisfiedDependencies;
344        }
345        
346}