001: /**
002: * <copyright>
003: *
004: * Copyright 2002-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */package org.cougaar.tools.csmart.recipe;
026:
027: import org.cougaar.tools.csmart.core.cdata.ComponentData;
028: import org.cougaar.tools.csmart.core.db.PDbBase;
029: import org.cougaar.tools.csmart.core.property.*;
030: import org.cougaar.tools.csmart.core.property.name.CompositeName;
031: import org.cougaar.tools.csmart.core.property.range.BooleanRange;
032: import org.cougaar.tools.csmart.society.AgentComponent;
033: import org.cougaar.tools.csmart.ui.viewer.CSMART;
034: import org.cougaar.util.log.Logger;
035:
036: import java.io.IOException;
037: import java.io.ObjectInputStream;
038: import java.util.ArrayList;
039: import java.util.HashSet;
040: import java.util.Iterator;
041:
042: /**
043: * Holds basic recipe functionality
044: *
045: */
046: public abstract class RecipeBase extends
047: ModifiableConfigurableComponent implements RecipeComponent,
048: PropertiesListener {
049:
050: protected transient Logger log;
051:
052: protected boolean modified = true;
053:
054: // modification event
055: public static final int RECIPE_SAVED = 3;
056:
057: // used to block modify notifications while saving
058: protected transient boolean saveInProgress = false;
059:
060: public RecipeBase(String name) {
061: super (name);
062: createLogger();
063: installListeners();
064: }
065:
066: private void createLogger() {
067: log = CSMART.createLogger(this .getClass().getName());
068: }
069:
070: public String getRecipeName() {
071: return getShortName();
072: }
073:
074: public void setName(String newName) {
075: if (newName == null || newName.equals("")
076: || newName.equals(getRecipeName()))
077: return;
078:
079: boolean temp = modified;
080: String oldname = getRecipeName();
081: super .setName(newName);
082:
083: // do the DB save that is necessary
084: try {
085: PDbBase.changeRecipeName(oldname, newName);
086: } catch (Exception e) {
087: if (log.isErrorEnabled()) {
088: log.error("setName exception changing name from "
089: + oldname + " to " + newName, e);
090: }
091: // On error, mark the recipe as modified
092: temp = true;
093: }
094:
095: // It may be that since recipes save via properties
096: // and the property names will have changed that I should
097: // do an xor or something perhaps, but more likely just
098: // mark it true always
099: modified = temp;
100: }
101:
102: // not needed; these are the same as in ModifiableConfigurableComponent
103: // public void addModificationListener(ModificationListener l)
104: // {
105: // getEventListenerList().add(ModificationListener.class, l);
106: // }
107:
108: // public void removeModificationListener(ModificationListener l)
109: // {
110: // getEventListenerList().remove(ModificationListener.class, l);
111: // }
112:
113: public Property addBooleanProperty(String name, boolean dflt) {
114: Property tmp = addProperty(name, new Boolean(dflt));
115: HashSet booleanSet = new HashSet();
116: booleanSet.add(new BooleanRange(false));
117: booleanSet.add(new BooleanRange(true));
118: tmp.setAllowedValues(booleanSet);
119: return tmp;
120: }
121:
122: /**
123: * Called when a new property has been added to the
124: * recipe.
125: *
126: * @param e Event for the new property
127: */
128: public void propertyAdded(PropertyEvent e) {
129: Property addedProperty = e.getProperty();
130: Property myProperty = getProperty(addedProperty.getName()
131: .last().toString());
132: if (myProperty != null) {
133: // set property visible?
134: addedProperty.addPropertyListener(myPropertyListener);
135: fireModification();
136: }
137: }
138:
139: /**
140: * Called when a property has been removed from the recipe.
141: */
142: public void propertyRemoved(PropertyEvent e) {
143: e.getProperty().removePropertyListener(myPropertyListener);
144: fireModification();
145: }
146:
147: /**
148: * Most recipes do not contain agents.
149: *
150: * @return an <code>AgentComponent[]</code> value
151: */
152: public AgentComponent[] getAgents() {
153: ArrayList agents = new ArrayList(
154: getDescendentsOfClass(AgentComponent.class));
155: return (AgentComponent[]) agents
156: .toArray(new AgentComponent[agents.size()]);
157: // return null;
158: }
159:
160: /**
161: * Most recipes do not add ComponentData, they modify it.
162: * This is a helper impl.
163: *
164: * @param data
165: * @return a <code>ComponentData</code> value
166: */
167: public ComponentData addComponentData(ComponentData data) {
168: return data;
169: }
170:
171: /**
172: * Save the recipe to the database.
173: * @return boolean true if save was successful
174: */
175: public boolean saveToDatabase() {
176: saveInProgress = true;
177: boolean result = false;
178: PDbBase pdb = null;
179: try {
180: pdb = new PDbBase();
181: pdb.insureLibRecipe(this );
182: result = true;
183: } catch (Exception sqle) {
184: if (log.isErrorEnabled()) {
185: log.error("Exception", sqle);
186: }
187: result = false;
188: } finally {
189: try {
190: if (pdb != null)
191: pdb.close();
192: } catch (Exception sqle) {
193: if (log.isErrorEnabled()) {
194: log.error("Exception", sqle);
195: }
196: }
197: }
198: saveInProgress = false;
199: if (result) {
200: modified = false;
201: fireModification(new ModificationEvent(this , RECIPE_SAVED));
202: }
203: return result;
204: }
205:
206: /**
207: * Return true if recipe is different than in the database.
208: * @return boolean true if recipe is different than in database
209: */
210: public boolean isModified() {
211: return modified;
212: }
213:
214: public void fireModification() {
215: modified = true;
216: super .fireModification();
217: }
218:
219: // only listen on local properties
220:
221: public void installListeners() {
222: addPropertiesListener(this );
223: for (Iterator i = getLocalPropertyNames(); i.hasNext();) {
224: Property p = getProperty((CompositeName) i.next());
225: if (p != null)
226: p.addPropertyListener(myPropertyListener);
227: }
228: }
229:
230: PropertyListener myPropertyListener = new PropertyListener() {
231: public void propertyValueChanged(PropertyEvent e) {
232: if (e == null || e.getProperty() == null)
233: return;
234: if (e.getProperty().getValue() == null) {
235: if (e.getPreviousValue() == null)
236: return;
237: else
238: fireModification();
239: } else if (e.getPreviousValue() == null) {
240: fireModification();
241: } else if (!e.getProperty().getValue().toString().trim()
242: .equals(e.getPreviousValue().toString())) {
243: fireModification();
244: }
245: }
246:
247: public void propertyOtherChanged(PropertyEvent e) {
248: fireModification();
249: }
250: };
251:
252: ModificationListener myModificationListener = new MyModificationListener();
253:
254: public int addChild(ComposableComponent c) {
255: ((ModifiableConfigurableComponent) c)
256: .addModificationListener(myModificationListener);
257: fireModification();
258: return super .addChild(c);
259: }
260:
261: public void removeChild(ComposableComponent c) {
262: ((ModifiableConfigurableComponent) c)
263: .removeModificationListener(myModificationListener);
264: fireModification();
265: super .removeChild(c);
266: }
267:
268: class MyModificationListener implements ModificationListener,
269: ConfigurableComponentListener {
270: public void modified(ModificationEvent e) {
271: // don't propagate modifications when we're saving
272: if (!saveInProgress)
273: fireModification();
274: }
275: }
276:
277: // Bug 1357: Add in own ModificationListener as per SocietyBase, it check on saveInProgress?
278:
279: public ModifiableComponent copy(String name) {
280: ModifiableComponent copiedComponent = super .copy(name);
281: if (copiedComponent != null)
282: ((RecipeBase) copiedComponent).modified = this .modified;
283: return copiedComponent;
284: }
285:
286: /**
287: * Indicate that the recipe is up-to-date with respect to the database.
288: * Use with caution! The only reason to reset this flag
289: * is that when a recipe is created from the database,
290: * it appears to be modified.
291: */
292: public void resetModified() {
293: modified = false;
294: // tell listeners recipe is now saved
295: fireModification(new ModificationEvent(this , RECIPE_SAVED));
296: }
297:
298: /**
299: * Recipes are keyed off of their name in the DB.
300: * So two recipes are equal iff their names are equal.
301: */
302: public boolean equals(Object o) {
303: if (o instanceof RecipeComponent) {
304: RecipeComponent that = (RecipeComponent) o;
305: if (!this .getRecipeName().equals(that.getRecipeName())) {
306: return false;
307: }
308: return true;
309: }
310: return false;
311: }
312:
313: private void readObject(ObjectInputStream ois) throws IOException,
314: ClassNotFoundException {
315: ois.defaultReadObject();
316: createLogger();
317: installListeners();
318: saveInProgress = false;
319: }
320:
321: }// RecipeBase
|