001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver.testdata;
006:
007: import junit.framework.TestCase;
008: import org.geotools.data.DataSourceException;
009: import org.geotools.data.DataStore;
010: import org.geotools.data.DataUtilities;
011: import org.geotools.data.property.PropertyDataStore;
012: import org.geotools.feature.FeatureType;
013: import org.geotools.feature.SchemaException;
014: import org.geotools.referencing.crs.DefaultGeographicCRS;
015: import org.geotools.styling.SLDParser;
016: import org.geotools.styling.Style;
017: import org.geotools.styling.StyleFactory;
018: import org.geotools.styling.StyleFactoryFinder;
019: import org.opengis.referencing.crs.CoordinateReferenceSystem;
020: import java.awt.Color;
021: import java.awt.Frame;
022: import java.awt.Graphics;
023: import java.awt.Panel;
024: import java.awt.event.WindowAdapter;
025: import java.awt.event.WindowEvent;
026: import java.awt.image.BufferedImage;
027: import java.io.File;
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.io.OutputStream;
031: import java.net.URL;
032: import java.util.Arrays;
033: import java.util.List;
034: import java.util.logging.Logger;
035:
036: /**
037: * <p>
038: * The properties files with CITE data are copied to the users' temp directory
039: * only the first time <code>getCiteDataStore()</code> is called, for each
040: * test run (i.e., they're not copied in <code>setUp()</code>).
041: * </p>
042: *
043: * @author Gabriel Roldan, Axios Engineering
044: * @version $Id: AbstractCiteDataTest.java 7746 2007-11-13 15:38:35Z aaime $
045: */
046: public abstract class AbstractCiteDataTest extends TestCase {
047: /** DOCUMENT ME! */
048: private static final Logger LOGGER = org.geotools.util.logging.Logging
049: .getLogger(AbstractCiteDataTest.class.getPackage()
050: .getName());
051:
052: /** featuretype name for CITE BasicPolygons features */
053: public static String BASIC_POLYGONS_TYPE = "BasicPolygons";
054:
055: /** featuretype name for CITE Bridges features */
056: public static String BRIDGES_TYPE = "Bridges";
057:
058: /** featuretype name for CITE Buildings features */
059: public static String BUILDINGS_TYPE = "Buildings";
060:
061: /** featuretype name for CITE Divided Routes features */
062: public static String DIVIDED_ROUTES_TYPE = "DividedRoutes";
063:
064: /** featuretype name for CITE Forests features */
065: public static String FORESTS_TYPE = "Forests";
066:
067: /** featuretype name for CITE Lakes features */
068: public static String LAKES_TYPE = "Lakes";
069:
070: /** featuretype name for CITE Map Neatliine features */
071: public static String MAP_NEATLINE_TYPE = "MapNeatline";
072:
073: /** featuretype name for CITE Named Places features */
074: public static String NAMED_PLACES_TYPE = "NamedPlaces";
075:
076: /** featuretype name for CITE Ponds features */
077: public static String PONDS_TYPE = "Ponds";
078:
079: /** featuretype name for CITE Road Segments features */
080: public static String ROAD_SEGMENTS_TYPE = "RoadSegments";
081:
082: /** featuretype name for CITE Streams features */
083: public static String STREAMS_TYPE = "Streams";
084:
085: /**
086: * used to create default styles for cite types loading them from
087: * test-data/styles/<cite-typename>.sld
088: */
089: private static final StyleFactory sFac = StyleFactoryFinder
090: .createStyleFactory();
091:
092: /** DOCUMENT ME! */
093: private static final int SHOW_TIMEOUT = 200;
094:
095: /** DOCUMENT ME! */
096: private static final boolean INTERACTIVE = true;
097:
098: /**
099: * Convenient array with all the CITE type names for dealing with copying
100: * and deleting files
101: */
102: public static String[] CITE_TYPE_NAMES = { BASIC_POLYGONS_TYPE,
103: BRIDGES_TYPE, BUILDINGS_TYPE, DIVIDED_ROUTES_TYPE,
104: FORESTS_TYPE, LAKES_TYPE, MAP_NEATLINE_TYPE,
105: NAMED_PLACES_TYPE, PONDS_TYPE, ROAD_SEGMENTS_TYPE,
106: STREAMS_TYPE };
107:
108: /**
109: * Since the PropertyDataStore does not provides CRS support, we force
110: * feature types to be in WGS84
111: */
112: static CoordinateReferenceSystem FORCED_WGS84 = DefaultGeographicCRS.WGS84;
113:
114: /** User temp dir, where to store .property files containing cite data */
115: private File tempDir;
116:
117: /** the DataStore instance that provides cite test data */
118: private PropertyDataStore propsDS;
119:
120: /**
121: * Creates a new CiteTestData object.
122: */
123: public AbstractCiteDataTest() {
124: super ("Cite Data based test case, using the propery datastore");
125: }
126:
127: /**
128: * does nothing but what super.setUp() does.
129: *
130: * @throws Exception should not do.
131: */
132: public void setUp() throws Exception {
133: super .setUp();
134: }
135:
136: /**
137: * deletes temporary files
138: *
139: * @throws Exception DOCUMENT ME!
140: */
141: public void tearDown() throws Exception {
142: deleteTempFiles();
143: super .tearDown();
144: }
145:
146: /**
147: * Returns a <code>DataStore</code> containing CITE feature types.
148: *
149: * @return a property files backed DataStore which forces all the
150: * FeatureTypes it serves to be in WGS84 CRS.
151: *
152: * @throws IOException DOCUMENT ME!
153: */
154: public DataStore getCiteDataStore() throws IOException {
155: if (this .propsDS == null) {
156: writeTempFiles();
157: this .propsDS = new ForceWGS84PropertyDataStore(this .tempDir);
158: assertContainsCiteTypes(this .propsDS);
159: }
160:
161: return this .propsDS;
162: }
163:
164: /**
165: * DOCUMENT ME!
166: *
167: * @param citeTypeName DOCUMENT ME!
168: *
169: * @return DOCUMENT ME!
170: *
171: * @throws Exception DOCUMENT ME!
172: */
173: protected Style getDefaultStyle(String citeTypeName)
174: throws Exception {
175: return getStyle(citeTypeName + ".sld");
176: }
177:
178: /**
179: * DOCUMENT ME!
180: *
181: * @param styleName DOCUMENT ME!
182: *
183: * @return DOCUMENT ME!
184: *
185: * @throws Exception DOCUMENT ME!
186: */
187: protected Style getStyle(String styleName) throws Exception {
188: SLDParser parser = new SLDParser(sFac);
189: URL styleRes = AbstractCiteDataTest.class
190: .getResource("test-data/styles/" + styleName);
191: parser.setInput(styleRes);
192:
193: Style s = parser.readXML()[0];
194:
195: return s;
196: }
197:
198: /**
199: * DOCUMENT ME!
200: *
201: * @param frameName DOCUMENT ME!
202: * @param image DOCUMENT ME!
203: */
204: protected void showImage(String frameName, final BufferedImage image) {
205: showImage(frameName, SHOW_TIMEOUT, image);
206: }
207:
208: /**
209: * Shows <code>image</code> in a Frame.
210: *
211: * @param frameName
212: * @param timeOut
213: * @param image
214: */
215: protected void showImage(String frameName, long timeOut,
216: final BufferedImage image) {
217: int width = image.getWidth();
218: int height = image.getHeight();
219:
220: if (((System.getProperty("java.awt.headless") == null) || !System
221: .getProperty("java.awt.headless").equals("true"))
222: && INTERACTIVE) {
223: Frame frame = new Frame(frameName);
224: frame.addWindowListener(new WindowAdapter() {
225: public void windowClosing(WindowEvent e) {
226: e.getWindow().dispose();
227: }
228: });
229:
230: Panel p = new Panel(null) { //no layout manager so it respects setSize
231: public void paint(Graphics g) {
232: g.drawImage(image, 0, 0, this );
233: }
234: };
235:
236: frame.add(p);
237: p.setSize(width, height);
238: frame.pack();
239: frame.setVisible(true);
240:
241: try {
242: Thread.sleep(timeOut);
243: } catch (InterruptedException e) {
244: e.printStackTrace();
245: }
246:
247: frame.dispose();
248: }
249: }
250:
251: /**
252: * Asserts that the image is not blank, in the sense that there must be
253: * pixels different from the passed background color.
254: *
255: * @param testName the name of the test to throw meaningfull messages if
256: * something goes wrong
257: * @param image the imgage to check it is not "blank"
258: * @param bgColor the background color for which differing pixels are
259: * looked for
260: */
261: protected void assertNotBlank(String testName, BufferedImage image,
262: Color bgColor) {
263: int pixelsDiffer = 0;
264:
265: for (int y = 0; y < image.getHeight(); y++) {
266: for (int x = 0; x < image.getWidth(); x++) {
267: if (image.getRGB(x, y) != bgColor.getRGB()) {
268: ++pixelsDiffer;
269: }
270: }
271: }
272:
273: LOGGER.info(testName + ": pixel count="
274: + (image.getWidth() * image.getHeight())
275: + " non bg pixels: " + pixelsDiffer);
276: assertTrue(testName + " image is comlpetely blank",
277: 0 < pixelsDiffer);
278: }
279:
280: /**
281: * Throws an assertion error if some of the CITE types are not contained in
282: * the passed DataStore.
283: *
284: * @param ds DOCUMENT ME!
285: *
286: * @throws IOException DOCUMENT ME!
287: */
288: private void assertContainsCiteTypes(DataStore ds)
289: throws IOException {
290: List typeNames = Arrays.asList(ds.getTypeNames());
291:
292: for (int i = 0; i < CITE_TYPE_NAMES.length; i++) {
293: assertTrue(CITE_TYPE_NAMES[i] + " not found", typeNames
294: .contains(CITE_TYPE_NAMES[i]));
295: }
296: }
297:
298: /**
299: * DOCUMENT ME!
300: *
301: * @throws IOException DOCUMENT ME!
302: */
303: private void writeTempFiles() throws IOException {
304: final File envTmpDir = new File(System
305: .getProperty("java.io.tmpdir"));
306:
307: this .tempDir = new File(envTmpDir, "cite_test_datastore");
308:
309: if (this .tempDir.exists()) {
310: this .tempDir.delete();
311: }
312:
313: this .tempDir.mkdir();
314:
315: if (!this .tempDir.exists() || !this .tempDir.isDirectory()) {
316: throw new IOException(this .tempDir.getAbsolutePath()
317: + " is not a writable directory");
318: }
319:
320: for (int i = 0; i < CITE_TYPE_NAMES.length; i++) {
321: writeTempFile(CITE_TYPE_NAMES[i]);
322: }
323: }
324:
325: /**
326: * Since it's called from inside tearDown, first checks that tempDir were
327: * created and if so, deletes the temporary files.
328: *
329: * @throws IOException DOCUMENT ME!
330: */
331: private void deleteTempFiles() throws IOException {
332: if (this .tempDir == null) {
333: return;
334: }
335:
336: for (int i = 0; i < CITE_TYPE_NAMES.length; i++) {
337: deleteTempFile(CITE_TYPE_NAMES[i]);
338: }
339: }
340:
341: /**
342: * DOCUMENT ME!
343: *
344: * @param typeName DOCUMENT ME!
345: *
346: * @throws IOException DOCUMENT ME!
347: * @throws NullPointerException DOCUMENT ME!
348: */
349: private void writeTempFile(final String typeName)
350: throws IOException {
351: final String fileName = typeName + ".properties";
352:
353: File outFile = new File(this .tempDir, fileName);
354:
355: //perhaps it was not deleted in a previous, broken run...
356: deleteTempFile(typeName);
357:
358: //Atomically creates a new, empty file named by this abstract
359: //pathname if and only if a file with this name does not yet exist.
360: outFile.createNewFile();
361:
362: // Request that the file or directory denoted by this abstract
363: // pathname be deleted when the virtual machine terminates.
364: outFile.deleteOnExit();
365:
366: String resourceName = "test-data/featureTypes/" + fileName;
367:
368: InputStream in = AbstractCiteDataTest.class
369: .getResourceAsStream(resourceName);
370:
371: if (in == null) {
372: throw new NullPointerException(resourceName
373: + " not found in classpath");
374: }
375:
376: OutputStream out = new java.io.FileOutputStream(outFile);
377: byte[] buff = new byte[512];
378: int count;
379:
380: while ((count = in.read(buff)) > -1) {
381: out.write(buff, 0, count);
382: }
383:
384: in.close();
385: out.flush();
386: out.close();
387: }
388:
389: /**
390: * DOCUMENT ME!
391: *
392: * @param typeName DOCUMENT ME!
393: */
394: private void deleteTempFile(String typeName) {
395: deleteTempFile(new File(this .tempDir, typeName + ".properties"));
396: }
397:
398: /**
399: * DOCUMENT ME!
400: *
401: * @param f DOCUMENT ME!
402: */
403: private void deleteTempFile(File f) {
404: f.delete();
405: }
406:
407: /**
408: * DOCUMENT ME!
409: *
410: * @author Gabriel Roldan, Axios Engineering
411: * @version $Id: AbstractCiteDataTest.java 7746 2007-11-13 15:38:35Z aaime $
412: */
413: static class ForceWGS84PropertyDataStore extends PropertyDataStore {
414: /**
415: * Creates a new ForceWGS84PropertyDataStore object.
416: *
417: * @param dir DOCUMENT ME!
418: */
419: public ForceWGS84PropertyDataStore(File dir) {
420: super (dir);
421: }
422:
423: /**
424: * DOCUMENT ME!
425: *
426: * @param typeName DOCUMENT ME!
427: *
428: * @return DOCUMENT ME!
429: *
430: * @throws IOException DOCUMENT ME!
431: * @throws DataSourceException DOCUMENT ME!
432: */
433: public FeatureType getSchema(String typeName)
434: throws IOException {
435: FeatureType schema = super .getSchema(typeName);
436:
437: try {
438: return DataUtilities.createSubType(schema, null,
439: FORCED_WGS84);
440: } catch (SchemaException e) {
441: throw new DataSourceException(e.getMessage(), e);
442: }
443: }
444:
445: /**
446: * DOCUMENT ME!
447: */
448:
449: /*
450: public FeatureReader getFeatureReader(Query query,
451: Transaction transaction) throws IOException {
452: FeatureReader reader = super.getFeatureReader(query, transaction);
453: try {
454: return new ForceCoordinateSystemFeatureReader(reader,
455: AbstractCiteDataTest.FORCED_WGS84);
456: } catch (SchemaException e) {
457: throw new DataSourceException(e.getMessage(), e);
458: }
459: }
460: */
461: }
462: }
|