View Javadoc
1   package org.kuali.ole.sys.batch;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Stack;
9   
10  import org.apache.commons.beanutils.PropertyUtils;
11  import org.apache.log4j.Logger;
12  
13  /**
14   * Class which tracks the current state of parsing - particularly which object should currently be parsed into
15   */
16  public class FlatFileParseTrackerImpl implements FlatFileParseTracker {
17  	static Logger LOG = Logger.getLogger(FlatFileParseTrackerImpl.class);
18  	protected FlatFileSpecification classIdentifier;
19  	protected Stack<Object> parseStack;
20  	protected List<Object> parsedParentObjects;
21  	protected Map<Class<?>, FlatFileChildMapEntry> childrenMap;
22  	protected int completedLineCount = 0;
23  	
24  	/**
25  	 * Initializes a new FlatFileParseTracker
26  	 * @param flatFileSpecification the FlatFileSpecificationBase instance which will determine which object should be instantiated for a given line
27  	 * @param specifications the specifications for all objects that will be parsed into, to build a parent/child map out of
28  	 */
29  	public void initialize(FlatFileSpecification flatFileClassIdentifier) {
30  		this.classIdentifier = flatFileClassIdentifier;
31  		this.parseStack = new Stack<Object>();
32  		this.parsedParentObjects = new ArrayList<Object>();
33  		constructChildrenMap();
34  	}
35  	
36  	/**
37  	 * Builds a parent/child map out of the given specifications
38  	 * @param specifications the specifications for the parse
39  	 */
40  	protected void constructChildrenMap() {
41  		childrenMap = new HashMap<Class<?>, FlatFileChildMapEntry>();
42  		
43  		for (FlatFileObjectSpecification specification : classIdentifier.getObjectSpecifications()) {
44  			if (specification.getParentBusinessObjectClass() != null) {
45  				final FlatFileChildMapEntry entry = new FlatFileChildMapEntry(specification.getParentBusinessObjectClass(), specification.getParentTargetProperty());
46  				childrenMap.put(specification.getBusinessObjectClass(), entry);
47  			}
48  		}
49  	}
50  	
51  	/**
52  	 * Determines which class should be parsed into and returns an instance of that
53  	 * @param lineToParse the line which is going to be parsed
54  	 * @return the object to parse into
55  	 */
56  	public Object getObjectToParseInto(String lineToParse) {
57  		final Class<?> lineClass = classIdentifier.determineClassForLine(lineToParse);
58  		
59  		if (lineClass == null) {
60  			// the prefix was insignificant; skip it
61  			return null;
62  		}
63  		
64  		try {
65  			Object parseIntoObject = lineClass.newInstance();
66  			parseStack.push(parseIntoObject);			
67  			return parseIntoObject;
68  		} catch (InstantiationException ie) {
69  			throw new RuntimeException("Could not instantiate object of class "+lineClass.getName()+" in FlatFileParse", ie);
70  		} catch (IllegalAccessException iae) {
71  			throw new RuntimeException("Illegal access attempting to instantiate object of class "+lineClass.getName()+" in FlatFileParse", iae);
72  		}
73  	}
74  	
75  	/**
76  	 * Called when a line has completed parsing. Throws an exception if a proper parent 
77  	 * is not found for the line being parsed
78  	 */
79  	@SuppressWarnings("unchecked")
80  	public void completeLineParse() {
81  		completedLineCount += 1;
82  		if (LOG.isDebugEnabled()) {
83  			LOG.debug("Completing parse of line: "+completedLineCount);
84  		}
85  		
86  		Object currentObject = parseStack.pop();
87  		final FlatFileChildMapEntry entry = getEntryForParsedIntoObject(currentObject);
88  		
89  		final Class<?> parentClass = (entry == null) ? null : entry.getParentBeanClass();
90  		final String propertyName = (entry == null) ? null : entry.getPropertyName();
91  		
92  		while (!parseStack.isEmpty()) {
93  			Object checkingObject = parseStack.pop();
94  			if (parentClass != null && parentClass.isAssignableFrom(checkingObject.getClass())) {
95  				try {
96  					if (Collection.class.isAssignableFrom(PropertyUtils.getPropertyType(
97  							checkingObject, propertyName))) {
98  						Collection childrenList = ((Collection) PropertyUtils.getProperty(
99  								checkingObject, propertyName));
100 						childrenList.add(currentObject);
101 					} else {
102 						PropertyUtils.setProperty(checkingObject, propertyName,currentObject);
103 					}
104 					parseStack.push(checkingObject);
105 					parseStack.push(currentObject);
106 					return;
107 				} catch (Exception e) {
108 					LOG.error(e.getMessage() + "occured when completing line parse; attempting to set object of type "+currentObject.getClass().getName()+" to the following parent: "+parentClass.getName()+"#"+propertyName, e);
109 					throw new RuntimeException(e.getMessage() + "occured when completing line parse; attempting to set object of type "+currentObject.getClass().getName()+" to the following parent: "+parentClass.getName()+"#"+propertyName,e);
110 				}
111 			}		
112 		}
113 		if (parentClass == null) {
114 			parseStack.push(currentObject);	
115 			parsedParentObjects.add(currentObject);
116 		} else {
117 			throw new IllegalStateException("A line of class "+currentObject.getClass().getName()+" cannot exist without a proper parent");      			
118 		}
119 	}
120 	
121 	/**
122 	 * Looks up the FlatFileChildMapEntry for the given object
123 	 * @param parsedIntoObject the object which has just completed being parsed into
124 	 * @return the FlatFileChildMapEntry which has the given object as a child object, or null if the object is a base object
125 	 */
126     public FlatFileChildMapEntry getEntryForParsedIntoObject(Object parsedIntoObject) {
127     	final FlatFileChildMapEntry entry = childrenMap.get(parsedIntoObject.getClass());
128     	return entry;
129     }
130 
131 	/**
132 	 * @return the List of parsed parent objects
133 	 */
134 	public List<Object> getParsedObjects() {
135 		return parsedParentObjects;
136 	}
137 	
138 	/**
139 	 * Inner class to make holding parent/child relationships easier
140 	 *
141 	 */
142 	private class FlatFileChildMapEntry {
143 		 protected Class<?> parentBeanClass;
144 		 protected String propertyName;
145 		
146 		 public FlatFileChildMapEntry() {
147 			 super();
148 		 }
149 		
150 		 public FlatFileChildMapEntry(Class<?> parentBeanClass, String propertyName) {
151 			 this();
152 			 this.parentBeanClass = parentBeanClass;
153 			 this.propertyName = propertyName;
154 		 }
155 		 
156 		 public Class<?> getParentBeanClass() {
157 			 return parentBeanClass;
158 		 }
159 
160 		 public String getPropertyName() {
161 			 return propertyName;
162 		 }
163 	}
164 }