001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2014-08-15 20:47:29 +0000 (Fri, 15 Aug 2014) $' 
007 * '$Revision: 32892 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.configuration;
031
032import java.util.ArrayList;
033import java.util.Iterator;
034import java.util.List;
035import java.util.StringTokenizer;
036import java.util.Vector;
037
038import org.kepler.build.modules.Module;
039
040/**
041 * Class that represents a configuration property within kepler
042 */
043public class ConfigurationProperty
044{
045  private Module module;
046  private String name;
047  private ConfigurationNamespace namespace;
048  private String value;
049  private List<ConfigurationProperty> propertiesList;
050  private ConfigurationProperty parent;
051  private boolean dirty;
052  private boolean mutable;
053  private Module originModule;
054  public static final ConfigurationNamespace namespaceDefault = new ConfigurationNamespace("configuration");
055  
056  /**
057   * constructor to build a configuration property for a module with a name and
058   * a list of properties to load
059   */
060  public ConfigurationProperty(Module module, String name, List<ConfigurationProperty> propertiesList)
061  {
062    this(module, name, namespaceDefault, propertiesList);
063  }
064  
065  /**
066   * constructor to build a configuration property for a module with a name, 
067   * namespace and a list of properties to nest within it
068   */
069  public ConfigurationProperty(Module module, String name, ConfigurationNamespace namespace, 
070                  List<ConfigurationProperty> propertiesList)
071  {
072          this(module, name, namespace, propertiesList, true);
073  }
074
075/**
076   * constructor to build a configuration property for a module with a name, 
077   * namespace and a list of properties to nest within it
078   */
079  public ConfigurationProperty(Module module, String name, ConfigurationNamespace namespace, 
080                  List<ConfigurationProperty> propertiesList, boolean notifyListeners)
081  {
082    init(module, name, namespace, null);
083    this.propertiesList = propertiesList;
084    Iterator<ConfigurationProperty>itr = propertiesList.iterator();
085    while (itr.hasNext())
086    {
087        ConfigurationProperty cp = itr.next();
088        cp.setParent(this);
089        cp.setNamespace(notifyListeners, this.getNamespace());
090        cp.setOriginModule(module);
091    }
092  }
093  
094  /**
095   * constructor to build a configuration property for a module with a name
096   * and a single value
097   */
098  public ConfigurationProperty(Module module, String name, String value)
099  {
100    this(module, name, namespaceDefault, value); 
101  }
102  
103  /**
104   * constructor to build a configuration property for a module with a name
105   * and a single value
106   */
107  public ConfigurationProperty(Module module, String name, ConfigurationNamespace namespace, String value)
108  {
109    init(module, name, namespace, value); 
110  }
111  
112  /**
113   * constructor to build a configuration property for a module with a name
114   * but with no value or nested property list.
115   */
116  public ConfigurationProperty(Module module, String name)
117  {
118    this(module, name, namespaceDefault, (String)null);
119  }
120  
121  /**
122   * create a configuration property with one nested property
123   */
124  public ConfigurationProperty(Module module, String name, ConfigurationProperty property)
125    throws NamespaceException, ConfigurationManagerException
126  {
127    this(module, name);
128    addProperty(property);
129  }
130  
131  /**
132   * add a nested property to this configuration property
133   */
134  public void addProperty(ConfigurationProperty property)
135    throws NamespaceException, ConfigurationManagerException
136  {
137    addProperty(property, false, true, true);
138  }
139  
140  /**
141   * add a nested property to this configuration property
142   */
143  public void addProperty(ConfigurationProperty property, boolean notifyListeners)
144    throws NamespaceException, ConfigurationManagerException
145  {
146    addProperty(property, false, notifyListeners, true);
147  }
148  
149  /**
150   * add a nested property to this configuration property.  if ignoreMutable
151   * is false, don't throw an exception if a property is mutable.  this method
152   * is used by configuration readers to build the configuration and should
153   * not be used in normal api calls.
154   */
155  protected void addProperty(ConfigurationProperty property, boolean ignoreMutable,
156                  boolean notifyListeners, boolean setDirty) 
157    throws NamespaceException, ConfigurationManagerException
158  {
159    if(!ignoreMutable && !mutable)
160    { //check to see if the property is not mutable.  if it is not, throw
161      //an exception
162      throw new ConfigurationManagerException("The property " + getName() + 
163        " is mutable and cannot be changed at runtime.");
164    }
165    
166    checkNamespace(this, property);  
167    propertiesList.add(property);
168    property.setParent(this);
169    if(setDirty) {
170        setDirty(true);
171    }
172    if (notifyListeners) {
173        notifyListeners(this);
174    }
175  }
176  
177  /**
178   * add a new property for a given module with a given name that has the given 
179   * property nested within it.
180   */
181  public void addProperty(Module module, String name, ConfigurationProperty property)
182    throws NamespaceException, ConfigurationManagerException
183  {
184    addProperty(module, name, namespaceDefault, property);
185  }
186  
187  /**
188   * add a new property for a given module with a given name that has the given 
189   * property nested within it.
190   */
191  public void addProperty(Module module, String name, ConfigurationNamespace namespace, 
192    ConfigurationProperty property)
193    throws NamespaceException, ConfigurationManagerException
194  {
195    ConfigurationProperty cp = new ConfigurationProperty(module, name, namespace, (String)null);
196    checkNamespace(this, cp);
197    checkNamespace(this, property);
198    cp.addProperty(property);
199    addProperty(cp);
200    setDirty(true);
201    notifyListeners(this);
202  }
203  
204  /**
205   * add propertyToAdd before the beforeProperty in the ordering of the list of 
206   * properties for this property
207   * @param index index in this property's property list to add propertyToAdd
208   * @param propertyToAdd the property to add
209   */
210  public void addPropertyAtIndex(int index, ConfigurationProperty propertyToAdd)
211    throws NamespaceException, ConfigurationManagerException
212  {
213    if(!mutable)
214    { //check to see if the property is not mutable.  if it is not, throw
215      //an exception
216      throw new ConfigurationManagerException("The property " + getName() + 
217        " is mutable and cannot be changed at runtime.");
218    }
219    checkNamespace(this, propertyToAdd);
220    propertiesList.add(index, propertyToAdd);
221    propertyToAdd.setParent(this);
222    setDirty(true);
223    notifyListeners(this);
224  }
225  
226  /**
227   * add a nested property if the contents are not already present,
228   * ignoring: namespaces, modules, originmodules. NOTE: if the
229   * contents are the same, but in a different order, the property is
230   * added.
231   */
232  public void addPropertyIfNotThere(ConfigurationProperty propertyToAdd)
233      throws NamespaceException, ConfigurationManagerException
234  {
235      addPropertyIfNotThere(propertyToAdd, true);
236  }
237      
238  public void addPropertyIfNotThere(ConfigurationProperty propertyToAdd, boolean setDirty)
239      throws NamespaceException, ConfigurationManagerException
240  {
241      if(!containsProperty(propertyToAdd))
242      {
243          addProperty(propertyToAdd, false, true, setDirty);
244      }
245  }
246  
247  /**
248   * add a nested property at index if the contents are not already present,
249   * ignoring: namespaces, modules, originmodules. NOTE: if the
250   * contents are the same, but in a different order, the property is
251   * added.
252   */
253  public void addPropertyIfNotThereAtIndex(int index, ConfigurationProperty propertyToAdd)
254      throws NamespaceException, ConfigurationManagerException
255  {
256      if(!containsProperty(propertyToAdd))
257      {
258          addPropertyAtIndex(index, propertyToAdd);
259      }
260  }
261
262  /**
263   * remove the given property
264   * 
265   * @param propertyToRemove the property to remove
266   * @return true if propertiesList contained this property
267   */
268  public boolean removeProperty(ConfigurationProperty propertyToRemove)
269  {
270    boolean removed = propertiesList.remove(propertyToRemove);
271    setDirty(true);
272    notifyListeners(this);
273    return removed;
274  }
275  
276  /**
277   * return a list of nested properties within this property
278   */
279  public List<ConfigurationProperty> getProperties()
280  {
281    return propertiesList;
282  }
283  
284  /**
285   * return a list of nested properties that have a specific name.  Use 
286   * dots to separate path names.  For example "some.config.path.value"
287   */
288  public List<ConfigurationProperty> getProperties(String name)
289  {
290    return getProperties(this, name, false);
291  }
292  
293  /**
294   * do a recursive get through this ConfigurationProperty
295   * 
296   * @param name the name to look for
297   * @param recursive
298   */
299  public List<ConfigurationProperty> getProperties(String name, boolean recursive)
300  {
301    return getProperties(this, name, recursive);
302  }
303  
304  /**
305   * get properties from a configurationProperty with a given name.  If 
306   * recursive is true, look through the entire structure of cp and return
307   * all properties with the given name.  
308   *
309   * @param cp the ConfigurationProperty to get from
310   * @param name the name to look for
311   * @param recursive if true, look through the tree recursively.
312   */
313  public static List<ConfigurationProperty> getProperties(
314    ConfigurationProperty cp, String name, boolean recursive)
315  {
316    //System.out.println("searching for " + name + " in " + cp.getName());
317    List<ConfigurationProperty> propList = cp.getProperties();
318    String[] s = processName(name);
319    if(s.length > 1)
320    {
321      return getPropertiesWithPath(cp, s, true);
322    }
323    
324    Vector<ConfigurationProperty> props = new Vector<ConfigurationProperty>();
325    
326    for(ConfigurationProperty prop : propList)
327    {
328      if(prop.getName().equals(name))
329      {
330        props.add(prop);
331      }
332      
333      if(recursive)
334      {
335        List<ConfigurationProperty> recursiveProps = getProperties(prop, name, recursive);
336        if(recursiveProps.size() > 0)
337        {
338          for(int j=0; j<recursiveProps.size(); j++)
339          {
340            props.add(recursiveProps.get(j));
341          }
342        }
343      }
344    }
345    return props;
346  }
347
348  /**
349   * if there are more than one property with the same name, just return
350   * the first one. Return null if the property is not found
351   */
352  public ConfigurationProperty getProperty(String name)
353  {
354    return getProperty(name, 0);
355  }
356  
357  /**
358   * return a property with a given name at the specified index.  
359   * Example:
360   * prop1
361   *   prop2=x
362   *   prop2=y
363   * getProperty("prop2", 1).getValue() == y
364   * getProperty("prop2", 0).getValue() == x
365   * returns null if the property does not exist
366   */
367  public ConfigurationProperty getProperty(String name, int index)
368  {
369    List propList = getProperties(name);
370    //prettyPrintList(propList);
371    if(propList.size() > index)
372    {
373      return (ConfigurationProperty)propList.get(index);
374    }
375    return null;
376  }
377  
378  /**
379   * get the property with the given index in the current list of properties
380   */
381  public ConfigurationProperty getProperty(int index)
382  {
383    return propertiesList.get(index);
384  }
385  
386  /**
387   * override originalProperty with newProperty. 
388   *
389   * @param originalProperty the property to override
390   * @param newProperty the property to take the place of originalProperty
391   * @param overrideNamespace true if you want to automatically set the namespace
392   * of newProperty (along with all of its subproperties) to that of 
393   * originalProperty.
394   */
395  public boolean overrideProperty(ConfigurationProperty originalProperty, 
396    ConfigurationProperty newProperty, boolean overrideNamespace)
397  {
398    if(overrideNamespace)
399    {
400      newProperty.setNamespace(originalProperty.getNamespace(), true);
401    }
402    
403    boolean replaced = replaceProperty(originalProperty, newProperty);
404    notifyListeners(this);
405    return replaced;
406  }
407  
408  /**
409   * returns a list of properties within this property that have a specific
410   * name and value.  This will return the parent property of any properties
411   * found.  Example:
412   * 
413   * &lt;a&gt;
414   *   &lt;name&gt;jim&lt;/name&gt;
415   *   &lt;otherprop&gt;XXX&lt;/otherprop&gt;
416   * &lt;/a&gt;
417   * &lt;b&gt;
418   *   &lt;name&gt;bob&lt;/name&gt;
419   *   &lt;otherprop&gt;XXX&lt;/otherprop&gt;
420   * &lt;/b&gt;
421   * &lt;c&gt;
422   *   &lt;name&gt;jim&lt;/name&gt;
423   *   &lt;otherprop&gt;XXX&lt;/otherprop&gt;
424   * &lt;/c&gt;
425   *
426   * a call to findProperties("name", "jim") would return elements &lt;a&gt; and &lt;c&gt; 
427   * in the list.
428   * Note: the name parameter can NOT use the shorthand "dot" notation.
429   * 
430   * @param name the name of the property to find
431   * @param value the value of the property to find
432   * @param recursive true if you want a recursive (deep) search
433   */
434  public List<ConfigurationProperty> findProperties(String name, String value, 
435    boolean recursive)
436  {
437    return findProperties(this, name, value, recursive);
438  }
439  
440  /**
441   * returns a list of properties within this property that have a specific
442   * name and value.  This will return the parent property of any properties
443   * found.  Example:
444   * 
445   * &lt;a&gt;
446   *   &lt;name&gt;jim&lt;/name&gt;
447   *   &lt;otherprop&gt;XXX&lt;/otherprop&gt;
448   * &lt;/a&gt;
449   * &lt;b&gt;
450   *   &lt;name&gt;bob&lt;/name&gt;
451   *   &lt;otherprop&gt;XXX&lt;/otherprop&gt;
452   * &lt;/b&gt;
453   * &lt;c&gt;
454   *   &lt;name&gt;jim&lt;/name&gt;
455   *   &lt;otherprop&gt;XXX&lt;/otherprop&gt;
456   * &lt;/c&gt;
457   *
458   * a call to findProperties("name", "jim") would return elements &lt;a&gt; and &lt;c&gt; 
459   * in the list.
460   * Note: the name parameter can NOT use the shorthand "dot" notation.
461   * Note: this method is non-recursive.
462   * 
463   * @param name the name of the property to find
464   * @param value the value of the property to find
465   */
466  public List<ConfigurationProperty> findProperties(String name, String value)
467  {
468    return findProperties(name, value, false);
469  }
470  
471  /**
472   * return the value of this property.  if the value is not set this returns null
473   */
474  public String getValue()
475  {
476    return value;
477  }
478  
479  /**
480   * set the value of this property
481   */
482  public void setValue(String value)
483    throws ConfigurationManagerException
484  {
485    if(!mutable)
486    { //check to see if the property is not mutable.  if it is not, throw
487      //an exception
488      throw new ConfigurationManagerException("The property " + getName() + 
489        " is mutable and cannot be changed at runtime.");
490    }
491    setDirty(true);
492    this.value = value;
493    notifyListeners(this);
494  }
495  
496  /**
497   * return true if this property has a string value
498   */
499  public boolean hasLeafValue()
500  {
501    if(value == null)
502    {
503      return false;
504    }
505    return true;
506  }
507  
508  /**
509   * return true if this property has nested properties
510   */
511  public boolean hasNestedProperties()
512  {
513    if(propertiesList.size() > 0)
514    {
515      return true;
516    }
517    return false;
518  }
519  
520  /**
521   * return the name of this property
522   */
523  public String getName()
524  {
525    return name;
526  }
527  
528  /**
529   * return the module associated with this property
530   */
531  public Module getModule()
532  {
533    return module;
534  }
535  
536  /** 
537   * return the namepace
538   */
539  public ConfigurationNamespace getNamespace()
540  {
541    return this.namespace;
542  }
543  
544  /**
545   * set dirty. generally other classes shouldn't call this
546   * but it's a good way to force serialization when nothing has
547   * actually changed.
548   */
549  public void setDirty(boolean dirty){
550          this.dirty = dirty;
551  }
552  
553  /**
554   * set the namespace of this property
555   */
556  public void setNamespace(boolean notifyListeners, ConfigurationNamespace namespace)
557  {
558    this.namespace = namespace;
559    if (notifyListeners){
560        notifyListeners(this);
561    }
562  }
563  
564  /**
565   * set the namespace of this property
566   */
567  public void setNamespace(ConfigurationNamespace namespace)
568  {
569    this.namespace = namespace;
570    notifyListeners(this);
571  }
572  
573  /**
574   * sets the namespace. If recursive is true, set all subproperties' namespaces
575   * as well.
576   */
577  public void setNamespace(ConfigurationNamespace namespace, boolean recursive)
578  {
579    setNamespace(namespace, recursive, true);
580  }
581  
582  /**
583   * sets the namespace. If recursive is true, set all subproperties' namespaces
584   * as well.
585   */
586  public void setNamespace(ConfigurationNamespace namespace, boolean recursive, boolean notifyListeners)
587  {
588    setNamespace(this, namespace, recursive, notifyListeners);
589    if (notifyListeners){
590        notifyListeners(this);
591    }
592  }
593  
594  /**
595   * returns a fully qualified name including the module and namespace
596   */
597  public String getFullName()
598  {
599    String modName = module.getName() + ".";
600    String ns = "";
601    if(namespace != null)
602    {
603      ns = namespace + ".";
604    }
605    
606    return modName + ns + name;
607  }
608  
609  /**
610   * returns true if this propert is mutable.  A mutable property is one that
611   * can be changed and realized without restarting the application.  Only mutable
612   * properties can be overwritten at runtime.  A property is assumed to be mutable
613   * unless it is set not to be.
614   */
615  public boolean isMutable()
616  {
617    return mutable;
618  }
619  
620  /**
621   * return true if this ConfigurationProperty contains a property with the given name..  
622   * Set recursive to true if you want to search this property's nested structure.
623   * @param property the property
624   */
625  public boolean containsProperty(String name, boolean recursive)
626  {
627    if(!recursive)
628    {
629      ConfigurationProperty cp = getProperty(name);
630      if(cp == null)
631      {
632        return false;
633      }
634      return true;
635    }
636    else
637    {
638      for(ConfigurationProperty cp : propertiesList)
639      {
640        if(cp.getName().equals(name))
641        {
642          return true;
643        }
644        if(cp.hasNestedProperties())
645        {
646          if(cp.containsProperty(name, recursive))
647          {
648            return true;
649          }
650        }
651      }
652      return false;
653    }
654  }
655
656  /**
657   * returns true if this property contains a nested property,
658   * ignoring: namespaces, modules, originmodules. NOTE: if the
659   * contents are the same, but in a different order, returns false.
660   */
661  public boolean containsProperty(ConfigurationProperty nestedProperty)
662  {
663      for(ConfigurationProperty subProperty : getProperties())
664      {
665          //System.out.println("checking");
666          //System.out.println(subProperty.toString(true, false, false, false));
667          //System.out.println(nestedProperty.toString(true, false, false, false));
668          if(subProperty.equalsContents(nestedProperty))
669          {
670              //System.out.println("found! not adding property.");
671              return true;
672          }
673      }
674
675      return false;
676  }
677
678  /**
679   * returns true if the property contents are identical to this one,
680   * ignoring: namespaces, modules, originmodules. NOTE: if the contents
681   * are the same, but in a different order, returns false.
682   */
683  public boolean equalsContents(ConfigurationProperty property)
684  {
685    if(property == null)
686    {
687        return false;
688    }
689    else if(property == this)
690    {
691        return true;
692    }
693    else
694    {
695        return toStringContents().equals(property.toStringContents());
696    }
697
698  }
699
700  /**
701   * return the xml version of this property
702   */
703  public String getXML()
704  {
705    return getXML(this, "");
706  }
707  
708  public String toString(boolean recursive)
709  {
710    StringBuilder s = new StringBuilder("{name=" +
711        name + ", module=" + module.getName() + ", namespace=" +
712        namespace + ", value=" + value);
713    if(recursive && propertiesList.size() > 0)
714    {
715      s.append(", propertyList={");
716      for(ConfigurationProperty cp : propertiesList)
717      {
718        s.append(cp.toString());
719      }
720      s.append("}");
721    }
722    
723    s.append("}");
724    return s.toString();
725  }
726  
727  /**
728   * return a string representation of this property
729   */
730  public String toString()
731  {
732    return toString(true);
733  }
734  
735  /** Returns the string contents of this property without the namespace,
736   *  module name, and origin module name (if present).
737   */
738  public String toStringContents()
739  {
740      return toString(true, false, false, false);
741  }
742
743  /**
744   * returns a string representation of this property
745   * @param recursive if true, string includes all sub-properties
746   * @param includeNamespace if true, string includes the namespace
747   * @param includeModule if true, string include module name
748   * @param includeOriginModule if true, string includes originModule name
749   */
750  public String toString(boolean recursive, boolean includeNamespace, boolean includeModule, boolean includeOriginModule)
751  {
752    String s;
753    s = "{name=" + name;
754
755    if(includeModule)
756    {
757        s += ", module=" + module.getName();
758    }
759
760    if(includeNamespace)
761    {
762        s += ", namespace=" + namespace;
763    }
764
765    s += ", value=" + value;
766
767    if(recursive && propertiesList.size() > 0)
768    {
769      s += ", propertyList={";
770      for(ConfigurationProperty cp : propertiesList)
771      {
772        if(includeOriginModule || !cp.getName().equals("originModule"))
773        {
774          s += cp.toString(recursive, includeNamespace, includeModule, includeOriginModule);
775        }
776      }
777      s +="}";
778    }
779
780    s += "}";
781    return s;
782  }
783
784  /**
785   * print this configurationProperty nicely showing the hierarchy
786   */
787  public void prettyPrint()
788  {
789    System.out.println(prettyPrint(this, ""));
790  }
791  
792  /**
793   * returns true if this property has changed since it was last saved
794   * @param recursive true if the dirty flag should be searched for in any
795   * contained configuration property
796   */
797  public boolean isDirty(boolean recursive)
798  {
799    if(!recursive)
800    {
801      return this.dirty;
802    }
803    
804    return isDirty(this);
805  }
806  
807  /**
808   * returns this congifuration property's dirty flag
809   */
810  public boolean isDirty()
811  {
812    return isDirty(false);
813  }
814  
815  /**
816   * returns the parent propert of this property.  null if this property
817   * does not have a parent.
818   */
819  public ConfigurationProperty getParent()
820  {
821    return this.parent;
822  }
823  
824  /**
825   * set the namespace on a configurationProperty
826   */
827  public static void setNamespace(ConfigurationProperty cp, ConfigurationNamespace namespace, 
828                  boolean recursive)
829  {
830          setNamespace(cp, namespace, recursive, true);
831  }
832  
833  /**
834   * set the namespace on a configurationProperty
835   */
836  public static void setNamespace(ConfigurationProperty cp, ConfigurationNamespace namespace, 
837                  boolean recursive, boolean notifyListeners)
838  {
839    cp.setNamespace(notifyListeners, namespace);
840    if(recursive)
841    {
842      List<ConfigurationProperty> propList = cp.getProperties();
843      for(ConfigurationProperty prop : propList)
844      {
845        setNamespace(prop, namespace, recursive, notifyListeners);
846      }
847    }
848  }
849  
850  /**
851   * Search a list of properties for properties that contain a name and a value
852   */
853  public static List<ConfigurationProperty> findProperties(
854    List<ConfigurationProperty> properties, String name, String value, boolean recursive)
855  {
856    Vector<ConfigurationProperty> results = new Vector<ConfigurationProperty>();
857    for(ConfigurationProperty cp : properties)
858    {
859      //System.out.println("searching " + cp.getName());
860      List<ConfigurationProperty> subresults = findProperties(cp, name, value, recursive);
861      for(ConfigurationProperty p : subresults)
862      {
863        if(!checkForDuplicates(results, p))
864        {
865          results.add(p);
866        }
867      }
868    }
869    return results;
870  }
871  
872  /**
873   * return a list of properties that have a name that matches name and a value
874   * that matches value within property.  If recursive is set to true, do a deep
875   * search of the property
876   */
877  public static List<ConfigurationProperty> findProperties(ConfigurationProperty property, 
878    String name, String value, boolean recursive)
879  {
880    Vector<ConfigurationProperty> found = new Vector<ConfigurationProperty>();
881    List<ConfigurationProperty> propertiesList = property.getProperties();
882    for(ConfigurationProperty cp : propertiesList)
883    {
884      //System.out.println("cp: " + cp.getName());
885      if(cp.getName().equals(name) && cp.getValue().equals(value) && !checkForDuplicates(found, cp))
886      {
887        found.add(cp.getParent());
888      }
889
890      if(recursive)
891      {
892        List<ConfigurationProperty> l = findProperties(cp, name, value, recursive);
893        //simplePrintList(l);
894        for(int j=0; j<l.size(); j++)
895        {
896          if(!checkForDuplicates(found, l.get(j)))
897          {
898            found.add(l.get(j));
899          }
900        }
901      }
902    }
903    
904    return found;
905  }
906  
907  /**
908   * pretty print a list of configuration properties
909   */
910  public static void prettyPrintList(List<ConfigurationProperty> l)
911  {
912    System.out.println("[");
913    for(int i=0; i<l.size(); i++)
914    {
915      System.out.print(i + " = {\n");
916      ConfigurationProperty cp = l.get(i);
917      System.out.println("module: " + cp.getModule().getName() + 
918        " name: " + cp.getName() + 
919        " namespace: " + cp.getNamespace().toString() +
920        " value: " + cp.getValue());
921      cp.prettyPrint();
922      System.out.print("}");
923      if(i != l.size() - 1)
924      {
925        System.out.print(",\n");
926      }
927    }
928    System.out.println("]");
929  }
930  
931  public static void simplePrintList(List<ConfigurationProperty> l)
932  {
933    System.out.print("\n[");
934    for(int i=0; i<l.size(); i++)
935    {
936      System.out.print(i + " = {");
937      ConfigurationProperty cp = l.get(i);
938      System.out.print("module: " + cp.getModule().getName() + " namespace: " 
939        + cp.getNamespace().toString() + " name: " + cp.getName());
940      System.out.print("}");
941      if(i != l.size() - 1)
942      {
943        System.out.print(",\n");
944      }
945    }
946    System.out.println("]");
947  }
948  
949  /**
950   * return a list of strings of the values of a property with a given name in 
951   * the list of properties
952   */
953  public static List<String> getValueList(List<ConfigurationProperty> propList, 
954    String propertyName, boolean recursive)
955  {
956    Vector<String> v = new Vector<String>();
957    for(ConfigurationProperty cp : propList)
958    {
959      if(cp.getName().equals(propertyName))
960      {
961        v.add(cp.getValue());
962      }
963      
964      if(recursive)
965      {
966        List<String> l = getValueList(cp.getProperties(), propertyName, recursive);
967        for(int j=0; j<l.size(); j++)
968        {
969          v.add(l.get(j));
970        }
971      }
972    }
973    return v;
974  }
975  
976  /**
977   *
978   */
979  public void setOriginModule(Module m)
980  {
981    this.originModule = m;
982  }
983  
984  /**
985   *
986   */
987  public Module getOriginModule()
988  {
989    return this.originModule;
990  }
991  
992  /**
993   * set the parent property of this property
994   */
995  public void setParent(ConfigurationProperty property)
996  {
997    this.parent = property;
998  }
999  
1000  /**
1001   * reset the dirty flag
1002   * @param recursive set to true if all properties that this property
1003   * contains should also be reset
1004   */
1005  protected void resetDirty(boolean recursive)
1006  {
1007    setDirty(false);
1008    
1009    if(!recursive)
1010    {
1011      return;
1012    }
1013    
1014    Iterator it = getProperties().iterator();
1015    while(it.hasNext())
1016    {
1017      ConfigurationProperty cp = (ConfigurationProperty)it.next();
1018      cp.resetDirty(true);
1019    }
1020  }
1021    
1022  /**
1023   * recursively return all properties with a path denoted in s.  If returnChildren
1024   * is true, return the found child nodes.  If it is false, return the 
1025   * found parent nodes.  For example, if you are getting a/b/c, if returnChildren
1026   * is true, return a list of the c's.  If returnChildren is false,
1027   * return a list of the a's.  
1028   */
1029  protected static List<ConfigurationProperty> getPropertiesWithPath(ConfigurationProperty property,
1030    String[] s, boolean returnChildren)
1031  {
1032    if(returnChildren)
1033    { //return the found children nodes
1034      List<ConfigurationProperty> properties = getProperties(property, s[0], true);
1035      for(int i=1; i<s.length; i++)
1036      {
1037        properties = getPropertiesFromList(properties, s[i]);
1038      }
1039      
1040      return properties;
1041    }
1042    
1043    //return the found parent nodes
1044    List<ConfigurationProperty> properties = getProperties(property, s[s.length - 1], true);
1045    for(int i=s.length - 2; i>=0; i--)
1046    {
1047      properties = getParentPropertiesFromList(properties, s[i]);
1048    }
1049    
1050    return properties;
1051  }
1052  
1053  /**
1054   * set whether this property is mutable. A mutable property is one that
1055   * can be changed and realized without restarting the application.  Only mutable
1056   * properties can be overwritten at runtime.  A property is assumed to be mutable
1057   * unless it is set not to be.
1058   */
1059  protected void setMutable(boolean mutable)
1060  {
1061    this.mutable = mutable;
1062  }
1063  
1064  /**
1065   * searches a list of properties for properties of a given name.  This is
1066   * not recursive.
1067   * If returnChild is true, return the child elements instead of the parents
1068   */
1069  private static List<ConfigurationProperty> getParentPropertiesFromList(
1070    List<ConfigurationProperty> propertyList, String name)
1071  {
1072    Vector<ConfigurationProperty> results = new Vector<ConfigurationProperty>();
1073    for(ConfigurationProperty prop : propertyList)
1074    {
1075      //System.out.println("prop.parent: " + prop.getParent().getName());
1076      if(prop.getParent().getName().equals(name))
1077      {
1078        results.add(prop.getParent());
1079      }
1080    }
1081    return results;
1082  }
1083  
1084  /**
1085   * Note: callers should notifyListeners.
1086   * 
1087   * replace a specific property within a property's subproperties and 
1088   * return the index.  This method is recursive.
1089   * 
1090   * @param findProperty the property to look for
1091   * @param replaceProperty the property to replace findProperty with
1092   * @return boolean true if the property was found and replaced
1093   */
1094  private boolean replaceProperty(ConfigurationProperty findProperty, 
1095    ConfigurationProperty replaceProperty)
1096  {
1097    boolean found = false;
1098    for(int i=0; i<this.propertiesList.size(); i++)
1099    {
1100      ConfigurationProperty cp = this.propertiesList.get(i);
1101      if(cp == findProperty)
1102      {
1103        this.propertiesList.remove(i);
1104        this.propertiesList.add(i, replaceProperty);
1105        return true;
1106      }
1107      
1108      List l = cp.getProperties();
1109      if(l.size() > 0)
1110      {
1111        found = cp.replaceProperty(findProperty, replaceProperty);
1112        if(found)
1113        {
1114          break;
1115        }
1116      }
1117    }
1118    
1119    return found;
1120  }
1121  
1122  /**
1123   * return true if v contains cp
1124   */
1125  private static boolean checkForDuplicates(Vector<ConfigurationProperty> v, ConfigurationProperty cp)
1126  {
1127    for(ConfigurationProperty p : v)
1128    {
1129      if(p == cp)
1130      {
1131        return true;
1132      }
1133    }
1134    return false;
1135  }
1136  
1137  /**
1138   * return true of the property or any of its sub properties are dirty
1139   */
1140  private boolean isDirty(ConfigurationProperty p)
1141  {
1142    if(p.isDirty())
1143    {
1144      return true;
1145    }
1146    
1147    List l = p.getProperties();
1148    Iterator it = l.iterator();
1149    while(it.hasNext())
1150    {
1151      ConfigurationProperty cp = (ConfigurationProperty)it.next();
1152      if(isDirty(cp))
1153      {
1154        return true;
1155      }
1156      else
1157      {
1158        continue;
1159      }
1160    }
1161        
1162    return false;
1163  }
1164  
1165  /**
1166   * return an xml representation of this property
1167   */
1168  private static String getXML(ConfigurationProperty cp, String spaces)
1169  {
1170    String s = "";
1171    String name = cp.getName();
1172    Iterator it = cp.getProperties().iterator();
1173    String value = cp.getValue();
1174    
1175    s += spaces;
1176    s += "<" + name + ">\n";
1177    
1178    if(!cp.isMutable())
1179    {
1180      s += spaces + "  <mutable>false</mutable>\n";
1181    }
1182    
1183    Module originModule = cp.getOriginModule();
1184    if (cp.getParent() == null)
1185    {
1186        System.out.println("ERROR ConfigurationProperty getXML(cp, spaces) cp.getParent() == null for cp:");
1187        cp.prettyPrint();
1188    }
1189    
1190    if(originModule != null && !originModule.getName().equals(cp.getParent().getModule().getName()))
1191    {
1192      s += spaces + "  <originModule>" + originModule.getName() + "</originModule>\n";
1193    }
1194    
1195    if(value != null && !value.equals(""))
1196    {
1197      s += spaces + "  " + value + "\n";
1198    }
1199    
1200    while(it.hasNext())
1201    {
1202      ConfigurationProperty c = (ConfigurationProperty)it.next();
1203      // don't add originModule property more than once per property
1204      if (!c.getName().equals("originModule")){
1205          s += getXML(c, spaces + "  ");
1206      }
1207    }
1208    
1209    s += spaces + "</" + name + ">\n";
1210    
1211    return s;
1212  }
1213  
1214  /**
1215   * format the cp nicely
1216   */
1217  private static String prettyPrint(ConfigurationProperty cp, String spaces)
1218  {
1219    String s = "";
1220    String name = cp.getName();
1221    Iterator it = cp.getProperties().iterator();
1222    String value = cp.getValue();
1223    String originModName = cp.getOriginModule().getName();
1224    
1225    boolean dirty = cp.isDirty();
1226    s += spaces;
1227    s += name + "(" + originModName + ")";
1228    if(dirty)
1229    {
1230      s += "(DIRTY)";
1231    }
1232    
1233    if(value != null && !value.equals(""))
1234    {
1235      s += " = " + value;
1236    }
1237    s += "\n";
1238    
1239    while(it.hasNext())
1240    {
1241      ConfigurationProperty c = (ConfigurationProperty)it.next();
1242      s += prettyPrint(c, spaces + "  ");
1243    }
1244    return s;
1245  }
1246  
1247  /**
1248   * process a name with . notations into a string array
1249   */
1250  protected static String[] processName(String name)
1251  {
1252    String[] s;
1253    if(name.indexOf(".") != -1)
1254    {
1255      StringTokenizer st = new StringTokenizer(name, ".");
1256      int num = st.countTokens();
1257      s = new String[num];
1258      int i = 0;
1259      while(st.hasMoreTokens())
1260      {
1261        String ss = st.nextToken();
1262        s[i] = ss;
1263        i++;
1264      }
1265      return s;
1266    }
1267    else
1268    {
1269      s = new String[1];
1270      s[0] = name;
1271      return s;
1272    }
1273  }
1274  
1275  /**
1276   * recursively return the properties in the list that match name
1277   */
1278  private static List<ConfigurationProperty> getPropertiesFromList(
1279    List<ConfigurationProperty> propertyList, String name)
1280  {
1281    if(propertyList == null)
1282    {
1283      return null;
1284    }
1285    Vector<ConfigurationProperty> results = new Vector<ConfigurationProperty>();
1286    for(ConfigurationProperty cp : propertyList)
1287    {
1288      List<ConfigurationProperty> cpPropList = cp.getProperties();
1289      for(ConfigurationProperty cpProp : cpPropList)
1290      {
1291        if(cpProp.getName().equals(name))
1292        {
1293          results.add(cpProp);
1294        }
1295      }
1296    }
1297    return results;
1298  }
1299  
1300  /**
1301   * initialize this property
1302   */
1303  private void init(Module module, String name, ConfigurationNamespace namespace, String value)
1304  {
1305    this.propertiesList = new ArrayList<ConfigurationProperty>();
1306    this.module = module;
1307    this.originModule = module;
1308    this.name = name;
1309    this.namespace = namespace;
1310    this.value = value;
1311    this.parent = null;
1312    this.mutable = true;
1313  }
1314  
1315  /**
1316   * check to see if prop1 and prop2 are in the same namespace.  If they aren't
1317   * throw a NamespaceException
1318   */
1319  private void checkNamespace(ConfigurationProperty prop1, ConfigurationProperty prop2)
1320    throws NamespaceException
1321  {
1322    ConfigurationNamespace p1ns = prop1.getNamespace();
1323    ConfigurationNamespace p2ns = prop2.getNamespace();
1324
1325    if(p1ns == null && p2ns == null)
1326    {
1327      return;
1328    }
1329    else if(p1ns == null || p2ns == null)
1330    {
1331      throw new NamespaceException("The namespace of configuration property " +
1332        prop1.getFullName() + " is not in the same namespace as " + 
1333        prop2.getFullName() + ".  One namespace is null and the other is not.");
1334    }
1335    else if(!p1ns.equals(p2ns))
1336    {
1337      throw new NamespaceException("The namespace of configuration property " +
1338        prop1.getFullName() + " is not in the same namespace as " + 
1339        prop2.getFullName() + ".  Properties must be in the same namespace " +
1340        "to be nested.");
1341    }
1342  }
1343  
1344  /**
1345   * notify listeners that the property has changed.
1346   */
1347  private void notifyListeners(ConfigurationProperty property)
1348  {
1349    try
1350    {
1351      ConfigurationManager.getInstance(false).notifyListeners(property);
1352    }
1353    catch(Exception e)
1354    {
1355      throw new RuntimeException("Could not notify listeners: " + e.getMessage());
1356    }
1357  }  
1358}