View Javadoc

1   /*
2    * This file is a part of CAST project.
3    * (c) Copyright 2007, AGH University of Science & Technology
4    * https://caribou.iisg.agh.edu.pl/trac/cast
5    *
6    * Licensed under the Eclipse Public License, Version 1.0 (the "License").
7    * You may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * http://www.eclipse.org/legal/epl-v10.html
10   */
11  /*
12   * File: BrokenDataInfo.java
13   * Created: 2008-04-07
14   * Author: entrop, kpietak
15   * $Id: BrokenDataInfo.java 2331 2009-01-13 12:56:25Z kpietak $
16   */
17  
18  package pl.edu.agh.cast.rawdata.stat;
19  
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.log4j.Logger;
29  import org.eclipse.core.runtime.Assert;
30  
31  import pl.edu.agh.cast.model.base.BasePlugin;
32  import pl.edu.agh.cast.rawdata.DataRow;
33  import pl.edu.agh.cast.rawdata.TabularData;
34  import pl.edu.agh.cast.rawdata.logging.IRawDataMonitorable;
35  import pl.edu.agh.cast.rawdata.logging.IRawDataObserver;
36  
37  /**
38   * Class which represents information about broken data in TabularData object. It contains list of DataRow objects
39   * mapped to corresponding BrokenRowInfo objects.
40   * 
41   * It contains statistics after data validation: all rows number, errors number, error rows number, etc. It is returned
42   * as a result of <code>DataLoaderWithStatistics.validate()</code> method.
43   * 
44   * @author AGH CAST Team
45   * 
46   *         TODO: add handling broken cell list
47   */
48  public class BrokenDataInfo implements IRawDataMonitorable {
49  
50  	private static Logger log = BasePlugin.getLogger();
51  
52  	private Map<DataRow, BrokenRowInfo> brokenRows;
53  
54  	private TabularData inputData;
55  
56  	// used for performance improvements
57  	private List<BrokenCellInfo> brokenCells;
58  
59  	private Map<ErrorType, List<BrokenCellInfo>> errorTypeToBrokenCells;
60  
61  	// removed data
62  	private Map<Integer, BrokenRowInfo> rowNumberToRemovedRowInfo;
63  
64  	private Map<Integer, DataRow> rowNumberToRemovedDataRow;
65  
66  	// observers
67  	private List<IRawDataObserver> observers;
68  
69  	/**
70  	 * Default constructor. Creates an instance associated with specified TabularData object.
71  	 * 
72  	 * @param inputData
73  	 *            TabularData which will be described by created object.
74  	 */
75  	public BrokenDataInfo(TabularData inputData) {
76  		this.brokenRows = new HashMap<DataRow, BrokenRowInfo>();
77  		this.brokenCells = new LinkedList<BrokenCellInfo>();
78  		this.errorTypeToBrokenCells = new HashMap<ErrorType, List<BrokenCellInfo>>();
79  		this.inputData = inputData;
80  		this.observers = new LinkedList<IRawDataObserver>();
81  
82  		rowNumberToRemovedRowInfo = new HashMap<Integer, BrokenRowInfo>();
83  		rowNumberToRemovedDataRow = new HashMap<Integer, DataRow>();
84  	}
85  
86  	/**
87  	 * Adds information about broken row in TabularData. Updates cache information about cell which contains errors or
88  	 * warnings & registers observer on source row.
89  	 * 
90  	 * @param brokenRowInfo
91  	 *            information about broken row to be added to data info object
92  	 */
93  	public void addBrokenRowInfo(BrokenRowInfo brokenRowInfo) {
94  		brokenRows.put(brokenRowInfo.getSourceRow(), brokenRowInfo);
95  		List<BrokenCellInfo> cells = brokenRowInfo.getBrokenCells();
96  		for (BrokenCellInfo cell : cells) {
97  			brokenCells.add(cell);
98  			ErrorType errorType = cell.getErrorInfo().getType();
99  			if (!errorTypeToBrokenCells.containsKey(errorType)) {
100 				errorTypeToBrokenCells.put(errorType, new LinkedList<BrokenCellInfo>());
101 			}
102 			errorTypeToBrokenCells.get(errorType).add(cell);
103 		}
104 
105 		// register observers
106 		for (IRawDataObserver observer : observers) {
107 			brokenRowInfo.getSourceRow().registerObserver(observer);
108 		}
109 	}
110 
111 	/**
112 	 * Removed the specified row. If row has associated BrokenRowInfo object, method <code>removeBrokenRowInfo</code> is
113 	 * called, otherwise row is removed straightforward from data. The deleted row is cached and can be restored using
114 	 * method <code>restoreDataRow</code>
115 	 * 
116 	 * @param row
117 	 *            row to be deleted
118 	 * @return broken row info associated with row or null if no such object
119 	 */
120 	public BrokenRowInfo removeDataRow(DataRow row) {
121 		BrokenRowInfo brokenRowInfo = getBrokenRowInfo(row);
122 		if (brokenRowInfo != null) { // row contains errors or warnings
123 			return removeBrokenRowInfo(brokenRowInfo, true);
124 		}
125 		// remove correct row
126 		inputData.removeRow(row);
127 		rowNumberToRemovedDataRow.put(row.getRowNumber(), row);
128 
129 		return null;
130 
131 	}
132 
133 	/**
134 	 * Returns list of <code>BrokenCellInfo</code> related to input row.
135 	 * 
136 	 * @param row
137 	 *            data row
138 	 * @return list of BrokenCellInfo objects related to the specified row.
139 	 */
140 	public List<BrokenCellInfo> getBrokenCellsRelatedTo(DataRow row) {
141 		List<BrokenCellInfo> relatedRows = new ArrayList<BrokenCellInfo>();
142 		for (BrokenCellInfo cellInfo : brokenCells) {
143 			DataRow related = cellInfo.getErrorInfo().getRelatedRow();
144 			if (related == row) {
145 				relatedRows.add(cellInfo);
146 			}
147 		}
148 
149 		return relatedRows;
150 	}
151 
152 	/**
153 	 * Removes row specified by specified <code>BrokenRowInfo</code> object. The source object is deleted from
154 	 * TabularData. The broken row info object is cached for undo action.
155 	 * 
156 	 * @param brokenRowInfo
157 	 *            broken row info object which indicated which row should be deleted.
158 	 * 
159 	 */
160 	public void removeBrokenRowInfo(final BrokenRowInfo brokenRowInfo) {
161 		removeBrokenRowInfo(brokenRowInfo, true);
162 	}
163 
164 	private BrokenRowInfo removeBrokenRowInfo(final BrokenRowInfo brokenRowInfo, boolean deleteSourceRow) {
165 		if (brokenRowInfo != null) {
166 			if (deleteSourceRow) {
167 				// remove data row
168 				inputData.removeRow(brokenRowInfo.getSourceRow());
169 			}
170 			// remove broken row info
171 			for (BrokenCellInfo brokenCellInfo : brokenRowInfo.getBrokenCells()) {
172 				brokenCells.remove(brokenCellInfo);
173 				errorTypeToBrokenCells.get(brokenCellInfo.getErrorInfo().getType()).remove(brokenCellInfo);
174 
175 			}
176 			brokenRows.remove(brokenRowInfo.getSourceRow());
177 
178 			// save in cache (for restoring)
179 			rowNumberToRemovedRowInfo.put(new Integer(brokenRowInfo.getSourceRow().getRowNumber()), brokenRowInfo);
180 		}
181 		return brokenRowInfo;
182 
183 	}
184 
185 	/**
186 	 * Restores row with specified row number. The restored row is placed at the beginning of rows in tabular data.
187 	 * 
188 	 * @param rowNumber
189 	 *            row number to be restored
190 	 * @return restored broken row info, null if no row has been restored or if restored row was without broken row
191 	 *         info.
192 	 */
193 	public BrokenRowInfo restoreDataRow(int rowNumber) {
194 		Integer rowNumberL = new Integer(rowNumber);
195 		BrokenRowInfo res = rowNumberToRemovedRowInfo.get(rowNumberL);
196 
197 		if (res != null) {
198 			// add source row
199 			inputData.restoreAsFirst(res.getSourceRow());
200 			addBrokenRowInfo(res);
201 			rowNumberToRemovedRowInfo.remove(rowNumberL);
202 		} else {
203 			DataRow row = rowNumberToRemovedDataRow.remove(rowNumberL);
204 			if (row != null) {
205 				inputData.restoreAsFirst(row);
206 			}
207 		}
208 
209 		return res;
210 	}
211 
212 	/**
213 	 * Replaces information about data row which is in newValue object. Updates statistics.
214 	 * 
215 	 * @param row
216 	 *            row with errors or warnings which broken info will be replaced
217 	 * @param newValue
218 	 *            value new broken row info; if <code>null</code> the method is equal to
219 	 *            <code>removeBrokenRowInfo(_brokenRows.get(row))</code>
220 	 */
221 	public void replaceBrokenRowInfo(DataRow row, BrokenRowInfo newValue) {
222 		removeBrokenRowInfo(brokenRows.get(row), false);
223 		if (newValue != null) {
224 			addBrokenRowInfo(newValue);
225 		}
226 	}
227 
228 	/**
229 	 * Returns list of all broken cells. Doesn't omit cells which row is marked as deleted.
230 	 * 
231 	 * @return unmodifiable list of broken cells
232 	 */
233 	public List<BrokenCellInfo> getBrokenCells() {
234 		return Collections.unmodifiableList(brokenCells);
235 	}
236 
237 	/**
238 	 * Returns broken row info associated with the specified row.
239 	 * 
240 	 * @param row
241 	 *            data row object
242 	 * @return broken row info associated with the specified row
243 	 */
244 	public BrokenRowInfo getBrokenRowInfo(DataRow row) {
245 		return brokenRows.get(row);
246 	}
247 
248 	/**
249 	 * Gets returns type mapped to number of occurrences in associated TabularData object.
250 	 * 
251 	 * @return map which contains error types with number of occurrences
252 	 */
253 	public Map<ErrorType, Integer> getErrorTypesWithOccurrences() {
254 		Map<ErrorType, Integer> res = new HashMap<ErrorType, Integer>();
255 		Set<ErrorType> keySet = errorTypeToBrokenCells.keySet();
256 
257 		for (ErrorType key : keySet) {
258 			res.put(key, getErrorsCountByType(key));
259 		}
260 
261 		return res;
262 	}
263 
264 	/**
265 	 * Returns list of <code>BrokenCellInfo</code> objects which are not removed.
266 	 * 
267 	 * @param errorType
268 	 *            type of error
269 	 * @return list of broken cell info objects
270 	 */
271 	public List<BrokenCellInfo> getBrokenCellsByType(ErrorType errorType) {
272 		return errorTypeToBrokenCells.get(errorType);
273 	}
274 
275 	public int getErrorsCount() {
276 		return getBrokenCellsCountBySeverity(ErrorSeverity.ERROR);
277 	}
278 
279 	public int getWarningsCount() {
280 		return getBrokenCellsCountBySeverity(ErrorSeverity.WARNING);
281 	}
282 
283 	public int getBrokenRowsCount() {
284 		return brokenRows.values().size();
285 	}
286 
287 	public int getRowsCount() {
288 		return inputData.getAllRows().size();
289 	}
290 
291 	/**
292 	 * Gets errors count for given error type
293 	 * 
294 	 * @param errorType
295 	 *            error type
296 	 * @return number of occurrences of given error type
297 	 */
298 	int getErrorsCountByType(ErrorType errorType) {
299 		List<BrokenCellInfo> cells = errorTypeToBrokenCells.get(errorType);
300 		if (cells != null) {
301 			return cells.size();
302 		}
303 		return 0;
304 	}
305 
306 	private int getBrokenCellsCountBySeverity(ErrorSeverity severity) {
307 		int res = 0;
308 		for (ErrorType errorType : errorTypeToBrokenCells.keySet()) {
309 			if (errorType.getSeverity().equals(severity)) {
310 				res += errorTypeToBrokenCells.get(errorType).size();
311 			}
312 		}
313 		return res;
314 	}
315 
316 	/**
317 	 * 
318 	 * {@inheritDoc}
319 	 * 
320 	 * @see pl.edu.agh.cast.rawdata.logging.IRawDataMonitorable#
321 	 *      registerObserver(pl.edu.agh.cast.rawdata.logging.IRawDataObserver)
322 	 * 
323 	 */
324 	public void registerObserver(IRawDataObserver observer) {
325 		Assert.isNotNull(observer, "Cannot register null value register"); //$NON-NLS-1$
326 		if (!observers.contains(observer)) {
327 			observers.add(observer);
328 			log.debug("The following observer has been registered: " //$NON-NLS-1$
329 			        + observer.toString());
330 
331 			Set<DataRow> rows = brokenRows.keySet();
332 
333 			for (DataRow row : rows) {
334 				row.registerObserver(observer);
335 			}
336 		}
337 	}
338 
339 	/**
340 	 * 
341 	 * {@inheritDoc}
342 	 * 
343 	 * @see pl.edu.agh.cast.rawdata.logging.IRawDataMonitorable#
344 	 *      unregisterObserver(pl.edu.agh.cast.rawdata.logging.IRawDataObserver)
345 	 */
346 	public void unregisterObserver(IRawDataObserver observer) {
347 		Assert.isNotNull(observer, "Cannot unregister null value register"); //$NON-NLS-1$
348 		if (observers.contains(observer)) {
349 			observers.remove(observer);
350 			Set<DataRow> rows = brokenRows.keySet();
351 
352 			for (DataRow row : rows) {
353 				row.unregisterObserver(observer);
354 			}
355 
356 			log.debug("The following observer has been unregistered: " //$NON-NLS-1$
357 			        + observer.toString());
358 		}
359 
360 	}
361 
362 }