Source Code Cross Referenced for MIFFile.java in  » GIS » GeoTools-2.4.1 » org » geotools » data » mif » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.data.mif 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2005-2006, GeoTools Project Managment Committee (PMC)
0005:         * 
0006:         *    This library is free software; you can redistribute it and/or
0007:         *    modify it under the terms of the GNU Lesser General Public
0008:         *    License as published by the Free Software Foundation;
0009:         *    version 2.1 of the License.
0010:         *
0011:         *    This library is distributed in the hope that it will be useful,
0012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014:         *    Lesser General Public License for more details.
0015:         */
0016:        package org.geotools.data.mif;
0017:
0018:        import com.vividsolutions.jts.geom.Coordinate;
0019:        import com.vividsolutions.jts.geom.Geometry;
0020:        import com.vividsolutions.jts.geom.GeometryFactory;
0021:        import com.vividsolutions.jts.geom.LineString;
0022:        import com.vividsolutions.jts.geom.LinearRing;
0023:        import com.vividsolutions.jts.geom.MultiLineString;
0024:        import com.vividsolutions.jts.geom.MultiPolygon;
0025:        import com.vividsolutions.jts.geom.Point;
0026:        import com.vividsolutions.jts.geom.Polygon;
0027:        import com.vividsolutions.jts.geom.PrecisionModel;
0028:        import com.vividsolutions.jts.geom.TopologyException;
0029:        import com.vividsolutions.jts.io.ParseException;
0030:        import org.geotools.data.FeatureReader;
0031:        import org.geotools.data.FeatureWriter;
0032:        import org.geotools.feature.AttributeType;
0033:        import org.geotools.feature.AttributeTypeFactory;
0034:        import org.geotools.feature.AttributeTypes;
0035:        import org.geotools.feature.Feature;
0036:        import org.geotools.feature.FeatureType;
0037:        import org.geotools.feature.FeatureTypeBuilder;
0038:        import org.geotools.feature.FeatureTypes;
0039:        import org.geotools.feature.SchemaException;
0040:        import java.io.BufferedReader;
0041:        import java.io.File;
0042:        import java.io.FileInputStream;
0043:        import java.io.FileNotFoundException;
0044:        import java.io.FileOutputStream;
0045:        import java.io.FileReader;
0046:        import java.io.IOException;
0047:        import java.io.PrintStream;
0048:        import java.net.URI;
0049:        import java.nio.channels.FileChannel;
0050:        import java.text.SimpleDateFormat;
0051:        import java.util.Date; // TODO use java.sql.Date?
0052:        import java.util.HashMap;
0053:        import java.util.Map;
0054:        import java.util.NoSuchElementException;
0055:        import java.util.Vector;
0056:        import java.util.logging.Logger;
0057:
0058:        /**
0059:         * <p>
0060:         * MIFFile class allows sequential reading and writing of Features in MapInfo
0061:         * MIF/MID text file format with a FeatureReader and FeatureWriter.
0062:         * </p>
0063:         * 
0064:         * <p>
0065:         * This class has been developed starting from MapInfoDataSource.
0066:         * </p>
0067:         * 
0068:         * <p>
0069:         * Open issues:
0070:         * </p>
0071:         * 
0072:         * <ul>
0073:         * <li>
0074:         * CoordSys clause parsing is still not supported
0075:         * </li>
0076:         * </ul>
0077:         * 
0078:         *
0079:         * @author Luca S. Percich, AMA-MI
0080:         * @author Paolo Rizzi, AMA-MI
0081:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/mif/src/main/java/org/geotools/data/mif/MIFFile.java $
0082:         * @version $Id: MIFFile.java 27862 2007-11-12 19:51:19Z desruisseaux $
0083:         */
0084:        public class MIFFile {
0085:            // Geometry type identifier constants 
0086:            private static final String TYPE_NONE = "none";
0087:            private static final String TYPE_POINT = "point";
0088:            private static final String TYPE_LINE = "line";
0089:            private static final String TYPE_PLINE = "pline";
0090:            private static final String TYPE_REGION = "region";
0091:            private static final String TYPE_TEXT = "text";
0092:
0093:            // The following object types are still not supported
0094:            private static final String TYPE_ARC = "arc";
0095:            private static final String TYPE_RECT = "rect"; // could be converted to polygon
0096:            private static final String TYPE_ROUNDRECT = "roundrect";
0097:            private static final String TYPE_ELLIPSE = "ellipse";
0098:
0099:            // New types introduced after version 6.0, still not supported
0100:            private static final String TYPE_MULTIPOINT = "multipoint";
0101:            private static final String TYPE_COLLECTION = "collection";
0102:
0103:            // String Style Constants  
0104:            private static final String CLAUSE_SYMBOL = "symbol";
0105:            private static final String CLAUSE_PEN = "pen";
0106:            private static final String CLAUSE_SMOOTH = "smooth";
0107:            private static final String CLAUSE_CENTER = "center";
0108:            private static final String CLAUSE_BRUSH = "brush";
0109:            private static final String CLAUSE_FONT = "font";
0110:            private static final String CLAUSE_ANGLE = "angle";
0111:            private static final String CLAUSE_JUSTIFY = "justify";
0112:            private static final String CLAUSE_SPACING = "spacing";
0113:            private static final String CLAUSE_RIGHT = "right";
0114:            private static final String CLAUSE_LABEL = "label";
0115:
0116:            // Header parse Constants (& parameter names) 
0117:            private static final String CLAUSE_COLUMNS = "columns";
0118:            public static final int MAX_STRING_LEN = 255; // Max length for MapInfo Char() fields
0119:
0120:            // Some (by now useless) default values
0121:            private static final String DEFAULT_PEN = "Pen (1,2,0)";
0122:            private static final String DEFAULT_BRUSH = "Brush (2,16777215,16777215)";
0123:            private static final String DEFAULT_SYMBOL = "Symbol (34,0,12)";
0124:            private static Logger LOGGER = org.geotools.util.logging.Logging
0125:                    .getLogger("org.geotools.data.mif.MIFFile");
0126:
0127:            // Header information
0128:            private HashMap header = new HashMap();
0129:
0130:            // File IO Variables
0131:            private File mifFile = null;
0132:
0133:            // File IO Variables
0134:            private File midFile = null;
0135:
0136:            // File IO Variables
0137:            private File mifFileOut = null;
0138:
0139:            // File IO Variables
0140:            private File midFileOut = null;
0141:            private Object[] featureDefaults = null;
0142:            private char chDelimiter = '\t'; // TAB is the default delimiter if not specified in header
0143:
0144:            // Schema variables
0145:            private FeatureType featureType = null;
0146:            private int numAttribs = 0;
0147:            private int geomFieldIndex = -1;
0148:            private URI namespace = null;
0149:
0150:            // Parameters for coordinate transformation during file i/o
0151:            private boolean useTransform = false;
0152:            private float multX = 1;
0153:            private float multY = 1;
0154:            private float sumX = 0;
0155:            private float sumY = 0;
0156:
0157:            // Options & parameters
0158:            private GeometryFactory geomFactory = null;
0159:            private Integer SRID = new Integer(0);
0160:            private String fieldNameCase;
0161:            private String geometryName;
0162:            private String geometryClass;
0163:            private boolean toGeometryCollection = false;
0164:
0165:            /**
0166:             * <p>
0167:             * This constructor opens an existing MIF/MID file, and creates the
0168:             * corresponding schema from the file header
0169:             * </p>
0170:             * 
0171:             * <p>
0172:             * Allowed parameters in params Map:
0173:             * </p>
0174:             * 
0175:             * <ul>
0176:             * <li>
0177:             * "namespace" = URI of the namespace prefix for FeatureTypes
0178:             * </li>
0179:             * <li>
0180:             * PARAM_GEOMFACTORY = GeometryFactory object to be used for creating
0181:             * geometries; alternatively, use PARAM_SRID;
0182:             * </li>
0183:             * <li>
0184:             * PARAM_SRID = SRID to be used for creating geometries;
0185:             * </li>
0186:             * <li>
0187:             * PARAM_FIELDCASE = field names tranformation: "upper" to uppercase |
0188:             * "lower" to lowercase | "" none;
0189:             * </li>
0190:             * <li>
0191:             * PARAM_GEOMNAME = &lt;String&gt, name of the geometry field (defaults to
0192:             * "the_geom");
0193:             * </li>
0194:             * <li>
0195:             * PARAM_GEOMTYPE = geometry type handling: "untyped" uses Geometry class |
0196:             * "typed" force geometry to the type of the first valid geometry found in
0197:             * file | "multi" like typed, but forces LineString to MultilineString and
0198:             * Polygon to MultiPolygon; | "Point" | "LineString" | "MultiLineString" |
0199:             * "Polygon" | "MultiPolygon" | "Text" forces Geometry to Point and
0200:             * creates a MIF_TEXT String field in the schema
0201:             * </li>
0202:             * </ul>
0203:             * 
0204:             * <p>
0205:             * Header clauses values can also be set in the params Map, but they might
0206:             * be overridden by values read from MIF header.
0207:             * </p>
0208:             * 
0209:             * <p>
0210:             * Basic usage:
0211:             * </p>
0212:             * <pre><code>
0213:             *   HashMap params = new HashMap();
0214:             *   // params.put(MIFFile.PARAM_GEOMFACTORY, new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING_SINGLE), SRID));
0215:             *   params.put(MIFFile.PARAM_SRID, new Integer(SRID));
0216:             *   params.put(MIFFile.PARAM_FIELDCASE, "upper");
0217:             *   params.put(MIFFile.PARAM_GEOMNAME, "GEOM");
0218:             *   params.put(MIFFile.PARAM_GEOMTYPE, "typed");
0219:             *   MIFFile mf = new MIFFile("c:/some_path/file.mif",params);
0220:             *   FeatureType ft = mf.getSchema();
0221:             *   FeatureReader fr = mf.getFeatureReader();	
0222:             *   while (fr.hasNext()) {
0223:             *   	Feature in = fr.next();
0224:             *   	doSomethingWithFeature(in);
0225:             *   }
0226:             *   fr.close(); // closes file resources
0227:             * </code></pre>
0228:             *
0229:             * @param path Full pathName of the mif file, can be specified without the
0230:             *        .mif extension
0231:             * @param params Parameters map
0232:             *
0233:             * @throws IOException If the specified mif file could not be opened
0234:             */
0235:            public MIFFile(String path, Map params) throws IOException {
0236:                // TODO use url instead of String
0237:                super ();
0238:
0239:                parseParams(params);
0240:
0241:                initFiles(path, true);
0242:
0243:                MIFFileTokenizer mifTokenizer = new MIFFileTokenizer(
0244:                        new BufferedReader(new FileReader(mifFile)));
0245:
0246:                try {
0247:                    readMifHeader(false, mifTokenizer);
0248:                } catch (Exception e) {
0249:                    throw new IOException("Can't read MIF header: "
0250:                            + e.toString());
0251:                } finally {
0252:                    try {
0253:                        mifTokenizer.close();
0254:                    } catch (Exception e) {
0255:                    }
0256:                }
0257:            }
0258:
0259:            /**
0260:             * <p>
0261:             * This constructor creates a a new MIF/MID file given schema and path.  If
0262:             * a .mif/.mid file pair already exists, it will be overwritten.
0263:             * </p>
0264:             * 
0265:             * <p>
0266:             * Basic usage:
0267:             * </p>
0268:             * <pre><code>
0269:             *   HashMap params = new HashMap();
0270:             *   params.put(MIFFile.MIFDataStore.HCLAUSE_COORDSYS, "Nonearth \"m\"");
0271:             * 
0272:             *   MIFFile mf = new MIFFile("c:/some_path/", ft, params);
0273:             * 
0274:             * 
0275:             *   FeatureWriter fw = mf.getFeatureWriter();
0276:             * 
0277:             *   while(...) {
0278:             * 	    Feature f = fw.next();
0279:             * 			f.setAttribute(...,...);
0280:             * 			fw.write();
0281:             * 	 }
0282:             * 
0283:             *   fw.close();
0284:             * </code></pre>
0285:             *
0286:             * @param path Full path & file name of the MIF file to create, can be
0287:             *        specified without the .mif extension
0288:             * @param featureType
0289:             * @param params Parameter map
0290:             *
0291:             * @throws IOException Couldn't open the specified mif file for writing
0292:             *         header
0293:             * @throws SchemaException Error setting the given FeatureType as the MIF
0294:             *         schema
0295:             */
0296:            public MIFFile(String path, FeatureType featureType, HashMap params)
0297:                    throws IOException, SchemaException {
0298:                // TODO use url instead of String
0299:                super ();
0300:
0301:                parseParams(params);
0302:
0303:                setSchema(featureType);
0304:                initFiles(path, false);
0305:
0306:                PrintStream outMif = new PrintStream(new FileOutputStream(
0307:                        mifFile, false));
0308:                PrintStream outMid = new PrintStream(new FileOutputStream(
0309:                        midFile, false));
0310:
0311:                // writes out header
0312:                outMif.println(exportHeader());
0313:
0314:                outMif.close();
0315:                outMid.close();
0316:            }
0317:
0318:            /**
0319:             * Parses the parameters map into fields:
0320:             *
0321:             * @param params
0322:             *
0323:             * @throws IOException Error getting parameters from the specified map
0324:             */
0325:            private void parseParams(Map params) throws IOException {
0326:                if (params == null) {
0327:                    params = new HashMap();
0328:                }
0329:
0330:                // Sets defaults for header
0331:                setHeaderClause(MIFDataStore.HCLAUSE_VERSION,
0332:                        (String) getParam(MIFDataStore.HCLAUSE_VERSION, "300",
0333:                                false, params));
0334:                setHeaderClause(MIFDataStore.HCLAUSE_CHARSET,
0335:                        (String) getParam(MIFDataStore.HCLAUSE_CHARSET,
0336:                                "WindowsLatin1", false, params));
0337:                setHeaderClause(MIFDataStore.HCLAUSE_DELIMITER,
0338:                        (String) getParam(MIFDataStore.HCLAUSE_DELIMITER,
0339:                                String.valueOf(chDelimiter), false, params));
0340:                chDelimiter = getHeaderClause(MIFDataStore.HCLAUSE_DELIMITER)
0341:                        .charAt(0);
0342:
0343:                setHeaderClause(MIFDataStore.HCLAUSE_UNIQUE, (String) getParam(
0344:                        MIFDataStore.HCLAUSE_UNIQUE, "", false, params));
0345:                setHeaderClause(MIFDataStore.HCLAUSE_INDEX, (String) getParam(
0346:                        MIFDataStore.HCLAUSE_INDEX, "", false, params));
0347:                setHeaderClause(MIFDataStore.HCLAUSE_COORDSYS,
0348:                        (String) getParam(MIFDataStore.HCLAUSE_COORDSYS, "",
0349:                                false, params));
0350:                setHeaderClause(MIFDataStore.HCLAUSE_TRANSFORM,
0351:                        (String) getParam(MIFDataStore.HCLAUSE_TRANSFORM, "",
0352:                                false, params));
0353:
0354:                SRID = (Integer) getParam(MIFDataStore.PARAM_SRID, new Integer(
0355:                        0), false, params);
0356:
0357:                geomFactory = (GeometryFactory) getParam(
0358:                        MIFDataStore.PARAM_GEOMFACTORY, null, false, params);
0359:
0360:                if (geomFactory == null) {
0361:                    geomFactory = new GeometryFactory(new PrecisionModel(
0362:                            PrecisionModel.FLOATING), SRID.intValue());
0363:                }
0364:
0365:                geometryName = (String) getParam(MIFDataStore.PARAM_GEOMNAME,
0366:                        "the_geom", false, params);
0367:                fieldNameCase = ((String) getParam(
0368:                        MIFDataStore.PARAM_FIELDCASE, "", false, params))
0369:                        .toLowerCase();
0370:
0371:                geometryClass = ((String) getParam(MIFDataStore.PARAM_GEOMTYPE,
0372:                        "untyped", false, params)).toLowerCase();
0373:
0374:                namespace = (URI) getParam("namespace",
0375:                        FeatureTypes.DEFAULT_NAMESPACE, false, params);
0376:            }
0377:
0378:            /**
0379:             * Returns a parameter value from the parameters map
0380:             *
0381:             * @param name
0382:             * @param defa
0383:             * @param required
0384:             * @param params
0385:             *
0386:             *
0387:             * @throws IOException if required parameter is missing
0388:             */
0389:            private Object getParam(String name, Object defa, boolean required,
0390:                    Map params) throws IOException {
0391:                Object result;
0392:
0393:                try {
0394:                    result = params.get(name);
0395:                } catch (Exception e) {
0396:                    result = null;
0397:                }
0398:
0399:                if (result == null) {
0400:                    if (required) {
0401:                        throw new IOException("MIFFile: parameter " + name
0402:                                + " is required");
0403:                    }
0404:
0405:                    result = defa;
0406:                }
0407:
0408:                return result;
0409:            }
0410:
0411:            /**
0412:             * <p>
0413:             * Sets the value for a Header Clause. Possible values are:
0414:             * </p>
0415:             * 
0416:             * <ul>
0417:             * <li>
0418:             * MIFDataStore.HCLAUSE_VERSION = Version number ("310")
0419:             * </li>
0420:             * <li>
0421:             * MIFDataStore.HCLAUSE_CHARSET = Charset name ("WindowsLatin1")
0422:             * </li>
0423:             * <li>
0424:             * MIFDataStore.HCLAUSE_UNIQUE = Comma-separated list of field indexes
0425:             * (1..numFields) corresponding to unique values (i.e. street names for
0426:             * street segments)
0427:             * </li>
0428:             * <li>
0429:             * MIFDataStore.HCLAUSE_INDEX = Comma-separated list of field indexes
0430:             * (1..numFields) indicating which fields have to be indexed in MapInfo
0431:             * </li>
0432:             * <li>
0433:             * MIFDataStore.HCLAUSE_COORDSYS = MapInfo CoordSys clause
0434:             * </li>
0435:             * <li>
0436:             * MIFDataStore.HCLAUSE_TRANSFORM = Comma-separated list of four
0437:             * transformation parameters ("1000, 1000, 0, 0")
0438:             * </li>
0439:             * </ul>
0440:             * 
0441:             *
0442:             * @param clause Name of the Header Clause
0443:             * @param value Value for the Header Clause
0444:             *
0445:             * @throws IOException Bad delimiter was specified
0446:             */
0447:            private void setHeaderClause(String clause, String value)
0448:                    throws IOException {
0449:                if (value == null) {
0450:                    value = "";
0451:                }
0452:
0453:                if (clause.equals(MIFDataStore.HCLAUSE_DELIMITER)
0454:                        && (value.equals("") || value.equals("\""))) {
0455:                    throw new IOException("Bad delimiter specified");
0456:                }
0457:
0458:                header.put(clause, value);
0459:            }
0460:
0461:            /**
0462:             * Gets the value for an header clause
0463:             *
0464:             * @param clause
0465:             *
0466:             */
0467:            public String getHeaderClause(String clause) {
0468:                try {
0469:                    return (String) getParam(clause, "", false, header);
0470:                } catch (Exception e) {
0471:                    return "";
0472:                }
0473:            }
0474:
0475:            /**
0476:             * <p>
0477:             * Opens the MIF file for input and returns a FeatureReader for accessing
0478:             * the features.
0479:             * </p>
0480:             * 
0481:             * <p>
0482:             * TODO Concurrent file access is still not handled. MUST LOCK FILE and
0483:             * return an error if another FeatureReader is open - Handle concurrent
0484:             * access with synchronized(mif) / or Filesystem locking is enough?
0485:             * </p>
0486:             *
0487:             * @return A FeatureReader for reading features from MIF/MID file
0488:             *
0489:             * @throws IOException
0490:             */
0491:            public FeatureReader getFeatureReader() throws IOException {
0492:                MIFFileTokenizer mifTokenizer = null;
0493:                MIFFileTokenizer midTokenizer = null;
0494:
0495:                // if exists outMIF throw new IOException("File is being accessed in write mode");
0496:                try {
0497:                    mifTokenizer = new MIFFileTokenizer(new BufferedReader(
0498:                            new FileReader(mifFile)));
0499:                    midTokenizer = new MIFFileTokenizer(new BufferedReader(
0500:                            new FileReader(midFile)));
0501:                    readMifHeader(true, mifTokenizer); // skips header
0502:
0503:                    return new Reader(mifTokenizer, midTokenizer);
0504:                } catch (Exception e) {
0505:                    if (mifTokenizer != null) {
0506:                        mifTokenizer.close();
0507:                    }
0508:
0509:                    if (midTokenizer != null) {
0510:                        midTokenizer.close();
0511:                    }
0512:
0513:                    throw new IOException("Error initializing reader: "
0514:                            + e.toString());
0515:                }
0516:            }
0517:
0518:            /**
0519:             * Returns a FeatureWriter for writing features to the MIF/MID file.
0520:             *
0521:             * @return A featureWriter for this file
0522:             *
0523:             * @throws IOException
0524:             */
0525:            public FeatureWriter getFeatureWriter() throws IOException {
0526:                return getFeatureWriter(false);
0527:            }
0528:
0529:            /**
0530:             * <p>
0531:             * Private FeatureWriter in append mode, could be called by
0532:             * DataStore.getFeatureWriterAppend(); not implemented yet
0533:             * </p>
0534:             *
0535:             * @param append
0536:             *
0537:             *
0538:             * @throws IOException
0539:             */
0540:            private FeatureWriter getFeatureWriter(boolean append)
0541:                    throws IOException {
0542:                if (append) {
0543:                    // copy inMif to OutMIf
0544:                } else {
0545:                    // WriteHeader
0546:                }
0547:
0548:                PrintStream outMif = new PrintStream(new FileOutputStream(
0549:                        mifFileOut, append));
0550:                PrintStream outMid = new PrintStream(new FileOutputStream(
0551:                        midFileOut, append));
0552:
0553:                return new Writer(outMif, outMid, append);
0554:            }
0555:
0556:            /**
0557:             * Creates the MIF file header
0558:             *
0559:             * @return the Header as a String
0560:             *
0561:             * @throws SchemaException A required header clause is missing.
0562:             */
0563:            private String exportHeader() throws SchemaException {
0564:                // Header tags passed in parameters are overridden by the tags read from mif file 
0565:                String header = exportClause(MIFDataStore.HCLAUSE_VERSION,
0566:                        true, false)
0567:                        + exportClause(MIFDataStore.HCLAUSE_CHARSET, true, true) // TODO Charset clause support should imply character conversion????
0568:                        + exportClause(MIFDataStore.HCLAUSE_DELIMITER, true,
0569:                                true)
0570:                        + exportClause(MIFDataStore.HCLAUSE_UNIQUE, false,
0571:                                false)
0572:                        + exportClause(MIFDataStore.HCLAUSE_INDEX, false, false)
0573:                        + exportClause(MIFDataStore.HCLAUSE_COORDSYS, false,
0574:                                false)
0575:                        + exportClause(MIFDataStore.HCLAUSE_TRANSFORM, false,
0576:                                false);
0577:
0578:                header += ("Columns " + (numAttribs - 1) + "\n");
0579:
0580:                for (int i = 1; i < numAttribs; i++) {
0581:                    AttributeType at = featureType.getAttributeType(i);
0582:                    header += ("  " + at.getName() + " "
0583:                            + getMapInfoAttrType(at) + "\n");
0584:                }
0585:
0586:                header += "Data\n";
0587:
0588:                return header;
0589:            }
0590:
0591:            private String exportClause(String clause, boolean required,
0592:                    boolean quote) throws SchemaException {
0593:                String result = getHeaderClause(clause);
0594:
0595:                if (!result.equals("")) {
0596:                    if (quote) {
0597:                        result = MIFStringTokenizer.strQuote(result);
0598:                    }
0599:
0600:                    return clause + " " + result + "\n";
0601:                }
0602:
0603:                if (required) {
0604:                    throw new SchemaException("Header clause " + clause
0605:                            + " is required.");
0606:                }
0607:
0608:                return "";
0609:            }
0610:
0611:            /**
0612:             * Maps an AttributeType to a MapInfo field type
0613:             *
0614:             * @param at Attribute Type
0615:             *
0616:             * @return the String description of the MapInfo Type
0617:             */
0618:            private String getMapInfoAttrType(AttributeType at) {
0619:                if (at.getType() == String.class) {
0620:                    int l = AttributeTypes.getFieldLength(at, MAX_STRING_LEN);
0621:
0622:                    if (l <= 0) {
0623:                        l = MAX_STRING_LEN;
0624:                    }
0625:
0626:                    return "Char(" + l + ")";
0627:                } else if (at.getType() == Integer.class) {
0628:                    return "Integer";
0629:                } else if ((at.getType() == Double.class)
0630:                        || (at.getType() == Float.class)) {
0631:                    return "Float";
0632:                } else if (at.getType() == Boolean.class) {
0633:                    return "Logical";
0634:                } else if (at.getType() == Date.class) {
0635:                    return "Date";
0636:                } else {
0637:                    return "Char(" + MAX_STRING_LEN + ")"; // TODO Should it raise an exception here (UnsupportedSchema) ?
0638:                }
0639:            }
0640:
0641:            /**
0642:             * Sets the path name of the MIF and MID files
0643:             *
0644:             * @param path The full path of the .mif file, with or without extension
0645:             * @param mustExist True if opening file for reading
0646:             *
0647:             * @throws FileNotFoundException
0648:             */
0649:            private void initFiles(String path, boolean mustExist)
0650:                    throws FileNotFoundException {
0651:                File file = new File(path);
0652:
0653:                if (file.isDirectory()) {
0654:                    throw new FileNotFoundException(path + " is a directory");
0655:                }
0656:
0657:                String fName = getMifName(file.getName());
0658:                file = file.getParentFile();
0659:
0660:                mifFile = getFileHandler(file, fName, ".mif", mustExist);
0661:                midFile = getFileHandler(file, fName, ".mid", mustExist);
0662:
0663:                mifFileOut = getFileHandler(file, fName, ".mif.out", false);
0664:                midFileOut = getFileHandler(file, fName, ".mid.out", false);
0665:            }
0666:
0667:            /**
0668:             * Returns the name of a .mif file without extension
0669:             *
0670:             * @param fName The file name, possibly with .mif extension
0671:             *
0672:             * @return The name with no extension
0673:             *
0674:             * @throws FileNotFoundException if extension was other than "mif"
0675:             */
0676:            protected static String getMifName(String fName)
0677:                    throws FileNotFoundException {
0678:                int ext = fName.lastIndexOf(".");
0679:
0680:                if (ext > 0) {
0681:                    String theExt = fName.substring(ext + 1).toLowerCase();
0682:
0683:                    if (!(theExt.equals("mif"))) {
0684:                        throw new FileNotFoundException(
0685:                                "Please specify a .mif file extension.");
0686:                    }
0687:
0688:                    fName = fName.substring(0, ext);
0689:                }
0690:
0691:                return fName;
0692:            }
0693:
0694:            /**
0695:             * Utility function for initFiles - returns a File given a parent path, the
0696:             * file name without extension and the extension Tests different extension
0697:             * case for case-sensitive filesystems
0698:             *
0699:             * @param path Directory containing the file
0700:             * @param fileName Name of the file with no extension
0701:             * @param ext extension with trailing "."
0702:             * @param mustExist If true, raises an excaption if the file does not exist
0703:             *
0704:             * @return The File object
0705:             *
0706:             * @throws FileNotFoundException
0707:             */
0708:            protected static File getFileHandler(File path, String fileName,
0709:                    String ext, boolean mustExist) throws FileNotFoundException {
0710:                File file = new File(path, fileName + ext);
0711:
0712:                if (file.exists() || !mustExist) {
0713:                    return file;
0714:                }
0715:
0716:                file = new File(path, fileName + ext.toUpperCase());
0717:
0718:                if (file.exists()) {
0719:                    return file;
0720:                }
0721:
0722:                throw new FileNotFoundException("Can't find file: "
0723:                        + file.getName());
0724:            }
0725:
0726:            /**
0727:             * Reads the header from the given MIF file stream tokenizer
0728:             *
0729:             * @param skipRead Skip the header, just to get to the data section
0730:             * @param mif
0731:             *
0732:             * @throws IOException
0733:             * @throws SchemaException Error reading header information
0734:             */
0735:            private void readMifHeader(boolean skipRead, MIFFileTokenizer mif)
0736:                    throws IOException, SchemaException {
0737:                try {
0738:                    String tok;
0739:                    boolean hasMifText = false;
0740:                    AttributeType[] columns = null;
0741:
0742:                    while (mif.readLine()) {
0743:                        tok = mif.getToken().toLowerCase();
0744:
0745:                        // "data" might be a field name, in this case the type name would follow on the same line 
0746:                        if (tok.equals("data") && mif.getLine().equals("")) {
0747:                            break;
0748:                        }
0749:
0750:                        if (skipRead) {
0751:                            continue;
0752:                        }
0753:
0754:                        if (tok.equals(MIFDataStore.HCLAUSE_VERSION)) {
0755:                            setHeaderClause(MIFDataStore.HCLAUSE_VERSION, mif
0756:                                    .getLine());
0757:
0758:                            continue;
0759:                        }
0760:
0761:                        if (tok.equals(MIFDataStore.HCLAUSE_CHARSET)) {
0762:                            setHeaderClause(MIFDataStore.HCLAUSE_CHARSET, mif
0763:                                    .getToken(' ', false, true));
0764:
0765:                            continue;
0766:                        }
0767:
0768:                        if (tok.equals(MIFDataStore.HCLAUSE_DELIMITER)) {
0769:                            setHeaderClause(MIFDataStore.HCLAUSE_DELIMITER, mif
0770:                                    .getToken(' ', false, true));
0771:                            chDelimiter = getHeaderClause(
0772:                                    MIFDataStore.HCLAUSE_DELIMITER).charAt(0);
0773:
0774:                            continue;
0775:                        }
0776:
0777:                        if (tok.equals(MIFDataStore.HCLAUSE_UNIQUE)) {
0778:                            setHeaderClause(MIFDataStore.HCLAUSE_UNIQUE, mif
0779:                                    .getLine());
0780:
0781:                            continue;
0782:                        }
0783:
0784:                        if (tok.equals(MIFDataStore.HCLAUSE_COORDSYS)) {
0785:                            setHeaderClause(MIFDataStore.HCLAUSE_COORDSYS, mif
0786:                                    .getLine());
0787:
0788:                            continue;
0789:                        }
0790:
0791:                        if (tok.equals(MIFDataStore.HCLAUSE_INDEX)) {
0792:                            setHeaderClause(MIFDataStore.HCLAUSE_INDEX, mif
0793:                                    .getLine());
0794:
0795:                            continue;
0796:                        }
0797:
0798:                        if (tok.equals(MIFDataStore.HCLAUSE_TRANSFORM)) {
0799:                            useTransform = true;
0800:                            multX = Float.parseFloat("0" + mif.getToken(','));
0801:                            multY = Float.parseFloat("0" + mif.getToken(','));
0802:                            sumX = Float.parseFloat("0" + mif.getToken(','));
0803:                            sumY = Float.parseFloat("0" + mif.getToken(','));
0804:
0805:                            if (multX == 0) {
0806:                                multX = 1;
0807:                            }
0808:
0809:                            if (multY == 0) {
0810:                                multY = 1;
0811:                            }
0812:
0813:                            continue;
0814:                        }
0815:
0816:                        if (tok.equals(CLAUSE_COLUMNS)) {
0817:                            int cols;
0818:
0819:                            try {
0820:                                cols = Integer.parseInt(mif.getLine());
0821:                            } catch (NumberFormatException nfexp) {
0822:                                throw new IOException("bad number of colums: "
0823:                                        + mif.getLine());
0824:                            }
0825:
0826:                            // Columns <n> does not take into account the geometry column, so we increment
0827:                            columns = new AttributeType[++cols];
0828:
0829:                            String name;
0830:                            String type;
0831:                            Object defa;
0832:                            Class typeClass;
0833:                            int size;
0834:
0835:                            for (int i = 1; i < cols; i++) {
0836:                                if (!mif.readLine()) {
0837:                                    throw new IOException(
0838:                                            "Expected column definition");
0839:                                }
0840:
0841:                                name = mif.getToken();
0842:
0843:                                if (fieldNameCase.equalsIgnoreCase("upper")) {
0844:                                    name = name.toUpperCase();
0845:                                } else if (fieldNameCase
0846:                                        .equalsIgnoreCase("lower")) {
0847:                                    name = name.toLowerCase();
0848:                                }
0849:
0850:                                type = mif.getToken('(').toLowerCase();
0851:                                defa = null;
0852:                                typeClass = null;
0853:                                size = 4;
0854:
0855:                                if (type.equals("float")
0856:                                        || type.equals("decimal")) {
0857:                                    typeClass = Double.class;
0858:                                    size = 8;
0859:                                    defa = new Double(0.0);
0860:
0861:                                    // TODO: check precision?
0862:                                } else if (type.startsWith("char")) {
0863:                                    typeClass = String.class;
0864:                                    size = Integer.parseInt(mif.getToken(')'));
0865:                                    defa = "";
0866:                                } else if (type.equals("integer")
0867:                                        || type.equals("smallint")) {
0868:                                    typeClass = Integer.class;
0869:                                    defa = new Integer(0);
0870:
0871:                                    // TODO: apply a restriction for Smallint (value between -32768 and +32767)
0872:                                } else if (type.equals("logical")) {
0873:                                    typeClass = Boolean.class;
0874:                                    size = 2; // ???
0875:                                    defa = new Boolean(false);
0876:                                } else if (type.equals("date")) {
0877:                                    typeClass = Date.class; // MapInfo format: yyyymmdd
0878:                                    size = 4; // ???
0879:                                    defa = null; // Dates are "nillable" (like Strings can be empty)
0880:                                } else {
0881:                                    LOGGER.fine("unknown type in mif/mid read "
0882:                                            + type + " storing as String");
0883:                                    typeClass = String.class;
0884:                                    size = 254;
0885:                                    defa = "";
0886:                                }
0887:
0888:                                // Apart from Geometry, MapInfo table fields cannot be null, so Nillable is always false and default value must always be provided!
0889:                                columns[i] = AttributeTypeFactory
0890:                                        .newAttributeType(name, typeClass,
0891:                                                (defa == null), size, defa);
0892:                            }
0893:                        }
0894:                    }
0895:
0896:                    // Builds schema if not in skip mode...
0897:                    if (!skipRead) {
0898:                        Class geomType = null;
0899:
0900:                        String geomClass = geometryClass.toLowerCase();
0901:
0902:                        if (geomClass.equals("untyped")) {
0903:                            geomType = Geometry.class;
0904:                        } else if (geomClass.equals("typed")) {
0905:                            toGeometryCollection = false;
0906:                        } else if (geomClass.equals("multi")) {
0907:                            toGeometryCollection = true;
0908:                        } else if (geomClass.equals("point")) {
0909:                            geomType = Point.class;
0910:                        } else if (geomClass.equals("text")) {
0911:                            geomType = Point.class;
0912:                            hasMifText = true;
0913:                        } else if (geomClass.equals("linestring")) {
0914:                            geomType = LineString.class;
0915:                        } else if (geomClass.equals("multilinestring")) {
0916:                            geomType = MultiLineString.class;
0917:                            toGeometryCollection = true;
0918:                        } else if (geomClass.equals("polygon")) {
0919:                            geomType = Polygon.class;
0920:                        } else if (geomClass.equals("multipolygon")) {
0921:                            geomType = MultiPolygon.class;
0922:                            toGeometryCollection = true;
0923:                        } else {
0924:                            throw new SchemaException(
0925:                                    "Bad geometry type option: " + geomClass);
0926:                        }
0927:
0928:                        // Determine geometry type from the first non-null geometry read from mif file
0929:                        if (geomType == null) {
0930:                            Reader reader = new Reader(mif, null);
0931:                            Geometry geom = null;
0932:
0933:                            while (!reader.mifEOF) {
0934:                                geom = reader.readGeometry();
0935:                                hasMifText = (!reader.mifText.equals(""));
0936:
0937:                                if (geom != null) {
0938:                                    geomType = geom.getClass();
0939:
0940:                                    if (toGeometryCollection) {
0941:                                        if (geomType
0942:                                                .isAssignableFrom(Polygon.class)) {
0943:                                            geomType = MultiPolygon.class;
0944:                                        } else if (geomType
0945:                                                .isAssignableFrom(LineString.class)) {
0946:                                            geomType = MultiLineString.class;
0947:                                        }
0948:                                    }
0949:
0950:                                    break;
0951:                                }
0952:                            }
0953:
0954:                            reader.close();
0955:                            reader = null;
0956:                        }
0957:
0958:                        if (geomType == null) {
0959:                            throw new SchemaException(
0960:                                    "Unable to determine geometry type from mif file");
0961:                        }
0962:
0963:                        columns[0] = AttributeTypeFactory.newAttributeType(
0964:                                geometryName, geomType, true);
0965:
0966:                        try {
0967:                            String typeName = mifFile.getName();
0968:                            typeName = typeName.substring(0, typeName
0969:                                    .indexOf("."));
0970:
0971:                            FeatureTypeBuilder builder = FeatureTypeBuilder
0972:                                    .newInstance(typeName);
0973:
0974:                            builder.setNamespace(namespace);
0975:
0976:                            for (int i = 0; i < columns.length; i++)
0977:                                builder.addType(columns[i]);
0978:
0979:                            if (hasMifText) {
0980:                                builder.addType(AttributeTypeFactory
0981:                                        .newAttributeType("MIF_TEXT",
0982:                                                String.class, true));
0983:                            }
0984:
0985:                            setSchema(builder.getFeatureType());
0986:                        } catch (SchemaException schexp) {
0987:                            throw new SchemaException(
0988:                                    "Exception creating feature type from MIF header: "
0989:                                            + schexp.toString());
0990:                        }
0991:                    }
0992:                } catch (Exception e) {
0993:                    throw new IOException(
0994:                            "IOException reading MIF header, line "
0995:                                    + mif.getLineNumber() + ": "
0996:                                    + e.getMessage());
0997:                }
0998:            }
0999:
1000:            /**
1001:             * Returns the MIF schema
1002:             *
1003:             * @return the current FeatureType associated with the MIF file
1004:             */
1005:            public FeatureType getSchema() {
1006:                return featureType;
1007:            }
1008:
1009:            /**
1010:             * Sets the schema (FeatureType) and creates value setters and IO object
1011:             * buffer
1012:             *
1013:             * @param ft
1014:             *
1015:             * @throws SchemaException The given FeatureType is not compatible with
1016:             *         MapInfo format
1017:             */
1018:            private void setSchema(FeatureType ft) throws SchemaException {
1019:                featureType = ft;
1020:
1021:                numAttribs = featureType.getAttributeCount();
1022:                geomFieldIndex = -1;
1023:
1024:                // Creates the input buffer for reading MID file
1025:                featureDefaults = new Object[numAttribs];
1026:
1027:                for (int i = 0; i < featureType.getAttributeCount(); i++) {
1028:                    AttributeType at = featureType.getAttributeType(i);
1029:
1030:                    Class atc = at.getType();
1031:
1032:                    if (Geometry.class.isAssignableFrom(atc)) {
1033:                        if (geomFieldIndex >= 0) {
1034:                            throw new SchemaException(
1035:                                    "Feature Types with more than one geometric attribute are not supported.");
1036:                        }
1037:
1038:                        if (i > 0) {
1039:                            throw new SchemaException(
1040:                                    "Geometry must be the first attribute in schema.");
1041:                        }
1042:
1043:                        geomFieldIndex = i; // = 0
1044:                    }
1045:                }
1046:
1047:                MIFValueSetter[] tmp = getValueSetters();
1048:
1049:                for (int i = 0; i < featureType.getAttributeCount(); i++) {
1050:                    if (i != geomFieldIndex) {
1051:                        tmp[i].setString("");
1052:                        featureDefaults[i] = tmp[i].getValue();
1053:                    }
1054:                }
1055:            }
1056:
1057:            /**
1058:             * Gets the ValueSetters
1059:             *
1060:             * @return An array of valueSetters to be used for IO operations
1061:             *
1062:             * @throws SchemaException An attribute of an unsupported type was found.
1063:             */
1064:            private MIFValueSetter[] getValueSetters() throws SchemaException {
1065:                MIFValueSetter[] fieldValueSetters = new MIFValueSetter[numAttribs];
1066:
1067:                for (int i = 0; i < featureType.getAttributeCount(); i++) {
1068:                    AttributeType at = featureType.getAttributeType(i);
1069:                    Class atc = at.getType();
1070:
1071:                    if (i == geomFieldIndex) {
1072:                        fieldValueSetters[i] = null;
1073:                    } else if (atc == Integer.class) {
1074:                        fieldValueSetters[i] = new MIFValueSetter("0") {
1075:                            protected void stringToValue() throws Exception {
1076:                                objValue = new Integer(strValue);
1077:                            }
1078:                        };
1079:                    } else if (atc == Double.class) {
1080:                        fieldValueSetters[i] = new MIFValueSetter("0") {
1081:                            protected void stringToValue() throws Exception {
1082:                                objValue = new Double(strValue);
1083:                            }
1084:
1085:                            protected void valueToString() {
1086:                                // TODO use DecimalFormat class!!!
1087:                                super .valueToString();
1088:                            }
1089:                        };
1090:                    } else if (atc == Float.class) {
1091:                        fieldValueSetters[i] = new MIFValueSetter("0") {
1092:                            protected void stringToValue() throws Exception {
1093:                                objValue = new Float(strValue);
1094:                            }
1095:
1096:                            protected void valueToString() {
1097:                                // TODO use DecimalFormat class!!!
1098:                                super .valueToString();
1099:                            }
1100:                        };
1101:                    } else if (atc == Boolean.class) {
1102:                        fieldValueSetters[i] = new MIFValueSetter("false") {
1103:                            protected void stringToValue() throws Exception {
1104:                                objValue = new Boolean(
1105:                                        "T".equalsIgnoreCase(strValue) ? "true"
1106:                                                : ("F"
1107:                                                        .equalsIgnoreCase(strValue) ? "false"
1108:                                                        : strValue));
1109:                            }
1110:
1111:                            protected void valueToString() {
1112:                                if ((objValue == null)
1113:                                        || (((Boolean) objValue).booleanValue() == false)) {
1114:                                    strValue = "F";
1115:                                } else {
1116:                                    strValue = "T";
1117:                                }
1118:                            }
1119:                        };
1120:                    } else if (Date.class.isAssignableFrom(atc)) {
1121:                        // TODO Check conversion of date values - switch to java.sql.Date
1122:                        fieldValueSetters[i] = new MIFValueSetter("") {
1123:                            protected SimpleDateFormat dateFormat = new SimpleDateFormat(
1124:                                    "yyyyMMdd");
1125:
1126:                            protected void stringToValue() throws Exception {
1127:                                if ((strValue != null) && !strValue.equals("")) {
1128:                                    objValue = dateFormat.parse(strValue);
1129:                                } else {
1130:                                    objValue = null;
1131:                                }
1132:
1133:                                // Date.valueOf(strValue.substring(0, 4) + "-" + strValue.substring(4, 6) + "-" + strValue.substring(6));
1134:                            }
1135:
1136:                            protected void valueToString() {
1137:                                if (objValue == null) {
1138:                                    strValue = "";
1139:                                } else {
1140:                                    strValue = dateFormat.format(objValue);
1141:
1142:                                    // strValue = ((Date) objValue).getYear() + "" + ((Date) objValue).getMonth() + "" + ((Date) objValue).getDay();
1143:                                }
1144:                            }
1145:                        };
1146:                    } else if (atc == String.class) {
1147:                        fieldValueSetters[i] = new MIFValueSetter("") {
1148:                            protected void stringToValue() throws Exception {
1149:                                objValue = new String(strValue);
1150:                            }
1151:
1152:                            // Quotes the string
1153:                            protected void valueToString() {
1154:                                strValue = new String("\""
1155:                                        + objValue.toString().replaceAll("\"",
1156:                                                "\"\"") + "\"");
1157:                            }
1158:                        };
1159:                    } else {
1160:                        throw new SchemaException(
1161:                                "Unsupported attribute type: " + atc.getName());
1162:                    }
1163:                }
1164:
1165:                return fieldValueSetters;
1166:            }
1167:
1168:            /**
1169:             * Utility function for copying or moving files
1170:             *
1171:             * @param in Source file
1172:             * @param out Destination file
1173:             * @param deleteIn If true, source will be deleted upon successfull copy
1174:             *
1175:             * @throws IOException
1176:             */
1177:            protected static void copyFileAndDelete(File in, File out,
1178:                    boolean deleteIn) throws IOException {
1179:                try {
1180:                    FileChannel sourceChannel = new FileInputStream(in)
1181:                            .getChannel();
1182:                    FileChannel destinationChannel = new FileOutputStream(out)
1183:                            .getChannel();
1184:                    destinationChannel.transferFrom(sourceChannel, 0,
1185:                            sourceChannel.size());
1186:                    if (deleteIn) {
1187:                        in.delete();
1188:                    }
1189:                } catch (Exception e) {
1190:                    throw new IOException(e.getMessage());
1191:                }
1192:            }
1193:
1194:            /**
1195:             * <p>
1196:             * Private FeatureReader inner class for reading Features from the MIF file
1197:             * </p>
1198:             */
1199:            private class Reader implements  FeatureReader {
1200:                private MIFFileTokenizer mif = null;
1201:                private MIFFileTokenizer mid = null;
1202:                private boolean mifEOF = false;
1203:                private String mifText = ""; // caption for text objects 
1204:                private Feature inputFeature = null;
1205:                private Object[] inputBuffer = null;
1206:                private MIFValueSetter[] fieldValueSetters;
1207:
1208:                private Reader(MIFFileTokenizer mifTokenizer,
1209:                        MIFFileTokenizer midTokenizer) throws IOException {
1210:                    inputBuffer = new Object[numAttribs];
1211:
1212:                    mif = mifTokenizer;
1213:                    mid = midTokenizer;
1214:
1215:                    // numAttribs == 0 when Reader is called from within readMifHeader for determining geometry Type
1216:                    if (numAttribs > 0) {
1217:                        try {
1218:                            fieldValueSetters = getValueSetters();
1219:                        } catch (SchemaException e) {
1220:                            throw new IOException(e.getMessage());
1221:                        }
1222:
1223:                        inputFeature = readFeature();
1224:                    }
1225:                }
1226:
1227:                public boolean hasNext() {
1228:                    return (inputFeature != null);
1229:                }
1230:
1231:                // Reads the next feature and returns the last one
1232:                public Feature next() throws NoSuchElementException {
1233:                    if (inputFeature == null) {
1234:                        throw new NoSuchElementException(
1235:                                "Reached the end of MIF file");
1236:                    }
1237:
1238:                    Feature temp = inputFeature;
1239:
1240:                    try {
1241:                        inputFeature = readFeature();
1242:                    } catch (Exception e) {
1243:                        throw new NoSuchElementException(
1244:                                "Error retrieving next feature: "
1245:                                        + e.toString());
1246:                    }
1247:
1248:                    return temp;
1249:                }
1250:
1251:                public FeatureType getFeatureType() {
1252:                    return featureType;
1253:                }
1254:
1255:                public void close() {
1256:                    try {
1257:                        if (mif != null) {
1258:                            mif.close();
1259:                        }
1260:
1261:                        if (mid != null) {
1262:                            mid.close();
1263:                        }
1264:                    } finally {
1265:                        mif = null;
1266:                        mid = null;
1267:                    }
1268:                }
1269:
1270:                protected void finalize() throws Throwable {
1271:                    close();
1272:                    super .finalize();
1273:                }
1274:
1275:                /**
1276:                 * Reads a single MIF Object (Point, Line, Region, etc.) as a Feature
1277:                 *
1278:                 * @return The feature, or null if the end of file was reached
1279:                 *
1280:                 * @throws IOException
1281:                 */
1282:                private Feature readFeature() throws IOException {
1283:                    Feature feature = null;
1284:                    Geometry geom = readGeometry();
1285:
1286:                    if (mifEOF) {
1287:                        return null;
1288:                    }
1289:
1290:                    if (!mid.readLine()) {
1291:                        // TODO According to MapInfo spec., MID file is optional... in this case we should return the default values for the feature
1292:                        if (geom != null) {
1293:                            throw new IOException("Unexpected end of MID file.");
1294:                        }
1295:
1296:                        return null;
1297:                    }
1298:
1299:                    // Reads data from mid file
1300:                    // Assumes that geomFieldIndex == 0
1301:                    try {
1302:                        String tok = "";
1303:                        int col = 0;
1304:
1305:                        while (!mid.isEmpty()) {
1306:                            tok = mid.getToken(chDelimiter, false, true);
1307:
1308:                            if (!fieldValueSetters[++col].setString(tok)) {
1309:                                LOGGER.severe("Bad value:"
1310:                                        + fieldValueSetters[col].getError());
1311:                            }
1312:
1313:                            inputBuffer[col] = fieldValueSetters[col]
1314:                                    .getValue();
1315:                        }
1316:
1317:                        if (!mifText.equals("")) {
1318:                            // MIF_TEXT MUST BE the LAST Field for now
1319:                            inputBuffer[++col] = mifText;
1320:
1321:                            // a better approach could be using a separate array of value setters for MIF_ fields
1322:                            // (TEXT, ANGLE...)
1323:                        }
1324:
1325:                        if (col != (numAttribs - 1)) {
1326:                            throw new Exception(
1327:                                    "Bad number of attributes read on MID row "
1328:                                            + mid.getLineNumber() + ": found "
1329:                                            + col + ", expecting " + numAttribs);
1330:                        }
1331:                    } catch (Exception e) {
1332:                        throw new IOException("Error reading MID file, line "
1333:                                + mid.getLineNumber() + ": " + e.getMessage());
1334:                    }
1335:
1336:                    // Now add geometry and build the feature
1337:                    try {
1338:                        inputBuffer[0] = geom;
1339:                        feature = featureType.create(inputBuffer);
1340:                    } catch (Exception e) {
1341:                        throw new IOException("Exception building feature: "
1342:                                + e.getMessage());
1343:                    }
1344:
1345:                    return feature;
1346:                }
1347:
1348:                /**
1349:                 * Reads one geometric object from the MIF file
1350:                 *
1351:                 * @return The geometry object
1352:                 *
1353:                 * @throws IOException Error retrieving geometry from input MIF stream
1354:                 */
1355:                private Geometry readGeometry() throws IOException {
1356:                    mifText = "";
1357:
1358:                    if (!mif.readLine()) {
1359:                        mifEOF = true;
1360:
1361:                        return null;
1362:                    }
1363:
1364:                    Geometry geom = null;
1365:
1366:                    try {
1367:                        // First of all reads geometry
1368:                        String objType = mif.getToken().toLowerCase();
1369:
1370:                        if (objType.equals(TYPE_NONE)) {
1371:                            geom = null;
1372:                        } else if (objType.equals(TYPE_POINT)) {
1373:                            geom = readPointObject();
1374:                        } else if (objType.equals(TYPE_LINE)) {
1375:                            geom = readLineObject();
1376:                        } else if (objType.equals(TYPE_PLINE)) {
1377:                            geom = readPLineObject();
1378:                        } else if (objType.equals(TYPE_REGION)) {
1379:                            geom = readRegionObject();
1380:                        } else if (objType.equals(TYPE_TEXT)) {
1381:                            geom = readTextObject();
1382:                        } else if (objType.equals(CLAUSE_PEN)
1383:                                || objType.equals(CLAUSE_SYMBOL)
1384:                                || objType.equals(CLAUSE_SMOOTH)
1385:                                || objType.equals(CLAUSE_CENTER)
1386:                                || objType.equals(CLAUSE_BRUSH)
1387:                                || objType.equals(CLAUSE_FONT)
1388:                                || objType.equals(CLAUSE_ANGLE)
1389:                                || objType.equals(CLAUSE_JUSTIFY)
1390:                                || objType.equals(CLAUSE_SPACING)
1391:                                || objType.equals(CLAUSE_LABEL)) {
1392:                            // Symply ignores styling clauses, so let's read the next lines
1393:                            geom = readGeometry();
1394:                        } else {
1395:                            // TODO add MultiPoint & Collection!!!
1396:                            throw new IOException(
1397:                                    "Unknown or unsupported object in mif file:"
1398:                                            + objType);
1399:                        }
1400:                    } catch (Exception e) {
1401:                        throw new IOException("File " + mifFile.getName()
1402:                                + ", line " + mif.getLineNumber() + ": "
1403:                                + e.getMessage());
1404:                    }
1405:
1406:                    return geom;
1407:                }
1408:
1409:                /**
1410:                 * Reads Multi-Line (PLine) information from the MIF stream
1411:                 *
1412:                 * @return The (MULTI)LINESTRING object read
1413:                 *
1414:                 * @throws IOException Error retrieving geometry from input MIF stream
1415:                 */
1416:                private Geometry readPLineObject() throws IOException {
1417:                    try {
1418:                        String tmp = mif.getToken(' ', true);
1419:                        int numsections = 1;
1420:                        int numpoints = 0;
1421:
1422:                        if (tmp.equalsIgnoreCase("MULTIPLE")) {
1423:                            numsections = Integer.parseInt(mif.getToken(' ',
1424:                                    true)); //read the number of sections
1425:                            numpoints = Integer.parseInt(mif
1426:                                    .getToken(' ', true)); //read the number of points
1427:                        } else {
1428:                            // already got the number of points, simply parse it
1429:                            numpoints = Integer.parseInt(tmp);
1430:                        }
1431:
1432:                        LineString[] lineStrings = new LineString[numsections];
1433:
1434:                        // Read each polyline
1435:                        for (int i = 0; i < lineStrings.length; i++) {
1436:                            if (numpoints == 0) {
1437:                                numpoints = Integer.parseInt(mif.getToken(' ',
1438:                                        true));
1439:                            }
1440:
1441:                            Coordinate[] coords = new Coordinate[numpoints];
1442:
1443:                            // Read each point
1444:                            for (int p = 0; p < coords.length; p++) {
1445:                                coords[p] = readMIFCoordinate();
1446:                            }
1447:
1448:                            numpoints = 0;
1449:
1450:                            lineStrings[i] = geomFactory
1451:                                    .createLineString(coords);
1452:                        }
1453:
1454:                        if ((numsections == 1) && !toGeometryCollection) {
1455:                            return (Geometry) lineStrings[0];
1456:                        }
1457:
1458:                        return (Geometry) geomFactory
1459:                                .createMultiLineString(lineStrings);
1460:                    } catch (Exception e) {
1461:                        throw new IOException(
1462:                                "Exception reading PLine data from MIF file : "
1463:                                        + e.toString());
1464:                    }
1465:                }
1466:
1467:                /**
1468:                 * Reads Region (Polygon) information from the MIF stream
1469:                 *
1470:                 * @return The (MULTI)POLYGON object
1471:                 *
1472:                 * @throws IOException Error retrieving geometry from input MIF stream
1473:                 */
1474:                private Geometry readRegionObject() throws IOException {
1475:                    try {
1476:                        int numpolygons = Integer.parseInt(mif.getToken(' ',
1477:                                true));
1478:
1479:                        Vector polygons = new Vector();
1480:
1481:                        LinearRing tmpRing = null;
1482:                        Polygon shell = null;
1483:                        LinearRing shellRing = null;
1484:                        Vector holes = null;
1485:                        boolean savePolygon;
1486:
1487:                        // Read all linearrings;
1488:                        for (int i = 0; i < numpolygons; i++) {
1489:                            // Read coordinates & create ring
1490:                            int numpoints = Integer.parseInt(mif.getToken(' ',
1491:                                    true));
1492:                            Coordinate[] coords = new Coordinate[numpoints + 1];
1493:
1494:                            for (int p = 0; p < numpoints; p++) {
1495:                                coords[p] = readMIFCoordinate();
1496:                            }
1497:
1498:                            coords[coords.length - 1] = coords[0];
1499:                            tmpRing = geomFactory.createLinearRing(coords);
1500:
1501:                            /*
1502:                             * In MIF format a polygon is described as a list of rings, with no info wether
1503:                             * a ring is a hole or a shell, so we have to determine it by checking if
1504:                             * a ring in contained in the previously defined shell
1505:                             */
1506:                            if ((shell != null) && shell.contains(tmpRing)) {
1507:                                holes.add(tmpRing);
1508:                                tmpRing = null; // mark as done
1509:                                savePolygon = (i == (numpolygons - 1));
1510:                            } else {
1511:                                // New polygon, must save previous if it's not the first ring
1512:                                savePolygon = (i > 0);
1513:                            }
1514:
1515:                            if (savePolygon) {
1516:                                LinearRing[] h = null;
1517:
1518:                                if (holes.size() > 0) {
1519:                                    h = new LinearRing[holes.size()];
1520:
1521:                                    for (int hole = 0; hole < holes.size(); hole++) {
1522:                                        h[hole] = (LinearRing) holes.get(hole);
1523:                                    }
1524:                                }
1525:
1526:                                polygons.add(geomFactory.createPolygon(
1527:                                        shellRing, h));
1528:
1529:                                shellRing = null;
1530:                            }
1531:
1532:                            // Build the polygon needed for testing holes
1533:                            if (tmpRing != null) {
1534:                                shellRing = tmpRing;
1535:                                shell = geomFactory.createPolygon(shellRing,
1536:                                        null);
1537:                                holes = new Vector();
1538:                            }
1539:                        }
1540:
1541:                        if (shellRing != null) {
1542:                            polygons.add(geomFactory.createPolygon(shellRing,
1543:                                    null));
1544:                        }
1545:
1546:                        try {
1547:                            if ((polygons.size() == 1) && !toGeometryCollection) {
1548:                                return (Polygon) polygons.get(0);
1549:                            }
1550:
1551:                            Polygon[] polys = new Polygon[polygons.size()];
1552:
1553:                            for (int i = 0; i < polygons.size(); i++) {
1554:                                polys[i] = (Polygon) polygons.get(i);
1555:                            }
1556:
1557:                            return geomFactory.createMultiPolygon(polys);
1558:                        } catch (TopologyException topexp) {
1559:                            throw new TopologyException(
1560:                                    "TopologyException reading Region polygon : "
1561:                                            + topexp.toString());
1562:                        }
1563:                    } catch (Exception e) {
1564:                        throw new IOException(
1565:                                "Exception reading Region data from MIF file : "
1566:                                        + e.toString());
1567:                    }
1568:                }
1569:
1570:                /**
1571:                 * Reads a couple of coordinates (x,y) from input stream, applying the
1572:                 * transform factor if required.
1573:                 *
1574:                 * @return A Coordinate object, or null if error encountered
1575:                 *
1576:                 * @throws IOException if couldn't build a valid Coordinate object
1577:                 */
1578:                private Coordinate readMIFCoordinate() throws IOException {
1579:                    String x;
1580:                    String y;
1581:
1582:                    try {
1583:                        x = mif.getToken(' ', true);
1584:                        y = mif.getToken();
1585:
1586:                        if (x.equals("") || y.equals("")) {
1587:                            throw new IOException("End of file.");
1588:                        }
1589:
1590:                        Coordinate result = new Coordinate(Double
1591:                                .parseDouble(x), Double.parseDouble(y));
1592:
1593:                        if (useTransform) {
1594:                            result.x = (result.x * multX) + sumX;
1595:                            result.y = (result.y * multY) + sumY;
1596:                        }
1597:
1598:                        return result;
1599:                    } catch (Exception e) {
1600:                        throw new IOException("Error getting coordinates: "
1601:                                + e.toString());
1602:                    }
1603:                }
1604:
1605:                /**
1606:                 * Reads Point information from the MIF stream
1607:                 *
1608:                 * @return The next POINT object read
1609:                 *
1610:                 * @throws IOException Error retrieving geometry from input MIF stream
1611:                 */
1612:                private Geometry readPointObject() throws IOException {
1613:                    return geomFactory.createPoint(readMIFCoordinate());
1614:                }
1615:
1616:                /**
1617:                 * Reads Line information from the MIF stream
1618:                 *
1619:                 * @return a LINESTRING object
1620:                 *
1621:                 * @throws IOException Error retrieving geometry from input MIF stream
1622:                 */
1623:                private Geometry readLineObject() throws IOException {
1624:                    Coordinate[] cPoints = new Coordinate[2];
1625:                    cPoints[0] = readMIFCoordinate();
1626:                    cPoints[1] = readMIFCoordinate();
1627:
1628:                    LineString[] result = { geomFactory
1629:                            .createLineString(cPoints) };
1630:
1631:                    if (toGeometryCollection) {
1632:                        return geomFactory.createMultiLineString(result);
1633:                    }
1634:
1635:                    return result[0];
1636:                }
1637:
1638:                private Geometry readTextObject() throws IOException {
1639:                    try {
1640:                        mifText = mif.getToken(' ', true, true);
1641:                    } catch (ParseException e) {
1642:                        throw new IOException(e.getMessage());
1643:                    }
1644:
1645:                    Coordinate c1 = readMIFCoordinate();
1646:                    Coordinate c2 = readMIFCoordinate();
1647:                    Coordinate p = new Coordinate((c1.x + c2.x) / 2,
1648:                            (c1.y + c2.y) / 2);
1649:
1650:                    return geomFactory.createPoint(p);
1651:                }
1652:            }
1653:
1654:            /**
1655:             * <p>
1656:             * MIF FeatureWriter
1657:             * </p>
1658:             */
1659:            private class Writer implements  FeatureWriter {
1660:                private PrintStream outMif = null;
1661:                private PrintStream outMid = null;
1662:                private FeatureReader innerReader = null;
1663:                private MIFValueSetter[] fieldValueSetters;
1664:                private Feature editFeature = null;
1665:                private Feature originalFeature = null;
1666:
1667:                private Writer(PrintStream mif, PrintStream mid, boolean append)
1668:                        throws IOException {
1669:                    innerReader = getFeatureReader();
1670:
1671:                    try {
1672:                        fieldValueSetters = getValueSetters();
1673:                    } catch (SchemaException e) {
1674:                        throw new IOException(e.getMessage());
1675:                    }
1676:
1677:                    outMif = mif;
1678:                    outMid = mid;
1679:
1680:                    try {
1681:                        if (!append) {
1682:                            outMif.println(exportHeader());
1683:                        }
1684:                    } catch (Exception e) {
1685:                        outMif = null;
1686:                        outMid = null;
1687:                        throw new IOException(e.getMessage());
1688:                    }
1689:                }
1690:
1691:                public FeatureType getFeatureType() {
1692:                    return featureType;
1693:                }
1694:
1695:                public Feature next() throws IOException {
1696:                    try {
1697:                        if (originalFeature != null) {
1698:                            writeFeature(originalFeature); // keep the original
1699:                        }
1700:
1701:                        if (innerReader.hasNext()) {
1702:                            originalFeature = innerReader.next(); // ;
1703:                            editFeature = featureType
1704:                                    .duplicate(originalFeature);
1705:                        } else {
1706:                            originalFeature = null;
1707:                            editFeature = featureType.create(featureDefaults);
1708:                        }
1709:
1710:                        return editFeature;
1711:                    } catch (Exception e) {
1712:                        throw new IOException(e.toString());
1713:                    }
1714:                }
1715:
1716:                public void remove() throws IOException {
1717:                    if (editFeature == null) {
1718:                        throw new IOException("Current feature is null");
1719:                    }
1720:
1721:                    editFeature = null;
1722:                    originalFeature = null;
1723:                }
1724:
1725:                public void write() throws IOException {
1726:                    if (editFeature == null) {
1727:                        throw new IOException("Current feature is null");
1728:                    }
1729:
1730:                    try {
1731:                        writeFeature(editFeature);
1732:                    } catch (Exception e) {
1733:                        editFeature = null;
1734:                        throw new IOException("Can't write feature: "
1735:                                + e.toString());
1736:                    }
1737:
1738:                    editFeature = null;
1739:                    originalFeature = null;
1740:                }
1741:
1742:                public boolean hasNext() throws IOException {
1743:                    return innerReader.hasNext();
1744:                }
1745:
1746:                public void close() throws IOException {
1747:                    while (hasNext())
1748:                        next();
1749:
1750:                    try {
1751:                        if (originalFeature != null) {
1752:                            writeFeature(originalFeature); // keep the original
1753:                        }
1754:                    } catch (Exception e) {
1755:                    }
1756:
1757:                    innerReader.close();
1758:                    innerReader = null;
1759:
1760:                    try {
1761:                        if (outMif != null) {
1762:                            outMif.close();
1763:                        }
1764:
1765:                        if (outMid != null) {
1766:                            outMid.close();
1767:                        }
1768:
1769:                        copyFileAndDelete(mifFileOut, mifFile, true);
1770:                        copyFileAndDelete(midFileOut, midFile, true);
1771:                    } catch (IOException e) {
1772:                    } finally {
1773:                        outMid = null;
1774:                        outMif = null;
1775:                    }
1776:                }
1777:
1778:                protected void finalize() throws Throwable {
1779:                    close();
1780:                    super .finalize();
1781:                }
1782:
1783:                /**
1784:                 * Writes the given Feature to file
1785:                 *
1786:                 * @param f The feature to write
1787:                 *
1788:                 * @throws IOException if cannot access file for reading
1789:                 * @throws SchemaException if given Feature is not compatible with
1790:                 *         MIFFile FeatureType. TODO: private
1791:                 */
1792:                public void writeFeature(Feature f) throws IOException,
1793:                        SchemaException {
1794:                    if ((outMif == null) || (outMid == null)) {
1795:                        throw new IOException(
1796:                                "Output stream has not been opened for writing.");
1797:                    }
1798:
1799:                    Geometry theGeom = (geomFieldIndex >= 0) ? (Geometry) f
1800:                            .getAttribute(geomFieldIndex) : null;
1801:                    String outGeom = exportGeometry(theGeom);
1802:
1803:                    if (outGeom.equals("")) {
1804:                        throw new SchemaException("Unsupported geometry type: "
1805:                                + theGeom.getClass().getName());
1806:                    }
1807:
1808:                    outMif.println(outGeom);
1809:
1810:                    int col;
1811:                    String outBuf = "";
1812:
1813:                    try {
1814:                        for (col = 1; col < numAttribs; col++) {
1815:                            fieldValueSetters[col]
1816:                                    .setValue(f.getAttribute(col));
1817:
1818:                            if (col > 1) {
1819:                                outBuf += chDelimiter;
1820:                            }
1821:
1822:                            outBuf += fieldValueSetters[col].getString();
1823:                        }
1824:                    } catch (Exception e) {
1825:                        throw new IOException("Error writing MID file: "
1826:                                + e.getMessage());
1827:                    }
1828:
1829:                    outMid.println(outBuf);
1830:                }
1831:
1832:                private String exportGeometry(Geometry geom) {
1833:                    // Style information is optional, so we will not export the default styles
1834:                    if ((geom == null) || (geom.isEmpty())) {
1835:                        return TYPE_NONE.toUpperCase();
1836:                    }
1837:
1838:                    if (geom instanceof  Point) {
1839:                        return TYPE_POINT + " "
1840:                                + exportCoord(((Point) geom).getCoordinate());
1841:                    }
1842:
1843:                    if (geom instanceof  LineString) {
1844:                        Coordinate[] coords = geom.getCoordinates();
1845:
1846:                        return TYPE_PLINE + " " + exportCoords(coords, false);
1847:                    }
1848:
1849:                    if (geom instanceof  MultiPolygon) {
1850:                        MultiPolygon mpoly = (MultiPolygon) geom;
1851:
1852:                        int nPol = mpoly.getNumGeometries();
1853:                        int nRings = nPol;
1854:
1855:                        for (int i = 0; i < nPol; i++) {
1856:                            nRings += ((Polygon) mpoly.getGeometryN(i))
1857:                                    .getNumInteriorRing();
1858:                        }
1859:
1860:                        String buf = TYPE_REGION + " " + (nRings);
1861:
1862:                        for (int i = 0; i < nPol; i++) {
1863:                            Polygon poly = (Polygon) mpoly.getGeometryN(i);
1864:
1865:                            buf += ("\n" + exportCoords(poly.getExteriorRing()
1866:                                    .getCoordinates(), true));
1867:
1868:                            for (int inner = 0; inner < poly
1869:                                    .getNumInteriorRing(); inner++) {
1870:                                buf += ("\n" + exportCoords(poly
1871:                                        .getInteriorRingN(inner)
1872:                                        .getCoordinates(), true));
1873:                            }
1874:                        }
1875:
1876:                        return buf;
1877:                    }
1878:
1879:                    if (geom instanceof  Polygon) {
1880:                        Polygon poly = (Polygon) geom;
1881:                        int nRings = poly.getNumInteriorRing();
1882:                        String buf = TYPE_REGION + " " + (1 + nRings) + "\n";
1883:                        buf += exportCoords(poly.getExteriorRing()
1884:                                .getCoordinates(), true);
1885:
1886:                        for (int i = 0; i < nRings; i++) {
1887:                            buf += ("\n" + exportCoords(poly
1888:                                    .getInteriorRingN(i).getCoordinates(), true));
1889:                        }
1890:
1891:                        return buf;
1892:                    }
1893:
1894:                    if (geom instanceof  MultiLineString) {
1895:                        MultiLineString multi = (MultiLineString) geom;
1896:                        String buf = TYPE_PLINE + " Multiple "
1897:                                + multi.getNumGeometries();
1898:
1899:                        for (int i = 0; i < multi.getNumGeometries(); i++) {
1900:                            buf += ("\n" + exportCoords(((LineString) multi
1901:                                    .getGeometryN(i)).getCoordinates(), false));
1902:                        }
1903:
1904:                        return buf;
1905:                    }
1906:
1907:                    return "";
1908:                }
1909:
1910:                /**
1911:                 * Renders a single coordinate
1912:                 *
1913:                 * @param coord The Coordinate object
1914:                 *
1915:                 * @return The coordinate as string
1916:                 */
1917:                private String exportCoord(Coordinate coord) {
1918:                    return coord.x + " " + coord.y;
1919:                }
1920:
1921:                /**
1922:                 * Renders a coordinate list, prefixing it with the number of points
1923:                 * SkipLast is used for Polygons (in Mapinfo the last vertex of a
1924:                 * polygon is not the clone of first one)
1925:                 *
1926:                 * @param coords The coordinates to render
1927:                 * @param skipLast if true, a polygon coordinate list will be rendered
1928:                 *
1929:                 * @return the coordinate list as string
1930:                 */
1931:                private String exportCoords(Coordinate[] coords,
1932:                        boolean skipLast) {
1933:                    int len = (skipLast) ? (coords.length - 1) : coords.length;
1934:
1935:                    String buf = String.valueOf(len);
1936:
1937:                    for (int i = 0; i < len; i++) {
1938:                        buf += ("\n" + exportCoord(coords[i]));
1939:                    }
1940:
1941:                    return buf;
1942:                }
1943:            }
1944:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.