View Javadoc

1   /* ===================================================================
2    * Copyright 2002-06 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  package com.sri.emo.commandline.defaults;
11  
12  import java.io.PrintWriter;
13  import java.io.StringWriter;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.HashMap;
17  import java.util.HashSet;
18  import java.util.Iterator;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Set;
22  import org.apache.log4j.Logger;
23  import com.jcorporate.expresso.core.db.DBException;
24  import com.jcorporate.expresso.core.dbobj.DBObject;
25  import com.jcorporate.expresso.core.dbobj.ValidValue;
26  import com.jcorporate.expresso.core.security.SuperUser;
27  import com.sri.common.util.ObjectCombinator;
28  import com.sri.emo.annotations.NodeTag;
29  import com.sri.emo.commandline.NodeTracker;
30  import com.sri.emo.commandline.SqlRowGenerator;
31  import com.sri.emo.controller.DecisionMatrix;
32  import com.sri.emo.dbobj.Node;
33  import com.sri.emo.dbobj.Part;
34  import com.sri.emo.dbobj.PartsFactory;
35  import com.sri.emo.dbobj.PickList;
36  import com.sri.emo.dbobj.StepAttributes;
37  import com.sri.emo.dbobj.WizDecisionSet;
38  import com.sri.emo.dbobj.WizDefinition;
39  import com.sri.emo.dbobj.WizStep;
40  import com.sri.emo.wizard.WizDefinitionRepository;
41  import com.sri.emo.wizard.completion.persistence.CompletionDefinition;
42  import com.sri.emo.wizard.expressoimpl.SelectionWizDefinitionRepository;
43  import com.sri.emo.wizard.selection.EmoSelectionWizard;
44  import com.sri.emo.wizard.selection.management.PromptStepType;
45  
46  /***
47   * Exports wizards.
48   * 
49   * @author Michael Rimov
50   */
51  public class WizardExporter extends AbstractNodeExportSupport {
52  
53  	/***
54  	 * Log4j logger.
55  	 */
56  	private final Logger log = Logger.getLogger(WizardExporter.class);
57  
58  	/***
59  	 * Set of tag names we're looking for in exporting.
60  	 */
61  	private final Set tags;
62  
63  	/***
64  	 * Constructs a wizard exporter with the specified services.
65  	 * 
66  	 * @param nodeTracker
67  	 *            tracker of nodes.
68  	 * @param generator
69  	 *            sql row generator for export.
70  	 * @param tags
71  	 *            the tags we're exporting.
72  	 */
73  	public WizardExporter(final NodeTracker nodeTracker,
74  			final SqlRowGenerator generator, String[] tags) {
75  		super(nodeTracker, generator);
76  
77  		this.tags = new HashSet(Arrays.asList(tags));
78  	}
79  
80  	/***
81  	 * Factory method to create the selection wizard repository.
82  	 * 
83  	 * @return
84  	 */
85  	protected WizDefinitionRepository getWizardRepository() {
86  		return new SelectionWizDefinitionRepository();
87  	}
88  
89  	/***
90  	 * Exports all wizards to the printwriter.
91  	 * 
92  	 * @param writer
93  	 *            the writer.
94  	 * @return number of total dbobject records exported.
95  	 * @throws DBException
96  	 */
97  	public int exportAllWizards(PrintWriter writer) throws DBException {
98  		int recordCount = 0;
99  		int wizardCount = 0;
100 		if (log.isInfoEnabled()) {
101 			log.info("Exporting Wizards");
102 		}
103 
104 		List allWizards = getWizardRepository().listAll(WizDefinition.FLD_NAME);
105 		for (Iterator wizardIterator = allWizards.iterator(); wizardIterator
106 				.hasNext();) {
107 			WizDefinition wizardDefinition = null;
108 			try {
109 				wizardDefinition = (WizDefinition) wizardIterator.next();
110 				recordCount += exportWizard(wizardDefinition, writer);
111 			} catch (Exception ex) {
112 				log.error("Error exporting wizard " + wizardDefinition, ex);
113 			}
114 		}
115 
116 		if (log.isInfoEnabled()) {
117 			log.info("Processed " + wizardCount + " wizards.");
118 		}
119 
120 		return recordCount;
121 	}
122 
123 	/***
124 	 * Exports a given wizard given the wizard definition.
125 	 * 
126 	 * @param wizardDefinition
127 	 * @param writer
128 	 * @return number of records exported.
129 	 * @throws DBException
130 	 */
131 	private int exportWizard(final WizDefinition wizardDefinition, final PrintWriter writer) throws DBException {
132 		int recordsExported = 0;
133 
134 		try {
135 
136 			if (isSelectionWizard(wizardDefinition)) {
137 				// We have to export everything or nothing -- so what we do
138 				// is have the code write to a string writer instead of the
139 				// normal
140 				// printstream.
141 				// If then everything was successfully added, we flush the
142 				// entire
143 				// printstream to the output writer.
144 				StringWriter stringWriter = new StringWriter();
145 				PrintWriter transactionalWriter = new PrintWriter(stringWriter);
146 				try {
147 					recordsExported += exportSelectionWizard(wizardDefinition,
148 							transactionalWriter);
149 				} finally {
150 					transactionalWriter.close();
151 				}
152 
153 				// If we got here, then we were successfull.
154 				if (recordsExported > 0) {
155 					writer.print(stringWriter.toString());
156 				}
157 				stringWriter = null;
158 				transactionalWriter = null;
159 			} else {
160 				List recordsToExport = exportCompletionWizard(wizardDefinition);
161 				for (Iterator i = recordsToExport.iterator(); i.hasNext();) {
162 					DBObject eachObject = (DBObject) i.next();
163 					recordsExported += this.writeDBObject(writer, eachObject);
164 				}
165 			}
166 
167 		} catch (DependentNodesNotTaggedException ex) {
168 			System.err.println(ex.getMessage());
169 			log.error("Error writing wizard " + wizardDefinition
170 					+ " missing one or more nodes", ex);
171 		} catch (IncompleteDecisionMatrixException ex) {
172 			System.err.println(ex.getMessage());
173 			log.error("Skipping selection wizard " + wizardDefinition
174 					+ " due to incomplete selection matrix.", ex);
175 		}
176 		return recordsExported;
177 	}
178 
179 	/***
180 	 * Checks to see if the given target node contains the tags necessary for
181 	 * export.
182 	 * 
183 	 * @param targetNode
184 	 * @return
185 	 * @throws DBException
186 	 */
187 	private boolean isNodeTaggedForExport(Node targetNode) throws DBException {
188 		List targetTags = targetNode.getTags();
189 		if (targetTags == null || targetTags.size() == 0) {
190 			return false;
191 		}
192 
193 		for (int i = 0; i < targetTags.size(); i++) {
194 			NodeTag eachTag = (NodeTag) targetTags.get(i);			
195 			if (tags.contains(eachTag.getTagValue())) {
196 				return true;
197 			}
198 		}
199 
200 		return false;
201 	}
202 
203 	/***
204 	 * Retrieve a list of dbobjects to export for the given completion wizard.
205 	 * 
206 	 * @param wizard
207 	 * @return list of dbobjects to export.
208 	 * @throws DBException
209 	 * @throws DependentNodesNotTaggedException
210 	 */
211 	private List exportCompletionWizard(final WizDefinition wizard)
212 			throws DBException, DependentNodesNotTaggedException {
213 		List recordsToExport = new ArrayList();
214 		recordsToExport.add(wizard);
215 		CompletionDefinition completion = (CompletionDefinition) wizard
216 				.getAdditionalInfo();
217 		Node targetNode = completion.getTargetNode();
218 
219 		if (!isNodeTaggedForExport(targetNode)) {
220 			throw new DependentNodesNotTaggedException(wizard, targetNode);
221 		}
222 
223 		recordsToExport.add(completion);
224 		List allDetails = completion.getCompletionDetails();
225 		for (Iterator detailIterator = allDetails.iterator(); detailIterator.hasNext();) {
226 			DBObject eachDetail = (DBObject)detailIterator.next();
227 			recordsToExport.add(eachDetail);
228 		}
229 
230 		return recordsToExport;
231 	}
232 
233 	/***
234 	 * Hack -- since the wizard API provides a unified view of all wizards, we
235 	 * check the wizard definition running controller. If it is selection wizard
236 	 * controller, then we return true.
237 	 * 
238 	 * @param wizardDefinition
239 	 *            the wizard definition to examine.
240 	 * @return
241 	 * @throws DBException
242 	 */
243 	private boolean isSelectionWizard(WizDefinition wizardDefinition)
244 			throws DBException {
245 		if (EmoSelectionWizard.class.getName().equals(
246 				wizardDefinition.getField(WizDefinition.FLD_WIZARD))) {
247 			return true;
248 		}
249 
250 		return false;
251 	}
252 
253 	/***
254 	 * Exports the selection wizard and related objects.
255 	 * 
256 	 * @param wizard
257 	 * @param writer
258 	 * @return
259 	 * @throws DBException
260 	 * @throws DependentNodesNotTaggedException
261 	 * @throws IncompleteDecisionMatrixException
262 	 */
263 	private int exportSelectionWizard(WizDefinition wizard, PrintWriter writer)
264 			throws DBException, DependentNodesNotTaggedException,
265 			IncompleteDecisionMatrixException {
266 		int result = 0;
267 		result += writeDBObject(writer, wizard);
268 		result += writeDBObject(writer, (DBObject) wizard.getAdditionalInfo());
269 
270 		List pages = wizard.getPageDefinitions();
271 		List notTaggedNodes = new ArrayList();
272 		for (Iterator pageIterator = pages.iterator(); pageIterator.hasNext();) {
273 			WizStep eachStep = (WizStep) pageIterator.next();
274 			try {
275 				result += writeDBObject(writer, eachStep);
276 	
277 				// Also add additional info
278 				if (eachStep.getNodeType() != null) {
279 					result += exportNodeType(writer, eachStep.getNodeType()
280 							.getEntityName());
281 				}
282 	
283 				// Write step attributes
284 				List stepAttributes = eachStep.getStepAttributeList();
285 				for (Iterator attributeIterator = stepAttributes.iterator(); attributeIterator
286 						.hasNext();) {
287 					StepAttributes eachAttribute = (StepAttributes) attributeIterator
288 							.next();
289 					result += writeDBObject(writer, eachAttribute);
290 				}
291 	
292 				switch (eachStep.getStepType()) {
293 				
294 				case PromptStepType.CUSTOM_PICKLIST:
295 					/***
296 					 * TODO: SelectionWizardManager.buildWizardSteps currently
297 					 * doesn't have an implementation for custom picklists??
298 					 */
299 					break;
300 	
301 				case PromptStepType.INSTANCE_PICKLIST:
302 					String nodeId = eachStep
303 							.getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID);
304 					Node n = new Node();
305 					n.setNodeId(nodeId);
306 					n.retrieve();
307 					if (!this.isNodeTaggedForExport(n)) {
308 						throw new DependentNodesNotTaggedException(wizard, n);
309 					}
310 	
311 					String attributeId = eachStep
312 							.getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_FIELD);
313 					Part part = new Part();
314 					part.setPartId(attributeId);
315 					part.retrieve();
316 					result += writeDBObject(writer, part);
317 	
318 					break;
319 	
320 				case PromptStepType.MODEL_PICKLIST:
321 					Part picklistPart = PartsFactory
322 							.getPart(eachStep
323 									.getStepAttribute(StepAttributes.ATTRIBUTE_MODEL_FIELD));
324 					if (picklistPart != null && picklistPart.hasPicklist()) {
325 						result += writeDBObject(writer, picklistPart);
326 					}
327 					break;
328 	
329 				}
330 	
331 				// Write picklist -- it may or may not be associated with the node.
332 				if (WizStep.CUSTOM_PICKLIST == eachStep.getStepType()) {
333 					String attribType = eachStep
334 							.getStepAttribute(StepAttributes.ATTRIBUTE_PICKLIST_ID);
335 	
336 					List list = eachStep.getPicklistMenu(attribType);
337 					for (Iterator picklistIterator = list.iterator(); picklistIterator
338 							.hasNext();) {
339 						PickList picklist = (PickList) picklistIterator.next();
340 						result += writeDBObject(writer, picklist);
341 					}
342 				}
343 				
344 			} catch (DependentNodesNotTaggedException ex) {
345 				//Gather all the nodes not tagged and throw later
346 				//throw them in one giant exception.
347 				assert ex.getSingleDependentNode() != null;
348 				notTaggedNodes.add(ex.getSingleDependentNode());
349 			}
350 
351 		}
352 		
353 		if (notTaggedNodes.size() > 0) {
354 			throw new DependentNodesNotTaggedException(wizard, (Node[])notTaggedNodes.toArray(new Node[notTaggedNodes.size()]));
355 		}
356 
357 		result += exportDecisionMatrixForWizard(wizard, writer);
358 
359 		return result;
360 	}
361 	
362 	private void checkAllDecisionNodesPopulated(WizDefinition wizard) throws IncompleteDecisionMatrixException, DBException {
363 		 List pertinentSteps = wizard.getPertinentDecisionSteps();		
364         DecisionMatrix decisionMatrix = new DecisionMatrix(wizard.getId());
365         ObjectCombinator combinator = buildObjectCombinator(pertinentSteps);
366         for (Iterator i = combinator.iterator(); i.hasNext();) {
367         	ValidValue[] pickList = (ValidValue[]) i.next();
368         	
369         	//Load the picklist into a map.
370         	Map idToVVMap = new HashMap(pickList.length);
371         	for (int index = 0; index < pickList.length; index++) {
372                 ValidValue onePick = pickList[index];
373                 WizStep oneStep = (WizStep) pertinentSteps.get(index);
374                 idToVVMap.put(new Integer(oneStep.getFieldInt(WizStep.FLD_ID)), new Integer(onePick.getKey()));
375         	}
376             WizDecisionSet decisionSet = decisionMatrix.getDecision(idToVVMap);
377             if (decisionSet == null) {
378            	 throw new IncompleteDecisionMatrixException(wizard);
379             }
380         }
381 	}
382 	
383     /***
384      * Constructs an object combinator given a list of wizard steps that
385      * are pertinent to the current wizard.
386      *
387      * @param pertinentSteps List A list of WizSteps.
388      * @return ObjectCombinator constructed ObjectCombinator
389      */
390     private ObjectCombinator buildObjectCombinator(final List pertinentSteps) {
391         ValidValue[][] allPossibleValidValues = new ValidValue[pertinentSteps.size()][];
392         for (int i = 0; i < pertinentSteps.size(); i++) {
393             WizStep oneStep = (WizStep) pertinentSteps.get(i);
394             allPossibleValidValues[i] = oneStep.getMenuItems();
395         }
396 
397         return new ObjectCombinator(allPossibleValidValues, ValidValue.class);
398     }
399 	
400 
401 	/***
402 	 * Exports the decision matrix.
403 	 * 
404 	 * @param writer
405 	 * @param wizard
406 	 * @return
407 	 * @throws DBException
408 	 * @throws IncompleteDecisionMatrixException
409 	 */
410 	private int exportDecisionMatrixForWizard(WizDefinition wizard,
411 			PrintWriter printWriter) throws DBException,
412 			DependentNodesNotTaggedException, IncompleteDecisionMatrixException {
413 		int count = 0;
414 		WizDecisionSet decisionQuery = new WizDecisionSet(SuperUser.INSTANCE);
415 		decisionQuery.setWizardId(wizard.getId());
416 
417 		checkAllDecisionNodesPopulated(wizard);
418 		
419 		List allDecisions = decisionQuery.searchAndRetrieveList();
420 		List failedNodes  = new ArrayList();
421 
422 		for (Iterator decisionIterator = allDecisions.iterator(); decisionIterator
423 				.hasNext();) {
424 			
425 			WizDecisionSet eachDecision = (WizDecisionSet) decisionIterator
426 					.next();
427 			Node decisionNode = eachDecision.getDecisionNode();
428 			
429 	
430 			try {
431 				if (!isNodeTaggedForExport(decisionNode)) {
432 					throw new DependentNodesNotTaggedException(wizard, decisionNode);
433 				}
434 	
435 				count += writeDBObject(printWriter, eachDecision);
436 	
437 				List allDecisionEntities = eachDecision.getDecisionEntities();
438 				for (Iterator entityIterator = allDecisionEntities.iterator(); entityIterator
439 						.hasNext();) {
440 					DBObject eachDecisionEntity = (DBObject) entityIterator.next();
441 					count += writeDBObject(printWriter, eachDecisionEntity);
442 				}
443 			} catch (DependentNodesNotTaggedException ex) {
444 				//Grab all dependent node failures so we 
445 				//can get all of the missing nodes in one run
446 				//
447 				failedNodes.add(decisionNode);
448 			}
449 		}
450 		
451 		
452 		if (failedNodes.size() > 0) {
453 			//Throw a new node exception, this time with all the failed nodes.
454 			throw new DependentNodesNotTaggedException(wizard, (Node[])failedNodes.toArray(new Node[failedNodes.size()]));
455 		}
456 
457 		return count;
458 	}
459 
460 }