From 9299ee8ad709aaf4131af10afeeb2e43b374fba3 Mon Sep 17 00:00:00 2001 From: ?? ? <ulysseskao@ximple.com.tw> Date: Mon, 10 Mar 2008 01:53:54 +0800 Subject: [PATCH] update for EOFM-8 --- ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java | 114 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java | 166 + .gitattributes | 27 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java | 31 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java | 230 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java | 271 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java | 18 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java | 58 ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java | 166 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java | 263 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java | 238 + ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java | 4774 +++++++++++++++++++++++++++++ ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java | 56 ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java | 291 + ximple-dgnio/pom.xml | 443 ++ ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java | 45 ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java | 34 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java | 193 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java | 718 ++++ ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java | 71 ximple-dgnio/.project | 23 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java | 16 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java | 534 +++ ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java | 129 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java | 246 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java | 90 ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java | 296 + ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java | 23 28 files changed, 9,564 insertions(+), 0 deletions(-) diff --git a/.gitattributes b/.gitattributes index 8e948c5..7715a78 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,28 @@ * text=auto !eol +ximple-dgnio/.project svneol=native#text/xml +ximple-dgnio/pom.xml svneol=native#text/xml +ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java svneol=native#text/plain +ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java svneol=native#text/plain diff --git a/ximple-dgnio/.project b/ximple-dgnio/.project new file mode 100644 index 0000000..4198653 --- /dev/null +++ b/ximple-dgnio/.project @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>ximple-dgnio</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.maven.ide.eclipse.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.maven.ide.eclipse.maven2Nature</nature> + </natures> +</projectDescription> diff --git a/ximple-dgnio/pom.xml b/ximple-dgnio/pom.xml new file mode 100644 index 0000000..7cacdd1 --- /dev/null +++ b/ximple-dgnio/pom.xml @@ -0,0 +1,443 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.ximple</groupId> + <artifactId>ximple-dgnio</artifactId> + <packaging>pom</packaging> + <version>1.0.0-SNAPSHOT</version> + <name>ximple-dgnio-1.0.x</name> + + <scm> + <connection> + scm:svn:http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/ + </connection> + <url>http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/</url> + </scm> + + <description>Ximple Dgn IO Library</description> + + <organization> + <name>Ximple</name> + <url>http://www.ximple.com.tw</url> + </organization> + + <inceptionYear>2008</inceptionYear> + + <!-- =========================================================== --> + <!-- Issue managements and mailing lists. --> + <!-- =========================================================== --> + <issueManagement> + <system>JIRA</system> + <url>http://www.ximple.com.tw/jira/browse/EOFMS</url> + </issueManagement> + + <!-- =========================================================== --> + <!-- Continuous Integration --> + <!-- =========================================================== --> + <ciManagement> + <system>continuum</system> + </ciManagement> + + <mailingLists> + <mailingList> + </mailingList> + </mailingLists> + + <developers> + <developer> + </developer> + </developers> + + <contributors> + </contributors> + + <dependencies> + <dependency> + <groupId>org.vividsolutions</groupId> + <artifactId>jts</artifactId> + <version>1.9</version> + </dependency> + <dependency> + <groupId>com.testng</groupId> + <artifactId>testng</artifactId> + <classifier>jdk15</classifier> + <version>5.7</version> + </dependency> + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + <version>3.2</version> + </dependency> + <dependency> + <groupId>org.apache.poi</groupId> + <artifactId>poi-contrib</artifactId> + <version>3.0.1-FINAL</version> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>1.2.15</version> + <!-- Same as the dependency in commons-logging --> + </dependency> + </dependencies> + + <!-- =========================================================== --> + <!-- Build Configuration --> + <!-- =========================================================== --> + <build> + <!-- ========================================================= --> + <!-- Maven plugins dependencies management. --> + <!-- It should not be needed since Maven select by default --> + <!-- the latest plugins. Unfortunatly, experience shows that --> + <!-- new plugin releases sometime introduce new bugs that --> + <!-- break our build. So it is saferto specify plugin --> + <!-- versions that are known to work. This list is in --> + <!-- alphabetical order for easier comparaison with latest --> + <!-- plugins at --> + <!-- http://www.ibiblio.org/maven2/org/apache/maven/plugins/ --> + <!-- ========================================================= --> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.1</version> + <configuration> + <descriptors> + <descriptor>build/maven/assembly/binaryDist.xml</descriptor> + <descriptor>build/maven/assembly/sourceDist.xml</descriptor> + </descriptors> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-clean-plugin</artifactId> + <version>2.1.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-clover-plugin</artifactId> + <version>2.3</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.0.2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-install-plugin</artifactId> + <version>2.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-plugin-plugin</artifactId> + <version>2.3</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>2.2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>2.0.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <version>2.2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-site-plugin</artifactId> + <version>2.0-beta-5</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.2</version> + <!-- Current version is 2.3, but it cause the following exception: + + Running org.geotools.gce.imagepyramid.ImagePyramidReaderTest + Error: Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode. + Occurs in: com.sun.media.jai.mlib.MediaLibAccessor + java.lang.NoClassDefFoundError: com/sun/medialib/mlib/Image + at com.sun.media.jai.mlib.MediaLibAccessor.setUseMlib(MediaLibAccessor.java:245) + at com.sun.media.jai.mlib.MlibAffineRIF.create(MlibAffineRIF.java:71) + --> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-report-plugin</artifactId> + <version>2.3</version> + </plugin> + + <!-- http://www.ibiblio.org/maven2/org/codehaus/mojo/ --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>changelog-maven-plugin</artifactId> + <version>2.0-beta-1</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>changes-maven-plugin</artifactId> + <version>2.0-beta-1</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jxr-maven-plugin</artifactId> + <version>2.0-beta-1</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>taglist-maven-plugin</artifactId> + <version>2.0</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jalopy-maven-plugin</artifactId> + <version>1.0-SNAPSHOT</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <configuration> + <outputDirectory>${src.output}</outputDirectory> + <attach>false</attach> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.4</version> + </plugin> + </plugins> + </pluginManagement> + + <!-- http://www.ibiblio.org/maven2/org/apache/maven/wagon/ --> + <extensions> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-webdav</artifactId> + <version>1.0-beta-2</version> + </extension> + </extensions> + + <plugins> + <!-- ======================================================= --> + <!-- Source reformat --> + <!-- (activated only on request, jalopy:format) --> + <!-- See developer's guide for automated activation --> + <!-- ======================================================= --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jalopy-maven-plugin</artifactId> + <configuration> + <convention>gt2/jalopygeotools.xml</convention> + <failOnError>false</failOnError> + </configuration> + <dependencies> + <dependency> + <groupId>org.geotools.maven</groupId> + <artifactId>gt2-build-configs</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + + + <!-- ======================================================= --> + <!-- Compilation. --> + <!-- ======================================================= --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <!-- The -source argument for the Java compiler. --> + <target>1.5</target> + <!-- The -target argument for the Java compiler. --> + <debug>true</debug> + <!-- Whether to include debugging information. --> + <encoding>ISO-8859-1</encoding> + <!-- The -encoding argument for the Java compiler. --> + </configuration> + </plugin> + + + <!-- ======================================================= --> + <!-- Tests. --> + <!-- ======================================================= --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <includes> + <include>**/*Test.java</include> + </includes> + <excludes> + <exclude>${online.skip.pattern}</exclude> + <exclude>${stress.skip.pattern}</exclude> + </excludes> + <argLine>-Xmx${test.maxHeapSize} -Dorg.geotools.test.extensive=${extensive.tests} + -Dorg.geotools.test.interactive=${interactive.tests} -Djava.awt.headless=${java.awt.headless} + </argLine> + <!-- Ignores test failure only if we are generating a --> + <!-- report for publication on the web site. See the --> + <!-- profiles section at the begining of this pom.xml file. --> + <testFailureIgnore> + ${allow.test.failure.ignore} + </testFailureIgnore> + + <!-- The two following options have the opposite value of what we would + like. They are that way because they don't seem to work as expected + with Surefire 2.3. TODO: Try again when Surefire 2.4 will be available. --> + + <!-- Option to print summary of test suites or just print the test cases that has errors. --> + <printSummary>true</printSummary> + <!-- Redirect the unit test standard output to a file. --> + <redirectTestOutputToFile>false</redirectTestOutputToFile> + </configuration> + </plugin> + + + <!-- ======================================================= --> + <!-- Code coverage --> + <!-- ======================================================= --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-clover-plugin</artifactId> + <configuration> + <jdk>1.5</jdk> + <licenseLocation> + http://svn.geotools.org/geotools/branches/2.4.x/build/maven/build-configs/src/main/resources/gt2/clover.license + </licenseLocation> + <flushPolicy>directed</flushPolicy> + </configuration> + <executions> + <execution> + <phase>pre-site</phase> + <goals> + <goal>instrument</goal> + <!-- aggregation is disabled due to the bug: --> + <!-- http://jira.codehaus.org/browse/MCLOVER-34 --> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.geotools.maven</groupId> + <artifactId>gt2-build-configs</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + + + <!-- ======================================================= --> + <!-- JAR packaging. --> + <!-- ======================================================= --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifest> + <addClasspath>true</addClasspath> + </manifest> + </archive> + </configuration> + </plugin> + + <!-- ======================================================= --> + <!-- Source packaging. --> + <!-- ======================================================= --> + <plugin> + <inherited>true</inherited> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <configuration> + <attach>true</attach> + </configuration> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + </build> + + <!-- =========================================================== --> + <!-- Repositories (ibiblio, refractions...). --> + <!-- This is where Maven looks for dependencies. --> + <!-- =========================================================== --> + <repositories> + <repository> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>central</id> + <name>Ximple Artifactory Maven Repository Switchboard</name> + <url>http://www.ximple.com.tw/artifactory/repo</url> + </repository> + + <repository> + <releases> + <enabled>false</enabled> + </releases> + <id>snapshots</id> + <name>Ximple Artifactory Maven Repository Switchboard</name> + <url>http://www.ximple.com.tw/artifactory/repo</url> + </repository> + </repositories> + + + <!-- =========================================================== --> + <!-- Plugin repositories. --> + <!-- This is where Maven looks for plugin dependencies. --> + <!-- =========================================================== --> + <pluginRepositories> + <pluginRepository> + <id>ximple-snapshots</id> + <name>ximple-shapshots</name> + <url>http://www.ximple.com.tw/artifactory/vplugins-snapshots</url> + <snapshots> + <enabled>true</enabled> + </snapshots> + <releases> + <enabled>false</enabled> + </releases> + </pluginRepository> + <pluginRepository> + <id>ximple</id> + <name>Ximple Maven 2 Repository</name> + <url>http://www.ximple.com.tw/artifactory/vplugins-releases</url> + <snapshots> + <enabled>false</enabled> + </snapshots> + <releases> + <enabled>true</enabled> + </releases> + </pluginRepository> + </pluginRepositories> +</project> \ No newline at end of file diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java new file mode 100644 index 0000000..0e0a507 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java @@ -0,0 +1,166 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * ArcElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/26 �U�� 06:41:45 + */ +public class ArcElement extends Element implements GeometryConverter +{ + public ArcElement(short[] raw) + { + super(raw); + } + + public double getPrimary() + { + short[] primary = new short[4]; + + System.arraycopy(raw, 22, primary, 0, 4); + + return Utility.DGNToIEEEDouble(primary) / 1000.0; + } + + public void setPrimary(double value) + { + double temp = value * 1000.0; + short[] primary = Utility.IEEEDoubleToDGN(temp); + + System.arraycopy(primary, 0, raw, 22, 4); + } + + public double getSecondary() + { + short[] secondary = new short[4]; + + System.arraycopy(raw, 26, secondary, 0, 4); + + return Utility.DGNToIEEEDouble(secondary) / 1000.0; + } + + public void setSecondary(double value) + { + double temp = value * 1000.0; + short[] secondary = Utility.IEEEDoubleToDGN(temp); + + System.arraycopy(secondary, 0, raw, 26, 4); + } + + public Coordinate getOrigin() + { + short[] x = new short[4]; + + System.arraycopy(raw, 32, x, 0, 4); + + double dx = Utility.ConverUnitToCoord((int) Utility.DGNToIEEEDouble(x)); + short[] y = new short[4]; + + System.arraycopy(raw, 36, y, 0, 4); + + double dy = Utility.ConverUnitToCoord((int) Utility.DGNToIEEEDouble(y)); + + return new Coordinate(dx, dy); + } + + public void setOrigin(Coordinate value) + { + double temp = Utility.ConverCoordToUnit(value.x); + short[] x = Utility.IEEEDoubleToDGN(temp); + + System.arraycopy(x, 0, raw, 32, 4); + temp = Utility.ConverCoordToUnit(value.y); + + short[] y = Utility.IEEEDoubleToDGN(temp); + + System.arraycopy(y, 0, raw, 36, 4); + } + + public double getStartAngle() + { + int angle = (int) (raw[18] << 16 & 0xffff0000); + + angle += raw[19] & 0x0000ffff; + + return Utility.ConverIntToRotation(angle); + } + + public void setStartAngle(double value) + { + int angle = Utility.ConverRotatioToInt(value); + + raw[18] = (short) (angle >>> 16 & 0x0000ffff); + raw[19] = (short) (angle & 0x0000ffff); + } + + public double getSweepAngle() + { + int angle = (int) (raw[20] << 16 & 0xffff0000); + + angle += raw[21] & 0x0000ffff; + + return Utility.ConverIntToRotation(angle); + } + + public void setSweepAngle(double value) + { + int angle = Utility.ConverRotatioToInt(value); + + raw[20] = (short) (angle >> 16 & 0x0000ffff); + raw[21] = (short) (angle & 0x0000ffff); + } + + public double getRotationAngle() + { + int rotation = (int) (raw[30] << 16 & 0xffff0000); + + rotation += raw[31] & 0x0000ffff; + + return Utility.ConverIntToRotation(rotation); + } + + public void setRotationAngle(double value) + { + int angle = Utility.ConverRotatioToInt(value); + + raw[30] = (short) (angle >> 16 & 0x0000ffff); + raw[31] = (short) (angle & 0x0000ffff); + } + + public Geometry toGeometry(GeometryFactory factory) + { + return null; // To change body of implemented methods use File | Settings | File Templates. + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.ARC); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new ArcElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java new file mode 100644 index 0000000..3407d2a --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java @@ -0,0 +1,230 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * ComplexChainElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 03:44:56 + */ +public class ComplexChainElement extends Element implements ComplexElement, GeometryConverter +{ + ArrayList list = new ArrayList(); + + public ComplexChainElement(short[] raw) + { + super(raw); + attrOffset = 4; + } + + public int size() + { + return list.size(); + } + + public boolean isEmpty() + { + return list.isEmpty(); + } + + public boolean contains(Object o) + { + return list.contains(o); + } + + public Iterator iterator() + { + return list.iterator(); + } + + public Object[] toArray() + { + return list.toArray(); + } + + public boolean add(Object o) + { + return list.add(o); + } + + public boolean remove(Object o) + { + return list.remove(o); + } + + public boolean addAll(Collection c) + { + return list.addAll(c); + } + + public boolean addAll(int index, Collection c) + { + return list.addAll(index, c); + } + + public void clear() + { + list.clear(); + } + + public Object get(int index) + { + return list.get(index); + } + + public Object set(int index, Object element) + { + return list.set(index, element); + } + + public void add(int index, Object element) + { + list.add(index, element); + } + + public Object remove(int index) + { + return list.remove(index); + } + + public int indexOf(Object o) + { + return list.indexOf(o); + } + + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + public ListIterator listIterator() + { + return list.listIterator(); + } + + public ListIterator listIterator(int index) + { + return list.listIterator(index); + } + + public List subList(int fromIndex, int toIndex) + { + return list.subList(fromIndex, toIndex); + } + + public boolean retainAll(Collection c) + { + return list.retainAll(c); + } + + public boolean removeAll(Collection c) + { + return list.removeAll(c); + } + + public boolean containsAll(Collection c) + { + return list.containsAll(c); + } + + public Object[] toArray(Object[] a) + { + return list.toArray(a); + } + + public Geometry toGeometry(GeometryFactory factory) + { + ArrayList list = new ArrayList(); + + if (size() == 1) + { + Element element = (Element) get(0); + + if (element instanceof LineStringElement) + { + return ((LineStringElement) element).toGeometry(factory); + } else if (element instanceof LineElement) + { + return ((LineElement) element).toGeometry(factory); + } else + { + if (element instanceof GeometryConverter) + { + return ((GeometryConverter) element).toGeometry(factory); + } + + return null; + } + } + + for (ListIterator it = listIterator(); it.hasNext(); ) + { + Element element = (Element) it.next(); + + if (element instanceof LineStringElement) + { + list.add(((LineStringElement) element).toGeometry(factory)); + } else if (element instanceof LineElement) + { + list.add(((LineElement) element).toGeometry(factory)); + } + } + + Geometry[] ga = (Geometry[]) list.toArray(new Geometry[list.size()]); + GeometryCollection geos = new GeometryCollection(ga, factory); + + return geos; + } + + public double getElementSize() + { + return raw[18]; + } + + public boolean isClosed() + { + if (isEmpty()) + { + return false; + } + + return false; + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.COMPLEXCHAIN); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new ComplexChainElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java new file mode 100644 index 0000000..08fa5ae --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java @@ -0,0 +1,16 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * ComplexElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 04:17:37 + */ +public interface ComplexElement extends List +{ +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java new file mode 100644 index 0000000..6bbda8b --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java @@ -0,0 +1,193 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * ComplexShapeElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 03:45:15 + */ +public class ComplexShapeElement extends Element implements ComplexElement, GeometryConverter +{ + ArrayList list = new ArrayList(); + + public ComplexShapeElement(short[] raw) + { + super(raw); + } + + public int size() + { + return list.size(); + } + + public boolean isEmpty() + { + return list.isEmpty(); + } + + public boolean contains(Object o) + { + return list.contains(o); + } + + public Iterator iterator() + { + return list.iterator(); + } + + public Object[] toArray() + { + return list.toArray(); + } + + public boolean add(Object o) + { + return list.add(o); + } + + public boolean remove(Object o) + { + return list.remove(o); + } + + public boolean addAll(Collection c) + { + return list.addAll(c); + } + + public boolean addAll(int index, Collection c) + { + return list.addAll(index, c); + } + + public void clear() + { + list.clear(); + } + + public Object get(int index) + { + return list.get(index); + } + + public Object set(int index, Object element) + { + return list.set(index, element); + } + + public void add(int index, Object element) + { + list.add(index, element); + } + + public Object remove(int index) + { + return list.remove(index); + } + + public int indexOf(Object o) + { + return list.indexOf(o); + } + + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + public ListIterator listIterator() + { + return list.listIterator(); + } + + public ListIterator listIterator(int index) + { + return list.listIterator(index); + } + + public List subList(int fromIndex, int toIndex) + { + return list.subList(fromIndex, toIndex); + } + + public boolean retainAll(Collection c) + { + return list.retainAll(c); + } + + public boolean removeAll(Collection c) + { + return list.removeAll(c); + } + + public boolean containsAll(Collection c) + { + return list.containsAll(c); + } + + public Object[] toArray(Object[] a) + { + return list.toArray(a); + } + + public Geometry toGeometry(GeometryFactory factory) + { + ArrayList list = new ArrayList(); + + for (ListIterator it = listIterator(); it.hasNext(); ) + { + Element element = (Element) it.next(); + + if (element instanceof LineStringElement) + { + list.add(((LineStringElement) element).toGeometry(factory)); + } else if (element instanceof LineElement) + { + list.add(((LineElement) element).toGeometry(factory)); + } + } + + Geometry[] ga = (Geometry[]) list.toArray(new Geometry[list.size()]); + GeometryCollection geos = new GeometryCollection(ga, factory); + + return geos; + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.COMPLEXSHAPE); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new ComplexShapeElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java new file mode 100644 index 0000000..f5936c7 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java @@ -0,0 +1,246 @@ +package com.ximple.io.dgn7; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Iterator; + +import org.apache.log4j.Logger; + +import oracle.jdbc.OracleConnection; +import oracle.sql.BLOB; + +/** + * Dgn7OracleReader + * User: Ulysses + * Date: 2007/10/24 + * Time: �U�� 01:01:08 + */ +public class Dgn7OracleReader implements Iterator<Element> +{ + private final static Logger logger = Logger.getLogger(Dgn7OracleReader.class); + + private String _sql; + private String _fieldName; + private Connection _connection; + private ResultSet _resultSet; + private static final int FETCHSIZE = 20; + private Element _element; + + public Dgn7OracleReader(String sql, String fieldName, OracleConnection connection) + { + this._sql = sql; + this._fieldName = fieldName; + this._connection = connection; + } + + public String getSql() + { + return _sql; + } + + public void setSql(String sql) + { + this._sql = sql; + } + + public String getFieldName() + { + return _fieldName; + } + + public void setFieldName(String fieldName) + { + this._fieldName = fieldName; + } + + public boolean hasNext() + { + if (_resultSet == null) + { + try + { + initializeReader(); + } catch (SQLException e) + { + throw new RuntimeException("initialize oralce error.", e); + } catch (Dgn7fileException e) + { + throw new RuntimeException("initialize oralce error.", e); + } + } + if (_element == null) + { + return false; + } + return true; + } + + public Element next() + { + Element result = _element; + + try + { + fetchElement(); + } catch (SQLException e) + { + throw new RuntimeException("Error:" + e.getMessage(), e); + } catch (Dgn7fileException e) + { + throw new RuntimeException("Error:" + e.getMessage(), e); + } + + return result; + } + + public void remove() + { + throw new RuntimeException("Not Support this method."); + } + + private boolean initializeReader() throws SQLException, Dgn7fileException + { + if (_resultSet != null) return true; + Statement stmtSrc = _connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + + stmtSrc.setFetchSize(FETCHSIZE); + _resultSet = stmtSrc.executeQuery(_sql); + + fetchElement(); + + return true; + } + + private boolean fetchElement() throws SQLException, Dgn7fileException + { + if (_resultSet.next()) + { + byte[] raw = null; + Object value = _resultSet.getObject(this._fieldName); + + if (value instanceof BLOB) + { + BLOB blob = (BLOB) value; + + try + { + raw = getBytesFromBLOB(blob); + } catch (IOException e) + { + throw new SQLException("IOError", e); + } + blob.close(); + } else if (value instanceof byte[]) + { + raw = (byte[]) value; + } + if (raw == null) + { + _element = null; + return false; + } + + ByteBuffer buffer = ByteBuffer.wrap(raw); + buffer.order(ByteOrder.LITTLE_ENDIAN); + short signature = buffer.getShort(); + + // byte type = (byte) (buffer.get() & 0x7f); + byte type = (byte) ((signature >>> 8) & 0x007f); + + // silly Bentley say contentLength is in 2-byte words + // and ByteByffer uses bytes. + // track the record location + int elementLength = (buffer.getShort() * 2) + 4; + ElementType recordType = ElementType.forID(type); + IElementHandler handler = recordType.getElementHandler(); + _element = (Element) handler.read(buffer, signature, elementLength); + if (recordType.isComplexElement() && (elementLength < raw.length)) + { + int offset = elementLength; + while (offset < (raw.length - 4)) + { + buffer.position(offset); + signature = buffer.getShort(); + type = (byte) ((signature >>> 8) & 0x007f); + elementLength = (buffer.getShort() * 2) + 4; + if (raw.length < (offset + elementLength)) + { + System.out.println("Length not match:" + offset +":"+ buffer.position() +":"+buffer.limit()); + break; + } + recordType = ElementType.forID(type); + handler = recordType.getElementHandler(); + if (handler != null) + { + Element subElement = (Element) handler.read(buffer, signature, elementLength); + ((ComplexElement) _element).add(subElement); + offset += elementLength; + } else + { + byte[] remain = new byte[buffer.remaining()]; + System.arraycopy(raw, offset, remain, 0, buffer.remaining()); + for (int i = 0; i < remain.length; i++) + { + if (remain[i] != 0) + { + logger.info("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]); + System.out.println("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]); + } + } + break; + } + } + } + + } else + { + _element = null; + return false; + } + return true; + } + + protected static byte[] getBytesFromBLOB(BLOB blob) throws SQLException, IOException + { + byte[] raw = null; + + // BLOB blob = (BLOB) rs.getBlob(1); + int optimalSize = blob.getChunkSize(); + byte[] chunk = new byte[optimalSize]; + InputStream is = blob.getBinaryStream(0); + ByteBuffer buffer = null; // ByteBuffer.allocate(optimalSize); + int len = 0; + + try + { + while ((len = (is.read(chunk))) != -1) + { + if (buffer != null) + { + buffer.limit(buffer.limit() + len); + } else + { + buffer = ByteBuffer.allocate(len); + } + + buffer.put(chunk); + } + + is.close(); + buffer.position(0); + raw = buffer.array(); + } catch (IOException e) + { + e.printStackTrace(); + throw e; + } + + return raw; + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java new file mode 100644 index 0000000..0d9f1b8 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java @@ -0,0 +1,31 @@ +package com.ximple.io.dgn7; + +/** + * Created by IntelliJ IDEA. + * User: Ulysses + * Date: 2007/9/13 + * Time: �W�� 11:19:08 + * To change this template use File | Settings | File Templates. + */ +public class Dgn7fileException extends Exception +{ + + public Dgn7fileException() + { + } + + public Dgn7fileException(String message) + { + super(message); + } + + public Dgn7fileException(String message, Throwable cause) + { + super(message, cause); + } + + public Dgn7fileException(Throwable cause) + { + super(cause); + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java new file mode 100644 index 0000000..0d50d54 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java @@ -0,0 +1,58 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.vividsolutions.jts.util.Assert; + +/** + * Dgn7fileHeader + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/17 �U�� 01:21:00 + */ +public class Dgn7fileHeader +{ + private short elmtype; + private byte[] raw; + + public Dgn7fileHeader() + { + } + + public void read(ByteBuffer file, boolean strict) throws IOException + { + file.order(ByteOrder.LITTLE_ENDIAN); + elmtype = file.getShort(); + + short wtf = file.getShort(); + int length = (wtf * 2); + + if (file.remaining() != (length)) + { + Assert.shouldNeverReachHere(); + } + + raw = new byte[length]; + file.get(raw, 0, file.remaining()); + } + + public String toString() + { + return "Dgn7fileHeader{" + "raw=" + ((raw == null) ? "null" : raw.length) + '}'; + } + + public int size() + { + if (raw == null) + { + return 0; + } + + return raw.length + 4; + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java new file mode 100644 index 0000000..475932c --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java @@ -0,0 +1,718 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.swing.*; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +import com.vividsolutions.jts.geom.GeometryFactory; + + +/** + * Dgn7fileReader + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/17 �U�� 01:24:10 + */ +public class Dgn7fileReader +{ + private static final Logger logger = LogManager.getLogger("com.isimple.glyphjump.io.dgn7"); + private Dgn7fileHeader header; + private ReadableByteChannel channel; + ByteBuffer buffer; + private ElementType fileElementType = ElementType.UNDEFINED; + private ByteBuffer headerTransfer; + private final Record record = new Record(); + private final boolean randomAccessEnabled; + private Lock lock; + private boolean useMemoryMappedBuffer; + private long currentOffset = 0L; + private StreamLogging streamLogger = new StreamLogging("Shapefile Reader"); + private int maxElementId = 0; + + public Dgn7fileReader(ReadableByteChannel channel, boolean strict, boolean useMemoryMapped, Lock lock) + throws IOException, Dgn7fileException + { + this.channel = channel; + this.useMemoryMappedBuffer = useMemoryMapped; + streamLogger.open(); + randomAccessEnabled = channel instanceof FileChannel; + this.lock = lock; + lock.lockRead(); + init(strict); + } + + public Dgn7fileReader(ReadableByteChannel channel, Lock lock) throws IOException, Dgn7fileException + { + this(channel, true, true, lock); + } + + // ensure the capacity of the buffer is of size by doubling the original + // capacity until it is big enough + // this may be naiive and result in out of MemoryError as implemented... + public static ByteBuffer ensureCapacity(ByteBuffer buffer, int size, boolean useMemoryMappedBuffer) + { + // This sucks if you accidentally pass is a MemoryMappedBuffer of size + // 80M + // like I did while messing around, within moments I had 1 gig of + // swap... + if (buffer.isReadOnly() || useMemoryMappedBuffer) + { + return buffer; + } + + int limit = buffer.limit(); + + while (limit < size) + { + limit *= 2; + } + + if (limit != buffer.limit()) + { + // if (record.ready) { + buffer = ByteBuffer.allocateDirect(limit); + + // } + // else { + // throw new IllegalArgumentException("next before hasNext"); + // } + } + + return buffer; + } + + // for filling a ReadableByteChannel + public static int fill(ByteBuffer buffer, ReadableByteChannel channel) throws IOException + { + int r = buffer.remaining(); + + // channel reads return -1 when EOF or other error + // because they a non-blocking reads, 0 is a valid return value!! + while ((buffer.remaining() > 0) && (r != -1)) + { + r = channel.read(buffer); + } + + if (r == -1) + { + buffer.limit(buffer.position()); + } + + return r; + } + + public static Dgn7fileHeader readHeader(ReadableByteChannel channel, boolean strict) throws IOException + { + ByteBuffer buffer = ByteBuffer.allocateDirect(4); + + if (fill(buffer, channel) == -1) + { + throw new EOFException("Premature end of header"); + } + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int length = buffer.getShort(2) * 2; + ByteBuffer old = buffer; + + old.position(0); + + // ensure enough capacity for one more record header + buffer = ByteBuffer.allocateDirect(length + 4); + buffer.put(old); + + if (fill(buffer, channel) == -1) + { + throw new EOFException("Premature end of header"); + } + + buffer.position(0); + + Dgn7fileHeader header = new Dgn7fileHeader(); + + header.read(buffer, strict); + + return header; + } + + public Dgn7fileHeader getHeader() + { + return header; + } + + public void close() throws IOException + { + lock.unlockRead(); + + if (channel.isOpen()) + { + channel.close(); + streamLogger.close(); + } + + if (buffer instanceof MappedByteBuffer) + { + NIOUtilities.clean(buffer); + } + + channel = null; + header = null; + } + + public boolean supportsRandomAccess() + { + return randomAccessEnabled; + } + + public Record nextElement() throws IOException, Dgn7fileException + { + // need to update position + buffer.position(this.toBufferOffset(record.end)); + + // record header is big endian + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // read shape record header + int recordNumber = ++maxElementId; + short signature = buffer.getShort(); + + // byte type = (byte) (buffer.get() & 0x7f); + byte type = (byte) ((signature >>> 8) & 0x007f); + + // silly Bentley say contentLength is in 2-byte words + // and ByteByffer uses bytes. + // track the record location + int elementLength = (buffer.getShort() * 2) + 4; + + if (!buffer.isReadOnly() &&!useMemoryMappedBuffer) + { + // capacity is less than required for the record + // copy the old into the newly allocated + if (buffer.capacity() < elementLength) + { + this.currentOffset += buffer.position(); + + ByteBuffer old = buffer; + + // ensure enough capacity for one more record header + buffer = ensureCapacity(buffer, elementLength, useMemoryMappedBuffer); + buffer.put(old); + fill(buffer, channel); + buffer.position(0); + } else + + // remaining is less than record length + // compact the remaining data and read again, + // allowing enough room for one more record header + if (buffer.remaining() < elementLength) + { + this.currentOffset += buffer.position(); + buffer.compact(); + fill(buffer, channel); + buffer.position(0); + } + } + + // shape record is all little endian + // buffer.order(ByteOrder.LITTLE_ENDIAN); + // read the type, handlers don't need it + ElementType recordType = ElementType.forID(type); + + logger.debug("nextElement at " + this.toBufferOffset(record.end) + ":type=" + type); + + // this usually happens if the handler logic is bunk, + // but bad files could exist as well... + + /* + * if (recordType != ElementType.NULL && recordType != fileElementType) + * { + * throw new IllegalStateException("ShapeType changed illegally from " + fileElementType + " to " + recordType); + * } + */ + + // peek at bounds, then reset for handler + // many handler's may ignore bounds reading, but we don't want to + // second guess them... + buffer.mark(); + + if (recordType.isMultiPoint()) + { + int lowCoorX = buffer.getInt(); + + lowCoorX = Utility.ConvertFromDGN(lowCoorX); + record.minX = Utility.ConverUnitToCoord(lowCoorX); + + int lowCoorY = buffer.getInt(); + + lowCoorY = Utility.ConvertFromDGN(lowCoorY); + record.minY = Utility.ConverUnitToCoord(lowCoorY); + + int lowCoorZ = buffer.getInt(); + + lowCoorZ = Utility.ConvertFromDGN(lowCoorZ); + record.minZ = Utility.ConverUnitToCoord(lowCoorZ); + + int highCoorX = buffer.getInt(); + + highCoorX = Utility.ConvertFromDGN(highCoorX); + record.maxX = Utility.ConverUnitToCoord(highCoorX); + + int highCoorY = buffer.getInt(); + + highCoorY = Utility.ConvertFromDGN(highCoorY); + record.maxY = Utility.ConverUnitToCoord(highCoorY); + + int highCoorZ = buffer.getInt(); + + highCoorZ = Utility.ConvertFromDGN(highCoorZ); + record.maxZ = Utility.ConverUnitToCoord(highCoorZ); + } + + buffer.reset(); + record.offset = record.end; + + // update all the record info. + record.length = elementLength; + record.signature = signature; + record.number = recordNumber; + + // remember, we read one int already... + record.end = this.toFileOffset(buffer.position()) + elementLength - 4; + // record.end = this.toFileOffset(buffer.position()) + elementLength; + + // mark this position for the reader + record.start = buffer.position(); + + // clear any cached record + record.handler = recordType.getElementHandler(); + record.element = null; + + return record; + } + + public void goTo(int offset) throws IOException, UnsupportedOperationException + { + if (randomAccessEnabled) + { + if (this.useMemoryMappedBuffer) + { + buffer.position(offset); + } else + { + /* + * Check to see if requested offset is already loaded; ensure + * that record header is in the buffer + */ + if ((this.currentOffset <= offset) && (this.currentOffset + buffer.limit() >= offset + 4)) + { + buffer.position(this.toBufferOffset(offset)); + } else + { + FileChannel fc = (FileChannel) this.channel; + + fc.position(offset); + this.currentOffset = offset; + buffer.position(0); + fill(buffer, fc); + buffer.position(0); + } + } + + int oldRecordOffset = record.end; + + record.end = offset; + + try + { + hasNext(); + } catch (IOException ioe) + { + record.end = oldRecordOffset; + + throw ioe; + } + } else + { + throw new UnsupportedOperationException("Random Access not enabled"); + } + } + + public Record elementAt(int offset) throws IOException, UnsupportedOperationException, Dgn7fileException + { + if (randomAccessEnabled) + { + this.goTo(offset); + + return nextElement(); + } + + throw new UnsupportedOperationException("Random Access not enabled"); + } + + public boolean hasNext() throws IOException + { + // mark current position + int position = buffer.position(); + + // ensure the proper position, regardless of read or handler behavior + try + { + buffer.position(this.toBufferOffset(record.end)); + } catch (IllegalArgumentException e) + { + logger.warn("position=" + this.toBufferOffset(record.end), e); + + return false; + } + + // no more data left + if (buffer.remaining() < 4) + { + return false; + } + + // looks good + boolean hasNext = true; + short type = buffer.getShort(); + + if (type == -1) + { + hasNext = false; + } + + // reset things to as they were + buffer.position(position); + + return hasNext; + } + + private void init(boolean strict) throws IOException, Dgn7fileException + { + header = readHeader(channel, strict); + + // fileElementType = header.getElementType(); + // handler = fileElementType.getElementHandler(); + + // recordHeader = ByteBuffer.allocateDirect(4); + // recordHeader.order(ByteOrder.BIG_ENDIAN); + // if (handler == null) + // { + // throw new IOException("Unsuported shape type:" + fileElementType); + // } + if ((channel instanceof FileChannel) && useMemoryMappedBuffer) + { + FileChannel fc = (FileChannel) channel; + + buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + + // buffer.position(100); + buffer.position(header.size()); + this.currentOffset = 0; + } else + { + // force useMemoryMappedBuffer to false + this.useMemoryMappedBuffer = false; + + // start with 8K buffer + buffer = ByteBuffer.allocateDirect(8 * 1024); + fill(buffer, channel); + buffer.flip(); + this.currentOffset = header.size(); + } + + headerTransfer = ByteBuffer.allocate(4); + headerTransfer.order(ByteOrder.LITTLE_ENDIAN); + + // make sure the record end is set now... + record.end = toFileOffset(buffer.position()); + } + + private int toBufferOffset(int offset) + { + return (int) (offset - currentOffset); + } + + private int toFileOffset(int offset) + { + return (int) (currentOffset + offset); + } + + public int getCount(int count) throws Dgn7fileException + { + try + { + if (channel == null) + { + return -1; + } + + count = 0; + + for (int tmp = readElement(); tmp != -1; tmp = readElement()) + { + count += tmp; + } + } catch (IOException ioe) + { + count = -1; + + // What now? This seems arbitrarily appropriate ! + throw new Dgn7fileException("Problem reading dgnfile record", ioe); + } + + return count; + } + + public int getCount() throws Dgn7fileException + { + return getCount(0); + } + + private int readElement() throws IOException + { + if (!fillBuffer()) + { + return -1; + } + + // burn the record number + buffer.getInt(); + + if (!fillBuffer()) + { + return -1; + } + + int recordlength = buffer.getInt() * 2; + + // Going to read the first 4 bytes of the record so + // subtract that from the record length + recordlength -= 4; + + if (!fillBuffer()) + { + return -1; + } + + // read record type (used to determine if record is a null record) + int type = buffer.getInt(); + + // go to end of record + while (buffer.limit() < buffer.position() + recordlength) + { + recordlength -= buffer.limit() - buffer.position(); + buffer.clear(); + + if (channel.read(buffer) < 1) + { + return -1; + } + } + + buffer.position(buffer.position() + recordlength); + + // return 0 if record is null. Null records should be counted. + if (type == 0) + { + // this is a null feature + return 0; + } + + return 1; + } + + private boolean fillBuffer() throws IOException + { + int result = 1; + + if (buffer.limit() <= buffer.position() + 4) + { + result = fill(buffer, channel); + } + + return result > 0; + } + + public static void main(String[] args) + { + JFileChooser jfc = new JFileChooser("D:/TEMP"); + File f = null; + int r = jfc.showOpenDialog(new JFrame()); + + if (r == JFileChooser.APPROVE_OPTION) + { + try + { + f = jfc.getSelectedFile(); + + FileChannel channel = new FileInputStream(f).getChannel(); + Dgn7fileReader reader = new Dgn7fileReader(channel, new Lock()); + + System.out.println(reader.getHeader().toString()); + + GeometryFactory factory = new GeometryFactory(); + int count, size; + + count = 0; + size = 0; + + try + { + Element lastComplex = null; + + while (reader.hasNext()) + { + size++; + + Dgn7fileReader.Record record = reader.nextElement(); + + if (record.element() != null) + { + Element element = (Element) record.element(); + ElementType type = element.getElementType(); + + if ((!type.isComplexElement()) && (!element.isComponentElement())) + { + if (lastComplex != null) + { + // @todo add process in here + count++; + lastComplex = null; + } + + // @todo add process in here + count++; + } else if (element.isComponentElement()) + { + if (lastComplex != null) + { + ((ComplexElement) lastComplex).add(element); + } + } else if (type.isComplexElement()) + { + if (lastComplex == null) + { + lastComplex = element; + } else + { + // @todo add process in here + count++; + lastComplex = element; + } + } + } + } + } catch (IOException e) + { + logger.warn("Stop read dgn file", e); + } catch (Dgn7fileException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } finally + { + reader.close(); + } + + System.out.println("count=" + count + " size=" + size); + // reader.close(); + } catch (IOException ioe) + { + System.out.println(ioe); + ioe.printStackTrace(); + } catch (Dgn7fileException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + System.exit(0); + } + + public final class Record + { + int length; + int number = 0; + int offset; // Relative to the whole file + int start = 0; // Relative to the current loaded buffer + short signature = 0; + + /** + * The minimum X value. + */ + public double minX; + + /** + * The minimum Y value. + */ + public double minY; + + /** + * The minimum Z value. + */ + public double minZ; + + /** + * The maximum X value. + */ + public double maxX; + + /** + * The maximum Y value. + */ + public double maxY; + + /** + * The maximum Z value. + */ + public double maxZ; + + // ElementType type; + int end = 0; // Relative to the whole file + Object element = null; + IElementHandler handler; + + public Object element() + { + if (element == null) + { + buffer.position(start); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + if (handler == null) + { + return null; + } + + element = handler.read(buffer, signature, length); + } + + return element; + } + + public int offset() + { + return offset; + } + + /** + * A summary of the record. + */ + public String toString() + { + return "Record " + number + " length " + length + " bounds " + minX + "," + minY + " " + maxX + "," + maxY; + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java new file mode 100644 index 0000000..8bdb62c --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java @@ -0,0 +1,271 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.List; + +import com.vividsolutions.jts.geom.Envelope; + +/** + * Record + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �W�� 11:14:50 + */ +public class Element +{ + public static final int CONSTRUCTION_CLASS = 0; + public static final int CONSTRUCTION_RULE_CLASS = 0; + public static final int DIMENSION_CLASS = 0; + public static final int LINEAR_PATTERNED_CLASS = 0; + public static final int MAX_ELEMENT_SIZE = 0; + public static final int MAX_VERTICES = 100; + public static final int PATTERN_AREA = 0; + public static final int PATTERN_COMPONENT_CLASS = 0; + public static final int PATTERN_CROSSHATCH = 0; + public static final int PATTERN_HATCH = 0; + public static final int PRIMARY_CLASS = 0; + public static final int PRIMARY_RULE_CLASS = 0; + short[] raw; + byte attrOffset = 0; + + public Element(short[] raw) + { + this.raw = raw; + } + + public int getLineStyle() + { + return 0; + } + + public Envelope getRange() + { + int lowCoorX = (int) ((raw[3] << 16) & 0xffff0000) + (raw[2] & 0x0000ffff); + + lowCoorX = Utility.ConvertFromDGN(lowCoorX); + + int lowCoorY = (int) ((raw[5] << 16) & 0xffff0000) + (raw[4] & 0x0000ffff); + + lowCoorY = Utility.ConvertFromDGN(lowCoorY); + + int highCoorX = (int) ((raw[9] << 16) & 0xffff0000) + (raw[8] & 0x0000ffff); + + highCoorX = Utility.ConvertFromDGN(highCoorX); + + int highCoorY = (int) ((raw[11] << 16) & 0xffff0000) + (raw[10] & 0x0000ffff); + + highCoorY = Utility.ConvertFromDGN(highCoorY); + + return new Envelope(Utility.ConverUnitToCoord(lowCoorX), Utility.ConverUnitToCoord(highCoorX), + Utility.ConverUnitToCoord(lowCoorY), Utility.ConverUnitToCoord(highCoorY)); + } + + public boolean isComponentElement() + { + if ((short) ((raw[0] >>> 7) & 0x0001) == 1) + { + return true; + } else + { + return false; + } + } + + public boolean removeUserAttributeData(int iLinkageId) + { + return true; + } + + public boolean removeUserAttributeData(int iLinkageId, int iLinkageIndex) + { + return true; + } + + public boolean isDeleted() + { + if ((short) ((raw[0] >>> 15) & 0x0001) == 1) + { + return true; + } else + { + return false; + } + } + + public int getColorIndex() + { + return ((raw[17] >>> 8) & 0x00ff); + } + + public int getType() + { + return ((raw[0] >>> 8) & 0x007f); + } + + public ElementType getElementType() + { + return ElementType.forID(getType()); + } + + public int getLevelIndex() + { + return (raw[0] & 0x003f); + } + + public int getWeight() + { + return ((raw[17] >>> 3) & 0x001f); + } + + public void addUserAttributeData(byte[] pDataBlock, Class dataClass, int iLinkageId) throws Element.Exception + { + } + + public void addUserAttributeData(byte[] pDataBlock, int iLinkageId, Object oDataDef) throws Element.Exception + { + } + + public boolean hasUserAttributeData() + { + if (raw[15] <= 0) + { + return false; + } + + short index = (short) (raw[15] + 16); + + if (index == -1) + { + return false; + } + + return true; + } + + public List<UserAttributeData> getUserAttributeData() + { + short[] data; + short length, nextAttribute; + + if (raw[15] <= 0) + { + return new ArrayList<UserAttributeData>(); + } + + short index = (short) (raw[15] + 16 + attrOffset); + + if (index == -1) + { + return null; + } + + ArrayList<UserAttributeData> aLinkageSet = new ArrayList<UserAttributeData>(); + + while (index < raw.length) + { + length = (short) (raw[index] & (short) 0x00ff); + + if (length == 0) + { + break; + } + + nextAttribute = (short) (index + length + 1); + data = new short[length]; + System.arraycopy(raw, index + 1, data, 0, length); + + if (data[0] == (short) 0x0020) + { + aLinkageSet.add(new FrammeAttributeData(data)); + } else + { + aLinkageSet.add(new UserAttributeData(data)); + } + + index = nextAttribute; + } + + return aLinkageSet; + } + + public void getUserAttributeData(byte[] pDataBlock, Class dataClass, int iLinkageId, int iLinkageIndex) + { + } + + public void getUserAttributeData(byte[] pDataBlock, int iLinkageId, Object oDataDef) + { + } + + public static class Exception extends java.lang.Exception + { + public Exception() + { + } + + // Constructs an Record.Exception with no detail message. + public Exception(String oStrMessage) + { + super(oStrMessage); + } + } + + + public static class ElementHandler implements IElementHandler + { + ElementType elementType; + + public ElementHandler(ElementType elementType) + { + this.elementType = elementType; + } + + public ElementType getElementType() + { + return elementType; + } + + public Object read(ByteBuffer buffer, short signature, int length) + { + byte[] dst = new byte[length - 4]; + try + { + buffer.get(dst, 0, dst.length); + } catch (BufferUnderflowException exception) + { + throw exception; + } + ByteBuffer tmpBuffer = ByteBuffer.wrap(dst); + tmpBuffer.order(ByteOrder.LITTLE_ENDIAN); + ShortBuffer sbuffer = tmpBuffer.asShortBuffer(); + + short[] rawMem = new short[(length / 2)]; + sbuffer.get(rawMem, 2, rawMem.length - 2); + rawMem[0] = signature; + rawMem[1] = (short) ((length / 2) - 2); + Element elm = createElement(rawMem); + + return elm; + } + + public void write(ByteBuffer buffer, Object element) + { + } + + public int getLength(Object element) + { + return ((Element) element).raw.length; + } + + protected Element createElement(short[] raw) + { + return new Element(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java new file mode 100644 index 0000000..dda70b2 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java @@ -0,0 +1,534 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +/*----------------------------------------------------------------------+ +| | +| Type Description | +| 1 Cell Library Header | +| 2 Cell (complex) | +| 3 Line | +| 4 Line String | +| 5 Group Data | +| 6 Shape | +| 7 Text Node (complex) | +| 8 Digitizer Setup Data | +| 9 Design File Header if level 8 | +| 10 Level Symbology | +| 11 Curve | +| 12 Complex String (complex) | +| 13 Conic | +| 14 Complex Shape (complex) | +| 15 Ellipse | +| 16 Arc | +| 17 Text | +| 18 Surface (complex) | +| 19 Solid (complex) | +| 20 not used | +| 21 B-Spline Pole | +| 22 Point String | +| 23 Circular Truncated Cone | +| 24 B-Spline Surface (complex) | +| 25 B-Spline Surface boundary | +| 26 B-Spline Knot Record | +| 27 B-Spline Curve (complex) | +| 28 B-Spline Weight Factor | +| 33 Dimension Record | +| 34 Shared Cell Definition Record | +| 35 Shared Cell Record | +| 36 Multiline Record | +| 37 Attribute Record | +| 38 DgnStore Component | +| 39 DgnStore Header | +| 66 MicroStation Application | +| 87 Raster Header | +| 88 Raster Component | +| 90 Raster Reference Attachment | +| 91 Raster Reference Component | +| 92 Raster Hierarchy Record | +| 93 Raster Hierarchy Component | +| 94 Raster Frame Record | +| 95 Table Entry Record | +| 96 Table Header Record | +| 97 View Group Record | +| 98 View Record | +| 99 Level Mask Record | +| 100 Reference Attach Record | +| 101 Matrix Header | +| 102 Matrix Int Data | +| 103 Matrix Double Data | +| 105 Mesh Header | +| 106 Extended Record (graphic) (complex) | +| 107 Extended Record (non-graphic) (complex) | +| 108 Reference Override Record | +| 110 Named Group Header | +| 111 Named Group Component | +| | ++----------------------------------------------------------------------*/ + +/** + * ElementType + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/17 �U�� 01:26:49 + */ +public final class ElementType +{ + /** + * Represents a Null shape (id = 0). + */ + public static final ElementType NULL = new ElementType(0, "Null"); + + /** + * Represents a Line shape (id = 3). + */ + public static final ElementType LINE = new ElementType(3, "Line"); + + /** + * Represents a LineString shape (id = 4). + */ + public static final ElementType LINESTRING = new ElementType(4, "LineString"); + + /** + * Represents a Shape shape (id = 6). + */ + public static final ElementType SHAPE = new ElementType(6, "Shape"); + + /** + * Represents an TextNode shape (id = 7). + */ + public static final ElementType TEXTNODE = new ElementType(7, "TextNode"); + + /** + * Represents an IGDSDIGITIZER shape (id = 8). + */ + public static final ElementType IGDSDIGITIZER = new ElementType(8, "IGDSDigitizer"); + + /** + * Represents an TCB shape (id = 9). + */ + public static final ElementType TCB = new ElementType(9, "Tcb"); + + /** + * Represents a LevelSymbology shape (id = 5). + */ + public static final ElementType LEVELSYMBOLOGY = new ElementType(10, "LevelSymbology"); + + /** + * Represents a ComplexChain shape (id = 15). + */ + public static final ElementType COMPLEXCHAIN = new ElementType(12, "ComplexChain"); + + /** + * Represents a ComplexShape shape (id = 25). + */ + public static final ElementType COMPLEXSHAPE = new ElementType(14, "ComplexShape"); + + /** + * Represents a Ellipse shape (id = 8). + */ + public static final ElementType ELLIPSE = new ElementType(15, "Ellipse"); + + /** + * Represents a Arc shape (id = 28). + */ + public static final ElementType ARC = new ElementType(16, "Arc"); + + /** + * Represents a Arc shape (id = 28). + */ + public static final ElementType TEXT = new ElementType(17, "Text"); + + /** + * Represents a Arc shape (id = 28). + */ + public static final ElementType POINTSTRING = new ElementType(22, "PointString"); + + /** + * Represents an Undefined shape (id = -1). + */ + public static final ElementType UNDEFINED = new ElementType(-1, "Undefined"); + + /** + * The integer id of this ElementType. + */ + public final int id; + + /** + * The human-readable name for this ElementType.<br> + * Could easily use ResourceBundle for internationialization. + */ + public final String name; + + /** + * Creates a new instance of ElementType. Hidden on purpose. + * + * @param id The id. + * @param name The name. + */ + protected ElementType(int id, String name) + { + this.id = id; + this.name = name; + } + + /** + * Get the name of this ElementType. + * + * @return The name. + */ + public String toString() + { + return name; + } + + /** + * Is this a multipoint shape? Hint- all shapes are multipoint except NULL, + * UNDEFINED, and the POINTs. + * + * @return true if multipoint, false otherwise. + */ + public boolean isMultiPoint() + { + boolean mp = true; + + if (this == UNDEFINED) + { + mp = false; + } else if (this == NULL) + { + mp = false; + } else if (this == IGDSDIGITIZER) + { + mp = false; + } else if (this == TCB) + { + mp = false; + } else if (this == LEVELSYMBOLOGY) + { + mp = false; + } + + return mp; + } + + public boolean isComplexElement() + { + if (id == 2) + { + return true; + } + + if (id == 7) + { + return true; + } + + if (id == 12) + { + return true; + } + + if (id == 14) + { + return true; + } + + if (id == 18) + { + return true; + } + + if (id == 19) + { + return true; + } + + if (id == 106) + { + return true; + } + + if (id == 107) + { + return true; + } + + return false; + } + + public boolean isPointType() + { + if (id == 7) + { + return true; + } + + if (id == 17) + { + return true; + } + + return false; + } + + public boolean isLineType() + { + if (id == 3) + { + return true; + } + + if (id == 4) + { + return true; + } + + if (id == 11) + { + return true; + } + + if (id == 12) + { + return true; + } + + if (id == 16) + { + return true; + } + + return false; + } + + public boolean isPolygonType() + { + if (id == 6) + { + return true; + } + + if (id == 14) + { + return true; + } + + return false; + } + + public boolean isMultiPointType() + { + if (id == 3) + { + return true; + } + + if (id == 4) + { + return true; + } + + if (id == 6) + { + return true; + } + + if (id == 11) + { + return true; + } + + if (id == 12) + { + return true; + } + + if (id == 13) + { + return true; + } + + if (id == 14) + { + return true; + } + + if (id == 15) + { + return true; + } + + if (id == 16) + { + return true; + } + + if (id == 22) + { + return true; + } + + return false; + } + + /** + * Determine the ElementType for the id. + * + * @param id The id to search for. + * @return The ElementType for the id. + */ + public static ElementType forID(int id) + { + ElementType t; + + switch (id) + { + case 0 : + t = NULL; + + break; + + case 3 : + t = LINE; + + break; + + case 4 : + t = LINESTRING; + + break; + + case 6 : + t = SHAPE; + + break; + + case 7 : + t = TEXTNODE; + + break; + + case 8 : + t = IGDSDIGITIZER; + + break; + + case 9 : + t = TCB; + + break; + + case 10 : + t = LEVELSYMBOLOGY; + + break; + + case 12 : + t = COMPLEXCHAIN; + + break; + + case 14 : + t = COMPLEXSHAPE; + + break; + + case 15 : + t = ELLIPSE; + + break; + + case 16 : + t = ARC; + + break; + + case 17 : + t = TEXT; + + break; + + default : + t = UNDEFINED; + + break; + } + + return t; + } + + public IElementHandler getElementHandler() throws Dgn7fileException + { + IElementHandler handler; + + switch (id) + { + case 3 : + handler = LineElement.ElementHandler.getInstance(); + + break; + + case 4 : + handler = LineStringElement.ElementHandler.getInstance(); + + break; + + case 6 : + handler = ShapeElement.ElementHandler.getInstance(); + + break; + + case 7 : + handler = TextNodeElement.ElementHandler.getInstance(); + + break; + + case 8 : + handler = new Element.ElementHandler(this); + + break; + + case 9 : + handler = new Element.ElementHandler(this); + + break; + + case 10 : + handler = new Element.ElementHandler(this); + + break; + + case 12 : + handler = ComplexChainElement.ElementHandler.getInstance(); + + break; + + case 14 : + handler = ComplexShapeElement.ElementHandler.getInstance(); + + break; + + case 15 : + handler = new Element.ElementHandler(this); + + break; + + case 16 : + handler = ArcElement.ElementHandler.getInstance(); + + break; + + case 17 : + handler = TextElement.ElementHandler.getInstance(); + + break; + + default : + handler = null; + } + + return handler; + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java new file mode 100644 index 0000000..3707168 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java @@ -0,0 +1,71 @@ +package com.ximple.io.dgn7; + +/** + * FrammeAttributeData + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 06:36:55 + */ +public class FrammeAttributeData extends UserAttributeData +{ + public FrammeAttributeData(short id) + { + super(id, 7); + } + + public FrammeAttributeData(short[] src) + { + super(src); + } + + public short getFsc() + { + int fsc = _src[3] & 0x0000ffff; + + return (short) fsc; + } + + public int getUfid() + { + int ufid = (int) (_src[2] << 16 & 0xffff0000); + + ufid += _src[1] & 0x0000ffff; + + return ufid; + } + + public byte getComponentID() + { + int cmpid = (int) (_src[5] & 0x000000ff); + + return (byte) cmpid; + } + + public byte getRuleNo() + { + int no = (int) ((_src[5] >> 8) & 0x000000ff); + + return (byte) no; + } + + public short getStatus() + { + int status = (int) (_src[4] & 0x0000ffff); + + return (short) status; + } + + public short getOccID() + { + int occid = (int) (_src[6] & 0x0000ffff); + + return (short) occid; + } + + public String toString() + { + return "FrammeData{" + getFsc() + "," + getUfid() + "," + getComponentID() + "," + getRuleNo() + "," + getStatus() + "," + + getOccID() + "}"; + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java new file mode 100644 index 0000000..2ac6fdc --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java @@ -0,0 +1,18 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * GeometryConverter + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �W�� 11:38:57 + */ +public interface GeometryConverter +{ + public Geometry toGeometry(GeometryFactory factory); +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java new file mode 100644 index 0000000..c98b9f3 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java @@ -0,0 +1,23 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.nio.ByteBuffer; + +/** + * IElementHandler + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/17 �U�� 01:50:26 + */ +public interface IElementHandler +{ + public ElementType getElementType(); + + public Object read(ByteBuffer buffer, short signature, int length); + + public void write(ByteBuffer buffer, Object element); + + public int getLength(Object element); +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java new file mode 100644 index 0000000..8b0a2bc --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java @@ -0,0 +1,129 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * LineElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �W�� 11:34:59 + */ +public class LineElement extends Element implements GeometryConverter +{ + public LineElement(short[] raw) + { + super(raw); + } + + public Coordinate getCentroid(double dTolerance) + { + return null; + } + + public Coordinate getEndPoint() + { + int endX = (int) ((raw[22] << 16) & 0xffff0000); + + endX = endX + (raw[23] & 0x0000ffff); + + double x = Utility.ConverUnitToCoord(endX); + int endY = (int) ((raw[24] << 16) & 0xffff0000); + + endY = endY + (raw[25] & 0x0000ffff); + + double y = Utility.ConverUnitToCoord(endY); + + return new Coordinate(x, y); + } + + public Coordinate getNormal() + { + return null; + } + + public Coordinate getOrigin() + { + return null; + } + + public Coordinate getStartPoint() + { + int startX = (int) ((raw[18] << 16) & 0xffff0000); + + startX = startX + (raw[19] & 0x0000ffff); + + double x = Utility.ConverUnitToCoord(startX); + int startY = (int) ((raw[20] << 16) & 0xffff0000); + + startY = startY + (raw[21] & 0x0000ffff); + + double y = Utility.ConverUnitToCoord(startY); + + return new Coordinate(x, y); + } + + public Coordinate getVertex(int index) + { + return (index == 0) + ? getStartPoint() + : getEndPoint(); + } + + public double getLength() + { + Coordinate p1 = getStartPoint(); + Coordinate p2 = getEndPoint(); + + return Utility.getLength(p1.x, p1.y, p2.x, p2.y); + } + + public Coordinate pointAtDistance(double dDistance, double dTolerance) + { + return null; + } + + public Coordinate[] getVertices() + { + Coordinate[] result = new Coordinate[2]; + + result[0] = getStartPoint(); + result[1] = getEndPoint(); + + return result; + } + + public Geometry toGeometry(GeometryFactory factory) + { + return factory.createLineString(getVertices()); + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.LINE); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new LineElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java new file mode 100644 index 0000000..bc163af --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java @@ -0,0 +1,166 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * LineStringElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 02:48:58 + */ +public class LineStringElement extends Element implements GeometryConverter +{ + public LineStringElement(short[] raw) + { + super(raw); + } + + public Coordinate getCentroid(double dTolerance) + { + return null; + } + + public Coordinate getEndPoint() + { + return new Coordinate(getX(getVerticeSize() - 1), getY(getVerticeSize() - 1)); + } + + public Coordinate getNormal() + { + return null; + } + + public Coordinate getOrigin() + { + return null; + } + + public Coordinate getStartPoint() + { + return new Coordinate(getX(0), getY(0)); + } + + public Coordinate getVertex(int index) + { + return (index == 0) + ? getStartPoint() + : getEndPoint(); + } + + public int getVerticeSize() + { + return raw[18] & 0x0000ffff; + } + + public double getLength() + { + double result = 0.0; + Coordinate[] vset = getVertices(); + + for (int i = 1; i < getVerticeSize(); i++) + { + Coordinate p1 = vset[i - 1]; + Coordinate p2 = vset[i]; + + result += Utility.getLength(p1.x, p1.y, p2.x, p2.y); + } + + return result; + } + + public Coordinate pointAtDistance(double dDistance, double dTolerance) + { + return null; + } + + public Coordinate[] getVertices() + { + Coordinate[] result = new Coordinate[getVerticeSize()]; + + for (int i = 0; i < getVerticeSize(); i++) + { + result[i] = new Coordinate(getX(i), getY(i)); + } + + return result; + } + + public Geometry toGeometry(GeometryFactory factory) + { + return factory.createLineString(getVertices()); + } + + protected double getX(int index) + { + if ((index < 0) || (index > getVerticeSize())) + { + return -1; + } + + int x = (int) ((raw[19 + (4 * index)] << 16) & 0xffff0000); + + x += (int) (raw[20 + (4 * index)] & 0x0000ffff); + + return Utility.ConverUnitToCoord(x); + } + + protected void setX(int index, double dx) + { + int newVal = Utility.ConverCoordToUnit(dx); + + raw[19 + (4 * index)] = (short) (newVal >> 16 & 0x0000ffff); + raw[20 + (4 * index)] = (short) (newVal & 0x0000ffff); + } + + protected double getY(int index) + { + if ((index < 0) || (index > getVerticeSize())) + { + return -1; + } + + int y = (int) ((raw[21 + (4 * index)] << 16) & 0xffff0000); + + y = y + (int) (raw[22 + (4 * index)] & 0x0000ffff); + + return Utility.ConverUnitToCoord(y); + } + + protected void setY(int index, double dy) + { + int newVal = Utility.ConverCoordToUnit(dy); + + raw[21 + (4 * index)] = (short) ((newVal >> 16) & 0x0000ffff); + raw[22 + (4 * index)] = (short) (newVal & 0x0000ffff); + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.LINESTRING); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new LineStringElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java new file mode 100644 index 0000000..e51dbf2 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java @@ -0,0 +1,263 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +/** + * Lock + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �W�� 10:27:24 + */ +public class Lock +{ + Logger logger = LogManager.getLogger("com.ximple.io.dgn7"); + + /** + * indicates a write is occurring + */ + int writeLocks = 0; + + /** + * if not null a writer is waiting for the lock or is writing. + */ + Thread writer; + + /** + * Thread->Owner map. If empty no read locks exist. + */ + Map owners = new HashMap(); + + /** + * If the lock can be read locked the lock will be read and default + * visibility for tests + * + * @return + * @throws java.io.IOException + */ + synchronized boolean canRead() throws IOException + { + if ((writer != null) && (writer != Thread.currentThread())) + { + return false; + } + + if (writer == null) + { + return true; + } + + if (owners.size() > 1) + { + return false; + } + + return true; + } + + /** + * If the lock can be read locked the lock will be read and default + * visibility for tests + * + * @return + * @throws IOException + */ + synchronized boolean canWrite() throws IOException + { + if (owners.size() > 1) + { + return false; + } + + if ((canRead()) && ((writer == Thread.currentThread()) || (writer == null))) + { + if (owners.isEmpty()) + { + return true; + } + + if (owners.containsKey(Thread.currentThread())) + { + return true; + } + } + + return false; + } + + /** + * Called by shapefileReader before a read is started and before an IOStream + * is openned. + * + * @throws IOException + */ + public synchronized void lockRead() throws IOException + { + if (!canRead()) + { + while ((writeLocks > 0) || (writer != null)) + { + try + { + wait(); + } catch (InterruptedException e) + { + throw(IOException) new IOException().initCause(e); + } + } + } + + assertTrue("A write lock exists that is owned by another thread", canRead()); + + Thread current = Thread.currentThread(); + Owner owner = (Owner) owners.get(current); + + if (owner != null) + { + owner.timesLocked++; + } else + { + owner = new Owner(current); + owners.put(current, owner); + } + + logger.debug("Start Read Lock:" + owner); + } + + private void assertTrue(String message, boolean b) + { + if (!b) + { + throw new AssertionError(message); + } + } + + /** + * Called by ShapefileReader after a read is complete and after the IOStream + * is closed. + */ + public synchronized void unlockRead() + { + assertTrue("Current thread does not have a readLock", owners.containsKey(Thread.currentThread())); + + Owner owner = (Owner) owners.get(Thread.currentThread()); + + assertTrue("Current thread has " + owner.timesLocked + "negative number of locks", owner.timesLocked > 0); + owner.timesLocked--; + + if (owner.timesLocked == 0) + { + owners.remove(Thread.currentThread()); + } + + notifyAll(); + logger.debug("unlock Read:" + owner); + } + + /** + * Called by ShapefileDataStore before a write is started and before an + * IOStream is openned. + * + * @throws IOException + */ + public synchronized void lockWrite() throws IOException + { + Thread currentThread = Thread.currentThread(); + + if (writer == null) + { + writer = currentThread; + } + + while (!canWrite()) + { + try + { + wait(); + } catch (InterruptedException e) + { + throw(IOException) new IOException().initCause(e); + } + + if (writer == null) + { + writer = currentThread; + } + } + + if (writer == null) + { + writer = currentThread; + } + + assertTrue("The current thread is not the writer", writer == currentThread); + assertTrue("There are read locks not belonging to the current thread.", canRead()); + writeLocks++; + logger.debug(currentThread.getName() + " is getting write lock:" + writeLocks); + } + + /** + * default visibility for tests + */ + synchronized int getReadLocks(Thread thread) + { + Owner owner = (Owner) owners.get(thread); + + if (owner == null) + { + return -1; + } + + return owner.timesLocked; + } + + public synchronized void unlockWrite() + { + if (writeLocks > 0) + { + assertTrue("current thread does not own the write lock", writer == Thread.currentThread()); + assertTrue("writeLock has already been unlocked", writeLocks > 0); + writeLocks--; + + if (writeLocks == 0) + { + writer = null; + } + } + + logger.debug("unlock write:" + Thread.currentThread().getName()); + notifyAll(); + } + + /** + * default visibility for tests + */ + synchronized boolean ownWriteLock(Thread thread) + { + return (writer == thread) && (writeLocks > 0); + } + + private class Owner + { + final Thread owner; + int timesLocked; + + Owner(Thread owner) + { + this.owner = owner; + timesLocked = 1; + } + + public String toString() + { + return owner.getName() + " has " + timesLocked + " locks"; + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java new file mode 100644 index 0000000..4f31770 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java @@ -0,0 +1,114 @@ +package com.ximple.io.dgn7; + +/* + * GeoTools - OpenSource mapping toolkit + * http://geotools.org + * (C) 2003-2006, Geotools Project Managment Committee (PMC) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +// J2SE dependencies +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * Utility class for managing memory mapped buffers. + * + * @since 2.0 + * @source $URL$ + * @version $Id$ + * @author Andres Aimes + */ +public class NIOUtilities { + /** + * {@code true} if a warning has already been logged. + */ + private static boolean warned = false; + + /** + * Do not allows instantiation of this class. + * + * @todo This constructor will become private when {@code NIOBufferUtils} + * will have been removed. + */ + protected NIOUtilities() { + } + + /** + * Really closes a {@code MappedByteBuffer} without the need to wait for garbage + * collection. Any problems with closing a buffer on Windows (the problem child in this + * case) will be logged as {@code SEVERE} to the logger of the package name. To + * force logging of errors, set the System property "org.geotools.io.debugBuffer" to "true". + * + * @param buffer The buffer to close. + * @return true if the operation was successful, false otherwise. + * + * @see java.nio.MappedByteBuffer + */ + public static boolean clean(final ByteBuffer buffer) { + if (buffer == null || ! buffer.isDirect() ) { + return false; + } + Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + Boolean success = Boolean.FALSE; + try { + Method getCleanerMethod = buffer.getClass().getMethod("cleaner", (Class[])null); + getCleanerMethod.setAccessible(true); + Object cleaner = getCleanerMethod.invoke(buffer, (Object[])null); + Method clean = cleaner.getClass().getMethod("clean", (Class[])null); + clean.invoke(cleaner, (Object[])null); + success = Boolean.TRUE; + } catch (Exception e) { + // This really is a show stopper on windows + if (isLoggable()) { + log(e, buffer); + } + } + return success; + } + }); + + return b.booleanValue(); + } + + /** + * Check if a warning message should be logged. + */ + private static synchronized boolean isLoggable() { + try { + return !warned && ( + System.getProperty("org.geotools.io.debugBuffer", "false").equalsIgnoreCase("true") || + System.getProperty("os.name").indexOf("Windows") >= 0 ); + } catch (SecurityException exception) { + // The utilities may be running in an Applet, in which case we + // can't read properties. Assumes we are not in debugging mode. + return false; + } + } + + /** + * Log a warning message. + */ + private static synchronized void log(final Exception e, final ByteBuffer buffer) { + warned = true; + String message = "Error attempting to close a mapped byte buffer : " + buffer.getClass().getName() + + "\n JVM : " + System.getProperty("java.version") + + ' ' + System.getProperty("java.vendor"); + Logger.getLogger("org.geotools.io").log(Level.SEVERE, message, e); + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java new file mode 100644 index 0000000..729413c --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java @@ -0,0 +1,56 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.LinearRing; + +/** + * ShapeElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 03:08:43 + */ +public class ShapeElement extends LineStringElement implements GeometryConverter +{ + public ShapeElement(short[] raw) + { + super(raw); + } + + public Geometry toGeometry(GeometryFactory factory) + { + LinearRing ring = factory.createLinearRing(this.getVertices()); + + return ring; + + // return factory.createPolygon(ring, null); + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.SHAPE); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new ShapeElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java new file mode 100644 index 0000000..db92597 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java @@ -0,0 +1,45 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +/** + * StreamLogging + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �W�� 10:31:08 + */ +public class StreamLogging +{ + private static final Logger LOGGER = LogManager.getLogger("com.ximple.io.dgn7"); + private String name; + private int open = 0; + + /** + * The name that will appear in the debug message + * + * @param name + */ + public StreamLogging(String name) + { + this.name = name; + } + + /** + * Call when reader or writer is opened + */ + public synchronized void open() + { + open++; + LOGGER.debug(name + " has been opened. Number open: " + open); + } + + public synchronized void close() + { + open--; + LOGGER.debug(name + " has been closed. Number open: " + open); + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java new file mode 100644 index 0000000..fe4de4a --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java @@ -0,0 +1,90 @@ +package com.ximple.io.dgn7; + +/** + * TcbElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 05:03:46 + */ +public class TcbElement extends Element +{ + public TcbElement(short[] raw) + { + super(raw); + } + + public boolean is2D() + { + int dimension = (int) (raw[607] & 0x00000004); + + if (dimension == 0) + { + return true; + } else + { + return false; + } + } + + public String getMasterUnitName() + { + byte[] master = new byte[1]; + + master[0] = (byte) (raw[560] & 0x00ff); + java.nio.charset.Charset.forName("US-ASCII"); + + // ASCIIEncoding encode = new ASCIIEncoding(); + StringBuffer sb = new StringBuffer(); + + sb.append((char) master[0]); + + // return encode.GetString(master); + return sb.toString(); + } + + public String getSubUnitName() + { + byte[] sub = new byte[2]; + + sub[0] = (byte) (raw[561] & 0x00ff); + sub[1] = (byte) (raw[561] >> 8 & 0x00ff); + + StringBuffer sb = new StringBuffer(); + + sb.append((char) sub[0]); + sb.append((char) sub[0]); + + return sb.toString(); + } + + public int getGraphicGroup() + { + return (int) (raw[594] & 0x0000ffff); + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.TCB); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new TcbElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java new file mode 100644 index 0000000..3fc5fca --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java @@ -0,0 +1,296 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * TextElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �W�� 11:45:29 + */ +public class TextElement extends Element implements GeometryConverter +{ + public static final int ED_CENTERJUSTIFICATION = 0; + +// Enter data field center justification + public static final int ED_LEFTJUSTIFICATION = 0; + +// Enter data field left justification + public static final int ED_RIGHTJUSTIFICATION = 0; + +// Enter data field right justification + public static final int TXTJUST_CB = 0; + +// Center/bottom text justification. + public static final int TXTJUST_CC = 0; + +// Center/center text justification. + public static final int TXTJUST_CT = 0; + +// Center/top text justification. + public static final int TXTJUST_LB = 0; + +// Left/bottom text justification. + public static final int TXTJUST_LC = 0; + +// Left/center text justification. + public static final int TXTJUST_LT = 0; + +// Left/top text justification. + public static final int TXTJUST_RB = 0; + +// Right/bottom text justification. + public static final int TXTJUST_RC = 0; + +// Right/center text justification. + public static final int TXTJUST_RT = 0; + + public TextElement(short[] raw) + { + super(raw); + } + + public Coordinate getOrigin() + { + int x = (int) (raw[25] << 16 & 0xffff0000); + + x += raw[26] & 0x0000ffff; + + double dx = Utility.ConverUnitToCoord(x); + int y = (int) (raw[27] << 16 & 0xffff0000); + + y += raw[28] & 0x0000ffff; + + double dy = Utility.ConverUnitToCoord(y); + + return new Coordinate(dx, dy); + } + + public Coordinate getUserOrigin() + { + Coordinate origin = getOrigin(); + double x = origin.x; + double weight = getUserSetWeight(); + double height = getUserSetHeight(); + double angle = Utility.ConverRotationToRadian(getRotationAngle()); + + x += weight * Math.cos(angle) - height * Math.sin(angle); + + double y = origin.y; + + y += weight * Math.cos(angle) - height * Math.sin(angle); + + return new Coordinate(x, y); + } + + private double getUserSetWeight() + { + int just = getJustification(); + Envelope range = getRange(); + double weight = (range.getWidth()); + + switch (just) + { + case 0 : + case 1 : + case 2 : + weight = 0; + + break; + + case 6 : + case 7 : + case 8 : + weight = weight / 2; + + break; + + case 12 : + case 13 : + case 14 : + break; + } + + return weight; + } + + private double getUserSetHeight() + { + int just = getJustification(); + double height = getTextHeight(); + + switch (just) + { + case 2 : + case 8 : + case 14 : // bottom + height = 0; + + break; + + case 1 : + case 7 : + case 13 : // center + height = height / 2; + + break; + + case 0 : + case 6 : + case 12 : // height + break; + } + + return height; + } + + public int getFontIndex() + { + return (int) raw[18] & 0x00000000ff; + } + + public boolean hasSlant() + { + return true; + } + + public boolean hasUnderline() + { + return true; + } + + public boolean isFixedWidthSpacing() + { + return true; + } + + public boolean isPlanar() + { + return true; + } + + public boolean isVertical() + { + return true; + } + + public double getTextHeight() + { + int height = (int) ((raw[21] << 16) & 0xffff0000); + + height += raw[22] & 0x0000ffff; + + return Utility.ConverIntToDouble(height); + } + + public double getTextWidth() + { + int length = (int) (raw[19] << 16 & 0xffff0000); + + length += raw[20] & 0x0000ffff; + + return Utility.ConverIntToDouble(length); + } + + public int getJustification() + { + return (int) ((raw[18] >>> 8) & 0x00000000ff); + } + + public double getRotationAngle() + { + int totation = (int) ((raw[23] << 16) & 0xffff0000); + + totation += raw[24] & 0x0000ffff; + + return Utility.ConverIntToRotation(totation); + } + + public boolean isChinese() + { + int isChinese = raw[30] & 0x0000ffff; + + if (isChinese == 0xfdff) + { + return true; + } else + { + return false; + } + } + + public int getTextLength() + { + int num = raw[29]; + + if (isChinese()) + { + num = (num / 2) - 1; + } + + return num; + } + + public String getText() + { + StringBuffer val = new StringBuffer(); + char[] temp; + int num = getTextLength(); + + if (!isChinese()) + { + temp = new char[num]; + + for (int i = 0; i < temp.length; i++) + { + if ((i % 2) == 0) + { + temp[i] = (char) (raw[30 + (int) (i / 2)] & (short) 0x00ff); + } else + { + temp[i] = (char) ((raw[30 + (int) (i / 2)] >> 8) & (short) 0x00ff); + } + + val.append(temp[i]); + } + } + + return val.toString(); + } + + public Geometry toGeometry(GeometryFactory factory) + { + return factory.createPoint(getUserOrigin()); + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.TEXT); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new TextElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java new file mode 100644 index 0000000..7991b77 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java @@ -0,0 +1,291 @@ +package com.ximple.io.dgn7; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * TextNodeElement + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 04:02:58 + */ +public class TextNodeElement extends Element implements ComplexElement, GeometryConverter +{ + ArrayList list = new ArrayList(); + + public TextNodeElement(short[] raw) + { + super(raw); + } + + public int size() + { + return list.size(); + } + + public boolean isEmpty() + { + return list.isEmpty(); + } + + public boolean contains(Object o) + { + return list.contains(o); + } + + public Iterator iterator() + { + return list.iterator(); + } + + public Object[] toArray() + { + return list.toArray(); + } + + public boolean add(Object o) + { + return list.add(o); + } + + public boolean remove(Object o) + { + return list.remove(o); + } + + public boolean addAll(Collection c) + { + return list.addAll(c); + } + + public boolean addAll(int index, Collection c) + { + return list.addAll(index, c); + } + + public void clear() + { + list.clear(); + } + + public Object get(int index) + { + return list.get(index); + } + + public Object set(int index, Object element) + { + return list.set(index, element); + } + + public void add(int index, Object element) + { + list.add(index, element); + } + + public Object remove(int index) + { + return list.remove(index); + } + + public int indexOf(Object o) + { + return list.indexOf(o); + } + + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + public ListIterator listIterator() + { + return list.listIterator(); + } + + public ListIterator listIterator(int index) + { + return list.listIterator(index); + } + + public List subList(int fromIndex, int toIndex) + { + return list.subList(fromIndex, toIndex); + } + + public boolean retainAll(Collection c) + { + return list.retainAll(c); + } + + public boolean removeAll(Collection c) + { + return list.removeAll(c); + } + + public boolean containsAll(Collection c) + { + return list.containsAll(c); + } + + public Object[] toArray(Object[] a) + { + return list.toArray(a); + } + + public String[] getTextArray() + { + ArrayList list = new ArrayList(); + + for (ListIterator it = listIterator(); it.hasNext(); ) + { + Element element = (Element) it.next(); + + if (element instanceof TextElement) + { + list.add(((TextElement) element).getText()); + } + } + + return (String[]) list.toArray(new String[list.size()]); + } + + public Geometry toGeometry(GeometryFactory factory) + { + /* + * CoordinateList coords = new CoordinateList(); + * for (ListIterator it = listIterator(); it.hasNext(); ) + * { + * Element element = (Element) it.next(); + * if (element instanceof TextElement) + * { + * coords.add(((TextElement) element).getUserOrigin()); + * } + * } + */ + return factory.createPoint(getOrigin()); + + // return factory.createMultiPoint(coords.toCoordinateArray()); + } + + public int getNumString() + { + return (int) (raw[19] & 0x0000ffff); + } + + public int getNodeNumber() + { + return (int) (raw[20] & 0x0000ffff); + } + + public int getMaxLength() + { + return (int) (raw[21] & 0x00ff); + } + + public int getMaxUsed() + { + return (int) ((raw[21] >> 8) & 0x00ff); + } + + public int getJustification() + { + return (int) ((raw[22] >> 8) & 0x00ff); + } + + public int getFontIndex() + { + return (int) (raw[22] & 0x00ff); + } + + public double getLineSpacing() + { + int lineSpace; + + lineSpace = (int) ((raw[23] << 16) & 0xffff0000); + lineSpace += (raw[24] & 0x0000ffff); + + return lineSpace; + } + + public double getTextNodeLength() + { + int lengthMult = -1; + + lengthMult = (int) ((raw[25] << 16) & 0xffff0000); + lengthMult += (raw[26] & 0x0000ffff); + + return Utility.ConverIntToDouble(lengthMult); + } + + public double getTextNodeHeight() + { + int heightMult = -1; + + heightMult = (int) ((raw[27] << 16) & 0xffff0000); + heightMult += (raw[28] & 0x0000ffff); + + return Utility.ConverIntToDouble(heightMult); + } + + public double getRotationAngle() + { + int rotation = (int) (raw[29] << 16 & 0xffff0000); + + rotation += raw[30]; + + return Utility.ConverIntToRotation(rotation); + } + + public Coordinate getOrigin() + { + int x = (int) ((raw[31] << 16) & 0xffff0000); + + x += raw[32] & 0x0000ffff; + + // return Utility.ConvertFromDGN(x); + double dx = Utility.ConverUnitToCoord(x); + int y = (int) ((raw[33] << 16) & 0xffff0000); + + y += (raw[34] & 0x0000ffff); + + double dy = Utility.ConverUnitToCoord(y); + + return new Coordinate(dx, dy); + } + + public static class ElementHandler extends Element.ElementHandler + { + private static ElementHandler instance = null; + + public ElementHandler() + { + super(ElementType.TEXTNODE); + } + + public static IElementHandler getInstance() + { + if (instance == null) + { + instance = new ElementHandler(); + } + + return instance; + } + + protected Element createElement(short[] raw) + { + return new TextNodeElement(raw); + } + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java new file mode 100644 index 0000000..5f0a9fe --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java @@ -0,0 +1,34 @@ +package com.ximple.io.dgn7; + +/** + * UserAttributeData + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 02:29:29 + */ +public class UserAttributeData +{ + protected short[] _src; + + public UserAttributeData(short id, int attributeCount) + { + _src = new short[attributeCount]; + _src[0] = id; + } + + public UserAttributeData(short[] src) + { + _src = src; + } + + public short getID() + { + return _src[0]; + } + + public void setID(short value) + { + _src[0] = value; + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java new file mode 100644 index 0000000..82a4c67 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java @@ -0,0 +1,238 @@ +package com.ximple.io.dgn7; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.vividsolutions.jts.geom.Envelope; + +/** + * Utility + * + * @author Ulysses + * @version 0.1 + * @since 2006/5/18 �U�� 01:33:00 + */ +public final class Utility +{ + public static double ConverIntToDouble(int src) + { + double newVal = (double) ((long) ((src * 6) / 1000.0 + 0.5)) / 1000.0; // ?[0.5?O?�X?F?|��????J + + return newVal; + } + + public static int ConverDoubleToInt(double src) + { + int newVal = (int) (src / 6 * 1000000.0); + + return newVal; + } + + public static int ConvertFromDGN(int aValue) + { + int newVal = 0; + + newVal = (((aValue ^ 0x00008000) << 16) & 0xffff0000); + newVal += (aValue >>> 16) & 0x0000ffff; + + return newVal; + } + + public static int ConverToDGN(int aValue) + { + int newVal = 0; + + newVal = (aValue << 16 & 0xffff0000); + newVal += (((aValue ^ 0x80000000) >>> 16) & 0x0000ffff); + + return newVal; + } + + public static double ConverIntToRotation(int aValue) + { + double newVal = aValue / 360000.0; + + if (newVal > 0) + { + newVal = (int) (newVal + 0.5); + } else + { + newVal = (int) (newVal - 0.5); + } + + return newVal; + } + + public static int ConverRotatioToInt(double aValue) + { + int newVal = (int) (aValue * 360000.0); + + return newVal; + } + + public static double ConverRotationToRadian(double aValue) + { + double newVal = aValue * Math.PI / 180; + + return newVal; + } + + public static double ConverUnitToCoord(int aValue) + { + double newVal = 0; + + newVal = aValue / 1000.0; + newVal += 2147483.648; // 2147483.648 = 2 ^ 31 + + return newVal; + } + + public static int ConverCoordToUnit(double aValue) + { + double newVal = aValue; + + newVal -= 2147483.648; + newVal = newVal * 1000.0; + + return (int) newVal; + } + + public static Envelope ConverUnitToCoord(Envelope range) + { + if (range == null) + { + return null; + } + + Envelope newRange = new Envelope(ConverUnitToCoord((int) range.getMinX()), ConverUnitToCoord((int) range.getMaxX()), + ConverUnitToCoord((int) range.getMinY()), ConverUnitToCoord((int) range.getMaxY())); + + return newRange; + } + + public static Envelope ConverCoordToUnit(Envelope range) + { + if (range == null) + { + return null; + } + + Envelope newRange = new Envelope(ConverCoordToUnit(range.getMinX()), ConverCoordToUnit(range.getMaxX()), + ConverCoordToUnit(range.getMinY()), ConverCoordToUnit(range.getMaxY())); + + return newRange; + } + + public static double DGNToIEEEDouble(short[] src) + { + int[] tmp = new int[2]; + long des = 0; + int sign; + int exponent; + int rndbits; + + if (src == null) + { + throw new RuntimeException("Source short array is null"); + } + + tmp[0] = (int) ((src[0] << 16) & 0xffff0000) | (src[1] & 0x0000ffff); // �X????? + tmp[1] = (int) ((src[2] << 16) & 0xffff0000) | (src[3] & 0x0000ffff); // ��C???? + sign = (int) (tmp[0] & 0x80000000); + exponent = (tmp[0] >>> 23) & 0x000000ff; + + if (exponent != 0) + { + exponent = exponent - 129 + 1023; + } + + rndbits = tmp[1] & 0x00000007; + tmp[1] = tmp[1] >>> 3; + tmp[1] = (tmp[1] & 0x1fffffff) | (tmp[0] << 29); + + if (rndbits != 0) + { + tmp[1] = tmp[1] | 0x00000001; + } + + tmp[0] = (tmp[0] >>> 3) & 0x000fffff; + tmp[0] = tmp[0] | (exponent << 20) | sign; + des = (((long) tmp[0] << 32)); + des = des | (long) tmp[1] & 0x00000000ffffffff; + + return Double.longBitsToDouble(des); + } + + public static short[] IEEEDoubleToDGN(double src) + { + long newVal = Double.doubleToLongBits(src); + + // uint[] tmp = new int[ 2 ]; + // ushort[] des = new short[ 4 ]; + int[] tmp = new int[2]; + short[] des = new short[4]; + int sign; + int exponent; + + tmp[0] = (int) ((newVal >>> 32) & 0x0ffffffff); + tmp[1] = (int) (newVal & 0x0ffffffff); + + // sign = ( int ) ( ( uint ) tmp[ 0 ] & 0x80000000 ); + sign = (int) tmp[0] & 0x80000000; + exponent = (tmp[0] >>> 20) & 0x07ff; + + if (exponent != 0) + { + exponent = exponent - 1023 + 129; + } + + if (exponent > 255) + { + if (sign != 0) + { + des[0] = -1; + } else + { + des[0] = 0x7fff; + } + + des[1] = -1; + des[2] = -1; + des[3] = -1; + + return des; + } else if ((exponent < 0) || ((exponent == 0) && (sign == 0))) + { + des[0] = 0x0; + des[1] = 0x0; + des[2] = 0x0; + des[3] = 0x0; + + return des; + } else + { + tmp[0] = (tmp[0] << 3) | (tmp[1] >> 29); + tmp[0] = tmp[0] & 0x007fffff; + tmp[0] = tmp[0] | (exponent << 23) | sign; + + // changed by phil 07/05/2004 + // tmp[ 1 ] = tmp[ 1 ] >> 3; + tmp[1] = tmp[1] << 3; + } + + des[0] = (short) ((tmp[0] >>> 16) & 0x0000ffff); + des[1] = (short) (tmp[0] & 0x0000ffff); + des[2] = (short) ((tmp[1] >>> 16) & 0x0000ffff); + des[3] = (short) (tmp[1] & 0x0000ffff); + + return des; + } + + public static double getLength(double x1, double y1, double x2, double y2) + { + double dx = x1 - x2; + double dy = y1 - y2; + double length = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); + + return length; + } +} diff --git a/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java b/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java new file mode 100644 index 0000000..de4c574 --- /dev/null +++ b/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java @@ -0,0 +1,4774 @@ +// +//(c) 2000 Sun Microsystems, Inc. +//ALL RIGHTS RESERVED +// +//License Grant- +// +// +//Permission to use, copy, modify, and distribute this Software and its +//documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is +//hereby granted. +// +//This Software is provided "AS IS". All express warranties, including any +//implied warranty of merchantability, satisfactory quality, fitness for a +//particular purpose, or non-infringement, are disclaimed, except to the extent +//that such disclaimers are held to be legally invalid. +// +//You acknowledge that Software is not designed, licensed or intended for use in +//the design, construction, operation or maintenance of any nuclear facility +//("High Risk Activities"). Sun disclaims any express or implied warranty of +//fitness for such uses. +// +//Please refer to the file http://www.sun.com/policies/trademarks/ for further +//important trademark information and to +//http://java.sun.com/nav/business/index.html for further important licensing +//information for the Java Technology. +// + +package com.ximple.util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.text.DecimalFormatSymbols; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Vector; + +/** + * PrintfFormat allows the formatting of an array of + * objects embedded within a string. Primitive types + * must be passed using wrapper types. The formatting + * is controlled by a control string. + * <p> + * A control string is a Java string that contains a + * control specification. The control specification + * starts at the first percent sign (%) in the string, + * provided that this percent sign + * <ol> + * <li>is not escaped protected by a matching % or is + * not an escape % character, + * <li>is not at the end of the format string, and + * <li>precedes a sequence of characters that parses as + * a valid control specification. + * </ol> + * </p><p> + * A control specification usually takes the form: + * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+ + * { [hlL] }+ [idfgGoxXeEcs] + * </pre> + * There are variants of this basic form that are + * discussed below.</p> + * <p> + * The format is composed of zero or more directives + * defined as follows: + * <ul> + * <li>ordinary characters, which are simply copied to + * the output stream; + * <li>escape sequences, which represent non-graphic + * characters; and + * <li>conversion specifications, each of which + * results in the fetching of zero or more arguments. + * </ul></p> + * <p> + * The results are undefined if there are insufficient + * arguments for the format. Usually an unchecked + * exception will be thrown. If the format is + * exhausted while arguments remain, the excess + * arguments are evaluated but are otherwise ignored. + * In format strings containing the % form of + * conversion specifications, each argument in the + * argument list is used exactly once.</p> + * <p> + * Conversions can be applied to the <code>n</code>th + * argument after the format in the argument list, + * rather than to the next unused argument. In this + * case, the conversion characer % is replaced by the + * sequence %<code>n</code>$, where <code>n</code> is + * a decimal integer giving the position of the + * argument in the argument list.</p> + * <p> + * In format strings containing the %<code>n</code>$ + * form of conversion specifications, each argument + * in the argument list is used exactly once.</p> + * + * <h4>Escape Sequences</h4> + * <p> + * The following table lists escape sequences and + * associated actions on display devices capable of + * the action. + * <table> + * <tr><th align=left>Sequence</th> + * <th align=left>Name</th> + * <th align=left>Description</th></tr> + * <tr><td>\\</td><td>backlash</td><td>None. + * </td></tr> + * <tr><td>\a</td><td>alert</td><td>Attempts to alert + * the user through audible or visible + * notification. + * </td></tr> + * <tr><td>\b</td><td>backspace</td><td>Moves the + * printing position to one column before + * the current position, unless the + * current position is the start of a line. + * </td></tr> + * <tr><td>\f</td><td>form-feed</td><td>Moves the + * printing position to the initial + * printing position of the next logical + * page. + * </td></tr> + * <tr><td>\n</td><td>newline</td><td>Moves the + * printing position to the start of the + * next line. + * </td></tr> + * <tr><td>\r</td><td>carriage-return</td><td>Moves + * the printing position to the start of + * the current line. + * </td></tr> + * <tr><td>\t</td><td>tab</td><td>Moves the printing + * position to the next implementation- + * defined horizontal tab position. + * </td></tr> + * <tr><td>\v</td><td>vertical-tab</td><td>Moves the + * printing position to the start of the + * next implementation-defined vertical + * tab position. + * </td></tr> + * </table></p> + * <h4>Conversion Specifications</h4> + * <p> + * Each conversion specification is introduced by + * the percent sign character (%). After the character + * %, the following appear in sequence:</p> + * <p> + * Zero or more flags (in any order), which modify the + * meaning of the conversion specification.</p> + * <p> + * An optional minimum field width. If the converted + * value has fewer characters than the field width, it + * will be padded with spaces by default on the left; + * t will be padded on the right, if the left- + * adjustment flag (-), described below, is given to + * the field width. The field width takes the form + * of a decimal integer. If the conversion character + * is s, the field width is the the minimum number of + * characters to be printed.</p> + * <p> + * An optional precision that gives the minumum number + * of digits to appear for the d, i, o, x or X + * conversions (the field is padded with leading + * zeros); the number of digits to appear after the + * radix character for the e, E, and f conversions, + * the maximum number of significant digits for the g + * and G conversions; or the maximum number of + * characters to be written from a string is s and S + * conversions. The precision takes the form of an + * optional decimal digit string, where a null digit + * string is treated as 0. If a precision appears + * with a c conversion character the precision is + * ignored. + * </p> + * <p> + * An optional h specifies that a following d, i, o, + * x, or X conversion character applies to a type + * short argument (the argument will be promoted + * according to the integral promotions and its value + * converted to type short before printing).</p> + * <p> + * An optional l (ell) specifies that a following + * d, i, o, x, or X conversion character applies to a + * type long argument.</p> + * <p> + * A field width or precision may be indicated by an + * asterisk (*) instead of a digit string. In this + * case, an integer argument supplised the field width + * precision. The argument that is actually converted + * is not fetched until the conversion letter is seen, + * so the the arguments specifying field width or + * precision must appear before the argument (if any) + * to be converted. If the precision argument is + * negative, it will be changed to zero. A negative + * field width argument is taken as a - flag, followed + * by a positive field width.</p> + * <p> + * In format strings containing the %<code>n</code>$ + * form of a conversion specification, a field width + * or precision may be indicated by the sequence + * *<code>m</code>$, where m is a decimal integer + * giving the position in the argument list (after the + * format argument) of an integer argument containing + * the field width or precision.</p> + * <p> + * The format can contain either numbered argument + * specifications (that is, %<code>n</code>$ and + * *<code>m</code>$), or unnumbered argument + * specifications (that is % and *), but normally not + * both. The only exception to this is that %% can + * be mixed with the %<code>n</code>$ form. The + * results of mixing numbered and unnumbered argument + * specifications in a format string are undefined.</p> + * + * <h4>Flag Characters</h4> + * <p> + * The flags and their meanings are:</p> + * <dl> + * <dt>'<dd> integer portion of the result of a + * decimal conversion (%i, %d, %f, %g, or %G) will + * be formatted with thousands' grouping + * characters. For other conversions the flag + * is ignored. The non-monetary grouping + * character is used. + * <dt>-<dd> result of the conversion is left-justified + * within the field. (It will be right-justified + * if this flag is not specified).</td></tr> + * <dt>+<dd> result of a signed conversion always + * begins with a sign (+ or -). (It will begin + * with a sign only when a negative value is + * converted if this flag is not specified.) + * <dt><space><dd> If the first character of a + * signed conversion is not a sign, a space + * character will be placed before the result. + * This means that if the space character and + + * flags both appear, the space flag will be + * ignored. + * <dt>#<dd> value is to be converted to an alternative + * form. For c, d, i, and s conversions, the flag + * has no effect. For o conversion, it increases + * the precision to force the first digit of the + * result to be a zero. For x or X conversion, a + * non-zero result has 0x or 0X prefixed to it, + * respectively. For e, E, f, g, and G + * conversions, the result always contains a radix + * character, even if no digits follow the radix + * character (normally, a decimal point appears in + * the result of these conversions only if a digit + * follows it). For g and G conversions, trailing + * zeros will not be removed from the result as + * they normally are. + * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G + * conversions, leading zeros (following any + * indication of sign or base) are used to pad to + * the field width; no space padding is + * performed. If the 0 and - flags both appear, + * the 0 flag is ignored. For d, i, o, x, and X + * conversions, if a precision is specified, the + * 0 flag will be ignored. For c conversions, + * the flag is ignored. + * </dl> + * + * <h4>Conversion Characters</h4> + * <p> + * Each conversion character results in fetching zero + * or more arguments. The results are undefined if + * there are insufficient arguments for the format. + * Usually, an unchecked exception will be thrown. + * If the format is exhausted while arguments remain, + * the excess arguments are ignored.</p> + * + * <p> + * The conversion characters and their meanings are: + * </p> + * <dl> + * <dt>d,i<dd>The int argument is converted to a + * signed decimal in the style [-]dddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + * <dt>o<dd> The int argument is converted to unsigned + * octal format in the style ddddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + * <dt>x<dd> The int argument is converted to unsigned + * hexadecimal format in the style dddd; the + * letters abcdef are used. The precision + * specifies the minimum numberof digits to + * appear; if the value being converted can be + * represented in fewer digits, it will be + * expanded with leading zeros. The default + * precision is 1. The result of converting 0 + * with an explicit precision of 0 is no + * characters. + * <dt>X<dd> Behaves the same as the x conversion + * character except that letters ABCDEF are + * used instead of abcdef. + * <dt>f<dd> The floating point number argument is + * written in decimal notation in the style + * [-]ddd.ddd, where the number of digits after + * the radix character (shown here as a decimal + * point) is equal to the precision + * specification. A Locale is used to determine + * the radix character to use in this format. + * If the precision is omitted from the + * argument, six digits are written after the + * radix character; if the precision is + * explicitly 0 and the # flag is not specified, + * no radix character appears. If a radix + * character appears, at least 1 digit appears + * before it. The value is rounded to the + * appropriate number of digits. + * <dt>e,E<dd>The floating point number argument is + * written in the style [-]d.ddde{+-}dd + * (the symbols {+-} indicate either a plus or + * minus sign), where there is one digit before + * the radix character (shown here as a decimal + * point) and the number of digits after it is + * equal to the precision. A Locale is used to + * determine the radix character to use in this + * format. When the precision is missing, six + * digits are written after the radix character; + * if the precision is 0 and the # flag is not + * specified, no radix character appears. The + * E conversion will produce a number with E + * instead of e introducing the exponent. The + * exponent always contains at least two digits. + * However, if the value to be written requires + * an exponent greater than two digits, + * additional exponent digits are written as + * necessary. The value is rounded to the + * appropriate number of digits. + * <dt>g,G<dd>The floating point number argument is + * written in style f or e (or in sytle E in the + * case of a G conversion character), with the + * precision specifying the number of + * significant digits. If the precision is + * zero, it is taken as one. The style used + * depends on the value converted: style e + * (or E) will be used only if the exponent + * resulting from the conversion is less than + * -4 or greater than or equal to the precision. + * Trailing zeros are removed from the result. + * A radix character appears only if it is + * followed by a digit. + * <dt>c,C<dd>The integer argument is converted to a + * char and the result is written. + * + * <dt>s,S<dd>The argument is taken to be a string and + * bytes from the string are written until the + * end of the string or the number of bytes + * indicated by the precision specification of + * the argument is reached. If the precision + * is omitted from the argument, it is taken to + * be infinite, so all characters up to the end + * of the string are written. + * <dt>%<dd>Write a % character; no argument is + * converted. + * </dl> + * <p> + * If a conversion specification does not match one of + * the above forms, an IllegalArgumentException is + * thrown and the instance of PrintfFormat is not + * created.</p> + * <p> + * If a floating point value is the internal + * representation for infinity, the output is + * [+]Infinity, where Infinity is either Infinity or + * Inf, depending on the desired output string length. + * Printing of the sign follows the rules described + * above.</p> + * <p> + * If a floating point value is the internal + * representation for "not-a-number," the output is + * [+]NaN. Printing of the sign follows the rules + * described above.</p> + * <p> + * In no case does a non-existent or small field width + * cause truncation of a field; if the result of a + * conversion is wider than the field width, the field + * is simply expanded to contain the conversion result. + * </p> + * <p> + * The behavior is like printf. One exception is that + * the minimum number of exponent digits is 3 instead + * of 2 for e and E formats when the optional L is used + * before the e, E, g, or G conversion character. The + * optional L does not imply conversion to a long long + * double. </p> + * <p> + * The biggest divergence from the C printf + * specification is in the use of 16 bit characters. + * This allows the handling of characters beyond the + * small ASCII character set and allows the utility to + * interoperate correctly with the rest of the Java + * runtime environment.</p> + * <p> + * Omissions from the C printf specification are + * numerous. All the known omissions are present + * because Java never uses bytes to represent + * characters and does not have pointers:</p> + * <ul> + * <li>%c is the same as %C. + * <li>%s is the same as %S. + * <li>u, p, and n conversion characters. + * <li>%ws format. + * <li>h modifier applied to an n conversion character. + * <li>l (ell) modifier applied to the c, n, or s + * conversion characters. + * <li>ll (ell ell) modifier to d, i, o, u, x, or X + * conversion characters. + * <li>ll (ell ell) modifier to an n conversion + * character. + * <li>c, C, d,i,o,u,x, and X conversion characters + * apply to Byte, Character, Short, Integer, Long + * types. + * <li>f, e, E, g, and G conversion characters apply + * to Float and Double types. + * <li>s and S conversion characters apply to String + * types. + * <li>All other reference types can be formatted + * using the s or S conversion characters only. + * </ul> + * <p> + * Most of this specification is quoted from the Unix + * man page for the sprintf utility.</p> + * + * @author Allan Jacobs + * @version 1 + * Release 1: Initial release. + * Release 2: Asterisk field widths and precisions + * %n$ and *m$ + * Bug fixes + * g format fix (2 digits in e form corrupt) + * rounding in f format implemented + * round up when digit not printed is 5 + * formatting of -0.0f + * round up/down when last digits are 50000... + */ +public final class PrintfFormat +{ + /** Vector of control strings and format literals. */ + private Vector vFmt = new Vector(); + + /** Character position. Used by the constructor. */ + private int cPos = 0; + + /** Character position. Used by the constructor. */ + private DecimalFormatSymbols dfs = null; + + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(String fmtArg) throws IllegalArgumentException + { + this(Locale.getDefault(), fmtArg); + } + + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(Locale locale, String fmtArg) throws IllegalArgumentException + { + dfs = new DecimalFormatSymbols(locale); + + int ePos = 0; + ConversionSpecification sFmt = null; + String unCS = this.nonControl(fmtArg, 0); + + if (unCS != null) + { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + + while ((cPos != -1) && (cPos < fmtArg.length())) + { + for (ePos = cPos + 1; ePos < fmtArg.length(); ePos++) + { + char c = 0; + + c = fmtArg.charAt(ePos); + + if (c == 'i') + { + break; + } + + if (c == 'd') + { + break; + } + + if (c == 'f') + { + break; + } + + if (c == 'g') + { + break; + } + + if (c == 'G') + { + break; + } + + if (c == 'o') + { + break; + } + + if (c == 'x') + { + break; + } + + if (c == 'X') + { + break; + } + + if (c == 'e') + { + break; + } + + if (c == 'E') + { + break; + } + + if (c == 'c') + { + break; + } + + if (c == 's') + { + break; + } + + if (c == '%') + { + break; + } + } + + ePos = Math.min(ePos + 1, fmtArg.length()); + sFmt = new ConversionSpecification(fmtArg.substring(cPos, ePos)); + vFmt.addElement(sFmt); + unCS = this.nonControl(fmtArg, ePos); + + if (unCS != null) + { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + } + } + + /** + * Return a substring starting at + * <code>start</code> and ending at either the end + * of the String <code>s</code>, the next unpaired + * percent sign, or at the end of the String if the + * last character is a percent sign. + * @param s Control string. + * @param start Position in the string + * <code>s</code> to begin looking for the start + * of a control string. + * @return the substring from the start position + * to the beginning of the control string. + */ + private String nonControl(String s, int start) + { + String ret = ""; + + cPos = s.indexOf("%", start); + + if (cPos == -1) + { + cPos = s.length(); + } + + return s.substring(start, cPos); + } + + /** + * Format an array of objects. Byte, Short, + * Integer, Long, Float, Double, and Character + * arguments are treated as wrappers for primitive + * types. + * @param o The array of objects to format. + * @return The formatted String. + */ + public String sprintf(Object[] o) + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + int i = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } else + { + if (cs.isPositionalSpecification()) + { + i = cs.getArgumentPosition() - 1; + + if (cs.isPositionalFieldWidth()) + { + int ifw = cs.getArgumentPositionForFieldWidth() - 1; + + cs.setFieldWidthWithArg(((Integer) o[ifw]).intValue()); + } + + if (cs.isPositionalPrecision()) + { + int ipr = cs.getArgumentPositionForPrecision() - 1; + + cs.setPrecisionWithArg(((Integer) o[ipr]).intValue()); + } + } else + { + if (cs.isVariableFieldWidth()) + { + cs.setFieldWidthWithArg(((Integer) o[i]).intValue()); + i++; + } + + if (cs.isVariablePrecision()) + { + cs.setPrecisionWithArg(((Integer) o[i]).intValue()); + i++; + } + } + + if (o[i] instanceof Byte) + { + sb.append(cs.internalsprintf(((Byte) o[i]).byteValue())); + } else if (o[i] instanceof Short) + { + sb.append(cs.internalsprintf(((Short) o[i]).shortValue())); + } else if (o[i] instanceof Integer) + { + sb.append(cs.internalsprintf(((Integer) o[i]).intValue())); + } else if (o[i] instanceof Long) + { + sb.append(cs.internalsprintf(((Long) o[i]).longValue())); + } else if (o[i] instanceof Float) + { + sb.append(cs.internalsprintf(((Float) o[i]).floatValue())); + } else if (o[i] instanceof Double) + { + sb.append(cs.internalsprintf(((Double) o[i]).doubleValue())); + } else if (o[i] instanceof Character) + { + sb.append(cs.internalsprintf(((Character) o[i]).charValue())); + } else if (o[i] instanceof String) + { + sb.append(cs.internalsprintf((String) o[i])); + } else + { + sb.append(cs.internalsprintf(o[i])); + } + + if (!cs.isPositionalSpecification()) + { + i++; + } + } + } + + return sb.toString(); + } + + /** + * Format nothing. Just use the control string. + * @return the formatted String. + */ + public String sprintf() + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } + } + + return sb.toString(); + } + + /** + * Format an int. + * @param x The int to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(int x) throws IllegalArgumentException + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } else + { + sb.append(cs.internalsprintf(x)); + } + } + + return sb.toString(); + } + + /** + * Format an long. + * @param x The long to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(long x) throws IllegalArgumentException + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } else + { + sb.append(cs.internalsprintf(x)); + } + } + + return sb.toString(); + } + + /** + * Format a double. + * @param x The double to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, + * d, d, x, X, or o. + */ + public String sprintf(double x) throws IllegalArgumentException + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } else + { + sb.append(cs.internalsprintf(x)); + } + } + + return sb.toString(); + } + + /** + * Format a String. + * @param x The String to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + public String sprintf(String x) throws IllegalArgumentException + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } else + { + sb.append(cs.internalsprintf(x)); + } + } + + return sb.toString(); + } + + /** + * Format an Object. Convert wrapper types to + * their primitive equivalents and call the + * appropriate internal formatting method. Convert + * Strings using an internal formatting method for + * Strings. Otherwise use the default formatter + * (use toString). + * @param x the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is inappropriate for + * formatting an unwrapped value. + */ + public String sprintf(Object x) throws IllegalArgumentException + { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + + while (e.hasMoreElements()) + { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + + if (c == '\0') + { + sb.append(cs.getLiteral()); + } else if (c == '%') + { + sb.append("%"); + } else + { + if (x instanceof Byte) + { + sb.append(cs.internalsprintf(((Byte) x).byteValue())); + } else if (x instanceof Short) + { + sb.append(cs.internalsprintf(((Short) x).shortValue())); + } else if (x instanceof Integer) + { + sb.append(cs.internalsprintf(((Integer) x).intValue())); + } else if (x instanceof Long) + { + sb.append(cs.internalsprintf(((Long) x).longValue())); + } else if (x instanceof Float) + { + sb.append(cs.internalsprintf(((Float) x).floatValue())); + } else if (x instanceof Double) + { + sb.append(cs.internalsprintf(((Double) x).doubleValue())); + } else if (x instanceof Character) + { + sb.append(cs.internalsprintf(((Character) x).charValue())); + } else if (x instanceof String) + { + sb.append(cs.internalsprintf((String) x)); + } else + { + sb.append(cs.internalsprintf(x)); + } + } + } + + return sb.toString(); + } + + /** + * <p> + * ConversionSpecification allows the formatting of + * a single primitive or object embedded within a + * string. The formatting is controlled by a + * format string. Only one Java primitive or + * object can be formatted at a time. + * <p> + * A format string is a Java string that contains + * a control string. The control string starts at + * the first percent sign (%) in the string, + * provided that this percent sign + * <ol> + * <li>is not escaped protected by a matching % or + * is not an escape % character, + * <li>is not at the end of the format string, and + * <li>precedes a sequence of characters that parses + * as a valid control string. + * </ol> + * <p> + * A control string takes the form: + * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+ + * { [hlL] }+ [idfgGoxXeEcs] + * </pre> + * <p> + * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The + * optional L does not imply conversion to a long + * long double. + */ + private class ConversionSpecification + { + /** Default precision. */ + private final static int defaultDigits = 6; + + /** + * The integer portion of the result of a decimal + * conversion (i, d, u, f, g, or G) will be + * formatted with thousands' grouping characters. + * For other conversions the flag is ignored. + */ + private boolean thousands = false; + + /** + * The result of the conversion will be + * left-justified within the field. + */ + private boolean leftJustify = false; + + /** + * The result of a signed conversion will always + * begin with a sign (+ or -). + */ + private boolean leadingSign = false; + + /** + * Flag indicating that left padding with spaces is + * specified. + */ + private boolean leadingSpace = false; + + /** + * For an o conversion, increase the precision to + * force the first digit of the result to be a + * zero. For x (or X) conversions, a non-zero + * result will have 0x (or 0X) prepended to it. + * For e, E, f, g, or G conversions, the result + * will always contain a radix character, even if + * no digits follow the point. For g and G + * conversions, trailing zeros will not be removed + * from the result. + */ + private boolean alternateForm = false; + + /** + * Flag indicating that left padding with zeroes is + * specified. + */ + private boolean leadingZeros = false; + + /** + * Flag indicating that the field width is *. + */ + private boolean variableFieldWidth = false; + + /** + * If the converted value has fewer bytes than the + * field width, it will be padded with spaces or + * zeroes. + */ + private int fieldWidth = 0; + + /** + * Flag indicating whether or not the field width + * has been set. + */ + private boolean fieldWidthSet = false; + + /** + * The minimum number of digits to appear for the + * d, i, o, u, x, or X conversions. The number of + * digits to appear after the radix character for + * the e, E, and f conversions. The maximum number + * of significant digits for the g and G + * conversions. The maximum number of bytes to be + * printed from a string in s and S conversions. + */ + private int precision = 0; + + /** + * Flag indicating that the precision is *. + */ + private boolean variablePrecision = false; + + /** + * Flag indicating whether or not the precision has + * been set. + */ + private boolean precisionSet = false; + + /* + */ + private boolean positionalSpecification = false; + private int argumentPosition = 0; + private boolean positionalFieldWidth = false; + private int argumentPositionForFieldWidth = 0; + private boolean positionalPrecision = false; + private int argumentPositionForPrecision = 0; + + /** + * Flag specifying that a following d, i, o, u, x, + * or X conversion character applies to a type + * short int. + */ + private boolean optionalh = false; + + /** + * Flag specifying that a following d, i, o, u, x, + * or X conversion character applies to a type lont + * int argument. + */ + private boolean optionall = false; + + /** + * Flag specifying that a following e, E, f, g, or + * G conversion character applies to a type double + * argument. This is a noop in Java. + */ + private boolean optionalL = false; + + /** Control string type. */ + private char conversionCharacter = '\0'; + + /** + * Position within the control string. Used by + * the constructor. + */ + private int pos = 0; + + /** Literal or control format string. */ + private String fmt; + + /** + * Constructor. Used to prepare an instance + * to hold a literal, not a control string. + */ + ConversionSpecification() + { + } + + /** + * Constructor for a conversion specification. + * The argument must begin with a % and end + * with the conversion character for the + * conversion specification. + * @param fmtArg String specifying the + * conversion specification. + * @exception IllegalArgumentException if the + * input string is null, zero length, or + * otherwise malformed. + */ + ConversionSpecification(String fmtArg) throws IllegalArgumentException + { + if (fmtArg == null) + { + throw new NullPointerException(); + } + + if (fmtArg.length() == 0) + { + throw new IllegalArgumentException("Control strings must have positive" + " lengths."); + } + + if (fmtArg.charAt(0) == '%') + { + fmt = fmtArg; + pos = 1; + setArgPosition(); + setFlagCharacters(); + setFieldWidth(); + setPrecision(); + setOptionalHL(); + + if (setConversionCharacter()) + { + if (pos == fmtArg.length()) + { + if (leadingZeros && leftJustify) + { + leadingZeros = false; + } + + if (precisionSet && leadingZeros) + { + if ((conversionCharacter == 'd') || (conversionCharacter == 'i') || (conversionCharacter == 'o') + || (conversionCharacter == 'x')) + { + leadingZeros = false; + } + } + } else + { + throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg); + } + } else + { + throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg); + } + } else + { + throw new IllegalArgumentException("Control strings must begin with %."); + } + } + + /** + * Set the String for this instance. + * @param s the String to store. + */ + void setLiteral(String s) + { + fmt = s; + } + + /** + * Get the String for this instance. Translate + * any escape sequences. + * + * @return s the stored String. + */ + String getLiteral() + { + StringBuffer sb = new StringBuffer(); + int i = 0; + + while (i < fmt.length()) + { + if (fmt.charAt(i) == '\\') + { + i++; + + if (i < fmt.length()) + { + char c = fmt.charAt(i); + + switch (c) + { + case 'a' : + sb.append((char) 0x07); + + break; + + case 'b' : + sb.append('\b'); + + break; + + case 'f' : + sb.append('\f'); + + break; + + case 'n' : + sb.append(System.getProperty("line.separator")); + + break; + + case 'r' : + sb.append('\r'); + + break; + + case 't' : + sb.append('\t'); + + break; + + case 'v' : + sb.append((char) 0x0b); + + break; + + case '\\' : + sb.append('\\'); + + break; + } + + i++; + } else + { + sb.append('\\'); + } + } else + { + i++; + } + } + + return fmt; + } + + /** + * Get the conversion character that tells what + * type of control character this instance has. + * + * @return the conversion character. + */ + char getConversionCharacter() + { + return conversionCharacter; + } + + /** + * Check whether the specifier has a variable + * field width that is going to be set by an + * argument. + * @return <code>true</code> if the conversion + * uses an * field width; otherwise + * <code>false</code>. + */ + boolean isVariableFieldWidth() + { + return variableFieldWidth; + } + + /** + * Set the field width with an argument. A + * negative field width is taken as a - flag + * followed by a positive field width. + * @param fw the field width. + */ + void setFieldWidthWithArg(int fw) + { + if (fw < 0) + { + leftJustify = true; + } + + fieldWidthSet = true; + fieldWidth = Math.abs(fw); + } + + /** + * Check whether the specifier has a variable + * precision that is going to be set by an + * argument. + * @return <code>true</code> if the conversion + * uses an * precision; otherwise + * <code>false</code>. + */ + boolean isVariablePrecision() + { + return variablePrecision; + } + + /** + * Set the precision with an argument. A + * negative precision will be changed to zero. + * @param pr the precision. + */ + void setPrecisionWithArg(int pr) + { + precisionSet = true; + precision = Math.max(pr, 0); + } + + /** + * Format an int argument using this conversion + * specification. + * @param s the int to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(int s) throws IllegalArgumentException + { + String s2 = ""; + + switch (conversionCharacter) + { + case 'd' : + case 'i' : + if (optionalh) + { + s2 = printDFormat((short) s); + } else if (optionall) + { + s2 = printDFormat((long) s); + } else + { + s2 = printDFormat(s); + } + + break; + + case 'x' : + case 'X' : + if (optionalh) + { + s2 = printXFormat((short) s); + } else if (optionall) + { + s2 = printXFormat((long) s); + } else + { + s2 = printXFormat(s); + } + + break; + + case 'o' : + if (optionalh) + { + s2 = printOFormat((short) s); + } else if (optionall) + { + s2 = printOFormat((long) s); + } else + { + s2 = printOFormat(s); + } + + break; + + case 'c' : + case 'C' : + s2 = printCFormat((char) s); + + break; + + default : + throw new IllegalArgumentException("Cannot format a int with a format using a " + conversionCharacter + + " conversion character."); + } + + return s2; + } + + /** + * Format a long argument using this conversion + * specification. + * @param s the long to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(long s) throws IllegalArgumentException + { + String s2 = ""; + + switch (conversionCharacter) + { + case 'd' : + case 'i' : + if (optionalh) + { + s2 = printDFormat((short) s); + } else if (optionall) + { + s2 = printDFormat(s); + } else + { + s2 = printDFormat((int) s); + } + + break; + + case 'x' : + case 'X' : + if (optionalh) + { + s2 = printXFormat((short) s); + } else if (optionall) + { + s2 = printXFormat(s); + } else + { + s2 = printXFormat((int) s); + } + + break; + + case 'o' : + if (optionalh) + { + s2 = printOFormat((short) s); + } else if (optionall) + { + s2 = printOFormat(s); + } else + { + s2 = printOFormat((int) s); + } + + break; + + case 'c' : + case 'C' : + s2 = printCFormat((char) s); + + break; + + default : + throw new IllegalArgumentException("Cannot format a long with a format using a " + conversionCharacter + + " conversion character."); + } + + return s2; + } + + /** + * Format a double argument using this conversion + * specification. + * @param s the double to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, i, d, + * x, X, or o. + */ + String internalsprintf(double s) throws IllegalArgumentException + { + String s2 = ""; + + switch (conversionCharacter) + { + case 'f' : + s2 = printFFormat(s); + + break; + + case 'E' : + case 'e' : + s2 = printEFormat(s); + + break; + + case 'G' : + case 'g' : + s2 = printGFormat(s); + + break; + + default : + throw new IllegalArgumentException("Cannot " + "format a double with a format using a " + conversionCharacter + + " conversion character."); + } + + return s2; + } + + /** + * Format a String argument using this conversion + * specification. + * @param s the String to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(String s) throws IllegalArgumentException + { + String s2 = ""; + + if ((conversionCharacter == 's') || (conversionCharacter == 'S')) + { + s2 = printSFormat(s); + } else + { + throw new IllegalArgumentException("Cannot " + "format a String with a format using a " + conversionCharacter + + " conversion character."); + } + + return s2; + } + + /** + * Format an Object argument using this conversion + * specification. + * @param s the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(Object s) + { + String s2 = ""; + + if ((conversionCharacter == 's') || (conversionCharacter == 'S')) + { + s2 = printSFormat(s.toString()); + } else + { + throw new IllegalArgumentException("Cannot format a String with a format using" + " a " + conversionCharacter + + " conversion character."); + } + + return s2; + } + + /** + * For f format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both + * a '+' and a ' ' are specified, the blank flag + * is ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the number of digits + * to appear after the radix character. Padding is + * with trailing 0s. + */ + private char[] fFormatDigits(double x) + { + // int defaultDigits=6; + String sx, sxOut; + int i, j, k; + int n1In, n2In; + int expon = 0; + boolean minusSign = false; + + if (x > 0.0) + { + sx = Double.toString(x); + } else if (x < 0.0) + { + sx = Double.toString(-x); + minusSign = true; + } else + { + sx = Double.toString(x); + + if (sx.charAt(0) == '-') + { + minusSign = true; + sx = sx.substring(1); + } + } + + int ePos = sx.indexOf('E'); + int rPos = sx.indexOf('.'); + + if (rPos != -1) + { + n1In = rPos; + } else if (ePos != -1) + { + n1In = ePos; + } else + { + n1In = sx.length(); + } + + if (rPos != -1) + { + if (ePos != -1) + { + n2In = ePos - rPos - 1; + } else + { + n2In = sx.length() - rPos - 1; + } + } else + { + n2In = 0; + } + + if (ePos != -1) + { + int ie = ePos + 1; + + expon = 0; + + if (sx.charAt(ie) == '-') + { + for (++ie; ie < sx.length(); ie++) + { + if (sx.charAt(ie) != '0') + { + break; + } + } + + if (ie < sx.length()) + { + expon = -Integer.parseInt(sx.substring(ie)); + } + } else + { + if (sx.charAt(ie) == '+') + { + ++ie; + } + + for (; ie < sx.length(); ie++) + { + if (sx.charAt(ie) != '0') + { + break; + } + } + + if (ie < sx.length()) + { + expon = Integer.parseInt(sx.substring(ie)); + } + } + } + + int p; + + if (precisionSet) + { + p = precision; + } else + { + p = defaultDigits - 1; + } + + char[] ca1 = sx.toCharArray(); + char[] ca2 = new char[n1In + n2In]; + char[] ca3, ca4, ca5; + + for (j = 0; j < n1In; j++) + { + ca2[j] = ca1[j]; + } + + i = j + 1; + + for (k = 0; k < n2In; j++, i++, k++) + { + ca2[j] = ca1[i]; + } + + if (n1In + expon <= 0) + { + ca3 = new char[-expon + n2In]; + + for (j = 0, k = 0; k < (-n1In - expon); k++, j++) + { + ca3[j] = '0'; + } + + for (i = 0; i < (n1In + n2In); i++, j++) + { + ca3[j] = ca2[i]; + } + } else + { + ca3 = ca2; + } + + boolean carry = false; + + if (p < -expon + n2In) + { + if (expon < 0) + { + i = p; + } else + { + i = p + n1In; + } + + carry = checkForCarry(ca3, i); + + if (carry) + { + carry = startSymbolicCarry(ca3, i - 1, 0); + } + } + + if (n1In + expon <= 0) + { + ca4 = new char[2 + p]; + + if (!carry) + { + ca4[0] = '0'; + } else + { + ca4[0] = '1'; + } + + if (alternateForm ||!precisionSet || (precision != 0)) + { + ca4[1] = '.'; + + for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++) + { + ca4[j] = ca3[i]; + } + + for (; j < ca4.length; j++) + { + ca4[j] = '0'; + } + } + } else + { + if (!carry) + { + if (alternateForm ||!precisionSet || (precision != 0)) + { + ca4 = new char[n1In + expon + p + 1]; + } else + { + ca4 = new char[n1In + expon]; + } + + j = 0; + } else + { + if (alternateForm ||!precisionSet || (precision != 0)) + { + ca4 = new char[n1In + expon + p + 2]; + } else + { + ca4 = new char[n1In + expon + 1]; + } + + ca4[0] = '1'; + j = 1; + } + + for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++) + { + ca4[j] = ca3[i]; + } + + for (; i < n1In + expon; i++, j++) + { + ca4[j] = '0'; + } + + if (alternateForm ||!precisionSet || (precision != 0)) + { + ca4[j] = '.'; + j++; + + for (k = 0; (i < ca3.length) && (k < p); i++, j++, k++) + { + ca4[j] = ca3[i]; + } + + for (; j < ca4.length; j++) + { + ca4[j] = '0'; + } + } + } + + int nZeros = 0; + + if (!leftJustify && leadingZeros) + { + int xThousands = 0; + + if (thousands) + { + int xlead = 0; + + if ((ca4[0] == '+') || (ca4[0] == '-') || (ca4[0] == ' ')) + { + xlead = 1; + } + + int xdp = xlead; + + for (; xdp < ca4.length; xdp++) + { + if (ca4[xdp] == '.') + { + break; + } + } + + xThousands = (xdp - xlead) / 3; + } + + if (fieldWidthSet) + { + nZeros = fieldWidth - ca4.length; + } + + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) + { + nZeros--; + } + + nZeros -= xThousands; + + if (nZeros < 0) + { + nZeros = 0; + } + } + + j = 0; + + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) + { + ca5 = new char[ca4.length + nZeros + 1]; + j++; + } else + { + ca5 = new char[ca4.length + nZeros]; + } + + if (!minusSign) + { + if (leadingSign) + { + ca5[0] = '+'; + } + + if (leadingSpace) + { + ca5[0] = ' '; + } + } else + { + ca5[0] = '-'; + } + + for (i = 0; i < nZeros; i++, j++) + { + ca5[j] = '0'; + } + + for (i = 0; i < ca4.length; i++, j++) + { + ca5[j] = ca4[i]; + } + + int lead = 0; + + if ((ca5[0] == '+') || (ca5[0] == '-') || (ca5[0] == ' ')) + { + lead = 1; + } + + int dp = lead; + + for (; dp < ca5.length; dp++) + { + if (ca5[dp] == '.') + { + break; + } + } + + int nThousands = (dp - lead) / 3; + + // Localize the decimal point. + if (dp < ca5.length) + { + ca5[dp] = dfs.getDecimalSeparator(); + } + + char[] ca6 = ca5; + + if (thousands && (nThousands > 0)) + { + ca6 = new char[ca5.length + nThousands + lead]; + ca6[0] = ca5[0]; + + for (i = lead, k = lead; i < dp; i++) + { + if ((i > 0) && (dp - i) % 3 == 0) + { + // ca6[k]=','; + ca6[k] = dfs.getGroupingSeparator(); + ca6[k + 1] = ca5[i]; + k += 2; + } else + { + ca6[k] = ca5[i]; + k++; + } + } + + for (; i < ca5.length; i++, k++) + { + ca6[k] = ca5[i]; + } + } + + return ca6; + } + + /** + * An intermediate routine on the way to creating + * an f format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @return the converted double value. + */ + private String fFormatString(double x) + { + boolean noDigits = false; + char[] ca6, ca7; + + if (Double.isInfinite(x)) + { + if (x == Double.POSITIVE_INFINITY) + { + if (leadingSign) + { + ca6 = "+Inf".toCharArray(); + } else if (leadingSpace) + { + ca6 = " Inf".toCharArray(); + } else + { + ca6 = "Inf".toCharArray(); + } + } else + { + ca6 = "-Inf".toCharArray(); + } + + noDigits = true; + } else if (Double.isNaN(x)) + { + if (leadingSign) + { + ca6 = "+NaN".toCharArray(); + } else if (leadingSpace) + { + ca6 = " NaN".toCharArray(); + } else + { + ca6 = "NaN".toCharArray(); + } + + noDigits = true; + } else + { + ca6 = fFormatDigits(x); + } + + ca7 = applyFloatPadding(ca6, false); + + return new String(ca7); + } + + /** + * For e format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear after the radix character. + * Padding is with trailing 0s. + * + * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The optional + * L does not imply conversion to a long long + * double. + */ + private char[] eFormatDigits(double x, char eChar) + { + char[] ca1, ca2, ca3; + + // int defaultDigits=6; + String sx, sxOut; + int i, j, k, p; + int n1In, n2In; + int expon = 0; + int ePos, rPos, eSize; + boolean minusSign = false; + + if (x > 0.0) + { + sx = Double.toString(x); + } else if (x < 0.0) + { + sx = Double.toString(-x); + minusSign = true; + } else + { + sx = Double.toString(x); + + if (sx.charAt(0) == '-') + { + minusSign = true; + sx = sx.substring(1); + } + } + + ePos = sx.indexOf('E'); + + if (ePos == -1) + { + ePos = sx.indexOf('e'); + } + + rPos = sx.indexOf('.'); + + if (rPos != -1) + { + n1In = rPos; + } else if (ePos != -1) + { + n1In = ePos; + } else + { + n1In = sx.length(); + } + + if (rPos != -1) + { + if (ePos != -1) + { + n2In = ePos - rPos - 1; + } else + { + n2In = sx.length() - rPos - 1; + } + } else + { + n2In = 0; + } + + if (ePos != -1) + { + int ie = ePos + 1; + + expon = 0; + + if (sx.charAt(ie) == '-') + { + for (++ie; ie < sx.length(); ie++) + { + if (sx.charAt(ie) != '0') + { + break; + } + } + + if (ie < sx.length()) + { + expon = -Integer.parseInt(sx.substring(ie)); + } + } else + { + if (sx.charAt(ie) == '+') + { + ++ie; + } + + for (; ie < sx.length(); ie++) + { + if (sx.charAt(ie) != '0') + { + break; + } + } + + if (ie < sx.length()) + { + expon = Integer.parseInt(sx.substring(ie)); + } + } + } + + if (rPos != -1) + { + expon += rPos - 1; + } + + if (precisionSet) + { + p = precision; + } else + { + p = defaultDigits - 1; + } + + if ((rPos != -1) && (ePos != -1)) + { + ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray(); + } else if (rPos != -1) + { + ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray(); + } else if (ePos != -1) + { + ca1 = sx.substring(0, ePos).toCharArray(); + } else + { + ca1 = sx.toCharArray(); + } + + boolean carry = false; + int i0 = 0; + + if (ca1[0] != '0') + { + i0 = 0; + } else + { + for (i0 = 0; i0 < ca1.length; i0++) + { + if (ca1[i0] != '0') + { + break; + } + } + } + + if (i0 + p < ca1.length - 1) + { + carry = checkForCarry(ca1, i0 + p + 1); + + if (carry) + { + carry = startSymbolicCarry(ca1, i0 + p, i0); + } + + if (carry) + { + ca2 = new char[i0 + p + 1]; + ca2[i0] = '1'; + + for (j = 0; j < i0; j++) + { + ca2[j] = '0'; + } + + for (i = i0, j = i0 + 1; j < p + 1; i++, j++) + { + ca2[j] = ca1[i]; + } + + expon++; + ca1 = ca2; + } + } + + if ((Math.abs(expon) < 100) &&!optionalL) + { + eSize = 4; + } else + { + eSize = 5; + } + + if (alternateForm ||!precisionSet || (precision != 0)) + { + ca2 = new char[2 + p + eSize]; + } else + { + ca2 = new char[1 + eSize]; + } + + if (ca1[0] != '0') + { + ca2[0] = ca1[0]; + j = 1; + } else + { + for (j = 1; j < ((ePos == -1) + ? ca1.length + : ePos); j++) + { + if (ca1[j] != '0') + { + break; + } + } + + if (((ePos != -1) && (j < ePos)) || ((ePos == -1) && (j < ca1.length))) + { + ca2[0] = ca1[j]; + expon -= j; + j++; + } else + { + ca2[0] = '0'; + j = 2; + } + } + + if (alternateForm ||!precisionSet || (precision != 0)) + { + ca2[1] = '.'; + i = 2; + } else + { + i = 1; + } + + for (k = 0; (k < p) && (j < ca1.length); j++, i++, k++) + { + ca2[i] = ca1[j]; + } + + for (; i < ca2.length - eSize; i++) + { + ca2[i] = '0'; + } + + ca2[i++] = eChar; + + if (expon < 0) + { + ca2[i++] = '-'; + } else + { + ca2[i++] = '+'; + } + + expon = Math.abs(expon); + + if (expon >= 100) + { + switch (expon / 100) + { + case 1 : + ca2[i] = '1'; + + break; + + case 2 : + ca2[i] = '2'; + + break; + + case 3 : + ca2[i] = '3'; + + break; + + case 4 : + ca2[i] = '4'; + + break; + + case 5 : + ca2[i] = '5'; + + break; + + case 6 : + ca2[i] = '6'; + + break; + + case 7 : + ca2[i] = '7'; + + break; + + case 8 : + ca2[i] = '8'; + + break; + + case 9 : + ca2[i] = '9'; + + break; + } + + i++; + } + + switch ((expon % 100) / 10) + { + case 0 : + ca2[i] = '0'; + + break; + + case 1 : + ca2[i] = '1'; + + break; + + case 2 : + ca2[i] = '2'; + + break; + + case 3 : + ca2[i] = '3'; + + break; + + case 4 : + ca2[i] = '4'; + + break; + + case 5 : + ca2[i] = '5'; + + break; + + case 6 : + ca2[i] = '6'; + + break; + + case 7 : + ca2[i] = '7'; + + break; + + case 8 : + ca2[i] = '8'; + + break; + + case 9 : + ca2[i] = '9'; + + break; + } + + i++; + + switch (expon % 10) + { + case 0 : + ca2[i] = '0'; + + break; + + case 1 : + ca2[i] = '1'; + + break; + + case 2 : + ca2[i] = '2'; + + break; + + case 3 : + ca2[i] = '3'; + + break; + + case 4 : + ca2[i] = '4'; + + break; + + case 5 : + ca2[i] = '5'; + + break; + + case 6 : + ca2[i] = '6'; + + break; + + case 7 : + ca2[i] = '7'; + + break; + + case 8 : + ca2[i] = '8'; + + break; + + case 9 : + ca2[i] = '9'; + + break; + } + + int nZeros = 0; + + if (!leftJustify && leadingZeros) + { + int xThousands = 0; + + if (thousands) + { + int xlead = 0; + + if ((ca2[0] == '+') || (ca2[0] == '-') || (ca2[0] == ' ')) + { + xlead = 1; + } + + int xdp = xlead; + + for (; xdp < ca2.length; xdp++) + { + if (ca2[xdp] == '.') + { + break; + } + } + + xThousands = (xdp - xlead) / 3; + } + + if (fieldWidthSet) + { + nZeros = fieldWidth - ca2.length; + } + + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) + { + nZeros--; + } + + nZeros -= xThousands; + + if (nZeros < 0) + { + nZeros = 0; + } + } + + j = 0; + + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) + { + ca3 = new char[ca2.length + nZeros + 1]; + j++; + } else + { + ca3 = new char[ca2.length + nZeros]; + } + + if (!minusSign) + { + if (leadingSign) + { + ca3[0] = '+'; + } + + if (leadingSpace) + { + ca3[0] = ' '; + } + } else + { + ca3[0] = '-'; + } + + for (k = 0; k < nZeros; j++, k++) + { + ca3[j] = '0'; + } + + for (i = 0; (i < ca2.length) && (j < ca3.length); i++, j++) + { + ca3[j] = ca2[i]; + } + + int lead = 0; + + if ((ca3[0] == '+') || (ca3[0] == '-') || (ca3[0] == ' ')) + { + lead = 1; + } + + int dp = lead; + + for (; dp < ca3.length; dp++) + { + if (ca3[dp] == '.') + { + break; + } + } + + int nThousands = dp / 3; + + // Localize the decimal point. + if (dp < ca3.length) + { + ca3[dp] = dfs.getDecimalSeparator(); + } + + char[] ca4 = ca3; + + if (thousands && (nThousands > 0)) + { + ca4 = new char[ca3.length + nThousands + lead]; + ca4[0] = ca3[0]; + + for (i = lead, k = lead; i < dp; i++) + { + if ((i > 0) && (dp - i) % 3 == 0) + { + // ca4[k]=','; + ca4[k] = dfs.getGroupingSeparator(); + ca4[k + 1] = ca3[i]; + k += 2; + } else + { + ca4[k] = ca3[i]; + k++; + } + } + + for (; i < ca3.length; i++, k++) + { + ca4[k] = ca3[i]; + } + } + + return ca4; + } + + /** + * Check to see if the digits that are going to + * be truncated because of the precision should + * force a round in the preceding digits. + * @param ca1 the array of digits + * @param icarry the index of the first digit that + * is to be truncated from the print + * @return <code>true</code> if the truncation forces + * a round that will change the print + */ + private boolean checkForCarry(char[] ca1, int icarry) + { + boolean carry = false; + + if (icarry < ca1.length) + { + if ((ca1[icarry] == '6') || (ca1[icarry] == '7') || (ca1[icarry] == '8') || (ca1[icarry] == '9')) + { + carry = true; + } else if (ca1[icarry] == '5') + { + int ii = icarry + 1; + + for (; ii < ca1.length; ii++) + { + if (ca1[ii] != '0') + { + break; + } + } + + carry = ii < ca1.length; + + if (!carry && (icarry > 0)) + { + carry = ((ca1[icarry - 1] == '1') || (ca1[icarry - 1] == '3') || (ca1[icarry - 1] == '5') + || (ca1[icarry - 1] == '7') || (ca1[icarry - 1] == '9')); + } + } + } + + return carry; + } + + /** + * Start the symbolic carry process. The process + * is not quite finished because the symbolic + * carry may change the length of the string and + * change the exponent (in e format). + * @param cLast index of the last digit changed + * by the round + * @param cFirst index of the first digit allowed + * to be changed by this phase of the round + * @return <code>true</code> if the carry forces + * a round that will change the print still + * more + */ + private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst) + { + boolean carry = true; + + for (int i = cLast; carry && (i >= cFirst); i--) + { + carry = false; + + switch (ca[i]) + { + case '0' : + ca[i] = '1'; + + break; + + case '1' : + ca[i] = '2'; + + break; + + case '2' : + ca[i] = '3'; + + break; + + case '3' : + ca[i] = '4'; + + break; + + case '4' : + ca[i] = '5'; + + break; + + case '5' : + ca[i] = '6'; + + break; + + case '6' : + ca[i] = '7'; + + break; + + case '7' : + ca[i] = '8'; + + break; + + case '8' : + ca[i] = '9'; + + break; + + case '9' : + ca[i] = '0'; + carry = true; + + break; + } + } + + return carry; + } + + /** + * An intermediate routine on the way to creating + * an e format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @param eChar an 'e' or 'E' to use in the + * converted double value. + * @return the converted double value. + */ + private String eFormatString(double x, char eChar) + { + boolean noDigits = false; + char[] ca4, ca5; + + if (Double.isInfinite(x)) + { + if (x == Double.POSITIVE_INFINITY) + { + if (leadingSign) + { + ca4 = "+Inf".toCharArray(); + } else if (leadingSpace) + { + ca4 = " Inf".toCharArray(); + } else + { + ca4 = "Inf".toCharArray(); + } + } else + { + ca4 = "-Inf".toCharArray(); + } + + noDigits = true; + } else if (Double.isNaN(x)) + { + if (leadingSign) + { + ca4 = "+NaN".toCharArray(); + } else if (leadingSpace) + { + ca4 = " NaN".toCharArray(); + } else + { + ca4 = "NaN".toCharArray(); + } + + noDigits = true; + } else + { + ca4 = eFormatDigits(x, eChar); + } + + ca5 = applyFloatPadding(ca4, false); + + return new String(ca5); + } + + /** + * Apply zero or blank, left or right padding. + * @param ca4 array of characters before padding is + * finished + * @param noDigits NaN or signed Inf + * @return a padded array of characters + */ + private char[] applyFloatPadding(char[] ca4, boolean noDigits) + { + char[] ca5 = ca4; + + if (fieldWidthSet) + { + int i, j, nBlanks; + + if (leftJustify) + { + nBlanks = fieldWidth - ca4.length; + + if (nBlanks > 0) + { + ca5 = new char[ca4.length + nBlanks]; + + for (i = 0; i < ca4.length; i++) + { + ca5[i] = ca4[i]; + } + + for (j = 0; j < nBlanks; j++, i++) + { + ca5[i] = ' '; + } + } + } else if (!leadingZeros || noDigits) + { + nBlanks = fieldWidth - ca4.length; + + if (nBlanks > 0) + { + ca5 = new char[ca4.length + nBlanks]; + + for (i = 0; i < nBlanks; i++) + { + ca5[i] = ' '; + } + + for (j = 0; j < ca4.length; i++, j++) + { + ca5[i] = ca4[j]; + } + } + } else if (leadingZeros) + { + nBlanks = fieldWidth - ca4.length; + + if (nBlanks > 0) + { + ca5 = new char[ca4.length + nBlanks]; + i = 0; + j = 0; + + if (ca4[0] == '-') + { + ca5[0] = '-'; + i++; + j++; + } + + for (int k = 0; k < nBlanks; i++, k++) + { + ca5[i] = '0'; + } + + for (; j < ca4.length; i++, j++) + { + ca5[i] = ca4[j]; + } + } + } + } + + return ca5; + } + + /** + * Format method for the f conversion character. + * @param x the double to format. + * @return the formatted String. + */ + private String printFFormat(double x) + { + return fFormatString(x); + } + + /** + * Format method for the e or E conversion + * character. + * @param x the double to format. + * @return the formatted String. + */ + private String printEFormat(double x) + { + if (conversionCharacter == 'e') + { + return eFormatString(x, 'e'); + } else + { + return eFormatString(x, 'E'); + } + } + + /** + * Format method for the g conversion character. + * + * For g format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear after the radix character. + * Padding is with trailing 0s. + * @param x the double to format. + * @return the formatted String. + */ + private String printGFormat(double x) + { + String sx, sy, sz, ret; + int savePrecision = precision; + int i; + char[] ca4, ca5; + boolean noDigits = false; + + if (Double.isInfinite(x)) + { + if (x == Double.POSITIVE_INFINITY) + { + if (leadingSign) + { + ca4 = "+Inf".toCharArray(); + } else if (leadingSpace) + { + ca4 = " Inf".toCharArray(); + } else + { + ca4 = "Inf".toCharArray(); + } + } else + { + ca4 = "-Inf".toCharArray(); + } + + noDigits = true; + } else if (Double.isNaN(x)) + { + if (leadingSign) + { + ca4 = "+NaN".toCharArray(); + } else if (leadingSpace) + { + ca4 = " NaN".toCharArray(); + } else + { + ca4 = "NaN".toCharArray(); + } + + noDigits = true; + } else + { + if (!precisionSet) + { + precision = defaultDigits; + } + + if (precision == 0) + { + precision = 1; + } + + int ePos = -1; + + if (conversionCharacter == 'g') + { + sx = eFormatString(x, 'e').trim(); + ePos = sx.indexOf('e'); + } else + { + sx = eFormatString(x, 'E').trim(); + ePos = sx.indexOf('E'); + } + + i = ePos + 1; + + int expon = 0; + + if (sx.charAt(i) == '-') + { + for (++i; i < sx.length(); i++) + { + if (sx.charAt(i) != '0') + { + break; + } + } + + if (i < sx.length()) + { + expon = -Integer.parseInt(sx.substring(i)); + } + } else + { + if (sx.charAt(i) == '+') + { + ++i; + } + + for (; i < sx.length(); i++) + { + if (sx.charAt(i) != '0') + { + break; + } + } + + if (i < sx.length()) + { + expon = Integer.parseInt(sx.substring(i)); + } + } + + // Trim trailing zeros. + // If the radix character is not followed by + // a digit, trim it, too. + if (!alternateForm) + { + if ((expon >= -4) && (expon < precision)) + { + sy = fFormatString(x).trim(); + } else + { + sy = sx.substring(0, ePos); + } + + i = sy.length() - 1; + + for (; i >= 0; i--) + { + if (sy.charAt(i) != '0') + { + break; + } + } + + if ((i >= 0) && (sy.charAt(i) == '.')) + { + i--; + } + + if (i == -1) + { + sz = "0"; + } else if (!Character.isDigit(sy.charAt(i))) + { + sz = sy.substring(0, i + 1) + "0"; + } else + { + sz = sy.substring(0, i + 1); + } + + if ((expon >= -4) && (expon < precision)) + { + ret = sz; + } else + { + ret = sz + sx.substring(ePos); + } + } else + { + if ((expon >= -4) && (expon < precision)) + { + ret = fFormatString(x).trim(); + } else + { + ret = sx; + } + } + + // leading space was trimmed off during + // construction + if (leadingSpace) + { + if (x >= 0) + { + ret = " " + ret; + } + } + + ca4 = ret.toCharArray(); + } + + // Pad with blanks or zeros. + ca5 = applyFloatPadding(ca4, false); + precision = savePrecision; + + return new String(ca5); + } + + /** + * Format method for the d conversion specifer and + * short argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printDFormat(short x) + { + return printDFormat(Short.toString(x)); + } + + /** + * Format method for the d conversion character and + * long argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printDFormat(long x) + { + return printDFormat(Long.toString(x)); + } + + /** + * Format method for the d conversion character and + * int argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printDFormat(int x) + { + return printDFormat(Integer.toString(x)); + } + + /** + * Utility method for formatting using the d + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printDFormat(String sx) + { + int nLeadingZeros = 0; + int nBlanks = 0, + n = 0; + int i = 0, + jFirst = 0; + boolean neg = sx.charAt(0) == '-'; + + if (sx.equals("0") && precisionSet && (precision == 0)) + { + sx = ""; + } + + if (!neg) + { + if (precisionSet && (sx.length() < precision)) + { + nLeadingZeros = precision - sx.length(); + } + } else + { + if (precisionSet && (sx.length() - 1) < precision) + { + nLeadingZeros = precision - sx.length() + 1; + } + } + + if (nLeadingZeros < 0) + { + nLeadingZeros = 0; + } + + if (fieldWidthSet) + { + nBlanks = fieldWidth - nLeadingZeros - sx.length(); + + if (!neg && (leadingSign || leadingSpace)) + { + nBlanks--; + } + } + + if (nBlanks < 0) + { + nBlanks = 0; + } + + if (leadingSign) + { + n++; + } else if (leadingSpace) + { + n++; + } + + n += nBlanks; + n += nLeadingZeros; + n += sx.length(); + + char[] ca = new char[n]; + + if (leftJustify) + { + if (neg) + { + ca[i++] = '-'; + } else if (leadingSign) + { + ca[i++] = '+'; + } else if (leadingSpace) + { + ca[i++] = ' '; + } + + char[] csx = sx.toCharArray(); + + jFirst = neg + ? 1 + : 0; + + for (int j = 0; j < nLeadingZeros; i++, j++) + { + ca[i] = '0'; + } + + for (int j = jFirst; j < csx.length; j++, i++) + { + ca[i] = csx[j]; + } + + for (int j = 0; j < nBlanks; i++, j++) + { + ca[i] = ' '; + } + } else + { + if (!leadingZeros) + { + for (i = 0; i < nBlanks; i++) + { + ca[i] = ' '; + } + + if (neg) + { + ca[i++] = '-'; + } else if (leadingSign) + { + ca[i++] = '+'; + } else if (leadingSpace) + { + ca[i++] = ' '; + } + } else + { + if (neg) + { + ca[i++] = '-'; + } else if (leadingSign) + { + ca[i++] = '+'; + } else if (leadingSpace) + { + ca[i++] = ' '; + } + + for (int j = 0; j < nBlanks; j++, i++) + { + ca[i] = '0'; + } + } + + for (int j = 0; j < nLeadingZeros; j++, i++) + { + ca[i] = '0'; + } + + char[] csx = sx.toCharArray(); + + jFirst = neg + ? 1 + : 0; + + for (int j = jFirst; j < csx.length; j++, i++) + { + ca[i] = csx[j]; + } + } + + return new String(ca); + } + + /** + * Format method for the x conversion character and + * short argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printXFormat(short x) + { + String sx = null; + + if (x == Short.MIN_VALUE) + { + sx = "8000"; + } else if (x < 0) + { + String t; + + if (x == Short.MIN_VALUE) + { + t = "0"; + } else + { + t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16); + + if ((t.charAt(0) == 'F') || (t.charAt(0) == 'f')) + { + t = t.substring(16, 32); + } + } + + switch (t.length()) + { + case 1 : + sx = "800" + t; + + break; + + case 2 : + sx = "80" + t; + + break; + + case 3 : + sx = "8" + t; + + break; + + case 4 : + switch (t.charAt(0)) + { + case '1' : + sx = "9" + t.substring(1, 4); + + break; + + case '2' : + sx = "a" + t.substring(1, 4); + + break; + + case '3' : + sx = "b" + t.substring(1, 4); + + break; + + case '4' : + sx = "c" + t.substring(1, 4); + + break; + + case '5' : + sx = "d" + t.substring(1, 4); + + break; + + case '6' : + sx = "e" + t.substring(1, 4); + + break; + + case '7' : + sx = "f" + t.substring(1, 4); + + break; + } + + break; + } + } else + { + sx = Integer.toString((int) x, 16); + } + + return printXFormat(sx); + } + + /** + * Format method for the x conversion character and + * long argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printXFormat(long x) + { + String sx = null; + + if (x == Long.MIN_VALUE) + { + sx = "8000000000000000"; + } else if (x < 0) + { + String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16); + + switch (t.length()) + { + case 1 : + sx = "800000000000000" + t; + + break; + + case 2 : + sx = "80000000000000" + t; + + break; + + case 3 : + sx = "8000000000000" + t; + + break; + + case 4 : + sx = "800000000000" + t; + + break; + + case 5 : + sx = "80000000000" + t; + + break; + + case 6 : + sx = "8000000000" + t; + + break; + + case 7 : + sx = "800000000" + t; + + break; + + case 8 : + sx = "80000000" + t; + + break; + + case 9 : + sx = "8000000" + t; + + break; + + case 10 : + sx = "800000" + t; + + break; + + case 11 : + sx = "80000" + t; + + break; + + case 12 : + sx = "8000" + t; + + break; + + case 13 : + sx = "800" + t; + + break; + + case 14 : + sx = "80" + t; + + break; + + case 15 : + sx = "8" + t; + + break; + + case 16 : + switch (t.charAt(0)) + { + case '1' : + sx = "9" + t.substring(1, 16); + + break; + + case '2' : + sx = "a" + t.substring(1, 16); + + break; + + case '3' : + sx = "b" + t.substring(1, 16); + + break; + + case '4' : + sx = "c" + t.substring(1, 16); + + break; + + case '5' : + sx = "d" + t.substring(1, 16); + + break; + + case '6' : + sx = "e" + t.substring(1, 16); + + break; + + case '7' : + sx = "f" + t.substring(1, 16); + + break; + } + + break; + } + } else + { + sx = Long.toString(x, 16); + } + + return printXFormat(sx); + } + + /** + * Format method for the x conversion character and + * int argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printXFormat(int x) + { + String sx = null; + + if (x == Integer.MIN_VALUE) + { + sx = "80000000"; + } else if (x < 0) + { + String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16); + + switch (t.length()) + { + case 1 : + sx = "8000000" + t; + + break; + + case 2 : + sx = "800000" + t; + + break; + + case 3 : + sx = "80000" + t; + + break; + + case 4 : + sx = "8000" + t; + + break; + + case 5 : + sx = "800" + t; + + break; + + case 6 : + sx = "80" + t; + + break; + + case 7 : + sx = "8" + t; + + break; + + case 8 : + switch (t.charAt(0)) + { + case '1' : + sx = "9" + t.substring(1, 8); + + break; + + case '2' : + sx = "a" + t.substring(1, 8); + + break; + + case '3' : + sx = "b" + t.substring(1, 8); + + break; + + case '4' : + sx = "c" + t.substring(1, 8); + + break; + + case '5' : + sx = "d" + t.substring(1, 8); + + break; + + case '6' : + sx = "e" + t.substring(1, 8); + + break; + + case '7' : + sx = "f" + t.substring(1, 8); + + break; + } + + break; + } + } else + { + sx = Integer.toString(x, 16); + } + + return printXFormat(sx); + } + + /** + * Utility method for formatting using the x + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printXFormat(String sx) + { + int nLeadingZeros = 0; + int nBlanks = 0; + + if (sx.equals("0") && precisionSet && (precision == 0)) + { + sx = ""; + } + + if (precisionSet) + { + nLeadingZeros = precision - sx.length(); + } + + if (nLeadingZeros < 0) + { + nLeadingZeros = 0; + } + + if (fieldWidthSet) + { + nBlanks = fieldWidth - nLeadingZeros - sx.length(); + + if (alternateForm) + { + nBlanks = nBlanks - 2; + } + } + + if (nBlanks < 0) + { + nBlanks = 0; + } + + int n = 0; + + if (alternateForm) + { + n += 2; + } + + n += nLeadingZeros; + n += sx.length(); + n += nBlanks; + + char[] ca = new char[n]; + int i = 0; + + if (leftJustify) + { + if (alternateForm) + { + ca[i++] = '0'; + ca[i++] = 'x'; + } + + for (int j = 0; j < nLeadingZeros; j++, i++) + { + ca[i] = '0'; + } + + char[] csx = sx.toCharArray(); + + for (int j = 0; j < csx.length; j++, i++) + { + ca[i] = csx[j]; + } + + for (int j = 0; j < nBlanks; j++, i++) + { + ca[i] = ' '; + } + } else + { + if (!leadingZeros) + { + for (int j = 0; j < nBlanks; j++, i++) + { + ca[i] = ' '; + } + } + + if (alternateForm) + { + ca[i++] = '0'; + ca[i++] = 'x'; + } + + if (leadingZeros) + { + for (int j = 0; j < nBlanks; j++, i++) + { + ca[i] = '0'; + } + } + + for (int j = 0; j < nLeadingZeros; j++, i++) + { + ca[i] = '0'; + } + + char[] csx = sx.toCharArray(); + + for (int j = 0; j < csx.length; j++, i++) + { + ca[i] = csx[j]; + } + } + + String caReturn = new String(ca); + + if (conversionCharacter == 'X') + { + caReturn = caReturn.toUpperCase(); + } + + return caReturn; + } + + /** + * Format method for the o conversion character and + * short argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printOFormat(short x) + { + String sx = null; + + if (x == Short.MIN_VALUE) + { + sx = "100000"; + } else if (x < 0) + { + String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8); + + switch (t.length()) + { + case 1 : + sx = "10000" + t; + + break; + + case 2 : + sx = "1000" + t; + + break; + + case 3 : + sx = "100" + t; + + break; + + case 4 : + sx = "10" + t; + + break; + + case 5 : + sx = "1" + t; + + break; + } + } else + { + sx = Integer.toString((int) x, 8); + } + + return printOFormat(sx); + } + + /** + * Format method for the o conversion character and + * long argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printOFormat(long x) + { + String sx = null; + + if (x == Long.MIN_VALUE) + { + sx = "1000000000000000000000"; + } else if (x < 0) + { + String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8); + + switch (t.length()) + { + case 1 : + sx = "100000000000000000000" + t; + + break; + + case 2 : + sx = "10000000000000000000" + t; + + break; + + case 3 : + sx = "1000000000000000000" + t; + + break; + + case 4 : + sx = "100000000000000000" + t; + + break; + + case 5 : + sx = "10000000000000000" + t; + + break; + + case 6 : + sx = "1000000000000000" + t; + + break; + + case 7 : + sx = "100000000000000" + t; + + break; + + case 8 : + sx = "10000000000000" + t; + + break; + + case 9 : + sx = "1000000000000" + t; + + break; + + case 10 : + sx = "100000000000" + t; + + break; + + case 11 : + sx = "10000000000" + t; + + break; + + case 12 : + sx = "1000000000" + t; + + break; + + case 13 : + sx = "100000000" + t; + + break; + + case 14 : + sx = "10000000" + t; + + break; + + case 15 : + sx = "1000000" + t; + + break; + + case 16 : + sx = "100000" + t; + + break; + + case 17 : + sx = "10000" + t; + + break; + + case 18 : + sx = "1000" + t; + + break; + + case 19 : + sx = "100" + t; + + break; + + case 20 : + sx = "10" + t; + + break; + + case 21 : + sx = "1" + t; + + break; + } + } else + { + sx = Long.toString(x, 8); + } + + return printOFormat(sx); + } + + /** + * Format method for the o conversion character and + * int argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printOFormat(int x) + { + String sx = null; + + if (x == Integer.MIN_VALUE) + { + sx = "20000000000"; + } else if (x < 0) + { + String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8); + + switch (t.length()) + { + case 1 : + sx = "2000000000" + t; + + break; + + case 2 : + sx = "200000000" + t; + + break; + + case 3 : + sx = "20000000" + t; + + break; + + case 4 : + sx = "2000000" + t; + + break; + + case 5 : + sx = "200000" + t; + + break; + + case 6 : + sx = "20000" + t; + + break; + + case 7 : + sx = "2000" + t; + + break; + + case 8 : + sx = "200" + t; + + break; + + case 9 : + sx = "20" + t; + + break; + + case 10 : + sx = "2" + t; + + break; + + case 11 : + sx = "3" + t.substring(1); + + break; + } + } else + { + sx = Integer.toString(x, 8); + } + + return printOFormat(sx); + } + + /** + * Utility method for formatting using the o + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printOFormat(String sx) + { + int nLeadingZeros = 0; + int nBlanks = 0; + + if (sx.equals("0") && precisionSet && (precision == 0)) + { + sx = ""; + } + + if (precisionSet) + { + nLeadingZeros = precision - sx.length(); + } + + if (alternateForm) + { + nLeadingZeros++; + } + + if (nLeadingZeros < 0) + { + nLeadingZeros = 0; + } + + if (fieldWidthSet) + { + nBlanks = fieldWidth - nLeadingZeros - sx.length(); + } + + if (nBlanks < 0) + { + nBlanks = 0; + } + + int n = nLeadingZeros + sx.length() + nBlanks; + char[] ca = new char[n]; + int i; + + if (leftJustify) + { + for (i = 0; i < nLeadingZeros; i++) + { + ca[i] = '0'; + } + + char[] csx = sx.toCharArray(); + + for (int j = 0; j < csx.length; j++, i++) + { + ca[i] = csx[j]; + } + + for (int j = 0; j < nBlanks; j++, i++) + { + ca[i] = ' '; + } + } else + { + if (leadingZeros) + { + for (i = 0; i < nBlanks; i++) + { + ca[i] = '0'; + } + } else + { + for (i = 0; i < nBlanks; i++) + { + ca[i] = ' '; + } + } + + for (int j = 0; j < nLeadingZeros; j++, i++) + { + ca[i] = '0'; + } + + char[] csx = sx.toCharArray(); + + for (int j = 0; j < csx.length; j++, i++) + { + ca[i] = csx[j]; + } + } + + return new String(ca); + } + + /** + * Format method for the c conversion character and + * char argument. + * + * The only flag character that affects c format is + * the '-', meaning that the output should be left + * justified within the field. The default is to + * pad with blanks on the left. + * + * The field width is treated as the minimum number + * of characters to be printed. Padding is with + * blanks by default. The default width is 1. + * + * The precision, if set, is ignored. + * @param x the char to format. + * @return the formatted String. + */ + private String printCFormat(char x) + { + int nPrint = 1; + int width = fieldWidth; + + if (!fieldWidthSet) + { + width = nPrint; + } + + char[] ca = new char[width]; + int i = 0; + + if (leftJustify) + { + ca[0] = x; + + for (i = 1; i <= width - nPrint; i++) + { + ca[i] = ' '; + } + } else + { + for (i = 0; i < width - nPrint; i++) + { + ca[i] = ' '; + } + + ca[i] = x; + } + + return new String(ca); + } + + /** + * Format method for the s conversion character and + * String argument. + * + * The only flag character that affects s format is + * the '-', meaning that the output should be left + * justified within the field. The default is to + * pad with blanks on the left. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is the + * smaller of the number of characters in the the + * input and the precision. Padding is with blanks + * by default. + * + * The precision, if set, specifies the maximum + * number of characters to be printed from the + * string. A null digit string is treated + * as a 0. The default is not to set a maximum + * number of characters to be printed. + * @param x the String to format. + * @return the formatted String. + */ + private String printSFormat(String x) + { + int nPrint = x.length(); + int width = fieldWidth; + + if (precisionSet && (nPrint > precision)) + { + nPrint = precision; + } + + if (!fieldWidthSet) + { + width = nPrint; + } + + int n = 0; + + if (width > nPrint) + { + n += width - nPrint; + } + + if (nPrint >= x.length()) + { + n += x.length(); + } else + { + n += nPrint; + } + + char[] ca = new char[n]; + int i = 0; + + if (leftJustify) + { + if (nPrint >= x.length()) + { + char[] csx = x.toCharArray(); + + for (i = 0; i < x.length(); i++) + { + ca[i] = csx[i]; + } + } else + { + char[] csx = x.substring(0, nPrint).toCharArray(); + + for (i = 0; i < nPrint; i++) + { + ca[i] = csx[i]; + } + } + + for (int j = 0; j < width - nPrint; j++, i++) + { + ca[i] = ' '; + } + } else + { + for (i = 0; i < width - nPrint; i++) + { + ca[i] = ' '; + } + + if (nPrint >= x.length()) + { + char[] csx = x.toCharArray(); + + for (int j = 0; j < x.length(); i++, j++) + { + ca[i] = csx[j]; + } + } else + { + char[] csx = x.substring(0, nPrint).toCharArray(); + + for (int j = 0; j < nPrint; i++, j++) + { + ca[i] = csx[j]; + } + } + } + + return new String(ca); + } + + /** + * Check for a conversion character. If it is + * there, store it. + * @return <code>true</code> if the conversion + * character is there, and + * <code>false</code> otherwise. + */ + private boolean setConversionCharacter() + { + /* idfgGoxXeEcs */ + boolean ret = false; + + conversionCharacter = '\0'; + + if (pos < fmt.length()) + { + char c = fmt.charAt(pos); + + if ((c == 'i') || (c == 'd') || (c == 'f') || (c == 'g') || (c == 'G') || (c == 'o') || (c == 'x') || (c == 'X') + || (c == 'e') || (c == 'E') || (c == 'c') || (c == 's') || (c == '%')) + { + conversionCharacter = c; + pos++; + ret = true; + } + } + + return ret; + } + + /** + * Check for an h, l, or L in a format. An L is + * used to control the minimum number of digits + * in an exponent when using floating point + * formats. An l or h is used to control + * conversion of the input to a long or short, + * respectively, before formatting. If any of + * these is present, store them. + */ + private void setOptionalHL() + { + optionalh = false; + optionall = false; + optionalL = false; + + if (pos < fmt.length()) + { + char c = fmt.charAt(pos); + + if (c == 'h') + { + optionalh = true; + pos++; + } else if (c == 'l') + { + optionall = true; + pos++; + } else if (c == 'L') + { + optionalL = true; + pos++; + } + } + } + + /** + * Set the precision. + */ + private void setPrecision() + { + int firstPos = pos; + + precisionSet = false; + + if ((pos < fmt.length()) && (fmt.charAt(pos) == '.')) + { + pos++; + + if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) + { + pos++; + + if (!setPrecisionArgPosition()) + { + variablePrecision = true; + precisionSet = true; + } + + return; + } else + { + while (pos < fmt.length()) + { + char c = fmt.charAt(pos); + + if (Character.isDigit(c)) + { + pos++; + } else + { + break; + } + } + + if (pos > firstPos + 1) + { + String sz = fmt.substring(firstPos + 1, pos); + + precision = Integer.parseInt(sz); + precisionSet = true; + } + } + } + } + + /** + * Set the field width. + */ + private void setFieldWidth() + { + int firstPos = pos; + + fieldWidth = 0; + fieldWidthSet = false; + + if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) + { + pos++; + + if (!setFieldWidthArgPosition()) + { + variableFieldWidth = true; + fieldWidthSet = true; + } + } else + { + while (pos < fmt.length()) + { + char c = fmt.charAt(pos); + + if (Character.isDigit(c)) + { + pos++; + } else + { + break; + } + } + + if ((firstPos < pos) && (firstPos < fmt.length())) + { + String sz = fmt.substring(firstPos, pos); + + fieldWidth = Integer.parseInt(sz); + fieldWidthSet = true; + } + } + } + + /** + * Store the digits <code>n</code> in %n$ forms. + */ + private void setArgPosition() + { + int xPos; + + for (xPos = pos; xPos < fmt.length(); xPos++) + { + if (!Character.isDigit(fmt.charAt(xPos))) + { + break; + } + } + + if ((xPos > pos) && (xPos < fmt.length())) + { + if (fmt.charAt(xPos) == '$') + { + positionalSpecification = true; + argumentPosition = Integer.parseInt(fmt.substring(pos, xPos)); + pos = xPos + 1; + } + } + } + + /** + * Store the digits <code>n</code> in *n$ forms. + */ + private boolean setFieldWidthArgPosition() + { + boolean ret = false; + int xPos; + + for (xPos = pos; xPos < fmt.length(); xPos++) + { + if (!Character.isDigit(fmt.charAt(xPos))) + { + break; + } + } + + if ((xPos > pos) && (xPos < fmt.length())) + { + if (fmt.charAt(xPos) == '$') + { + positionalFieldWidth = true; + argumentPositionForFieldWidth = Integer.parseInt(fmt.substring(pos, xPos)); + pos = xPos + 1; + ret = true; + } + } + + return ret; + } + + /** + * Store the digits <code>n</code> in *n$ forms. + */ + private boolean setPrecisionArgPosition() + { + boolean ret = false; + int xPos; + + for (xPos = pos; xPos < fmt.length(); xPos++) + { + if (!Character.isDigit(fmt.charAt(xPos))) + { + break; + } + } + + if ((xPos > pos) && (xPos < fmt.length())) + { + if (fmt.charAt(xPos) == '$') + { + positionalPrecision = true; + argumentPositionForPrecision = Integer.parseInt(fmt.substring(pos, xPos)); + pos = xPos + 1; + ret = true; + } + } + + return ret; + } + + boolean isPositionalSpecification() + { + return positionalSpecification; + } + + int getArgumentPosition() + { + return argumentPosition; + } + + boolean isPositionalFieldWidth() + { + return positionalFieldWidth; + } + + int getArgumentPositionForFieldWidth() + { + return argumentPositionForFieldWidth; + } + + boolean isPositionalPrecision() + { + return positionalPrecision; + } + + int getArgumentPositionForPrecision() + { + return argumentPositionForPrecision; + } + + /** + * Set flag characters, one of '-+#0 or a space. + */ + private void setFlagCharacters() + { + /* '-+ #0 */ + thousands = false; + leftJustify = false; + leadingSign = false; + leadingSpace = false; + alternateForm = false; + leadingZeros = false; + + for (; pos < fmt.length(); pos++) + { + char c = fmt.charAt(pos); + + if (c == '\'') + { + thousands = true; + } else if (c == '-') + { + leftJustify = true; + leadingZeros = false; + } else if (c == '+') + { + leadingSign = true; + leadingSpace = false; + } else if (c == ' ') + { + if (!leadingSign) + { + leadingSpace = true; + } + } else if (c == '#') + { + alternateForm = true; + } else if (c == '0') + { + if (!leftJustify) + { + leadingZeros = true; + } + } else + { + break; + } + } + } + } +} -- Gitblit v0.0.0-SNAPSHOT