001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.feature.visitor;
017:
018: import org.geotools.factory.CommonFactoryFinder;
019: import org.geotools.factory.GeoTools;
020: import org.geotools.factory.Hints;
021: import org.geotools.feature.AttributeType;
022: import org.geotools.feature.Feature;
023: import org.geotools.feature.FeatureType;
024: import org.geotools.filter.IllegalFilterException;
025: import org.opengis.filter.FilterFactory;
026: import org.opengis.filter.expression.Expression;
027:
028: /**
029: * Calculates the Average
030: *
031: * @author Cory Horner, Refractions
032: *
033: * @since 2.2.M2
034: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/feature/visitor/AverageVisitor.java $
035: */
036: public class AverageVisitor implements FeatureCalc {
037: private Expression expr;
038:
039: /**
040: * This flag lets us know that an optimized result was stored and therefore
041: * we don't have enough data to perform any merges.
042: */
043: private boolean isOptimized = false;
044: AverageStrategy strategy;
045:
046: /**
047: * Constructor class for the AverageVisitor using AttributeType ID
048: *
049: * @param attributeTypeIndex integer representing the AttributeType
050: * @param type FeatureType
051: *
052: * @throws IllegalFilterException
053: */
054: public AverageVisitor(int attributeTypeIndex, FeatureType type)
055: throws IllegalFilterException {
056: FilterFactory factory = CommonFactoryFinder
057: .getFilterFactory(null);
058: AttributeType attributeType = type
059: .getAttributeType(attributeTypeIndex);
060: expr = factory.property(attributeType.getName());
061: createStrategy(attributeType.getType());
062: }
063:
064: /**
065: * Constructor class for the AverageVisitor using AttributeType Name
066: *
067: * @param attrName string respresenting the AttributeType
068: * @param type FeatureType
069: *
070: * @throws IllegalFilterException
071: */
072: public AverageVisitor(String attrName, FeatureType type)
073: throws IllegalFilterException {
074: FilterFactory factory = CommonFactoryFinder
075: .getFilterFactory(null);
076: AttributeType attributeType = type.getAttributeType(attrName);
077: expr = factory.property(attributeType.getName());
078: createStrategy(attributeType.getType());
079: }
080:
081: /**
082: * Constructor class for the AverageVisitor using an expression
083: *
084: * @param expr
085: *
086: * @throws IllegalFilterException
087: */
088: public AverageVisitor(Expression expr)
089: throws IllegalFilterException {
090: this .expr = expr;
091: }
092:
093: /**
094: * Factory method for creating the appropriate strategy object
095: *
096: * <P></p>
097: *
098: * @param type the class to use for the calculation
099: *
100: * @return a strategy object of the correct data type to be used for
101: * calculating the average
102: */
103: private static AverageStrategy createStrategy(Class type) {
104: if (type == Integer.class) {
105: return new IntegerAverageStrategy();
106: } else if (type == Double.class) {
107: return new DoubleAverageStrategy();
108: } else if (type == Long.class) {
109: return new LongAverageStrategy();
110: } else if (type == Float.class) {
111: return new FloatAverageStrategy();
112: }
113:
114: return null;
115: }
116:
117: public void visit(Feature feature) {
118: Object value = expr.evaluate(feature);
119:
120: if (strategy == null) {
121: Class type = value.getClass();
122: strategy = createStrategy(type);
123: }
124:
125: strategy.add(value);
126: }
127:
128: public Expression getExpression() {
129: return expr;
130: }
131:
132: /**
133: * Returns the average from the visitor's current
134: *
135: * @return the average
136: */
137: public Object getAverage() {
138: return strategy.getResult();
139: }
140:
141: /**
142: * Resets the "Average" strategy pattern
143: */
144: public void reset() {
145: strategy = null;
146: isOptimized = false;
147: }
148:
149: /**
150: * Returns a CalcResult object (containing the Average)
151: *
152: */
153: public CalcResult getResult() {
154: return new AverageResult(strategy, isOptimized);
155: }
156:
157: public void setValue(Object newAverage) {
158: reset();
159:
160: Class type = newAverage.getClass();
161: strategy = createStrategy(type);
162: strategy.add(newAverage);
163: isOptimized = true;
164: }
165:
166: public void setValue(int newCount, Object newSum) {
167: reset();
168: strategy = createStrategy(newSum.getClass());
169: strategy.set(newCount, newSum);
170: isOptimized = false; // this is an optimization, but we have the same
171:
172: // information we would otherwise have if we had
173: // in fact visited each feature
174: }
175:
176: /**
177: * Encapsulates the strategy pattern for the "Average" Visitor
178: */
179: interface AverageStrategy {
180: public void add(Object value);
181:
182: public Object getResult();
183:
184: public Object getSum();
185:
186: public int getCount();
187:
188: public void set(int count, Object sum);
189: }
190:
191: /**
192: * Implements the average calculation for values of the type double
193: */
194: static class DoubleAverageStrategy implements AverageStrategy {
195: double number = 0;
196: int count = 0;
197:
198: public void add(Object value) {
199: number += ((Number) value).doubleValue();
200: count++;
201: }
202:
203: public Object getResult() {
204: return new Double(number / count);
205: }
206:
207: public Object getSum() {
208: return new Double(number);
209: }
210:
211: public int getCount() {
212: return count;
213: }
214:
215: public void set(int newCount, Object sum) {
216: number = ((Number) sum).doubleValue();
217: count = newCount;
218: }
219: }
220:
221: /**
222: * Implements the average calculation for values of the type float
223: */
224: static class FloatAverageStrategy implements AverageStrategy {
225: float number = 0;
226: int count = 0;
227:
228: public void add(Object value) {
229: number += ((Number) value).floatValue();
230: count++;
231: }
232:
233: public Object getResult() {
234: return new Float((float) number / count);
235: }
236:
237: public Object getSum() {
238: return new Float(number);
239: }
240:
241: public int getCount() {
242: return count;
243: }
244:
245: public void set(int newCount, Object sum) {
246: number = ((Number) sum).floatValue();
247: count = newCount;
248: }
249: }
250:
251: /**
252: * Implements the average calculation for values of the type long
253: */
254: static class LongAverageStrategy implements AverageStrategy {
255: long number = 0;
256: int count = 0;
257:
258: public void add(Object value) {
259: number += ((Number) value).longValue();
260: count++;
261: }
262:
263: public Object getResult() {
264: return new Double((double) number / count);
265: }
266:
267: public Object getSum() {
268: return new Long(number);
269: }
270:
271: public int getCount() {
272: return count;
273: }
274:
275: public void set(int newCount, Object sum) {
276: number = ((Number) sum).longValue();
277: count = newCount;
278: }
279: }
280:
281: /**
282: * Implements the average calculation for values of the type integer
283: */
284: static class IntegerAverageStrategy implements AverageStrategy {
285: int number = 0;
286: int count = 0;
287:
288: public void add(Object value) {
289: number += ((Number) value).intValue();
290: count++;
291: }
292:
293: public Object getResult() {
294: return new Double((double) number / count);
295: }
296:
297: public Object getSum() {
298: return new Integer(number);
299: }
300:
301: public int getCount() {
302: return count;
303: }
304:
305: public void set(int newCount, Object sum) {
306: number = ((Number) sum).intValue();
307: count = newCount;
308: }
309: }
310:
311: /**
312: *
313: */
314: public static class AverageResult extends AbstractCalcResult {
315: private AverageStrategy averageStrategy;
316: private boolean isOptimized = false;
317:
318: public AverageResult(Object newAverageStrategy) {
319: averageStrategy = (AverageStrategy) newAverageStrategy;
320: }
321:
322: public AverageResult(Object newAverageStrategy,
323: boolean isOptimized) {
324: averageStrategy = (AverageStrategy) newAverageStrategy;
325: this .isOptimized = isOptimized;
326: }
327:
328: public AverageResult(int newCount, Object newSum) {
329: averageStrategy = createStrategy(newSum.getClass());
330: averageStrategy.set(newCount, newSum);
331: }
332:
333: public Object getResult() {
334: return averageStrategy.getResult();
335: }
336:
337: public Object getValue() {
338: return averageStrategy.getResult();
339: }
340:
341: /**
342: * The count used to calculate the average
343: *
344: * @return the count, or -1 if unknown
345: */
346: public int getCount() {
347: if (isOptimized) {
348: return -1; //there is no count, as an optimization was used.
349: } else {
350: return averageStrategy.getCount();
351: }
352: }
353:
354: /**
355: * The sum used to calculate the average
356: *
357: */
358: public Object getSum() {
359: if (isOptimized) {
360: return null;
361: } else {
362: return averageStrategy.getSum();
363: }
364: }
365:
366: /**
367: * Determines if the target CalcResult object can be merged with this
368: * CalcResult object
369: *
370: * @param targetResults a second CalcResult object (target)
371: *
372: * @return boolean
373: */
374: public boolean isCompatible(CalcResult targetResults) {
375: if (targetResults instanceof AverageResult) {
376: return true;
377: } else {
378: return false;
379: }
380: }
381:
382: /**
383: * Merges the contents of a CalcResult Object with another CalcResult
384: * Object. If the two CalcResult objects are compatible, the merged
385: * result (a new object) is returned.
386: *
387: * @param resultsToAdd the CalcResult to merge the results with
388: *
389: * @return a new merged CalcResult object
390: *
391: * @throws IllegalArgumentException when the resultsToAdd are
392: * inappropriate
393: */
394: public CalcResult merge(CalcResult resultsToAdd) {
395: if (!isCompatible(resultsToAdd)) {
396: throw new IllegalArgumentException(
397: "Parameter is not a compatible type");
398: }
399:
400: if (resultsToAdd instanceof AverageResult) {
401: AverageResult moreResults = (AverageResult) resultsToAdd;
402:
403: //ensure both results are NOT optimized
404: if (isOptimized || moreResults.isOptimized) {
405: throw new IllegalArgumentException(
406: "Optimized average results cannot be merged.");
407: }
408:
409: Number[] sums = new Number[] {
410: (Number) averageStrategy.getSum(),
411: (Number) moreResults.averageStrategy.getSum() };
412: Number newSum = CalcUtil.sum(sums);
413: Number newCount = (Number) new Integer(averageStrategy
414: .getCount()
415: + moreResults.averageStrategy.getCount());
416: Number[] params = new Number[] { newSum, newCount };
417: Object newAverage = CalcUtil.getObject(params);
418: AverageStrategy newAverageObj;
419: newAverageObj = createStrategy(newAverage.getClass());
420: newAverageObj.set(newCount.intValue(), newSum);
421:
422: return new AverageResult(newAverageObj);
423: } else {
424: throw new IllegalArgumentException(
425: "The CalcResults claim to be compatible, but the appropriate merge method has not been implemented.");
426: }
427: }
428: }
429: }
|