## Retrotranslator

#### Contents

1. What is Retrotranslator?
2. What Java 5.0 features are supported?
3. How to use Retrotranslator from the command line?
4. How to use Retrotranslator from Apache Ant or Maven?
5. How to use Retrotranslator from IntelliJ IDEA?
6. How to produce a jar file compatible with Java 1.4?
7. How to use annotations and generics at runtime on Android?
8. How to use Just-in-Time Retrotranslator?
9. What API is supported on Java 1.4 and Java 1.3?
10. How to write an extension?
11. What are the limitations?
12. Alternative tools
13. Contact
14. License

#### What is Retrotranslator?

Retrotranslator is a tool that makes Java applications compatible with Java 1.4, Java 1.3 and other environments. It supports all Java 5.0 language features and a significant part of the Java 5.0 API on both J2SE 1.4 and J2SE 1.3. In other Java environments only the Java 5.0 features that don't depend on the new API are supported. Retrotranslator employs the ASM bytecode manipulation framework to transform compiled Java classes and the backport of the Java 5.0 concurrency utilities to emulate the Java 5.0 API.

#### What Java 5.0 features are supported?

- Generics
- Annotations
- Reflection on generics and annotations
- Typesafe enums
- Autoboxing/unboxing
- Enhanced for loop
- Varargs
- Covariant return types
- Formatted output
- Static import
- Concurrency utilities
- Collections framework enhancements

#### How to use Retrotranslator from the command line?

1. Download and unzip the binary distribution file `Retrotranslator-n.n.n-bin.zip`, where n.n.n is the latest Retrotranslator release number.
2. Compile your classes with Java 5.0 or Java 6 and put them into some directory, e.g. `myclasses`.
3. Go to the unzipped directory and execute:
   `java -jar retrotranslator-transformer-n.n.n.jar -srcdir myclasses`
   Use appropriate options to verify the result and for troubleshooting, e.g. -verify, -classpath, -advanced, and -smart.
4. Put `retrotranslator-runtime-n.n.n.jar` and `backport-util-concurrent-n.n.jar` into the classpath of your application if you use the Java 5.0 API.
5. Run or debug the application as usual on Java 1.4.

The command line syntax:
`java -jar retrotranslator-transformer-n.n.n.jar <options>`
or
`java -cp retrotranslator-transformer-n.n.n.jar net.sf.retrotranslator.transformer.Retrotranslator <options>`

Option | Description | Default
--- | --- | ---
`-srcdir <path>` | The directory with the files to process (can be specified several times). | -
`-srcjar <file>` | The jar file with the files to process (can be specified several times). | -
`-destdir <path>` | The directory to place processed files. | The source.
`-destjar <file>` | The jar file to place processed files. | The source.
`-srcmask <mask>` | The wildcard pattern specifying the files to transform rather than copy (classes or UTF-8 text files), e.g. `*.class;?*.tld`. | `*.class`
`-target <version>` | The version of the JVM where the translated application should be able to run (1.1, 1.2, 1.3, 1.4, or 1.5). The Java 5.0 API is supported only for the 1.4 and 1.3 targets, but user-defined backport classes can be used for other targets as well. | `1.4`
`-classpath <path>` | The dependencies of the translated classes including backport packages and jar files from the target JVM. For the 1.4 target the following files should be specified among others: `rt.jar`, `jce.jar`, and `jsse.jar` from Sun JRE 1.4; `retrotranslator-runtime-n.n.n.jar` and `backport-util-concurrent-n.n.jar`. For the 1.3 target the files are: `rt.jar` from Sun JRE 1.3, `retrotranslator-runtime13-n.n.n.jar` and `backport-util-concurrent-java12-n.n.jar`. This option can be omitted if the current Java environment matches the target one. | The current classpath.
`-verify` | Asks the translator to examine translated bytecode for references to classes, methods, or fields that cannot be found in the classpath. Every use of Java 5.0 API will result in a warning message if the classpath contains jar files from JRE 1.4. | `false`
`-support <features>` | Enables advanced features. The names specified should be separated with semicolons, e.g. `ThreadLocal.remove;BigDecimal.setScale`. | -
`-advanced` | Enables all advanced features at once, but it's recommended to enable only required features in order to avoid compatibility issues. | `false`
`-smart` | Makes all backport classes inheritable provided that the classpath correctly reflects the target environment. For example, the backport of the `Writer.append(String)` method can be used to translate the following expression: `new FileWriter("file.tmp").append("Hello")`. | `false`
`-syncvolatile` | Makes access to volatile fields of Java 5.0 classes compliant with Java 5.0 memory model at the cost of decreased performance. Highly recommended to use. | `false`
`-syncfinal` | Makes access to final instance fields of Java 5.0 classes compliant with Java 5.0 memory model at the cost of decreased performance. Not recommended to use. | `false`
`-backport <names>` | The backport names separated with semicolons, e.g. `com.myco.all;java.util:com.myco.ju;javax.net.SocketFactory:com.myco.jsse.Factory`. The corresponding backport classes must be present in the classpath. | -
`-embed <package>` | The unique package name for a partial copy of `retrotranslator-runtime-n.n.n.jar` and `backport-util-concurrent-n.n.jar` to be put along with translated classes. This makes the translated application independent of other versions of Retrotranslator present in the classpath. | -
`-lazy` | Asks the translator to transform and verify only the classes compiled with a target greater than the current one. | `false`
`-stripsign` | Whether to remove signature (generics) information. | `false`
`-stripannot` | Whether to remove annotations and related attributes. | `false`
`-verbose` | Asks the translator for verbose output. | `false`
`-retainapi` | Asks the translator to modify classes for JVM compatibility but keep use of API unless overriden with the backport option. Any references introduced by a compiler remain unchanged, like the use of `java.lang.StringBuilder` for string concatenation or the implicit `valueOf` method calls for autoboxing. | `false`
`-keepclasslit` | Prevents replacement of certain types like `java.lang.Iterable` or `java.util.Queue` with their base types in class literals. | `false`
`-retainflags` | Whether to keep Java 5.0 specific access modifiers. | `false`
`-uptodatecheck` | Asks the translator to skip processing of files if the destination files are newer. | `false`
`-reflection <mode>` | Setting this option to `safe` makes the translator include additional metadata into classes, so reflection works even if the classes are unavailable as resources. | `normal`

#### How to use Retrotranslator from Apache Ant or Maven?

The distribution contains an Apache Ant task `net.sf.retrotranslator.transformer.RetrotranslatorTask`. Every command line option can be set using the corresponding attribute. In addition the source files can be specified with nested `fileset`, `jarfileset`, and `dirset` elements and the classpath can be set with nested `classpath` elements or the `classpathref` attribute. The source directories specified with `srcdir`, `dirset`, and the `dir` attribute of `fileset` should contain the root package of the classes. In case of warnings the build fails unless the value of the `failonwarning` attribute is set to `false`. The following script can be used to build one jar compatible with Java 1.4 and another one compatible with Java 1.3.

        <taskdef name="retrotranslator" classname="net.sf.retrotranslator.transformer.RetrotranslatorTask">
            <classpath>
                <fileset dir="../Retrotranslator-n.n.n-bin">
                    <include name="retrotranslator-transformer-n.n.n.jar" />
                    <include name="retrotranslator-runtime-n.n.n.jar" />
                    <include name="backport-util-concurrent-n.n.jar" />
                </fileset>
            </classpath>
        </taskdef>

        <retrotranslator target="1.4" destjar="build/application14.jar"
                         smart="true" verify="true" failonwarning="false">
            <fileset dir="build/classes" includes="**/*.class" />
            <jarfileset dir="build/lib" includes="**/*.jar" />
            <classpath>
                <fileset dir="../j2sdk1.4.2_17/jre/lib" includes="**/*.jar"/>
                <fileset dir="../Retrotranslator-n.n.n-bin">
                    <include name="retrotranslator-runtime-n.n.n.jar" />
                    <include name="backport-util-concurrent-n.n.jar" />
                </fileset>
                <fileset dir="lib" includes="**/*.jar"/>
            </classpath>
        </retrotranslator>

        <retrotranslator target="1.3" destjar="build/application13.jar"
                         smart="true" verify="true" failonwarning="false">
            <fileset dir="build/classes" includes="**/*.class" />
            <jarfileset dir="build/lib" includes="**/*.jar" />
            <classpath>
                <fileset dir="../jdk1.3.1_20/jre/lib" includes="**/*.jar"/>
                <fileset dir="../Retrotranslator-n.n.n-bin">
                    <include name="retrotranslator-runtime13-n.n.n.jar" />
                    <include name="backport-util-concurrent-java12-n.n.jar" />
                </fileset>
                <fileset dir="lib" includes="**/*.jar"/>
            </classpath>
        </retrotranslator>

Maven users can use the Retrotranslator plugin from the Mojo Project.

#### How to use Retrotranslator from IntelliJ IDEA?

There is a plugin to automatically translate and verify classes compiled by IntelliJ IDEA, so you can develop in Java 5.0 but run and debug on Java 1.4.

#### How to produce a jar file compatible with Java 1.4?

If you have a `myapplication5.jar` file built with Java 5.0 you can use the following command to produce `myapplication4.jar`. The jar file will be compatible with Java 1.4 and independent of Retrotranslator because backport classes are added to the translated application with a different package name.

`java -jar retrotranslator-transformer-n.n.n.jar -srcjar myapplication5.jar -destjar myapplication4.jar -embed com.mycompany.internal`

Also it is recommended to specify the classpath, verify, and smart options. In case of verification failure try the support option.

#### How to use annotations and generics at runtime on Android?

The Android SDK m5-rc15 does not support generics and annotations at runtime. This issue can be fixed if both classes that read generics or annotations and classes containing them are processed with Retrotranslator. When using Apache Ant add a target like the following one to the `build.xml` file created by the `activityCreator` script and make the `dex` target depend on it.

        <target name="translate" depends="compile">
            <taskdef name="retrotranslator" classname="net.sf.retrotranslator.transformer.RetrotranslatorTask">
                <classpath>
                    <fileset dir="../Retrotranslator-n.n.n-bin">
                      <include name="retrotranslator-transformer-n.n.n.jar"/>
                      <include name="retrotranslator-android-n.n.n.jar"/>
                      <include name="retrotranslator-runtime-n.n.n.jar"/>
                      <include name="backport-util-concurrent-n.n.jar"/>
                    </fileset>
                </classpath>
            </taskdef>
            <retrotranslator srcdir="${outdir-classes}" target="1.5" reflection="safe" stripannot="true" embed="."/>
        </target>

If you're using the ADT plugin in the Eclipse IDE you cannot run Retrotranslator on your code, but the dependencies of your application can be translated manually before adding them to the Eclipse project.

`java -jar retrotranslator-transformer-n.n.n.jar -srcjar mylibrary.jar -destjar mylibrary-android.jar -target 1.5 -reflection safe -stripannot -classpath retrotranslator-android-n.n.n.jar`

If any part of your application declares or analyzes annotations you can extract it into a separate library that should be processed with Retrotranslator and added to the main project. Note that you also have to add the `retrotranslator-android-n.n.n.jar` to the project since the translated libraries depend on it.

#### How to use Just-in-Time Retrotranslator?

In order to run a Java 5.0 application on Java 1.4 start it with JIT Retrotranslator:

- `java -cp retrotranslator-transformer-n.n.n.jar net.sf.retrotranslator.transformer.JITRetrotranslator <options> -jar <jarfile> [<args...>]`
- `java -cp retrotranslator-transformer-n.n.n.jar:<classpath> net.sf.retrotranslator.transformer.JITRetrotranslator <options> <class> [<args...>]`

The options include `support`, `advanced`, `smart`, `backport`, `syncvolatile`, `syncfinal`, and `keepclasslit`. When running on J2SE 5.0 JIT Retrotranslator simply calls the application, but on J2SE 1.4 it also translates classes compiled for Java 5.0 or later. However this capability depends on the current JVM and the application itself, so under certain conditions JIT Retrotranslator may be unable to translate either a jar file or classes from the classpath or both.

#### What API is supported on Java 1.4 and Java 1.3?

Package | Class | Members | Compatibility notes
--- | --- | --- | ---
`java.io` | `Closeable2` | * (all members introduced in Java 5.0) |
`Flushable2` | * |
`PrintStream` | * (11 new methods and constructors) `PrintStream(File, String)5`, `PrintStream(String, String)5` |
`PrintWriter` | * (11 new methods and constructors) | The `PrintWriter.format` and `PrintWriter.printf` methods always flush the output buffer.
`Reader5` | * (1 new method) |
`Writer` | * (3 new methods) |
`java.lang` | `Appendable2` | * |
`Boolean` | * (2 new methods) `toString(boolean)6`, `valueOf(boolean)6` |
`Byte` | * (1 new method) |
`Character` | * (44 new methods) `getDirectionality(int)5`, `isMirrored(int)5`, `toString(char)6` | New members of `Character.UnicodeBlock` are not supported. All supplementary code points are considered as unassigned.
`CharSequence6` | * |
`Class` | * (21 new methods) | Enable feature "`Class.forName`" to use backported classes as a fallback when calling this method3. Enable features "`Class.getMethod`" and "`Class.getDeclaredMethod`" to use most of backported static methods as a fallback when calling these methods and for more uniform support of generics and covariant return types on different platforms3.
`Deprecated` | * |
`Double` | `valueOf(double)` |
`Enum` | * |
`Float` | `valueOf(float)` |
`IllegalArgumentException` | * (2 new constructors) |
`IllegalStateException` | * (2 new constructors) |
`Integer` | `valueOf(int), signum(int)` |
`Iterable2` | * |
`Long` | `valueOf(long), signum(long)` |
`Math` | `cbrt(double), cosh(double), expm1(double), log10(double), log1p(double), signum(double), signum(float), sinh(double), tanh(double)` | The quality of computation may be insufficient for applications of certain types.
`Package` | * (4 new methods) | Enable feature "`Retrotranslator.fixHyphen`" to change the name of `package-info` classes to `package$info`3.
`Readable2,5` | * |
`Short` | * (2 new methods) |
`StackTraceElement6` | * (1 new constructor) |
`String` | * (10 new methods and constructors) `isEmpty()4` | Enable features "`String.matches`", "`String.replaceAll`", "`String.replaceFirst`" and "`String.split`" to support Java 5.0 features in regular expressions when calling these methods3.
`StringBuffer` | * (11 new methods and constructors) `append(StringBuffer)6` | Enable feature "`StringBuffer.trimToSize`" to use an empty implementation of the `StringBuffer.trimToSize()` method3.
`StringBuilder` | * | `StringBuilder` is being replaced with `StringBuffer`.
`SuppressWarnings` | * |
`System` | `nanoTime()1, clearProperty(String)` | The `System.nanoTime()` method precision may vary on different platforms. Enable feature "`System.getProperty`" to identify the current JVM as compatible with Java 5.0 when calling this method at runtime3.
`Thread` | * (8 new methods) | The `Thread.getId()` method does not reflect the order in which threads have been created. The `Thread.getStackTrace()` and `Thread.getAllStackTraces()` methods return non-empty stack trace only for the current thread. Enable feature "`Thread.getState`" to support this method, but it will detect only `NEW`, `RUNNABLE` and `TERMINATED` states.3
Enable features "`Thread.setUncaughtExceptionHandler`" and "`Thread.setDefaultUncaughtExceptionHandler`" to support exception handlers for threads created by translated code (in contrast to Java 5.0 the default `UncaughtExceptionHandler` takes precedence over `ThreadGroup`)3.
`Thread.State` | * |
`Thread.UncaughtExceptionHandler2` | * |
`ThreadLocal` | * (1 new method) | Enable feature "`ThreadLocal.remove`" to use alternative `ThreadLocal` and `InheritableThreadLocal` implementations with method `remove()`3.
`TypeNotPresentException` | * |
`Throwable` | `Throwable(String, Throwable)6`, `Throwable(String, Throwable)6`, `getCause()6`, `getStackTrace()6`, `initCause(Throwable)6` | The additional constructors and methods are available for all Java 1.3 exception classes if the `smart` option has been used.
`UnsupportedOperationException` | * (2 new constructors) |
`java.lang.annotation` | * (all classes) | * |
`java.lang.instrument` | * | * | Bytecode instrumentation is not implemented.
`java.lang.management` | `ManagementFactory` | `getPlatformMBeanServer()` | The `ManagementFactory.getPlatformMBeanServer()` method simply returns the first registered `MBeanServer` or creates it when no one exists. Requires an implementation of JMX 1.2.
`java.lang.ref` | `SoftReference` | * | Enable feature "`SoftReference.NullReferenceQueue`" to support `null` for the second parameter of `SoftReference(Object,ReferenceQueue)` on all platforms3.
`WeakReference` | * | Enable feature "`WeakReference.NullReferenceQueue`" to support `null` for the second parameter of `WeakReference(Object,ReferenceQueue)` on all platforms3.
`java.lang.reflect` | `AccessibleObject` | * (4 new methods) |
`AnnotatedElement2` | * |
`Constructor` | * (11 new methods) |
`Field` | * (8 new methods) |
`GenericArrayType` | * |
`GenericDeclaration2` | * |
`GenericSignatureFormatError` | * |
`MalformedParameterizedTypeException` | * |
`Member` | * (1 new method) |
`Method` | * (14 new methods) |
`ParameterizedType` | * |
`Type2` | * |
`TypeVariable` | * |
`WildcardType` | * |
`java.math` | `BigDecimal` | `ZERO, ONE, TEN, BigDecimal(int), BigDecimal(long), BigDecimal(char[]), BigDecimal(char[], int, int),
divide(BigDecimal),
divideAndRemainder(BigDecimal),
divideToIntegralValue(BigDecimal),
pow(int),
remainder(BigDecimal),
stripTrailingZeros(),
toPlainString(),
valueOf(double),
valueOf(long)`
Enable feature "`BigDecimal.setScale`" to support negative scales in the `BigDecimal.setScale(int, int)` method3.
`BigInteger` | `TEN` |
`java.net` | `HttpURLConnection` | * (6 new methods) | Enable features "`HttpURLConnection.setChunkedStreamingMode`", "`HttpURLConnection.setFixedLengthStreamingMode`" to use the corresponding methods, but on Java 1.4 they will simply return. Consider using alternative or writing own protocol handlers.
`Proxy5` | * |
`ProxySelector5` | * |
`URL5` | * (2 new methods) | The `Proxy` is ignored by the `URL.openConnection(Proxy)` method.
`URLConnection` | * (4 new methods) | Enable features "`URLConnection.getConnectTimeout`", "`URLConnection.setConnectTimeout`", "`URLConnection.getReadTimeout`", "`URLConnection.setReadTimeout`" to use the corresponding methods, but on Java 1.4 they will simply return. Consider using alternative or writing own protocol handlers.
`java.nio5` | `CharBuffer` | * (4 new methods) |
`Charset` | * (1 new method) | The `Charset.defaultCharset()` method returns UTF-8 if the default charset is unavailable (occurs on JDK 1.4.0).
`java.rmi.server` | `RemoteObjectInvocationHandler` | * |
`java.text` | `DecimalFormat` | * (2 new methods) | Enable feature "`DecimalFormat.setParseBigDecimal`" to support the `DecimalFormat.setParseBigDecimal(boolean)` method, but parsing and formatting precision will still be limited by the `java.lang.Double` or `java.lang.Long` precision3.
`java.util` | `AbstractQueue1` | * |
`ArrayDeque1,4` | * |
`Arrays` | * (21 new methods) `copyOf(...)`4 (10 methods) `copyOfRange(...)`4 (10 methods) |
`Calendar` | `getTimeInMillis()`6, `setTimeInMillis(long)`6 |
`Collections1` | * (13 new methods) `newSetFromMap(Map)`4, `list(Enumeration)`6, `replaceAll(List, Object, Object)`6, `swap(List, int, int)`6 |
`Deque1,2,4` | * |
`EnumMap` | * |
`EnumSet` | * |
`Formatter` | * |
`LinkedList` | * (5 new methods) |
`PriorityQueue1` | * |
`Properties` | * | Requires an implementation of JAXP 1.3 for J2SE 1.3.
`Queue1,2` | * |
`Timer` | * (3 new methods and constructors) | Enable feature "`Timer.All`" to use alternative `Timer` and `TimerTask` implementations in order to be able to call `Timer(String)`, `Timer(String, boolean)`, and `Timer.purge()`3.
`UUID` | * |
`java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks` | almost all classes1 | almost all methods | The `LockSupport` class may be unusable due to insufficient performance. The `Condition.awaitNanos(long)` method has little accuracy guarantees.
`java.util.regex5` | `Matcher` | `quoteReplacement(String), toMatchResult()` |
`MatchResult2` | * |
`Pattern` | `LITERAL3, quote(String)` | Enable features "`Pattern.compile`" and "`Pattern.matches`" to support Java 5.0 features in regular expressions when calling these methods3.
`javax.net.ssl5` | `HttpsURLConnection` | * (2 new methods) |

1 Supported via the Backport of JSR 166.
2 In most cases this type is being replaced with its base type.
3 Supported only when the corresponding feature is enabled via the `support` or `advanced` options.
4 Introduced in Java 6.
5 Not supported on J2SE 1.3.
6 Introduced in Java 1.4 and supported on J2SE 1.3.

#### How to write an extension?

In order to support the API unavailable on the target platform Retrotranslator should be able to replace all references to new classes, constructors, methods, and fields with references to backports compatible with the platform. The location of the backports must be specified with the classpath option. The default backports for the 1.4 target have been packaged into the `retrotranslator-runtime-n.n.n.jar` and `backport-util-concurrent-n.n.jar` files, and to complement or override them additional backport names can be specified via the backport option or a properties file. The backport names can have five different forms, the first one declares a universal backport package and the others allow reusing existing backports.

Backport name form | Example
--- | ---
`<universal backport package name>` | `net.sf.retrotranslator.runtime com.mycompany.backport`
`<original package name>:<backport package name>` | `java.util.concurrent:edu.emory.mathcs.backport.java.util.concurrent com.sun.org.apache.xerces.internal:org.apache.xerces`
`<original class name>:<backport class name>` | `java.lang.StringBuilder:java.lang.StringBuffer java.util.LinkedHashMap:org.apache.commons.collections.map.LinkedMap`
`<original method name>:<backport method name>` | `java.lang.System.nanoTime:edu.emory.mathcs.backport.java.util.concurrent.helpers.Utils.nanoTime`
`<original field name>:<backport field name>` | `java.util.Collections.EMPTY_MAP:edu.emory.mathcs.backport.java.util.Collections.EMPTY_MAP`

The names of backport classes in a universal backport package consist of the backport package name, the name of the original class, and an optional trailing underscore. For example, `net.sf.retrotranslator.runtime.java.util.EnumSet_` is a complete backport of `java.util.EnumSet`. But when classes exist on the target platform then the backports of their new fields, constructors and methods are grouped into classes with a leading underscore in their names. Look at the `net.sf.retrotranslator.runtime.java.math._BigDecimal` class:

- For a static field there is a public static field with the same name and type.
- For a static method there is a public static method with the same signature.
- For an instance method there is a public static method with the same signature but with an additional first parameter representing the instance.
- For a constructor there is a public static `convertConstructorArguments` method that accepts constructor's arguments and returns an argument for a Java 1.4 constructor.

The `net.sf.retrotranslator.runtime.java.io._PrintStream` and `net.sf.retrotranslator.runtime.java.lang._SecurityException` classes use another type of constructor backports. There is a public static `createInstanceBuilder` method that accepts constructor's arguments an returns an object with public `argument1`...`argumentN` methods and an optional public void `initialize` method. All the `argumentX` methods don't have any parameters and provide arguments for a constructor that exists on the target platform. The `initialize` method has a single parameter for the created instance and can be used for postprocessing. And the most flexible but unsupported in certain cases way has been used by `net.sf.retrotranslator.runtime.java.lang._StackTraceElement`.

If backported methods require access to non-public methods or fields of the instance, they can do it with reflection when the security manager allows such access. The backports of public instance fields are not supported, but private instance fields can be emulated using a weak identity map, see `net.sf.retrotranslator.runtime.java.lang._Thread` for an example.

#### What are the limitations?

- The Java 5.0 memory model is not fully supported even with the syncvolatile and syncfinal options if the fields are being accessed via reflection.
- Only the classes, methods, and fields listed above should work and the other features, like formatted input, are not supported.
- The Java 1.4 compilers and applications performing bean introspection may fail to work correctly with covariant return types.
- The backported implementation of the Java 5.0 API is not interoperable with the original implementation.
- Reflection-based tools may be unable to discover the backported implementation of the Java 5.0 API.
- The Java 5.0 reflection methods may return incomplete information for dynamically generated classes.
- The constants inlined by a compiler and access modifiers are ignored during the verification.

#### Alternative tools

- Retroweaver
- Declawer
- JBossRetro

#### Contact

- Project summary
- Latest documentation
- Open discussion
- Help
- Bugs
- Feature requests
- Author

#### License

        Copyright (c) 2005 - 2008 Taras Puchko
        All rights reserved.

        Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions
        are met:
        1. Redistributions of source code must retain the above copyright
           notice, this list of conditions and the following disclaimer.
        2. Redistributions in binary form must reproduce the above copyright
           notice, this list of conditions and the following disclaimer in the
           documentation and/or other materials provided with the distribution.
        3. Neither the name of the copyright holders nor the names of its
           contributors may be used to endorse or promote products derived from
           this software without specific prior written permission.

        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
        LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
        CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
        THE POSSIBILITY OF SUCH DAMAGE.