mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Merge branch 'master' into regex
This commit is contained in:
commit
28251840ec
124
README.md
124
README.md
|
@ -4,73 +4,42 @@ TeaVM
|
|||
What is TeaVM?
|
||||
--------------
|
||||
|
||||
In short, TeaVM gets a bytecode, running over JVM, and translates it to the JavaScript code,
|
||||
which does exactly the same thing as the original bytecode does.
|
||||
It is based on its cross-compiler which transforms `class` files to JavaScript.
|
||||
But there is something more:
|
||||
TeaVM is an ahead-of-time translator from Java bytecode to JVM.
|
||||
It can be compared with GWT, however TeaVM does not require source code of your application and
|
||||
all required libraries.
|
||||
You can use TeaVM for building applications for the browser, due to the following features:
|
||||
|
||||
* a sophisticated per-method dependency manager, which greatly reduces the JavaScript output;
|
||||
* an optimizer capable of things like devirtualization, inlining, constant propagation,
|
||||
loop invariant motion and many other;
|
||||
* implementation of subset of core Java library;
|
||||
* per-method dependency analyzer, that determines a set of methods that are really needed
|
||||
to run your application, so TeaVM won't translate whole JAR files;
|
||||
* fast JavaScript; for now it is almost as fast as the JavaScript, generated by GWT;
|
||||
* Java class library emulation;
|
||||
* integration with Maven and Eclipse;
|
||||
* generation of source maps;
|
||||
* debugger;
|
||||
* interoperation with JavaScript libraries together with the set of predefined browser interfaces.
|
||||
|
||||
How to use
|
||||
----------
|
||||
|
||||
There is no TeaVM artifacts in the central Maven repository yet.
|
||||
So first you need to clone project and install it into the local repository.
|
||||
In order to install project, just run `mvn install` when you are in the project's root directory.
|
||||
Quick start
|
||||
-----------
|
||||
|
||||
There are several options of using TeaVM. One is the maven build. First, you write your code as if it were an
|
||||
ordinary Java project:
|
||||
There are several options of using TeaVM. One is the Maven build.
|
||||
The easiest way to create a new TeaVM project is to type in the command line:
|
||||
|
||||
```Java
|
||||
package org.teavm.samples;
|
||||
mvn -DarchetypeCatalog=local \
|
||||
-DarchetypeGroupId=org.teavm \
|
||||
-DarchetypeArtifactId=teavm-maven-webapp \
|
||||
-DarchetypeVersion=0.2.0 archetype:generate
|
||||
|
||||
public class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, world!");
|
||||
}
|
||||
}
|
||||
```
|
||||
Now you can execute `mvn clean package` and get the generated `war` file.
|
||||
Deploy this `war` in Tomcat or another container, or simply unzip it and open the `index.html` page.
|
||||
|
||||
Second, you include the following plugin in your `pom.xml` build section:
|
||||
It is much easier to develop TeaVM applications using Eclipse.
|
||||
If you prefer Eclipse, please read [this tutorial](https://github.com/konsoletyper/teavm/wiki/Eclipse-tutorial).
|
||||
|
||||
```XML
|
||||
<plugin>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-maven-plugin</artifactId>
|
||||
<version>0.1</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-classlib</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-javascript</id>
|
||||
<goals>
|
||||
<goal>build-javascript</goal>
|
||||
</goals>
|
||||
<phase>process-classes</phase>
|
||||
<configuration>
|
||||
<minifying>true</minifying>
|
||||
<mainClass>org.teavm.samples.HelloWorld</mainClass>
|
||||
<mainPageIncluded>true</mainPageIncluded>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
```
|
||||
To learn TeaVM deeper, you take a look at the [teavm-samples](teavm-samples) module,
|
||||
containing examples of TeaVM-based projects.
|
||||
Also you can read [project's wiki](https://github.com/konsoletyper/teavm/wiki/).
|
||||
|
||||
Now you can execute `mvn clean package` and get the generated JavaScript files in `target/javascript` folder.
|
||||
Just open `target/javascript/main.html` page in your browser, open developer's console and press *Refresh* and
|
||||
see what happen.
|
||||
|
||||
There is [teavm-samples](teavm-samples) module,
|
||||
containing a complete buildable and runnable example.
|
||||
|
||||
DukeScript
|
||||
----------
|
||||
|
@ -80,9 +49,12 @@ easily talk to JavaScript environment to (usually) animate an HTML page. While D
|
|||
implementation of JVM, called [Bck2Brwsr](http://wiki.apidesign.org/wiki/Bck2Brwsr), TeaVM also provides
|
||||
support for running DukeScript applications, using [teavm-html4j](teavm-html4j) plugin.
|
||||
|
||||
|
||||
Live examples
|
||||
-------------
|
||||
|
||||
Compare the speed of JavaScript produced by TeaVM and GWT here: http://teavm.org/live-examples/jbox2d-benchmark/
|
||||
|
||||
Thanks to [Jaroslav Tulach](http://wiki.apidesign.org/wiki/User:JaroslavTulach), author of DukeScript, we have several
|
||||
DukeScript example applications. One is the minesweeper game.
|
||||
You can try its TeaVM-compiled version [here](http://xelfi.cz/minesweeper/teavm/), and then take a look at
|
||||
|
@ -93,39 +65,3 @@ Another example is avaialble [here](http://graphhopper.com/teavm/).
|
|||
It uses [GraphHopper](https://github.com/graphhopper/graphhopper/) to build route in browser.
|
||||
Unlike original GraphHopper example it works completely in browser instead of querying server.
|
||||
Thanks to [Peter Karich](https://github.com/karussell).
|
||||
|
||||
Advantages over GWT
|
||||
-------------------
|
||||
|
||||
You may notice that TeaVM idea is much similar to GWT. So why we need TeaVM instead of GWT?
|
||||
|
||||
Unlinke GWT, TeaVM gets the compiled bytecode, not Java sources.
|
||||
Thereby it **does not depend on a specific language syntax**. Even not on a specific language.
|
||||
So, when the next Java version gets a new feature, you can use it in your source code
|
||||
and TeaVM compiler remains unbroken. Also you may want thigs Scala, Kotlin or Ceilon. TeaVM supports them.
|
||||
|
||||
To represent a source code, GWT uses abstract syntax trees (AST).
|
||||
TeaVM uses control flow graph (CFG) of methods. CFG are much easier to optimize,
|
||||
so TeaVM **applies aggressive optimizations** to you code to make it running faster.
|
||||
|
||||
TeaVM compiler is faster. And TeaVM does not produce permutations.
|
||||
So with TeaVM you have no permutation explosion problem.
|
||||
|
||||
Advantages over JavaScript
|
||||
--------------------------
|
||||
|
||||
JavaScript suffers of its dynamic typing. When you write a new code, dynamic typing accelerates
|
||||
the development process, allowing you to write less boilerplate code.
|
||||
But when you are to maintain a large code base, you may need static typing.
|
||||
Also, it is not dynamic typing that really makes code short.
|
||||
Good static typed languages can [infer variable types for you](http://en.wikipedia.org/wiki/Type_inference).
|
||||
And they usually have a lot more useful features like [lambda functions](http://en.wikipedia.org/wiki/Lambda_function),
|
||||
[lexical closures](http://en.wikipedia.org/wiki/Closure_%28computer_science%29),
|
||||
[implicit type casting](http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion), etc.
|
||||
|
||||
With JavaScript you sometimes have to include large library for only one feature. Or you include many different
|
||||
libraries for different purposes and your project size grows. TeaVM translates only the methods which
|
||||
are really needed. So you can depend on as much libraries as you want and get
|
||||
|
||||
With JavaScript you are limited to one language. TeaVM supports many of the JVM languages.
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
<module name="StringLiteralEquality"/>
|
||||
<module name="IllegalThrows"/>
|
||||
<module name="IllegalThrows">
|
||||
<property name="illegalClassNames" value="java.lang.Error, java.lang.RuntimeException"/>
|
||||
</module>
|
||||
<module name="ExplicitInitialization"/>
|
||||
<module name="DefaultComesLast"/>
|
||||
<module name="FallThrough"/>
|
||||
|
@ -42,7 +44,6 @@
|
|||
</module>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="TrailingComment"/>
|
||||
<module name="RedundantModifier"/>
|
||||
<module name="ClassTypeParameterName"/>
|
||||
<module name="LocalFinalVariableName"/>
|
81
pom.xml
81
pom.xml
|
@ -65,17 +65,23 @@
|
|||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<html4j.version>0.7.5</html4j.version>
|
||||
<sonatypeOssDistMgmtSnapshotsUrl>https://oss.sonatype.org/content/repositories/snapshots/</sonatypeOssDistMgmtSnapshotsUrl>
|
||||
<html4j.version>0.9</html4j.version>
|
||||
<jetty.version>9.2.1.v20140609</jetty.version>
|
||||
<slf4j.version>1.7.7</slf4j.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>teavm-core</module>
|
||||
<module>teavm-classlib</module>
|
||||
<module>teavm-maven-plugin</module>
|
||||
<module>teavm-maven</module>
|
||||
<module>teavm-dom</module>
|
||||
<module>teavm-jso</module>
|
||||
<module>teavm-html4j</module>
|
||||
<module>teavm-samples</module>
|
||||
<module>teavm-platform</module>
|
||||
<module>teavm-cli</module>
|
||||
<module>teavm-chrome-rdp</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -131,6 +137,31 @@
|
|||
<artifactId>maven-core</artifactId>
|
||||
<version>3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-artifact</artifactId>
|
||||
<version>3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.websocket</groupId>
|
||||
<artifactId>javax.websocket-api</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>javax-websocket-server-impl</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
@ -177,6 +208,40 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.11</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<linkXRef>false</linkXRef>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<propertyExpansion>config_loc=${basedir}/..</propertyExpansion>
|
||||
<configLocation>../checkstyle.xml</configLocation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
@ -203,17 +268,17 @@
|
|||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>enable-scala</id>
|
||||
<modules>
|
||||
<module>teavm-scala-samples</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>enable-samples</id>
|
||||
<modules>
|
||||
<module>teavm-samples</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>enable-eclipse</id>
|
||||
<modules>
|
||||
<module>teavm-eclipse</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
/.settings
|
||||
/.classpath
|
||||
/.project
|
||||
/.cache
|
108
teavm-chrome-rdp/pom.xml
Normal file
108
teavm-chrome-rdp/pom.xml
Normal file
|
@ -0,0 +1,108 @@
|
|||
<!--
|
||||
Copyright 2014 Alexey Andreev.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm</artifactId>
|
||||
<version>0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>teavm-chrome-rdp</artifactId>
|
||||
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<name>TeaVM debugging backend for Google Chrome RDP</name>
|
||||
<description>TeaVM debugging backend for Google Chrome RDP</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>javax-websocket-server-impl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.websocket</groupId>
|
||||
<artifactId>javax.websocket-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>javax-websocket-server-impl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
<version>1.9.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<version>1.9.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<configuration>
|
||||
<configLocation>../checkstyle.xml</configLocation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.teavm.chromerdp</Export-Package>
|
||||
<Bundle-SymbolicName>teavm-chrome-rdp</Bundle-SymbolicName>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface ChromeRDPContainer {
|
||||
void setDebugger(ChromeRDPDebuggerEndpoint debugger);
|
||||
}
|
|
@ -0,0 +1,538 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.teavm.chromerdp.data.*;
|
||||
import org.teavm.chromerdp.messages.*;
|
||||
import org.teavm.debugging.javascript.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeConsumer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ChromeRDPDebugger.class);
|
||||
private static final Object dummy = new Object();
|
||||
private ChromeRDPExchange exchange;
|
||||
private ConcurrentMap<JavaScriptDebuggerListener, Object> listeners = new ConcurrentHashMap<>();
|
||||
private ConcurrentMap<JavaScriptLocation, RDPBreakpoint> breakpointLocationMap = new ConcurrentHashMap<>();
|
||||
private ConcurrentMap<RDPBreakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
||||
private volatile RDPCallFrame[] callStack = new RDPCallFrame[0];
|
||||
private ConcurrentMap<String, String> scripts = new ConcurrentHashMap<>();
|
||||
private ConcurrentMap<String, String> scriptIds = new ConcurrentHashMap<>();
|
||||
private boolean suspended;
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
private ConcurrentMap<Integer, ResponseHandler> responseHandlers = new ConcurrentHashMap<>();
|
||||
private AtomicInteger messageIdGenerator = new AtomicInteger();
|
||||
private Lock breakpointLock = new ReentrantLock();
|
||||
|
||||
private List<JavaScriptDebuggerListener> getListeners() {
|
||||
return new ArrayList<>(listeners.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExchange(ChromeRDPExchange exchange) {
|
||||
if (this.exchange == exchange) {
|
||||
return;
|
||||
}
|
||||
if (this.exchange != null) {
|
||||
this.exchange.removeListener(exchangeListener);
|
||||
}
|
||||
this.exchange = exchange;
|
||||
if (exchange != null) {
|
||||
for (RDPBreakpoint breakpoint : breakpoints.keySet().toArray(new RDPBreakpoint[0])) {
|
||||
updateBreakpoint(breakpoint);
|
||||
}
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.attached();
|
||||
}
|
||||
} else {
|
||||
suspended = false;
|
||||
callStack = null;
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.detached();
|
||||
}
|
||||
}
|
||||
if (this.exchange != null) {
|
||||
this.exchange.addListener(exchangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
private ChromeRDPExchangeListener exchangeListener = new ChromeRDPExchangeListener() {
|
||||
@Override public void received(String messageText) throws IOException {
|
||||
receiveMessage(messageText);
|
||||
}
|
||||
};
|
||||
|
||||
private void receiveMessage(final String messageText) {
|
||||
new Thread() {
|
||||
@Override public void run() {
|
||||
try {
|
||||
JsonNode jsonMessage = mapper.readTree(messageText);
|
||||
if (jsonMessage.has("id")) {
|
||||
Response response = mapper.reader(Response.class).readValue(jsonMessage);
|
||||
if (response.getError() != null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Error message #{} received from browser: {}", jsonMessage.get("id"),
|
||||
response.getError().toString());
|
||||
}
|
||||
}
|
||||
responseHandlers.remove(response.getId()).received(response.getResult());
|
||||
} else {
|
||||
Message message = mapper.reader(Message.class).readValue(messageText);
|
||||
if (message.getMethod() == null) {
|
||||
return;
|
||||
}
|
||||
switch (message.getMethod()) {
|
||||
case "Debugger.paused":
|
||||
firePaused(parseJson(SuspendedNotification.class, message.getParams()));
|
||||
break;
|
||||
case "Debugger.resumed":
|
||||
fireResumed();
|
||||
break;
|
||||
case "Debugger.scriptParsed":
|
||||
scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Error receiving message from Google Chrome", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private synchronized void firePaused(SuspendedNotification params) {
|
||||
suspended = true;
|
||||
CallFrameDTO[] callFrameDTOs = params.getCallFrames();
|
||||
RDPCallFrame[] callStack = new RDPCallFrame[callFrameDTOs.length];
|
||||
for (int i = 0; i < callStack.length; ++i) {
|
||||
callStack[i] = map(callFrameDTOs[i]);
|
||||
}
|
||||
this.callStack = callStack;
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.paused();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void fireResumed() {
|
||||
suspended = false;
|
||||
callStack = null;
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.resumed();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void scriptParsed(ScriptParsedNotification params) {
|
||||
if (scripts.putIfAbsent(params.getScriptId(), params.getUrl()) != null) {
|
||||
return;
|
||||
}
|
||||
scriptIds.put(params.getUrl(), params.getScriptId());
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.scriptAdded(params.getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addListener(JavaScriptDebuggerListener listener) {
|
||||
listeners.put(listener, dummy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(JavaScriptDebuggerListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend() {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.pause");
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.resume");
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepInto() {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.stepInto");
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepOut() {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.stepOut");
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepOver() {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.stepOver");
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void continueToLocation(JavaScriptLocation location) {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.continueToLocation");
|
||||
ContinueToLocationCommand params = new ContinueToLocationCommand();
|
||||
params.setLocation(unmap(location));
|
||||
message.setParams(mapper.valueToTree(params));
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuspended() {
|
||||
return exchange != null && suspended;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttached() {
|
||||
return exchange != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
if (exchange != null) {
|
||||
exchange.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptCallFrame[] getCallStack() {
|
||||
if (exchange == null) {
|
||||
return null;
|
||||
}
|
||||
JavaScriptCallFrame[] callStack = this.callStack;
|
||||
return callStack != null ? callStack.clone() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
|
||||
RDPBreakpoint breakpoint;
|
||||
|
||||
breakpointLock.lock();
|
||||
try {
|
||||
breakpoint = breakpointLocationMap.get(location);
|
||||
if (breakpoint == null) {
|
||||
breakpoint = new RDPBreakpoint(this, location);
|
||||
breakpointLocationMap.put(location, breakpoint);
|
||||
updateBreakpoint(breakpoint);
|
||||
}
|
||||
breakpoint.referenceCount.incrementAndGet();
|
||||
breakpoints.put(breakpoint, dummy);
|
||||
} finally {
|
||||
breakpointLock.unlock();
|
||||
}
|
||||
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
void destroyBreakpoint(RDPBreakpoint breakpoint) {
|
||||
if (breakpoint.referenceCount.decrementAndGet() > 0) {
|
||||
return;
|
||||
}
|
||||
breakpointLock.lock();
|
||||
try {
|
||||
if (breakpoint.referenceCount.get() > 0) {
|
||||
return;
|
||||
}
|
||||
breakpointLocationMap.remove(breakpoint.getLocation());
|
||||
breakpoints.remove(breakpoint);
|
||||
if (breakpoint.chromeId != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Removing breakpoint at {}", breakpoint.getLocation());
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setMethod("Debugger.removeBreakpoint");
|
||||
RemoveBreakpointCommand params = new RemoveBreakpointCommand();
|
||||
params.setBreakpointId(breakpoint.chromeId);
|
||||
message.setParams(mapper.valueToTree(params));
|
||||
sendMessage(message);
|
||||
}
|
||||
breakpoint.debugger = null;
|
||||
breakpoint.chromeId = null;
|
||||
} finally {
|
||||
breakpointLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void fireScriptAdded(String script) {
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.scriptAdded(script);
|
||||
}
|
||||
}
|
||||
|
||||
void updateBreakpoint(final RDPBreakpoint breakpoint) {
|
||||
if (exchange == null || breakpoint.chromeId != null) {
|
||||
return;
|
||||
}
|
||||
final Message message = new Message();
|
||||
message.setId(messageIdGenerator.incrementAndGet());
|
||||
message.setMethod("Debugger.setBreakpoint");
|
||||
SetBreakpointCommand params = new SetBreakpointCommand();
|
||||
params.setLocation(unmap(breakpoint.getLocation()));
|
||||
message.setParams(mapper.valueToTree(params));
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Setting breakpoint at {}, message id is ", breakpoint.getLocation(), message.getId());
|
||||
}
|
||||
ResponseHandler handler = new ResponseHandler() {
|
||||
@Override public void received(JsonNode node) throws IOException {
|
||||
if (node != null) {
|
||||
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
|
||||
breakpoint.chromeId = response.getBreakpointId();
|
||||
} else {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Error setting breakpoint at {}, message id is {}",
|
||||
breakpoint.getLocation(), message.getId());
|
||||
}
|
||||
breakpoint.chromeId = null;
|
||||
}
|
||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||
listener.breakpointChanged(breakpoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
responseHandlers.put(message.getId(), handler);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
List<RDPLocalVariable> getScope(String scopeId) {
|
||||
if (exchange == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setId(messageIdGenerator.incrementAndGet());
|
||||
message.setMethod("Runtime.getProperties");
|
||||
GetPropertiesCommand params = new GetPropertiesCommand();
|
||||
params.setObjectId(scopeId);
|
||||
params.setOwnProperties(true);
|
||||
message.setParams(mapper.valueToTree(params));
|
||||
final BlockingQueue<List<RDPLocalVariable>> sync = new LinkedTransferQueue<>();
|
||||
responseHandlers.put(message.getId(), new ResponseHandler() {
|
||||
@Override public void received(JsonNode node) throws IOException {
|
||||
GetPropertiesResponse response = mapper.reader(GetPropertiesResponse.class).readValue(node);
|
||||
sync.add(parseProperties(response.getResult()));
|
||||
}
|
||||
});
|
||||
sendMessage(message);
|
||||
try {
|
||||
return sync.take();
|
||||
} catch (InterruptedException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
String getClassName(String objectId) {
|
||||
if (exchange == null) {
|
||||
return null;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setId(messageIdGenerator.incrementAndGet());
|
||||
message.setMethod("Runtime.callFunctionOn");
|
||||
CallFunctionCommand params = new CallFunctionCommand();
|
||||
CallArgumentDTO arg = new CallArgumentDTO();
|
||||
arg.setObjectId(objectId);
|
||||
params.setObjectId(objectId);
|
||||
params.setArguments(new CallArgumentDTO[] { arg });
|
||||
params.setFunctionDeclaration("$dbg_class");
|
||||
message.setParams(mapper.valueToTree(params));
|
||||
final BlockingQueue<String> sync = new LinkedTransferQueue<>();
|
||||
responseHandlers.put(message.getId(), new ResponseHandler() {
|
||||
@Override public void received(JsonNode node) throws IOException {
|
||||
if (node == null) {
|
||||
sync.add("");
|
||||
} else {
|
||||
CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node);
|
||||
RemoteObjectDTO result = response.getResult();
|
||||
sync.add(result.getValue() != null ? result.getValue().getTextValue() : "");
|
||||
}
|
||||
}
|
||||
});
|
||||
sendMessage(message);
|
||||
try {
|
||||
String result = sync.take();
|
||||
return result.isEmpty() ? null : result;
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String getRepresentation(String objectId) {
|
||||
if (exchange == null) {
|
||||
return null;
|
||||
}
|
||||
Message message = new Message();
|
||||
message.setId(messageIdGenerator.incrementAndGet());
|
||||
message.setMethod("Runtime.callFunctionOn");
|
||||
CallFunctionCommand params = new CallFunctionCommand();
|
||||
CallArgumentDTO arg = new CallArgumentDTO();
|
||||
arg.setObjectId(objectId);
|
||||
params.setObjectId(objectId);
|
||||
params.setArguments(new CallArgumentDTO[] { arg });
|
||||
params.setFunctionDeclaration("$dbg_repr");
|
||||
message.setParams(mapper.valueToTree(params));
|
||||
final BlockingQueue<RepresentationWrapper> sync = new LinkedTransferQueue<>();
|
||||
responseHandlers.put(message.getId(), new ResponseHandler() {
|
||||
@Override public void received(JsonNode node) throws IOException {
|
||||
if (node == null) {
|
||||
sync.add(new RepresentationWrapper(null));
|
||||
} else {
|
||||
CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node);
|
||||
RemoteObjectDTO result = response.getResult();
|
||||
sync.add(new RepresentationWrapper(result.getValue() != null ?
|
||||
result.getValue().getTextValue() : null));
|
||||
}
|
||||
}
|
||||
});
|
||||
sendMessage(message);
|
||||
try {
|
||||
RepresentationWrapper result = sync.take();
|
||||
return result.repr;
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RepresentationWrapper {
|
||||
String repr;
|
||||
|
||||
public RepresentationWrapper(String repr) {
|
||||
super();
|
||||
this.repr = repr;
|
||||
}
|
||||
}
|
||||
|
||||
private List<RDPLocalVariable> parseProperties(PropertyDescriptorDTO[] properties) {
|
||||
List<RDPLocalVariable> variables = new ArrayList<>();
|
||||
if (properties != null) {
|
||||
for (PropertyDescriptorDTO property : properties) {
|
||||
RemoteObjectDTO remoteValue = property.getValue();
|
||||
RDPValue value;
|
||||
switch (remoteValue.getType()) {
|
||||
case "undefined":
|
||||
value = new RDPValue(this, "undefined", "undefined", null, false);
|
||||
break;
|
||||
case "object":
|
||||
case "function":
|
||||
value = new RDPValue(this, null, remoteValue.getType(), remoteValue.getObjectId(), true);
|
||||
break;
|
||||
default:
|
||||
value = new RDPValue(this, remoteValue.getValue().asText(), remoteValue.getType(),
|
||||
remoteValue.getObjectId(), false);
|
||||
break;
|
||||
}
|
||||
|
||||
RDPLocalVariable var = new RDPLocalVariable(property.getName(), value);
|
||||
variables.add(var);
|
||||
}
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
private <T> T parseJson(Class<T> type, JsonNode node) throws IOException {
|
||||
return mapper.reader(type).readValue(node);
|
||||
}
|
||||
|
||||
private void sendMessage(Message message) {
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
exchange.send(mapper.writer().writeValueAsString(message));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
RDPCallFrame map(CallFrameDTO dto) {
|
||||
String scopeId = null;
|
||||
RDPValue thisObject = null;
|
||||
RDPValue closure = null;
|
||||
for (ScopeDTO scope : dto.getScopeChain()) {
|
||||
if (scope.getType().equals("local")) {
|
||||
scopeId = scope.getObject().getObjectId();
|
||||
} else if (scope.getType().equals("closure")) {
|
||||
closure = new RDPValue(this, scope.getObject().getDescription(), scope.getObject().getType(),
|
||||
scope.getObject().getObjectId(), true);
|
||||
} else if (scope.getType().equals("global")) {
|
||||
thisObject = new RDPValue(this, scope.getObject().getDescription(), scope.getObject().getType(),
|
||||
scope.getObject().getObjectId(), true);
|
||||
}
|
||||
}
|
||||
return new RDPCallFrame(this, dto.getCallFrameId(), map(dto.getLocation()), new RDPScope(this, scopeId),
|
||||
thisObject, closure);
|
||||
}
|
||||
|
||||
JavaScriptLocation map(LocationDTO dto) {
|
||||
return new JavaScriptLocation(scripts.get(dto.getScriptId()), dto.getLineNumber(), dto.getColumnNumber());
|
||||
}
|
||||
|
||||
LocationDTO unmap(JavaScriptLocation location) {
|
||||
LocationDTO dto = new LocationDTO();
|
||||
dto.setScriptId(scriptIds.get(location.getScript()));
|
||||
dto.setLineNumber(location.getLine());
|
||||
dto.setColumnNumber(location.getColumn());
|
||||
return dto;
|
||||
}
|
||||
|
||||
interface ResponseHandler {
|
||||
void received(JsonNode node) throws IOException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@ServerEndpoint("/")
|
||||
public class ChromeRDPDebuggerEndpoint implements ChromeRDPExchange {
|
||||
public static final int MAX_MESSAGE_SIZE = 65534;
|
||||
private Session session;
|
||||
private ChromeRDPExchangeConsumer debugger;
|
||||
private List<ChromeRDPExchangeListener> listeners = new ArrayList<>();
|
||||
private StringBuilder messageBuffer = new StringBuilder();
|
||||
|
||||
@OnOpen
|
||||
public void open(Session session) {
|
||||
this.session = session;
|
||||
session.setMaxIdleTimeout(0);
|
||||
Object debugger = session.getUserProperties().get("chrome.rdp");
|
||||
if (debugger instanceof ChromeRDPExchangeConsumer) {
|
||||
this.debugger = (ChromeRDPExchangeConsumer)debugger;
|
||||
this.debugger.setExchange(this);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void close() {
|
||||
if (this.debugger != null) {
|
||||
this.debugger.setExchange(null);
|
||||
this.debugger = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void receive(String message) throws IOException {
|
||||
char ctl = message.charAt(0);
|
||||
messageBuffer.append(message.substring(1));
|
||||
if (ctl == '.') {
|
||||
message = messageBuffer.toString();
|
||||
for (ChromeRDPExchangeListener listener : listeners) {
|
||||
listener.received(message);
|
||||
}
|
||||
messageBuffer = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
int index = 0;
|
||||
while (message.length() - index > MAX_MESSAGE_SIZE) {
|
||||
int next = index + MAX_MESSAGE_SIZE;
|
||||
session.getAsyncRemote().sendText("," + message.substring(index, next));
|
||||
index = next;
|
||||
}
|
||||
session.getAsyncRemote().sendText("." + message.substring(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ChromeRDPExchangeListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ChromeRDPExchangeListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public interface ChromeRDPExchange {
|
||||
void send(String message);
|
||||
|
||||
void disconnect();
|
||||
|
||||
void addListener(ChromeRDPExchangeListener listener);
|
||||
|
||||
void removeListener(ChromeRDPExchangeListener listener);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public interface ChromeRDPExchangeConsumer {
|
||||
void setExchange(ChromeRDPExchange exchange);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public interface ChromeRDPExchangeListener {
|
||||
void received(String message) throws IOException;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.util.*;
|
||||
import javax.websocket.Decoder;
|
||||
import javax.websocket.Encoder;
|
||||
import javax.websocket.Extension;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class ChromeRDPServer {
|
||||
private int port = 2357;
|
||||
private ChromeRDPExchangeConsumer exchangeConsumer;
|
||||
private Server server;
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public ChromeRDPExchangeConsumer getExchangeConsumer() {
|
||||
return exchangeConsumer;
|
||||
}
|
||||
|
||||
public void setExchangeConsumer(ChromeRDPExchangeConsumer exchangeConsumer) {
|
||||
this.exchangeConsumer = exchangeConsumer;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(port);
|
||||
server.addConnector(connector);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
context.setContextPath("/");
|
||||
server.setHandler(context);
|
||||
|
||||
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
|
||||
|
||||
try {
|
||||
wscontainer.addEndpoint(new RPDEndpointConfig());
|
||||
server.start();
|
||||
server.join();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
server.stop();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private class RPDEndpointConfig implements ServerEndpointConfig {
|
||||
private Map<String, Object> userProperties = new HashMap<>();
|
||||
|
||||
public RPDEndpointConfig() {
|
||||
userProperties.put("chrome.rdp", exchangeConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<? extends Decoder>> getDecoders() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<? extends Encoder>> getEncoders() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getUserProperties() {
|
||||
return userProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configurator getConfigurator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEndpointClass() {
|
||||
return ChromeRDPDebuggerEndpoint.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Extension> getExtensions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSubprotocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.teavm.debugging.javascript.JavaScriptBreakpoint;
|
||||
import org.teavm.debugging.javascript.JavaScriptLocation;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class RDPBreakpoint implements JavaScriptBreakpoint {
|
||||
volatile String chromeId;
|
||||
ChromeRDPDebugger debugger;
|
||||
private JavaScriptLocation location;
|
||||
AtomicInteger referenceCount = new AtomicInteger();
|
||||
|
||||
RDPBreakpoint(ChromeRDPDebugger debugger, JavaScriptLocation location) {
|
||||
this.debugger = debugger;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptLocation getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (debugger != null) {
|
||||
debugger.destroyBreakpoint(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return chromeId != null && debugger != null && debugger.isAttached();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import org.teavm.debugging.javascript.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class RDPCallFrame implements JavaScriptCallFrame {
|
||||
private JavaScriptDebugger debugger;
|
||||
private String chromeId;
|
||||
private JavaScriptLocation location;
|
||||
private Map<String, JavaScriptVariable> variables;
|
||||
private JavaScriptValue thisObject;
|
||||
private JavaScriptValue closure;
|
||||
|
||||
public RDPCallFrame(JavaScriptDebugger debugger, String chromeId, JavaScriptLocation location,
|
||||
Map<String, ? extends JavaScriptVariable> variables, JavaScriptValue thisObject,
|
||||
JavaScriptValue closure) {
|
||||
this.debugger = debugger;
|
||||
this.chromeId = chromeId;
|
||||
this.location = location;
|
||||
this.variables = Collections.unmodifiableMap(variables);
|
||||
this.thisObject = thisObject;
|
||||
this.closure = closure;
|
||||
}
|
||||
|
||||
public String getChromeId() {
|
||||
return chromeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptLocation getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, JavaScriptVariable> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptDebugger getDebugger() {
|
||||
return debugger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptValue getThisVariable() {
|
||||
return thisObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptValue getClosureVariable() {
|
||||
return closure;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import org.teavm.debugging.javascript.JavaScriptValue;
|
||||
import org.teavm.debugging.javascript.JavaScriptVariable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class RDPLocalVariable implements JavaScriptVariable {
|
||||
private String name;
|
||||
private RDPValue value;
|
||||
|
||||
public RDPLocalVariable(String name, RDPValue value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaScriptValue getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class RDPScope extends AbstractMap<String, RDPLocalVariable> {
|
||||
private AtomicReference<Map<String, RDPLocalVariable>> backingMap = new AtomicReference<>();
|
||||
private ChromeRDPDebugger debugger;
|
||||
private String id;
|
||||
|
||||
public RDPScope(ChromeRDPDebugger debugger, String id) {
|
||||
this.debugger = debugger;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, RDPLocalVariable>> entrySet() {
|
||||
initBackingMap();
|
||||
return backingMap.get().entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
initBackingMap();
|
||||
return backingMap.get().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RDPLocalVariable get(Object key) {
|
||||
initBackingMap();
|
||||
return backingMap.get().get(key);
|
||||
}
|
||||
|
||||
private void initBackingMap() {
|
||||
if (backingMap.get() != null) {
|
||||
return;
|
||||
}
|
||||
Map<String, RDPLocalVariable> newBackingMap = new HashMap<>();
|
||||
if (id != null) {
|
||||
for (RDPLocalVariable variable : debugger.getScope(id)) {
|
||||
newBackingMap.put(variable.getName(), variable);
|
||||
}
|
||||
}
|
||||
backingMap.compareAndSet(null, newBackingMap);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.teavm.debugging.javascript.JavaScriptValue;
|
||||
import org.teavm.debugging.javascript.JavaScriptVariable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class RDPValue implements JavaScriptValue {
|
||||
private AtomicReference<String> representation = new AtomicReference<>();
|
||||
private AtomicReference<String> className = new AtomicReference<>();
|
||||
private String typeName;
|
||||
private ChromeRDPDebugger debugger;
|
||||
private String objectId;
|
||||
private Map<String, ? extends JavaScriptVariable> properties;
|
||||
private boolean innerStructure;
|
||||
|
||||
public RDPValue(ChromeRDPDebugger debugger, String representation, String typeName, String objectId,
|
||||
boolean innerStructure) {
|
||||
this.representation.set(representation == null && objectId == null ? "" : representation);
|
||||
this.typeName = typeName;
|
||||
this.debugger = debugger;
|
||||
this.objectId = objectId;
|
||||
this.innerStructure = innerStructure;
|
||||
properties = objectId != null ? new RDPScope(debugger, objectId) :
|
||||
Collections.<String, RDPLocalVariable>emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
if (representation.get() == null) {
|
||||
representation.compareAndSet(null, debugger.getRepresentation(objectId));
|
||||
}
|
||||
return representation.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
if (className.get() == null) {
|
||||
if (objectId != null) {
|
||||
String computedClassName = debugger.getClassName(objectId);
|
||||
className.compareAndSet(null, computedClassName != null ? computedClassName : "@Object");
|
||||
} else {
|
||||
className.compareAndSet(null, "@" + typeName);
|
||||
}
|
||||
}
|
||||
return className.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Map<String, JavaScriptVariable> getProperties() {
|
||||
return (Map<String, JavaScriptVariable>)properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInnerStructure() {
|
||||
return innerStructure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return objectId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CallArgumentDTO {
|
||||
private String objectId;
|
||||
private JsonNode value;
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public JsonNode getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(JsonNode value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CallFrameDTO {
|
||||
private String callFrameId;
|
||||
private LocationDTO location;
|
||||
private ScopeDTO[] scopeChain;
|
||||
|
||||
public String getCallFrameId() {
|
||||
return callFrameId;
|
||||
}
|
||||
|
||||
public void setCallFrameId(String callFrameId) {
|
||||
this.callFrameId = callFrameId;
|
||||
}
|
||||
|
||||
public LocationDTO getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(LocationDTO location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public ScopeDTO[] getScopeChain() {
|
||||
return scopeChain;
|
||||
}
|
||||
|
||||
public void setScopeChain(ScopeDTO[] scopeChain) {
|
||||
this.scopeChain = scopeChain;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class LocationDTO {
|
||||
private int columnNumber;
|
||||
private int lineNumber;
|
||||
private String scriptId;
|
||||
|
||||
public int getColumnNumber() {
|
||||
return columnNumber;
|
||||
}
|
||||
|
||||
public void setColumnNumber(int columnNumber) {
|
||||
this.columnNumber = columnNumber;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public void setLineNumber(int lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public String getScriptId() {
|
||||
return scriptId;
|
||||
}
|
||||
|
||||
public void setScriptId(String scriptId) {
|
||||
this.scriptId = scriptId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Message {
|
||||
private Integer id;
|
||||
private String method;
|
||||
private JsonNode params;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public JsonNode getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(JsonNode params) {
|
||||
this.params = params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class PropertyDescriptorDTO {
|
||||
private String name;
|
||||
private RemoteObjectDTO value;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public RemoteObjectDTO getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(RemoteObjectDTO value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class RemoteObjectDTO {
|
||||
private String className;
|
||||
private String description;
|
||||
private String objectId;
|
||||
private String type;
|
||||
private JsonNode value;
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public JsonNode getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(JsonNode value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Response {
|
||||
private int id;
|
||||
private JsonNode result;
|
||||
private JsonNode error;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public JsonNode getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(JsonNode result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public JsonNode getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(JsonNode error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.data;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ScopeDTO {
|
||||
private RemoteObjectDTO object;
|
||||
private String type;
|
||||
|
||||
public RemoteObjectDTO getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public void setObject(RemoteObjectDTO object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.CallArgumentDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CallFunctionCommand {
|
||||
private String objectId;
|
||||
private String functionDeclaration;
|
||||
private CallArgumentDTO[] arguments;
|
||||
private boolean returnByValue;
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public String getFunctionDeclaration() {
|
||||
return functionDeclaration;
|
||||
}
|
||||
|
||||
public void setFunctionDeclaration(String functionDeclaration) {
|
||||
this.functionDeclaration = functionDeclaration;
|
||||
}
|
||||
|
||||
public CallArgumentDTO[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public void setArguments(CallArgumentDTO[] arguments) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public boolean isReturnByValue() {
|
||||
return returnByValue;
|
||||
}
|
||||
|
||||
public void setReturnByValue(boolean returnByValue) {
|
||||
this.returnByValue = returnByValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.RemoteObjectDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CallFunctionResponse {
|
||||
private RemoteObjectDTO result;
|
||||
private boolean wasThrown;
|
||||
|
||||
public RemoteObjectDTO getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(RemoteObjectDTO result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public boolean isWasThrown() {
|
||||
return wasThrown;
|
||||
}
|
||||
|
||||
public void setWasThrown(boolean wasThrown) {
|
||||
this.wasThrown = wasThrown;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.LocationDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ContinueToLocationCommand {
|
||||
private LocationDTO location;
|
||||
|
||||
public LocationDTO getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(LocationDTO location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GetPropertiesCommand {
|
||||
private String objectId;
|
||||
private boolean ownProperties;
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public boolean isOwnProperties() {
|
||||
return ownProperties;
|
||||
}
|
||||
|
||||
public void setOwnProperties(boolean ownProperties) {
|
||||
this.ownProperties = ownProperties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.PropertyDescriptorDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GetPropertiesResponse {
|
||||
private PropertyDescriptorDTO[] result;
|
||||
|
||||
public PropertyDescriptorDTO[] getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(PropertyDescriptorDTO[] properties) {
|
||||
this.result = properties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class RemoveBreakpointCommand {
|
||||
private String breakpointId;
|
||||
|
||||
public String getBreakpointId() {
|
||||
return breakpointId;
|
||||
}
|
||||
|
||||
public void setBreakpointId(String breakpointId) {
|
||||
this.breakpointId = breakpointId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ScriptParsedNotification {
|
||||
private String scriptId;
|
||||
private String url;
|
||||
|
||||
public String getScriptId() {
|
||||
return scriptId;
|
||||
}
|
||||
|
||||
public void setScriptId(String scriptId) {
|
||||
this.scriptId = scriptId;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.LocationDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SetBreakpointCommand {
|
||||
private LocationDTO location;
|
||||
|
||||
public LocationDTO getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(LocationDTO location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.LocationDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SetBreakpointResponse {
|
||||
private String breakpointId;
|
||||
private LocationDTO actualLocation;
|
||||
|
||||
public String getBreakpointId() {
|
||||
return breakpointId;
|
||||
}
|
||||
|
||||
public void setBreakpointId(String breakpointId) {
|
||||
this.breakpointId = breakpointId;
|
||||
}
|
||||
|
||||
public LocationDTO getActualLocation() {
|
||||
return actualLocation;
|
||||
}
|
||||
|
||||
public void setActualLocation(LocationDTO actualLocation) {
|
||||
this.actualLocation = actualLocation;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.chromerdp.messages;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.teavm.chromerdp.data.CallFrameDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SuspendedNotification {
|
||||
private CallFrameDTO[] callFrames;
|
||||
private String reason;
|
||||
private JsonNode data;
|
||||
|
||||
public CallFrameDTO[] getCallFrames() {
|
||||
return callFrames;
|
||||
}
|
||||
|
||||
public void setCallFrames(CallFrameDTO[] callFrames) {
|
||||
this.callFrames = callFrames;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public JsonNode getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(JsonNode data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
98
teavm-chrome-rdp/src/main/js/chrome/main.js
Normal file
98
teavm-chrome-rdp/src/main/js/chrome/main.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
debuggerAgentMap = {};
|
||||
|
||||
chrome.browserAction.onClicked.addListener(function(tab) {
|
||||
new DebuggerAgent(tab).attach();
|
||||
});
|
||||
function DebuggerAgent(tab) {
|
||||
this.pendingMessages = [];
|
||||
this.connection = null;
|
||||
this.tab = null;
|
||||
this.debuggee = { tabId : tab.id };
|
||||
this.attachedToDebugger = false;
|
||||
this.messageBuffer = "";
|
||||
debuggerAgentMap[tab.id] = this;
|
||||
}
|
||||
DebuggerAgent.MAX_MESSAGE_SIZE = 65534;
|
||||
DebuggerAgent.prototype.attach = function() {
|
||||
chrome.debugger.attach(this.debuggee, "1.0", (function(callback) {
|
||||
this.attachedToDebugger = true;
|
||||
chrome.debugger.sendCommand(this.debuggee, "Debugger.enable", {}, callback);
|
||||
}).bind(this, this.connectToServer.bind(this)));
|
||||
};
|
||||
DebuggerAgent.prototype.connectToServer = function() {
|
||||
this.connection = new WebSocket("ws://localhost:2357/");
|
||||
this.connection.onmessage = function(event) {
|
||||
var str = event.data;
|
||||
var ctl = str.substring(0, 1);
|
||||
this.messageBuffer += str.substring(1);
|
||||
if (ctl == '.') {
|
||||
this.receiveMessage(JSON.parse(this.messageBuffer));
|
||||
this.messageBuffer = "";
|
||||
}
|
||||
}.bind(this);
|
||||
this.connection.onclose = function(event) {
|
||||
if (this.connection != null) {
|
||||
this.connection = null;
|
||||
this.disconnect();
|
||||
}
|
||||
}.bind(this);
|
||||
this.connection.onopen = function() {
|
||||
for (var i = 0; i < this.pendingMessages.length; ++i) {
|
||||
this.sendMessage(this.pendingMessages[i]);
|
||||
}
|
||||
this.pendingMessages = null;
|
||||
}.bind(this);
|
||||
};
|
||||
DebuggerAgent.prototype.receiveMessage = function(message) {
|
||||
chrome.debugger.sendCommand(this.debuggee, message.method, message.params, function(response) {
|
||||
if (message.id) {
|
||||
var responseToServer = { id : message.id, result : response,
|
||||
error : response ? undefined : chrome.runtime.lastError };
|
||||
this.sendMessage(responseToServer);
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
DebuggerAgent.prototype.sendMessage = function(message) {
|
||||
var str = JSON.stringify(message);
|
||||
while (str.length > DebuggerAgent.MAX_MESSAGE_SIZE) {
|
||||
var part = "," + str.substring(0, DebuggerAgent.MAX_MESSAGE_SIZE);
|
||||
this.connection.send(part);
|
||||
str = str.substring(DebuggerAgent.MAX_MESSAGE_SIZE);
|
||||
}
|
||||
this.connection.send("." + str);
|
||||
}
|
||||
DebuggerAgent.prototype.disconnect = function() {
|
||||
if (this.connection) {
|
||||
var conn = this.connection;
|
||||
this.connection = null;
|
||||
conn.close();
|
||||
}
|
||||
if (this.attachedToDebugger) {
|
||||
chrome.debugger.detach(this.debuggee);
|
||||
this.attachedToDebugger = false;
|
||||
}
|
||||
if (this.debuggee) {
|
||||
delete debuggerAgentMap[this.debuggee.tabId];
|
||||
this.debuggee = null;
|
||||
}
|
||||
};
|
||||
|
||||
chrome.debugger.onEvent.addListener(function(source, method, params) {
|
||||
var agent = debuggerAgentMap[source.tabId];
|
||||
if (!agent) {
|
||||
return;
|
||||
}
|
||||
var message = { method : method, params : params };
|
||||
if (agent.pendingMessages) {
|
||||
agent.pendingMessages.push(message);
|
||||
} else if (agent.connection) {
|
||||
agent.sendMessage(message);
|
||||
}
|
||||
});
|
||||
chrome.debugger.onDetach.addListener(function(source) {
|
||||
var agent = debuggerAgentMap[source.tabId];
|
||||
if (agent) {
|
||||
agent.attachedToDebugger = false;
|
||||
agent.disconnect();
|
||||
}
|
||||
});
|
18
teavm-chrome-rdp/src/main/js/chrome/manifest.json
Normal file
18
teavm-chrome-rdp/src/main/js/chrome/manifest.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
|
||||
"name": "TeaVM debugger agent",
|
||||
"description": "TeaVM debugger agent, that sends RDP commands over WebSocket",
|
||||
"version": "0.2",
|
||||
|
||||
"permissions" : ["debugger", "activeTab", "tabs"],
|
||||
|
||||
"browser_action" : {
|
||||
"default_icon": "teavm-16.png",
|
||||
"default_title ": "Connect to TeaVM debugger"
|
||||
},
|
||||
|
||||
"background": {
|
||||
"scripts": ["main.js"]
|
||||
}
|
||||
}
|
BIN
teavm-chrome-rdp/src/main/js/chrome/teavm-16.png
Normal file
BIN
teavm-chrome-rdp/src/main/js/chrome/teavm-16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 713 B |
|
@ -24,28 +24,58 @@
|
|||
</parent>
|
||||
<artifactId>teavm-classlib</artifactId>
|
||||
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<name>TeaVM Java class library</name>
|
||||
<description>TeaVM Java class library emulation</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-platform</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.2.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<name>TeaVM JCL</name>
|
||||
<description>TeaVM Java class library emulation</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.teavm.classlib.*</Export-Package>
|
||||
<Bundle-SymbolicName>teavm-classlib</Bundle-SymbolicName>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-maven-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-platform</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-javascript-tests</id>
|
||||
|
@ -55,7 +85,11 @@
|
|||
<phase>process-test-classes</phase>
|
||||
<configuration>
|
||||
<minifying>false</minifying>
|
||||
<numThreads>1</numThreads>
|
||||
<sourceMapsGenerated>true</sourceMapsGenerated>
|
||||
<sourceFilesCopied>true</sourceFilesCopied>
|
||||
<properties>
|
||||
<java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available>
|
||||
</properties>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -79,7 +113,9 @@
|
|||
<argument>java.lang.annotation</argument>
|
||||
<argument>java.lang.reflect</argument>
|
||||
<argument>java.io</argument>
|
||||
<argument>java.math</argument>
|
||||
<argument>java.net</argument>
|
||||
<argument>java.text</argument>
|
||||
<argument>java.util</argument>
|
||||
<argument>java.util.logging</argument>
|
||||
<argument>java.util.concurrent</argument>
|
||||
|
@ -89,6 +125,15 @@
|
|||
</arguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>org/teavm/platform/metadata/*.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
|
|
|
@ -32,7 +32,7 @@ public class ClassLookupDependencySupport implements DependencyListener {
|
|||
|
||||
@Override
|
||||
public void classAchieved(DependencyAgent agent, String className) {
|
||||
allClasses.propagate(className);
|
||||
allClasses.propagate(agent.getType(className));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,8 +41,8 @@ public class ClassLookupDependencySupport implements DependencyListener {
|
|||
if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) {
|
||||
final DependencyStack stack = method.getStack();
|
||||
allClasses.addConsumer(new DependencyConsumer() {
|
||||
@Override public void consume(String type) {
|
||||
ClassReader cls = agent.getClassSource().get(type);
|
||||
@Override public void consume(DependencyAgentType type) {
|
||||
ClassReader cls = agent.getClassSource().get(type.getName());
|
||||
if (cls == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class EnumDependencySupport implements DependencyListener {
|
|||
if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) {
|
||||
return;
|
||||
}
|
||||
allEnums.propagate(className);
|
||||
allEnums.propagate(agent.getType(className));
|
||||
if (enumConstantsStack != null) {
|
||||
MethodReader method = cls.getMethod(new MethodDescriptor("values",
|
||||
ValueType.arrayOf(ValueType.object(cls.getName()))));
|
||||
|
@ -55,7 +55,7 @@ public class EnumDependencySupport implements DependencyListener {
|
|||
if (method.getReference().getClassName().equals("java.lang.Class") &&
|
||||
method.getReference().getName().equals("getEnumConstantsImpl")) {
|
||||
allEnums.connect(method.getResult().getArrayItem());
|
||||
method.getResult().propagate("[java.lang.Enum");
|
||||
method.getResult().propagate(agent.getType("[java.lang.Enum"));
|
||||
enumConstantsStack = method.getStack();
|
||||
for (String cls : agent.getAchievableClasses()) {
|
||||
classAchieved(agent, cls);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class FirstDayOfWeekMetadataGenerator extends WeekMetadataGenerator {
|
||||
@Override
|
||||
protected Map<String, Integer> getWeekData(CLDRReader reader) {
|
||||
return reader.getFirstDayMap();
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.impl;
|
||||
|
||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
@ -32,6 +33,7 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
host.add(new EnumTransformer());
|
||||
host.add(new ClassLookupDependencySupport());
|
||||
host.add(new NewInstanceDependencySupport());
|
||||
host.add(new ObjectEnrichRenderer());
|
||||
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
|
||||
host.add(serviceLoaderSupp);
|
||||
MethodReference loadServicesMethod = new MethodReference("java.util.ServiceLoader", new MethodDescriptor(
|
||||
|
@ -40,5 +42,7 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
host.add(loadServicesMethod, serviceLoaderSupp);
|
||||
JavacSupport javacSupport = new JavacSupport();
|
||||
host.add(javacSupport);
|
||||
|
||||
host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class MinimalDaysInFirstWeekMetadataGenerator extends WeekMetadataGenerator {
|
||||
@Override
|
||||
protected Map<String, Integer> getWeekData(CLDRReader reader) {
|
||||
return reader.getMinDaysMap();
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ public class NewInstanceDependencySupport implements DependencyListener {
|
|||
}
|
||||
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", ValueType.VOID));
|
||||
if (method != null) {
|
||||
allClassesNode.propagate(className);
|
||||
allClassesNode.propagate(agent.getType(className));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,8 @@ public class NewInstanceDependencySupport implements DependencyListener {
|
|||
newInstanceStack = method.getStack();
|
||||
allClassesNode.connect(method.getResult());
|
||||
method.getResult().addConsumer(new DependencyConsumer() {
|
||||
@Override public void consume(String type) {
|
||||
attachConstructor(agent, type);
|
||||
@Override public void consume(DependencyAgentType type) {
|
||||
attachConstructor(agent, type.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.teavm.javascript.RenderingContext;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.vm.BuildTarget;
|
||||
import org.teavm.vm.spi.RendererListener;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class ObjectEnrichRenderer implements RendererListener {
|
||||
private RenderingContext context;
|
||||
|
||||
@Override
|
||||
public void begin(RenderingContext context, BuildTarget buildTarget) throws IOException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeClass(ClassReader cls) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterClass(ClassReader cls) throws IOException {
|
||||
if (cls.getName().equals("java.lang.Object")) {
|
||||
MethodReader toString = cls.getMethod(new MethodDescriptor("toString", String.class));
|
||||
if (toString != null) {
|
||||
String clsName = context.getNaming().getNameFor(cls.getName());
|
||||
String toStringName = context.getNaming().getNameFor(toString.getReference());
|
||||
context.getWriter().append(clsName).append(".prototype.toString").ws().append('=').ws()
|
||||
.append("function()").ws().append('{').indent().softNewLine();
|
||||
context.getWriter().append("return this.").append(toStringName).ws().append('?').ws()
|
||||
.append("$rt_ustr(this.").append(toStringName).append("())").ws().append(':')
|
||||
.append("Object.prototype.toString.call(this);").softNewLine();
|
||||
context.getWriter().outdent().append("}").newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() throws IOException {
|
||||
}
|
||||
}
|
|
@ -89,7 +89,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
|
|||
while (resources.hasMoreElements()) {
|
||||
URL resource = resources.nextElement();
|
||||
try (InputStream stream = resource.openStream()) {
|
||||
parseServiceFile(className, stream);
|
||||
parseServiceFile(agent, className, stream);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -97,7 +97,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseServiceFile(String service, InputStream input) throws IOException {
|
||||
private void parseServiceFile(DependencyAgent agent, String service, InputStream input) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
|
@ -114,7 +114,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
|
|||
serviceMap.put(service, implementors);
|
||||
}
|
||||
implementors.add(line);
|
||||
allClassesNode.propagate(line);
|
||||
allClassesNode.propagate(agent.getType(line));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,12 +122,12 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
|
|||
public void methodAchieved(final DependencyAgent agent, MethodDependency method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
||||
method.getResult().propagate("[java.lang.Object");
|
||||
method.getResult().propagate(agent.getType("[java.lang.Object"));
|
||||
stack = method.getStack();
|
||||
allClassesNode.connect(method.getResult().getArrayItem());
|
||||
method.getResult().getArrayItem().addConsumer(new DependencyConsumer() {
|
||||
@Override public void consume(String type) {
|
||||
initConstructor(agent, type);
|
||||
@Override public void consume(DependencyAgentType type) {
|
||||
initConstructor(agent, type.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public abstract class WeekMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
ResourceMap<IntResource> map = context.createResourceMap();
|
||||
for (Map.Entry<String, Integer> entry : getWeekData(context.getService(CLDRReader.class)).entrySet()) {
|
||||
IntResource valueRes = context.createResource(IntResource.class);
|
||||
valueRes.setValue(entry.getValue());
|
||||
map.put(entry.getKey(), valueRes);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
protected abstract Map<String, Integer> getWeekData(CLDRReader reader);
|
||||
}
|
|
@ -166,7 +166,7 @@ public class JCLComparisonBuilder {
|
|||
return false;
|
||||
}
|
||||
int slashIndex = name.lastIndexOf('/');
|
||||
return packages.contains(name.substring(0, slashIndex));
|
||||
return slashIndex >= 0 && packages.contains(name.substring(0, slashIndex));
|
||||
}
|
||||
|
||||
private void compareClass(InputStream input) throws IOException {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class AvailableLocalesMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
CLDRReader reader = context.getService(CLDRReader.class);
|
||||
ResourceArray<StringResource> result = context.createResourceArray();
|
||||
for (String locale : reader.getAvailableLocales()) {
|
||||
StringResource localeRes = context.createResource(StringResource.class);
|
||||
localeRes.setValue(locale);
|
||||
result.add(localeRes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class CLDRDateFormats {
|
||||
private String shortFormat;
|
||||
private String mediumFormat;
|
||||
private String longFormat;
|
||||
private String fullFormat;
|
||||
|
||||
CLDRDateFormats(String shortFormat, String mediumFormat, String longFormat, String fullFormat) {
|
||||
this.shortFormat = shortFormat;
|
||||
this.mediumFormat = mediumFormat;
|
||||
this.longFormat = longFormat;
|
||||
this.fullFormat = fullFormat;
|
||||
}
|
||||
|
||||
public String getShortFormat() {
|
||||
return shortFormat;
|
||||
}
|
||||
|
||||
public String getMediumFormat() {
|
||||
return mediumFormat;
|
||||
}
|
||||
|
||||
public String getLongFormat() {
|
||||
return longFormat;
|
||||
}
|
||||
|
||||
public String getFullFormat() {
|
||||
return fullFormat;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import org.teavm.platform.metadata.Resource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface CLDRDecimalData extends Resource {
|
||||
int getMaximumFractionDigits();
|
||||
|
||||
void setMaximumFractionDigits(int value);
|
||||
|
||||
int getMaximumIntegerDigits();
|
||||
|
||||
void setMaximumIntegerDigits(int value);
|
||||
|
||||
int getMinimumFractionDigits();
|
||||
|
||||
void setMinimumFractionDigits(int value);
|
||||
|
||||
int getMinimumIntegerDigits();
|
||||
|
||||
void setMinimumIntegerDigits(int value);
|
||||
|
||||
int getGroupingSeparator();
|
||||
|
||||
void setGroupingSeparator(int value);
|
||||
|
||||
int getDecimalSeparator();
|
||||
|
||||
void setDecimalSeparator(int value);
|
||||
|
||||
int getPerMill();
|
||||
|
||||
void setPerMill(int value);
|
||||
|
||||
int getPercent();
|
||||
|
||||
void setPercent(int value);
|
||||
|
||||
String getNaN();
|
||||
|
||||
void setNaN(String nan);
|
||||
|
||||
String getInfinity();
|
||||
|
||||
void setInfinity(String infinity);
|
||||
|
||||
int getMinusSign();
|
||||
|
||||
void setMinusSign(int value);
|
||||
|
||||
int getMonetaryDecimalSeparator();
|
||||
|
||||
void setMonetaryDecimalSeparator(int value);
|
||||
|
||||
String getExponentSeparator();
|
||||
|
||||
void setExponentSeparator(String value);
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import org.teavm.classlib.impl.FirstDayOfWeekMetadataGenerator;
|
||||
import org.teavm.classlib.impl.MinimalDaysInFirstWeekMetadataGenerator;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class CLDRHelper {
|
||||
public static String getCode(String language, String country) {
|
||||
return !country.isEmpty() ? language + "-" + country : language;
|
||||
}
|
||||
|
||||
public static String getLikelySubtags(String localeCode) {
|
||||
ResourceMap<StringResource> map = getLikelySubtagsMap();
|
||||
return map.has(localeCode) ? map.get(localeCode).getValue() : localeCode;
|
||||
}
|
||||
|
||||
@MetadataProvider(LikelySubtagsMetadataGenerator.class)
|
||||
private static native ResourceMap<StringResource> getLikelySubtagsMap();
|
||||
|
||||
public static String[] resolveEras(String language, String country) {
|
||||
return resolveDateFormatSymbols(getErasMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateSymbolsMetadataGenerator.class)
|
||||
private static native ResourceMap<ResourceArray<StringResource>> getErasMap();
|
||||
|
||||
public static String[] resolveAmPm(String language, String country) {
|
||||
return resolveDateFormatSymbols(getAmPmMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateSymbolsMetadataGenerator.class)
|
||||
private static native ResourceMap<ResourceArray<StringResource>> getAmPmMap();
|
||||
|
||||
public static String[] resolveMonths(String language, String country) {
|
||||
return resolveDateFormatSymbols(getMonthMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateSymbolsMetadataGenerator.class)
|
||||
private static native ResourceMap<ResourceArray<StringResource>> getMonthMap();
|
||||
|
||||
public static String[] resolveShortMonths(String language, String country) {
|
||||
return resolveDateFormatSymbols(getShortMonthMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateSymbolsMetadataGenerator.class)
|
||||
private static native ResourceMap<ResourceArray<StringResource>> getShortMonthMap();
|
||||
|
||||
public static String[] resolveWeekdays(String language, String country) {
|
||||
return resolveDateFormatSymbols(getWeekdayMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateSymbolsMetadataGenerator.class)
|
||||
private static native ResourceMap<ResourceArray<StringResource>> getWeekdayMap();
|
||||
|
||||
public static String[] resolveShortWeekdays(String language, String country) {
|
||||
return resolveDateFormatSymbols(getShortWeekdayMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateSymbolsMetadataGenerator.class)
|
||||
private static native ResourceMap<ResourceArray<StringResource>> getShortWeekdayMap();
|
||||
|
||||
private static String[] resolveDateFormatSymbols(ResourceMap<ResourceArray<StringResource>> map, String language,
|
||||
String country) {
|
||||
String localeCode = getCode(language, country);
|
||||
ResourceArray<StringResource> arrayRes = map.has(localeCode) ? map.get(localeCode) :
|
||||
map.has(language) ? map.get(language) : map.get("root");
|
||||
String[] result = new String[arrayRes.size()];
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i] = arrayRes.get(i).getValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@MetadataProvider(LanguageMetadataGenerator.class)
|
||||
public static native ResourceMap<ResourceMap<StringResource>> getLanguagesMap();
|
||||
|
||||
@MetadataProvider(CountryMetadataGenerator.class)
|
||||
public static native ResourceMap<ResourceMap<StringResource>> getCountriesMap();
|
||||
|
||||
@MetadataProvider(DefaultLocaleMetadataGenerator.class)
|
||||
public static native StringResource getDefaultLocale();
|
||||
|
||||
@MetadataProvider(AvailableLocalesMetadataGenerator.class)
|
||||
public static native ResourceArray<StringResource> getAvailableLocales();
|
||||
|
||||
@MetadataProvider(MinimalDaysInFirstWeekMetadataGenerator.class)
|
||||
public static native ResourceMap<IntResource> getMinimalDaysInFirstWeek();
|
||||
|
||||
@MetadataProvider(FirstDayOfWeekMetadataGenerator.class)
|
||||
public static native ResourceMap<IntResource> getFirstDayOfWeek();
|
||||
|
||||
public static DateFormatCollection resolveDateFormats(String language, String country) {
|
||||
return resolveDateFormats(getDateFormatMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateFormatMetadataGenerator.class)
|
||||
private static native ResourceMap<DateFormatCollection> getDateFormatMap();
|
||||
|
||||
public static DateFormatCollection resolveTimeFormats(String language, String country) {
|
||||
return resolveDateFormats(getTimeFormatMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateFormatMetadataGenerator.class)
|
||||
private static native ResourceMap<DateFormatCollection> getTimeFormatMap();
|
||||
|
||||
public static DateFormatCollection resolveDateTimeFormats(String language, String country) {
|
||||
return resolveDateFormats(getDateTimeFormatMap(), language, country);
|
||||
}
|
||||
|
||||
@MetadataProvider(DateFormatMetadataGenerator.class)
|
||||
private static native ResourceMap<DateFormatCollection> getDateTimeFormatMap();
|
||||
|
||||
public static String resolveNumberFormat(String language, String country) {
|
||||
return resolveFormatSymbols(getNumberFormatMap(), language, country);
|
||||
}
|
||||
|
||||
private static native ResourceMap<StringResource> getNumberFormatMap();
|
||||
|
||||
public static String resolveDecimalFormat(String language, String country) {
|
||||
return resolveFormatSymbols(getDecimalFormatMap(), language, country);
|
||||
}
|
||||
|
||||
private static native ResourceMap<StringResource> getDecimalFormatMap();
|
||||
|
||||
public static String resolvePercentFormat(String language, String country) {
|
||||
return resolveFormatSymbols(getPercentFormatMap(), language, country);
|
||||
}
|
||||
|
||||
private static native ResourceMap<StringResource> getPercentFormatMap();
|
||||
|
||||
private static DateFormatCollection resolveDateFormats(ResourceMap<DateFormatCollection> map,
|
||||
String language, String country) {
|
||||
String localeCode = getCode(language, country);
|
||||
return map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) : map.get("root");
|
||||
}
|
||||
|
||||
private static String resolveFormatSymbols(ResourceMap<StringResource> map, String language, String country) {
|
||||
String localeCode = getCode(language, country);
|
||||
StringResource res = map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) :
|
||||
map.get("root");
|
||||
return res.getValue();
|
||||
}
|
||||
|
||||
public static CLDRDecimalData resolveDecimalData(String language, String country) {
|
||||
ResourceMap<CLDRDecimalData> map = getDecimalDataMap();
|
||||
String localeCode = getCode(language, country);
|
||||
return map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) :
|
||||
map.get("root");
|
||||
}
|
||||
|
||||
private static native ResourceMap<CLDRDecimalData> getDecimalDataMap();
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class CLDRLocale {
|
||||
final Map<String, String> languages = new LinkedHashMap<>();
|
||||
final Map<String, String> territories = new LinkedHashMap<>();
|
||||
String[] eras;
|
||||
String[] dayPeriods;
|
||||
String[] months;
|
||||
String[] shortMonths;
|
||||
String[] weekdays;
|
||||
String[] shortWeekdays;
|
||||
CLDRDateFormats dateFormats;
|
||||
CLDRDateFormats timeFormats;
|
||||
CLDRDateFormats dateTimeFormats;
|
||||
|
||||
public Map<String, String> getLanguages() {
|
||||
return Collections.unmodifiableMap(languages);
|
||||
}
|
||||
|
||||
public Map<String, String> getTerritories() {
|
||||
return Collections.unmodifiableMap(territories);
|
||||
}
|
||||
|
||||
public String[] getEras() {
|
||||
return Arrays.copyOf(eras, eras.length);
|
||||
}
|
||||
|
||||
public String[] getDayPeriods() {
|
||||
return Arrays.copyOf(dayPeriods, dayPeriods.length);
|
||||
}
|
||||
|
||||
public String[] getMonths() {
|
||||
return Arrays.copyOf(months, months.length);
|
||||
}
|
||||
|
||||
public String[] getShortMonths() {
|
||||
return Arrays.copyOf(shortMonths, shortMonths.length);
|
||||
}
|
||||
|
||||
public String[] getWeekdays() {
|
||||
return Arrays.copyOf(weekdays, weekdays.length);
|
||||
}
|
||||
|
||||
public String[] getShortWeekdays() {
|
||||
return Arrays.copyOf(shortWeekdays, shortWeekdays.length);
|
||||
}
|
||||
|
||||
public CLDRDateFormats getDateFormats() {
|
||||
return dateFormats;
|
||||
}
|
||||
|
||||
public CLDRDateFormats getTimeFormats() {
|
||||
return timeFormats;
|
||||
}
|
||||
|
||||
public CLDRDateFormats getDateTimeFormats() {
|
||||
return dateTimeFormats;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class CLDRReader {
|
||||
private static String[] weekdayKeys = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
|
||||
private Map<String, CLDRLocale> knownLocales = new LinkedHashMap<>();
|
||||
private Map<String, Integer> minDaysMap = new LinkedHashMap<>();
|
||||
private Map<String, Integer> firstDayMap = new LinkedHashMap<>();
|
||||
private Map<String, String> likelySubtags = new LinkedHashMap<>();
|
||||
private Set<String> availableLocales = new LinkedHashSet<>();
|
||||
private Set<String> availableLanguages = new LinkedHashSet<>();
|
||||
private Set<String> availableCountries = new LinkedHashSet<>();
|
||||
private boolean initialized;
|
||||
private Properties properties;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public CLDRReader(Properties properties, ClassLoader classLoader) {
|
||||
this.properties = properties;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
private synchronized void ensureInitialized() {
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
findAvailableLocales(properties);
|
||||
readCLDR(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
private void findAvailableLocales(Properties properties) {
|
||||
String availableLocalesString = properties.getProperty("java.util.Locale.available", "en_EN").trim();
|
||||
for (String locale : Arrays.asList(availableLocalesString.split(" *, *"))) {
|
||||
int countryIndex = locale.indexOf('_');
|
||||
if (countryIndex > 0) {
|
||||
String language = locale.substring(0, countryIndex);
|
||||
String country = locale.substring(countryIndex + 1);
|
||||
availableLocales.add(language + "-" + country);
|
||||
availableLocales.add(language);
|
||||
availableLanguages.add(language);
|
||||
availableCountries.add(country);
|
||||
} else {
|
||||
availableLocales.add(locale);
|
||||
availableLanguages.add(locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readCLDR(ClassLoader classLoader) {
|
||||
try (ZipInputStream input = new ZipInputStream(classLoader.getResourceAsStream(
|
||||
"org/teavm/classlib/impl/unicode/cldr-json.zip"))) {
|
||||
while (true) {
|
||||
ZipEntry entry = input.getNextEntry();
|
||||
if (entry == null) {
|
||||
break;
|
||||
}
|
||||
if (!entry.getName().endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getName().equals("supplemental/weekData.json")) {
|
||||
readWeekData(input);
|
||||
continue;
|
||||
} else if (entry.getName().equals("supplemental/likelySubtags.json")) {
|
||||
readLikelySubtags(input);
|
||||
}
|
||||
int objectIndex = entry.getName().lastIndexOf('/');
|
||||
String objectName = entry.getName().substring(objectIndex + 1);
|
||||
String localeName = entry.getName().substring(0, objectIndex);
|
||||
if (localeName.startsWith("/")) {
|
||||
localeName = localeName.substring(1);
|
||||
}
|
||||
if (!localeName.equals("root") && !availableLocales.contains(localeName)) {
|
||||
continue;
|
||||
}
|
||||
CLDRLocale localeInfo = knownLocales.get(localeName);
|
||||
if (localeInfo == null) {
|
||||
localeInfo = new CLDRLocale();
|
||||
knownLocales.put(localeName, localeInfo);
|
||||
}
|
||||
switch (objectName) {
|
||||
case "languages.json":
|
||||
readLanguages(localeName, localeInfo, input);
|
||||
break;
|
||||
case "territories.json":
|
||||
readCountries(localeName, localeInfo, input);
|
||||
break;
|
||||
case "ca-gregorian.json": {
|
||||
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
|
||||
readEras(localeName, localeInfo, root);
|
||||
readAmPms(localeName, localeInfo, root);
|
||||
readMonths(localeName, localeInfo, root);
|
||||
readShortMonths(localeName, localeInfo, root);
|
||||
readWeekdays(localeName, localeInfo, root);
|
||||
readShortWeekdays(localeName, localeInfo, root);
|
||||
readDateFormats(localeName, localeInfo, root);
|
||||
readTimeFormats(localeName, localeInfo, root);
|
||||
readDateTimeFormats(localeName, localeInfo, root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error reading CLDR file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void readLanguages(String localeCode, CLDRLocale locale, InputStream input) {
|
||||
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
|
||||
JsonObject languagesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("localeDisplayNames").getAsJsonObject().get("languages").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> property : languagesJson.entrySet()) {
|
||||
String language = property.getKey();
|
||||
if (availableLanguages.contains(language)) {
|
||||
locale.languages.put(language, property.getValue().getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readCountries(String localeCode, CLDRLocale locale, InputStream input) {
|
||||
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
|
||||
JsonObject countriesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("localeDisplayNames").getAsJsonObject().get("territories").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> property : countriesJson.entrySet()) {
|
||||
String country = property.getKey();
|
||||
if (availableCountries.contains(country)) {
|
||||
locale.territories.put(country, property.getValue().getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readEras(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("eras").getAsJsonObject().get("eraAbbr").getAsJsonObject();
|
||||
String bc = erasJson.get("0").getAsString();
|
||||
String ac = erasJson.get("1").getAsString();
|
||||
locale.eras = new String[] { bc, ac };
|
||||
}
|
||||
|
||||
private void readAmPms(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject ampmJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("dayPeriods").getAsJsonObject()
|
||||
.get("format").getAsJsonObject().get("abbreviated").getAsJsonObject();
|
||||
String am = ampmJson.get("am").getAsString();
|
||||
String pm = ampmJson.get("pm").getAsString();
|
||||
locale.dayPeriods = new String[] { am, pm };
|
||||
}
|
||||
|
||||
private void readMonths(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject monthsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("months").getAsJsonObject()
|
||||
.get("format").getAsJsonObject().get("wide").getAsJsonObject();
|
||||
locale.months = new String[12];
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
locale.months[i] = monthsJson.get(String.valueOf(i + 1)).getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
private void readShortMonths(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject monthsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("months").getAsJsonObject()
|
||||
.get("format").getAsJsonObject().get("abbreviated").getAsJsonObject();
|
||||
locale.shortMonths = new String[12];
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
locale.shortMonths[i] = monthsJson.get(String.valueOf(i + 1)).getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
private void readWeekdays(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject weekdaysJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("days").getAsJsonObject()
|
||||
.get("format").getAsJsonObject().get("wide").getAsJsonObject();
|
||||
locale.weekdays = new String[7];
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
locale.weekdays[i] = weekdaysJson.get(weekdayKeys[i]).getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
private void readShortWeekdays(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject weekdaysJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("days").getAsJsonObject()
|
||||
.get("format").getAsJsonObject().get("abbreviated").getAsJsonObject();
|
||||
locale.shortWeekdays = new String[7];
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
locale.shortWeekdays[i] = weekdaysJson.get(weekdayKeys[i]).getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
private void readDateFormats(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject formatsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("dateFormats").getAsJsonObject();
|
||||
locale.dateFormats = new CLDRDateFormats(formatsJson.get("short").getAsString(),
|
||||
formatsJson.get("medium").getAsString(), formatsJson.get("long").getAsString(),
|
||||
formatsJson.get("full").getAsString());
|
||||
}
|
||||
|
||||
private void readTimeFormats(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject formatsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("timeFormats").getAsJsonObject();
|
||||
locale.timeFormats = new CLDRDateFormats(formatsJson.get("short").getAsString(),
|
||||
formatsJson.get("medium").getAsString(), formatsJson.get("long").getAsString(),
|
||||
formatsJson.get("full").getAsString());
|
||||
}
|
||||
|
||||
private void readDateTimeFormats(String localeCode, CLDRLocale locale, JsonObject root) {
|
||||
JsonObject formatsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
|
||||
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
|
||||
.get("gregorian").getAsJsonObject().get("dateTimeFormats").getAsJsonObject();
|
||||
locale.dateTimeFormats = new CLDRDateFormats(formatsJson.get("short").getAsString(),
|
||||
formatsJson.get("medium").getAsString(), formatsJson.get("long").getAsString(),
|
||||
formatsJson.get("full").getAsString());
|
||||
}
|
||||
|
||||
private void readWeekData(InputStream input) {
|
||||
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
|
||||
JsonObject weekJson = root.get("supplemental").getAsJsonObject().get("weekData").getAsJsonObject();
|
||||
JsonObject minDaysJson = weekJson.get("minDays").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> property : minDaysJson.entrySet()) {
|
||||
minDaysMap.put(property.getKey(), property.getValue().getAsInt());
|
||||
}
|
||||
JsonObject firstDayJson = weekJson.get("firstDay").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> property : firstDayJson.entrySet()) {
|
||||
firstDayMap.put(property.getKey(), getNumericDay(property.getValue().getAsString()));
|
||||
}
|
||||
}
|
||||
|
||||
private void readLikelySubtags(InputStream input) {
|
||||
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
|
||||
JsonObject likelySubtagsJson = root.get("supplemental").getAsJsonObject().get("likelySubtags")
|
||||
.getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> property : likelySubtagsJson.entrySet()) {
|
||||
likelySubtags.put(property.getKey(), property.getValue().getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
private int getNumericDay(String day) {
|
||||
switch (day) {
|
||||
case "sun":
|
||||
return 1;
|
||||
case "mon":
|
||||
return 2;
|
||||
case "tue":
|
||||
return 3;
|
||||
case "wed":
|
||||
return 4;
|
||||
case "thu":
|
||||
return 5;
|
||||
case "fri":
|
||||
return 6;
|
||||
case "sat":
|
||||
return 7;
|
||||
default:
|
||||
throw new IllegalArgumentException("Can't recognize day name: " + day);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, CLDRLocale> getKnownLocales() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableMap(knownLocales);
|
||||
}
|
||||
|
||||
public Set<String> getAvailableLocales() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableSet(availableLocales);
|
||||
}
|
||||
|
||||
public Set<String> getAvailableLanguages() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableSet(availableLanguages);
|
||||
}
|
||||
|
||||
public Set<String> getAvailableCountries() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableSet(availableCountries);
|
||||
}
|
||||
|
||||
public Map<String, Integer> getMinDaysMap() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableMap(minDaysMap);
|
||||
}
|
||||
|
||||
public Map<String, Integer> getFirstDayMap() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableMap(firstDayMap);
|
||||
}
|
||||
|
||||
public Map<String, String> getLikelySubtags() {
|
||||
ensureInitialized();
|
||||
return Collections.unmodifiableMap(likelySubtags);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class CountryMetadataGenerator extends LocaleMetadataGenerator {
|
||||
@Override
|
||||
protected Map<String, String> getNameMap(CLDRLocale locale) {
|
||||
return locale.getTerritories();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import org.teavm.platform.metadata.Resource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface DateFormatCollection extends Resource {
|
||||
String getShortFormat();
|
||||
|
||||
void setShortFormat(String format);
|
||||
|
||||
String getMediumFormat();
|
||||
|
||||
void setMediumFormat(String format);
|
||||
|
||||
String getLongFormat();
|
||||
|
||||
void setLongFormat(String format);
|
||||
|
||||
String getFullFormat();
|
||||
|
||||
void setFullFormat(String format);
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class DateFormatMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
switch (method.getName()) {
|
||||
case "getDateFormatMap":
|
||||
return getDateFormatMap(context, new FormatExtractor() {
|
||||
@Override public CLDRDateFormats extract(CLDRLocale locale) {
|
||||
return locale.getDateFormats();
|
||||
}
|
||||
});
|
||||
case "getTimeFormatMap":
|
||||
return getDateFormatMap(context, new FormatExtractor() {
|
||||
@Override public CLDRDateFormats extract(CLDRLocale locale) {
|
||||
return locale.getTimeFormats();
|
||||
}
|
||||
});
|
||||
case "getDateTimeFormatMap":
|
||||
return getDateFormatMap(context, new FormatExtractor() {
|
||||
@Override public CLDRDateFormats extract(CLDRLocale locale) {
|
||||
return locale.getDateTimeFormats();
|
||||
}
|
||||
});
|
||||
default:
|
||||
throw new IllegalArgumentException("Method is not supported: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
private Resource getDateFormatMap(MetadataGeneratorContext context, FormatExtractor extractor) {
|
||||
CLDRReader reader = context.getService(CLDRReader.class);
|
||||
ResourceMap<DateFormatCollection> result = context.createResourceMap();
|
||||
for (Map.Entry<String, CLDRLocale> entry : reader.getKnownLocales().entrySet()) {
|
||||
DateFormatCollection formatRes = context.createResource(DateFormatCollection.class);
|
||||
CLDRDateFormats formats = extractor.extract(entry.getValue());
|
||||
formatRes.setShortFormat(formats.getShortFormat());
|
||||
formatRes.setMediumFormat(formats.getMediumFormat());
|
||||
formatRes.setLongFormat(formats.getLongFormat());
|
||||
formatRes.setFullFormat(formats.getFullFormat());
|
||||
result.put(entry.getKey(), formatRes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
interface FormatExtractor {
|
||||
CLDRDateFormats extract(CLDRLocale locale);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class DateSymbolsMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
switch (method.getName()) {
|
||||
case "getErasMap":
|
||||
return generateSymbols(context, new ResourceExtractor() {
|
||||
@Override public String[] extract(CLDRLocale locale) { return locale.getEras(); }
|
||||
});
|
||||
case "getAmPmMap":
|
||||
return generateSymbols(context, new ResourceExtractor() {
|
||||
@Override public String[] extract(CLDRLocale locale) { return locale.getDayPeriods(); }
|
||||
});
|
||||
case "getMonthMap":
|
||||
return generateSymbols(context, new ResourceExtractor() {
|
||||
@Override public String[] extract(CLDRLocale locale) { return locale.getMonths(); }
|
||||
});
|
||||
case "getShortMonthMap":
|
||||
return generateSymbols(context, new ResourceExtractor() {
|
||||
@Override public String[] extract(CLDRLocale locale) { return locale.getShortMonths(); }
|
||||
});
|
||||
case "getWeekdayMap":
|
||||
return generateSymbols(context, new ResourceExtractor() {
|
||||
@Override public String[] extract(CLDRLocale locale) { return locale.getWeekdays(); }
|
||||
});
|
||||
case "getShortWeekdayMap":
|
||||
return generateSymbols(context, new ResourceExtractor() {
|
||||
@Override public String[] extract(CLDRLocale locale) { return locale.getShortWeekdays(); }
|
||||
});
|
||||
default:
|
||||
throw new AssertionError("Unsupported method: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
private Resource generateSymbols(MetadataGeneratorContext context, ResourceExtractor extractor) {
|
||||
CLDRReader reader = context.getService(CLDRReader.class);
|
||||
ResourceMap<ResourceArray<StringResource>> result = context.createResourceMap();
|
||||
for (Map.Entry<String, CLDRLocale> localeEntry : reader.getKnownLocales().entrySet()) {
|
||||
ResourceArray<StringResource> symbolsRes = context.createResourceArray();
|
||||
result.put(localeEntry.getKey(), symbolsRes);
|
||||
for (String symbol : extractor.extract(localeEntry.getValue())) {
|
||||
StringResource symbolRes = context.createResource(StringResource.class);
|
||||
symbolRes.setValue(symbol);
|
||||
symbolsRes.add(symbolRes);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static interface ResourceExtractor {
|
||||
String[] extract(CLDRLocale locale);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.MetadataGenerator;
|
||||
import org.teavm.platform.metadata.MetadataGeneratorContext;
|
||||
import org.teavm.platform.metadata.Resource;
|
||||
import org.teavm.platform.metadata.StringResource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class DefaultLocaleMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
StringResource result = context.createResource(StringResource.class);
|
||||
result.setValue(context.getProperties().getProperty("java.util.Locale.default", "en_EN"));
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class LanguageMetadataGenerator extends LocaleMetadataGenerator {
|
||||
@Override
|
||||
protected Map<String, String> getNameMap(CLDRLocale locale) {
|
||||
return locale.getLanguages();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class LikelySubtagsMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
CLDRReader reader = context.getService(CLDRReader.class);
|
||||
ResourceMap<StringResource> map = context.createResourceMap();
|
||||
for (Map.Entry<String, String> entry : reader.getLikelySubtags().entrySet()) {
|
||||
StringResource subtagRes = context.createResource(StringResource.class);
|
||||
subtagRes.setValue(entry.getValue());
|
||||
map.put(entry.getKey(), subtagRes);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.impl.unicode;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public abstract class LocaleMetadataGenerator implements MetadataGenerator {
|
||||
@Override
|
||||
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
|
||||
ResourceMap<ResourceMap<StringResource>> result = context.createResourceMap();
|
||||
CLDRReader reader = context.getService(CLDRReader.class);
|
||||
for (Map.Entry<String, CLDRLocale> entry : reader.getKnownLocales().entrySet()) {
|
||||
CLDRLocale locale = entry.getValue();
|
||||
ResourceMap<StringResource> names = context.createResourceMap();
|
||||
result.put(entry.getKey(), names);
|
||||
for (Map.Entry<String, String> nameEntry : getNameMap(locale).entrySet()) {
|
||||
StringResource name = context.createResource(StringResource.class);
|
||||
name.setValue(nameEntry.getValue());
|
||||
names.put(nameEntry.getKey(), name);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract Map<String, String> getNameMap(CLDRLocale locale);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.io;
|
||||
|
||||
import org.teavm.classlib.java.lang.TString;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TEOFException extends TIOException {
|
||||
private static final long serialVersionUID = 3045477060413545010L;
|
||||
|
||||
public TEOFException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TEOFException(TString message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ import java.io.IOException;
|
|||
import org.teavm.classlib.impl.unicode.UnicodeHelper;
|
||||
import org.teavm.classlib.impl.unicode.UnicodeSupport;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.dependency.DependencyChecker;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
|
@ -52,11 +52,11 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "obtainDigitMapping":
|
||||
case "obtainClasses":
|
||||
method.getResult().propagate("java.lang.String");
|
||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ package org.teavm.classlib.java.lang;
|
|||
|
||||
import java.io.IOException;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.dependency.DependencyChecker;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
|
@ -186,7 +186,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodAchieved(DependencyChecker checker, MethodDependency graph) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency graph) {
|
||||
switch (graph.getReference().getName()) {
|
||||
case "voidClass":
|
||||
case "booleanClass":
|
||||
|
@ -202,10 +202,10 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
|
|||
case "getComponentType0":
|
||||
case "forNameImpl":
|
||||
case "getDeclaringClass":
|
||||
graph.getResult().propagate("java.lang.Class");
|
||||
graph.getResult().propagate(agent.getType("java.lang.Class"));
|
||||
break;
|
||||
case "newInstance":
|
||||
checker.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",
|
||||
agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",
|
||||
ValueType.VOID), graph.getStack()).use();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -17,16 +17,12 @@ package org.teavm.classlib.java.lang;
|
|||
|
||||
import java.io.IOException;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.dependency.DependencyChecker;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.dependency.*;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
import org.teavm.javascript.ni.GeneratorContext;
|
||||
import org.teavm.javascript.ni.Injector;
|
||||
import org.teavm.javascript.ni.InjectorContext;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -62,13 +58,13 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "clone":
|
||||
method.getVariable(0).connect(method.getResult());
|
||||
break;
|
||||
case "getClass":
|
||||
achieveGetClass(checker, method);
|
||||
achieveGetClass(agent, method);
|
||||
break;
|
||||
case "wrap":
|
||||
method.getVariable(1).connect(method.getResult());
|
||||
|
@ -87,12 +83,10 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
|
|||
writer.append(".constructor)");
|
||||
}
|
||||
|
||||
private void achieveGetClass(DependencyChecker checker, MethodDependency method) {
|
||||
String classClass = "java.lang.Class";
|
||||
MethodReference initMethod = new MethodReference(classClass, new MethodDescriptor("createNew",
|
||||
ValueType.object(classClass)));
|
||||
checker.addEntryPoint(initMethod);
|
||||
method.getResult().propagate("java.lang.Class");
|
||||
private void achieveGetClass(DependencyAgent agent, MethodDependency method) {
|
||||
MethodReference initMethod = new MethodReference(Class.class, "createNew", Class.class);
|
||||
agent.linkMethod(initMethod, method.getStack()).use();
|
||||
method.getResult().propagate(agent.getType("java.lang.Class"));
|
||||
}
|
||||
|
||||
private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.teavm.dependency.DependencyChecker;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.javascript.ni.Injector;
|
||||
|
@ -29,7 +29,7 @@ import org.teavm.model.MethodReference;
|
|||
*/
|
||||
public class StringNativeGenerator implements Injector, DependencyPlugin {
|
||||
@Override
|
||||
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "wrap":
|
||||
method.getVariable(1).connect(method.getResult());
|
||||
|
|
|
@ -54,16 +54,16 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "doArrayCopy":
|
||||
achieveArrayCopy(method);
|
||||
break;
|
||||
case "setOut":
|
||||
achieveSetOut(checker, method);
|
||||
achieveSetOut(agent, method);
|
||||
break;
|
||||
case "setErr":
|
||||
achieveSetErr(checker, method);
|
||||
achieveSetErr(agent, method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -97,13 +97,13 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
|||
src.getArrayItem().connect(dest.getArrayItem());
|
||||
}
|
||||
|
||||
private void achieveSetErr(DependencyChecker checker, MethodDependency method) {
|
||||
FieldDependency fieldDep = checker.linkField(new FieldReference("java.lang.System", "err"), method.getStack());
|
||||
private void achieveSetErr(DependencyAgent agent, MethodDependency method) {
|
||||
FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "err"), method.getStack());
|
||||
method.getVariable(1).connect(fieldDep.getValue());
|
||||
}
|
||||
|
||||
private void achieveSetOut(DependencyChecker checker, MethodDependency method) {
|
||||
FieldDependency fieldDep = checker.linkField(new FieldReference("java.lang.System", "out"), method.getStack());
|
||||
private void achieveSetOut(DependencyAgent agent, MethodDependency method) {
|
||||
FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "out"), method.getStack());
|
||||
method.getVariable(1).connect(fieldDep.getValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,17 +82,14 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
|
|||
return this;
|
||||
}
|
||||
ensureCapacity(length + string.length());
|
||||
if (index < length) {
|
||||
for (int i = length - 1; i >= index; --i) {
|
||||
buffer[i + string.length()] = buffer[i];
|
||||
}
|
||||
length += string.length();
|
||||
for (int i = length - 1; i >= index; --i) {
|
||||
buffer[i + string.length()] = buffer[i];
|
||||
}
|
||||
length += string.length();
|
||||
int j = index;
|
||||
for (int i = 0; i < string.length(); ++i) {
|
||||
buffer[j++] = string.charAt(i);
|
||||
}
|
||||
length = j;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TArithmeticException extends TRuntimeException {
|
||||
private static final long serialVersionUID = 8084592456171302650L;
|
||||
|
||||
public TArithmeticException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TArithmeticException(TString message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -163,4 +163,12 @@ public class TClass<T> extends TObject {
|
|||
@GeneratedBy(ClassNativeGenerator.class)
|
||||
@PluggableDependency(ClassNativeGenerator.class)
|
||||
public native TClass<?> getDeclaringClass();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <U> TClass<? extends U> asSubclass(TClass<U> clazz) {
|
||||
if (!clazz.isAssignableFrom(this)) {
|
||||
throw new TClassCastException();
|
||||
}
|
||||
return (TClass<? extends U>)this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
doubleMantissa = abs * 0x1p1022 * binaryExponent(negExp - 1022);
|
||||
}
|
||||
long mantissa = (long)(doubleMantissa + 0.5) & 0xFFFFFFFFFFFFFL;
|
||||
return mantissa | ((exp + 1023L) << 52) | (value < 0 ? (1L << 63) : 0);
|
||||
return mantissa | ((exp + 1023L) << 52) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1L << 63) : 0);
|
||||
}
|
||||
|
||||
public static double longBitsToDouble(long bits) {
|
||||
|
|
|
@ -263,7 +263,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
doubleMantissa = abs * 0x1p126f * binaryExponent(negExp - 126);
|
||||
}
|
||||
int mantissa = (int)(doubleMantissa + 0.5f) & 0x7FFFFF;
|
||||
return mantissa | ((exp + 127) << 23) | (value < 0 ? (1 << 31) : 0);
|
||||
return mantissa | ((exp + 127) << 23) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1 << 31) : 0);
|
||||
}
|
||||
|
||||
public static float intBitsToFloat(int bits) {
|
||||
|
|
|
@ -79,6 +79,9 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
|
|||
break;
|
||||
}
|
||||
int value = 0;
|
||||
if (index == s.length()) {
|
||||
throw new TNumberFormatException();
|
||||
}
|
||||
while (index < s.length()) {
|
||||
int digit = TCharacter.getNumericValue(s.charAt(index++));
|
||||
if (digit < 0) {
|
||||
|
|
|
@ -190,7 +190,7 @@ public class TLong extends TNumber implements TComparable<TLong> {
|
|||
return toString(i, 2);
|
||||
}
|
||||
|
||||
public TString toString(long value) {
|
||||
public static TString toString(long value) {
|
||||
return TString.wrap(new TStringBuilder().append(value).toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -152,11 +152,11 @@ public final class TMath extends TObject {
|
|||
}
|
||||
|
||||
public static double ulp(double d) {
|
||||
return pow(1, -getExponent(d) - 52);
|
||||
return pow(2, getExponent(d) - 52);
|
||||
}
|
||||
|
||||
public static float ulp(float d) {
|
||||
return (float)pow(1, -getExponent(d) - 23);
|
||||
return (float)pow(2, getExponent(d) - 23);
|
||||
}
|
||||
|
||||
public static double signum(double d) {
|
||||
|
@ -213,6 +213,7 @@ public final class TMath extends TObject {
|
|||
int exp = 0;
|
||||
double[] exponents = ExponentConstants.exponents;
|
||||
double[] negativeExponents = ExponentConstants.negativeExponents;
|
||||
double[] negativeExponents2 = ExponentConstants.negativeExponents2;
|
||||
if (d > 1) {
|
||||
int expBit = 1 << (exponents.length - 1);
|
||||
for (int i = exponents.length - 1; i >= 0; --i) {
|
||||
|
@ -225,12 +226,12 @@ public final class TMath extends TObject {
|
|||
} else if (d < 1) {
|
||||
int expBit = 1 << (negativeExponents.length - 1);
|
||||
int offset = 0;
|
||||
if (d <= 0x1p-1023) {
|
||||
if (d < 0x1p-1022) {
|
||||
d *= 0x1p52;
|
||||
offset = 52;
|
||||
}
|
||||
for (int i = negativeExponents.length - 1; i >= 0; --i) {
|
||||
if (d <= negativeExponents[i]) {
|
||||
for (int i = negativeExponents2.length - 1; i >= 0; --i) {
|
||||
if (d < negativeExponents2[i]) {
|
||||
d *= exponents[i];
|
||||
exp |= expBit;
|
||||
}
|
||||
|
@ -246,6 +247,7 @@ public final class TMath extends TObject {
|
|||
int exp = 0;
|
||||
float[] exponents = FloatExponents.exponents;
|
||||
float[] negativeExponents = FloatExponents.negativeExponents;
|
||||
float[] negativeExponents2 = FloatExponents.negativeExponents2;
|
||||
if (f > 1) {
|
||||
int expBit = 1 << (exponents.length - 1);
|
||||
for (int i = exponents.length - 1; i >= 0; --i) {
|
||||
|
@ -258,12 +260,12 @@ public final class TMath extends TObject {
|
|||
} else if (f < 1) {
|
||||
int expBit = 1 << (negativeExponents.length - 1);
|
||||
int offset = 0;
|
||||
if (f <= 0x1p-127) {
|
||||
if (f < 0x1p-126) {
|
||||
f *= 0x1p23f;
|
||||
offset = 23;
|
||||
}
|
||||
for (int i = negativeExponents.length - 1; i >= 0; --i) {
|
||||
if (f <= negativeExponents[i]) {
|
||||
for (int i = negativeExponents2.length - 1; i >= 0; --i) {
|
||||
if (f < negativeExponents2[i]) {
|
||||
f *= exponents[i];
|
||||
exp |= expBit;
|
||||
}
|
||||
|
@ -281,9 +283,9 @@ public final class TMath extends TObject {
|
|||
return direction > start ? start + ulp(start) : start - ulp(start);
|
||||
}
|
||||
|
||||
public static float nextAfter(float start, float direction) {
|
||||
public static float nextAfter(float start, double direction) {
|
||||
if (start == direction) {
|
||||
return direction;
|
||||
return start;
|
||||
}
|
||||
return direction > start ? start + ulp(start) : start - ulp(start);
|
||||
}
|
||||
|
@ -301,11 +303,15 @@ public final class TMath extends TObject {
|
|||
0x1p256, 0x1p512 };
|
||||
public static double[] negativeExponents = { 0x1p-1, 0x1p-2, 0x1p-4, 0x1p-8, 0x1p-16, 0x1p-32,
|
||||
0x1p-64, 0x1p-128, 0x1p-256, 0x1p-512 };
|
||||
public static double[] negativeExponents2 = { 0x1p-0, 0x1p-1, 0x1p-3, 0x1p-7, 0x1p-15, 0x1p-31,
|
||||
0x1p-63, 0x1p-127, 0x1p-255, 0x1p-511 };
|
||||
}
|
||||
|
||||
private static class FloatExponents {
|
||||
public static float[] exponents = { 0x1p1f, 0x1p2f, 0x1p4f, 0x1p8f, 0x1p16f, 0x1p32f, 0x1p64f };
|
||||
public static float[] negativeExponents = { 0x1p-1f, 0x1p-2f, 0x1p-4f, 0x1p-8f, 0x1p-16f, 0x1p-32f,
|
||||
0x1p-64f };
|
||||
0x1p-64f };
|
||||
public static float[] negativeExponents2 = { 0x1p-0f, 0x1p-1f, 0x1p-3f, 0x1p-7f, 0x1p-15f, 0x1p-31f,
|
||||
0x1p-63f };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public final class TStrictMath extends TObject {
|
||||
public static double E = 2.71828182845904523536;
|
||||
public static double PI = 3.14159265358979323846;
|
||||
|
||||
private TStrictMath() {
|
||||
}
|
||||
|
||||
public static double sin(double a) {
|
||||
return TMath.sin(a);
|
||||
}
|
||||
|
||||
public static double cos(double a) {
|
||||
return TMath.cos(a);
|
||||
}
|
||||
|
||||
public static double tan(double a) {
|
||||
return TMath.tan(a);
|
||||
}
|
||||
|
||||
public static double asin(double a) {
|
||||
return TMath.asin(a);
|
||||
}
|
||||
|
||||
public static double acos(double a) {
|
||||
return TMath.acos(a);
|
||||
}
|
||||
|
||||
public static double atan(double a) {
|
||||
return TMath.atan(a);
|
||||
}
|
||||
|
||||
public static double toRadians(double angdeg) {
|
||||
return TMath.toRadians(angdeg);
|
||||
}
|
||||
|
||||
public static double toDegrees(double angrad) {
|
||||
return TMath.toDegrees(angrad);
|
||||
}
|
||||
|
||||
public static double exp(double a) {
|
||||
return TMath.exp(a);
|
||||
}
|
||||
|
||||
public static double log(double a) {
|
||||
return TMath.log(a);
|
||||
}
|
||||
|
||||
public static double log10(double a) {
|
||||
return TMath.log10(a);
|
||||
}
|
||||
|
||||
public static double sqrt(double a) {
|
||||
return TMath.sqrt(a);
|
||||
}
|
||||
|
||||
public static double cbrt(double a) {
|
||||
return TMath.cbrt(a);
|
||||
}
|
||||
|
||||
public static double IEEEremainder(double f1, double f2) {
|
||||
return TMath.IEEEremainder(f1, f2);
|
||||
}
|
||||
|
||||
public static double ceil(double a) {
|
||||
return TMath.ceil(a);
|
||||
}
|
||||
|
||||
public static double floor(double a) {
|
||||
return TMath.floor(a);
|
||||
}
|
||||
|
||||
public static double rint(double a) {
|
||||
return TMath.rint(a);
|
||||
}
|
||||
|
||||
public static double atan2(double y, double x) {
|
||||
return TMath.atan2(y, x);
|
||||
}
|
||||
|
||||
public static double pow(double a, double b) {
|
||||
return TMath.pow(a, b);
|
||||
}
|
||||
|
||||
public static int round(float a) {
|
||||
return TMath.round(a);
|
||||
}
|
||||
|
||||
public static long round(double a) {
|
||||
return TMath.round(a);
|
||||
}
|
||||
|
||||
public static double random() {
|
||||
return TMath.random();
|
||||
}
|
||||
|
||||
public static int abs(int a) {
|
||||
return TMath.abs(a);
|
||||
}
|
||||
|
||||
public static long abs(long a) {
|
||||
return TMath.abs(a);
|
||||
}
|
||||
|
||||
public static float abs(float a) {
|
||||
return TMath.abs(a);
|
||||
}
|
||||
|
||||
public static double abs(double a) {
|
||||
return TMath.abs(a);
|
||||
}
|
||||
|
||||
public static int max(int a, int b) {
|
||||
return TMath.max(a, b);
|
||||
}
|
||||
|
||||
public static long max(long a, long b) {
|
||||
return TMath.max(a, b);
|
||||
}
|
||||
|
||||
public static float max(float a, float b) {
|
||||
return TMath.max(a, b);
|
||||
}
|
||||
|
||||
public static double max(double a, double b) {
|
||||
return TMath.max(a, b);
|
||||
}
|
||||
|
||||
public static int min(int a, int b) {
|
||||
return TMath.min(a, b);
|
||||
}
|
||||
|
||||
public static long min(long a, long b) {
|
||||
return TMath.min(a, b);
|
||||
}
|
||||
|
||||
public static float min(float a, float b) {
|
||||
return TMath.min(a, b);
|
||||
}
|
||||
|
||||
public static double min(double a, double b) {
|
||||
return TMath.min(a, b);
|
||||
}
|
||||
|
||||
public static double ulp(double d) {
|
||||
return TMath.ulp(d);
|
||||
}
|
||||
|
||||
public static float ulp(float f) {
|
||||
return TMath.ulp(f);
|
||||
}
|
||||
|
||||
public static double signum(double d) {
|
||||
return TMath.signum(d);
|
||||
}
|
||||
|
||||
public static float signum(float f) {
|
||||
return TMath.signum(f);
|
||||
}
|
||||
|
||||
public static double sinh(double x) {
|
||||
return TMath.sinh(x);
|
||||
}
|
||||
|
||||
public static double cosh(double x) {
|
||||
return TMath.cosh(x);
|
||||
}
|
||||
|
||||
public static double tanh(double x) {
|
||||
return TMath.tanh(x);
|
||||
}
|
||||
|
||||
public static double hypot(double x, double y) {
|
||||
return TMath.hypot(x, y);
|
||||
}
|
||||
|
||||
public static double expm1(double x) {
|
||||
return TMath.expm1(x);
|
||||
}
|
||||
|
||||
public static double log1p(double x) {
|
||||
return TMath.log1p(x);
|
||||
}
|
||||
|
||||
public static double copySign(double magnitude, double sign) {
|
||||
return TMath.copySign(magnitude, sign);
|
||||
}
|
||||
|
||||
public static float copySign(float magnitude, float sign) {
|
||||
return TMath.copySign(magnitude, sign);
|
||||
}
|
||||
|
||||
public static int getExponent(float f) {
|
||||
return TMath.getExponent(f);
|
||||
}
|
||||
|
||||
public static int getExponent(double d) {
|
||||
return TMath.getExponent(d);
|
||||
}
|
||||
|
||||
public static double nextAfter(double start, double direction) {
|
||||
return TMath.nextAfter(start, direction);
|
||||
}
|
||||
|
||||
public static float nextAfter(float start, double direction) {
|
||||
return TMath.nextAfter(start, direction);
|
||||
}
|
||||
|
||||
public static double nextUp(double d) {
|
||||
return TMath.nextUp(d);
|
||||
}
|
||||
|
||||
public static float nextUp(float f) {
|
||||
return TMath.nextUp(f);
|
||||
}
|
||||
}
|
|
@ -400,7 +400,7 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
|||
int sz = length() - target.length();
|
||||
int i = 0;
|
||||
outer:
|
||||
for (; i < sz; ++i) {
|
||||
for (; i <= sz; ++i) {
|
||||
for (int j = 0; j < target.length(); ++j) {
|
||||
if (charAt(i + j) != target.charAt(j)) {
|
||||
sb.append(charAt(i));
|
||||
|
@ -556,13 +556,8 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
|||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
hashCode ^= 734262231;
|
||||
for (char c : characters) {
|
||||
hashCode = (hashCode << 4) | (hashCode >>> 28);
|
||||
hashCode ^= 347236277 ^ c;
|
||||
if (hashCode == 0) {
|
||||
++hashCode;
|
||||
}
|
||||
hashCode = 31 * hashCode + c;
|
||||
}
|
||||
}
|
||||
return hashCode;
|
||||
|
|
|
@ -77,4 +77,8 @@ public class TThread extends TObject implements TRunnable {
|
|||
public long getId() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static boolean holdsLock(@SuppressWarnings("unused") TObject obj) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang.ref;
|
||||
|
||||
import org.teavm.classlib.java.lang.TObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public abstract class TReference<T> extends TObject {
|
||||
public T get() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
public boolean isEnqueued() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean enqueue() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang.ref;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TWeakReference<T> extends TReference<T> {
|
||||
private T value;
|
||||
|
||||
public TWeakReference(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
value = null;
|
||||
}
|
||||
}
|
|
@ -17,10 +17,7 @@ package org.teavm.classlib.java.lang.reflect;
|
|||
|
||||
import java.io.IOException;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.dependency.DependencyChecker;
|
||||
import org.teavm.dependency.DependencyConsumer;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.dependency.*;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
import org.teavm.javascript.ni.GeneratorContext;
|
||||
import org.teavm.model.ClassReader;
|
||||
|
@ -41,16 +38,16 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
|||
ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
|
||||
|
||||
@Override
|
||||
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "getLength":
|
||||
achieveGetLength(checker, method);
|
||||
achieveGetLength(agent, method);
|
||||
break;
|
||||
case "newInstanceImpl":
|
||||
method.getResult().propagate("[java.lang.Object");
|
||||
method.getResult().propagate(agent.getType("[java.lang.Object"));
|
||||
break;
|
||||
case "getImpl":
|
||||
achieveGet(checker, method);
|
||||
achieveGet(agent, method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -81,13 +78,12 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
|||
writer.append("return " + array + ".data.length;").softNewLine();
|
||||
}
|
||||
|
||||
private void achieveGetLength(final DependencyChecker checker, final MethodDependency method) {
|
||||
private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) {
|
||||
method.getVariable(1).addConsumer(new DependencyConsumer() {
|
||||
@Override public void consume(String type) {
|
||||
if (!type.startsWith("[")) {
|
||||
MethodReference cons = new MethodReference("java.lang.IllegalArgumentException",
|
||||
new MethodDescriptor("<init>", ValueType.VOID));
|
||||
checker.addEntryPoint(cons);
|
||||
@Override public void consume(DependencyAgentType type) {
|
||||
if (!type.getName().startsWith("[")) {
|
||||
MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", void.class);
|
||||
agent.linkMethod(cons, method.getStack()).use();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -129,19 +125,19 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
|||
writer.outdent().append("}").softNewLine();
|
||||
}
|
||||
|
||||
private void achieveGet(final DependencyChecker checker, final MethodDependency method) {
|
||||
private void achieveGet(final DependencyAgent agent, final MethodDependency method) {
|
||||
method.getVariable(1).getArrayItem().connect(method.getResult());
|
||||
method.getVariable(1).addConsumer(new DependencyConsumer() {
|
||||
@Override public void consume(String type) {
|
||||
if (type.startsWith("[")) {
|
||||
type = type.substring(1);
|
||||
@Override public void consume(DependencyAgentType type) {
|
||||
if (type.getName().startsWith("[")) {
|
||||
String typeName = type.getName().substring(1);
|
||||
for (int i = 0; i < primitiveTypes.length; ++i) {
|
||||
if (primitiveTypes[i].toString().equals(type)) {
|
||||
if (primitiveTypes[i].toString().equals(typeName)) {
|
||||
String wrapper = "java.lang." + primitiveWrappers[i];
|
||||
MethodReference methodRef = new MethodReference(wrapper, "valueOf",
|
||||
primitiveTypes[i], ValueType.object(wrapper));
|
||||
checker.linkMethod(methodRef, method.getStack()).use();
|
||||
method.getResult().propagate("java.lang." + primitiveWrappers[i]);
|
||||
agent.linkMethod(methodRef, method.getStack()).use();
|
||||
method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* Static library that provides all the <b>bit level</b> operations for
|
||||
* {@link TBigInteger}. The operations are:
|
||||
* <ul type="circle">
|
||||
* <li>Left Shifting</li>
|
||||
* <li>Right Shifting</li>
|
||||
* <li>Bit clearing</li>
|
||||
* <li>Bit setting</li>
|
||||
* <li>Bit counting</li>
|
||||
* <li>Bit testing</li>
|
||||
* <li>Getting of the lowest bit set</li>
|
||||
* </ul>
|
||||
* All operations are provided in immutable way, and some in both mutable and
|
||||
* immutable.
|
||||
*/
|
||||
class TBitLevel {
|
||||
|
||||
/** Just to denote that this class can't be instantiated. */
|
||||
private TBitLevel() {
|
||||
}
|
||||
|
||||
/** @see TBigInteger#bitLength() */
|
||||
static int bitLength(TBigInteger val) {
|
||||
if (val.sign == 0) {
|
||||
return 0;
|
||||
}
|
||||
int bLength = (val.numberLength << 5);
|
||||
int highDigit = val.digits[val.numberLength - 1];
|
||||
|
||||
if (val.sign < 0) {
|
||||
int i = val.getFirstNonzeroDigit();
|
||||
// We reduce the problem to the positive case.
|
||||
if (i == val.numberLength - 1) {
|
||||
highDigit--;
|
||||
}
|
||||
}
|
||||
// Subtracting all sign bits
|
||||
bLength -= Integer.numberOfLeadingZeros(highDigit);
|
||||
return bLength;
|
||||
}
|
||||
|
||||
/** @see TBigInteger#bitCount() */
|
||||
static int bitCount(TBigInteger val) {
|
||||
int bCount = 0;
|
||||
|
||||
if (val.sign == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = val.getFirstNonzeroDigit();
|
||||
if (val.sign > 0) {
|
||||
for (; i < val.numberLength; i++) {
|
||||
bCount += Integer.bitCount(val.digits[i]);
|
||||
}
|
||||
} else {// (sign < 0)
|
||||
// this digit absorbs the carry
|
||||
bCount += Integer.bitCount(-val.digits[i]);
|
||||
for (i++; i < val.numberLength; i++) {
|
||||
bCount += Integer.bitCount(~val.digits[i]);
|
||||
}
|
||||
// We take the complement sum:
|
||||
bCount = (val.numberLength << 5) - bCount;
|
||||
}
|
||||
return bCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a fast bit testing for positive numbers. The bit to to be tested
|
||||
* must be in the range {@code [0, val.bitLength()-1]}
|
||||
*/
|
||||
static boolean testBit(TBigInteger val, int n) {
|
||||
// PRE: 0 <= n < val.bitLength()
|
||||
return ((val.digits[n >> 5] & (1 << (n & 31))) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are 1s in the lowest bits of this BigInteger
|
||||
*
|
||||
* @param numberOfBits
|
||||
* the number of the lowest bits to check
|
||||
* @return false if all bits are 0s, true otherwise
|
||||
*/
|
||||
static boolean nonZeroDroppedBits(int numberOfBits, int digits[]) {
|
||||
int intCount = numberOfBits >> 5;
|
||||
int bitCount = numberOfBits & 31;
|
||||
int i;
|
||||
|
||||
for (i = 0; (i < intCount) && (digits[i] == 0); i++) {
|
||||
// do nothing
|
||||
}
|
||||
return ((i != intCount) || (digits[i] << (32 - bitCount) != 0));
|
||||
}
|
||||
|
||||
/** @see TBigInteger#shiftLeft(int) */
|
||||
static TBigInteger shiftLeft(TBigInteger source, int count) {
|
||||
int intCount = count >> 5;
|
||||
count &= 31; // %= 32
|
||||
int resLength = source.numberLength + intCount + ((count == 0) ? 0 : 1);
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
shiftLeft(resDigits, source.digits, intCount, count);
|
||||
TBigInteger result = new TBigInteger(source.sign, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code val <<= count}.
|
||||
*/
|
||||
// val should have enough place (and one digit more)
|
||||
static void inplaceShiftLeft(TBigInteger val, int count) {
|
||||
int intCount = count >> 5; // count of integers
|
||||
val.numberLength += intCount +
|
||||
(Integer.numberOfLeadingZeros(val.digits[val.numberLength - 1]) - (count & 31) >= 0 ? 0 : 1);
|
||||
shiftLeft(val.digits, val.digits, intCount, count & 31);
|
||||
val.cutOffLeadingZeroes();
|
||||
val.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstractly shifts left an array of integers in little endian (i.e. shift
|
||||
* it right). Total shift distance in bits is intCount * 32 + count
|
||||
*
|
||||
* @param result
|
||||
* the destination array
|
||||
* @param source
|
||||
* the source array
|
||||
* @param intCount
|
||||
* the shift distance in integers
|
||||
* @param count
|
||||
* an additional shift distance in bits
|
||||
*/
|
||||
static void shiftLeft(int result[], int source[], int intCount, int count) {
|
||||
if (count == 0) {
|
||||
System.arraycopy(source, 0, result, intCount, result.length - intCount);
|
||||
} else {
|
||||
int rightShiftCount = 32 - count;
|
||||
|
||||
result[result.length - 1] = 0;
|
||||
for (int i = result.length - 1; i > intCount; i--) {
|
||||
result[i] |= source[i - intCount - 1] >>> rightShiftCount;
|
||||
result[i - 1] = source[i - intCount - 1] << count;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < intCount; i++) {
|
||||
result[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts the source digits left one bit, creating a value whose magnitude
|
||||
* is doubled.
|
||||
*
|
||||
* @param result
|
||||
* an array of digits that will hold the computed result when
|
||||
* this method returns. The size of this array is
|
||||
* {@code srcLen + 1}, and the format is the same as
|
||||
* {@link TBigInteger#digits}.
|
||||
* @param source
|
||||
* the array of digits to shift left, in the same format as
|
||||
* {@link TBigInteger#digits}.
|
||||
* @param srcLen
|
||||
* the length of {@code source}; may be less than
|
||||
* {@code source.length}
|
||||
*/
|
||||
static void shiftLeftOneBit(int result[], int source[], int srcLen) {
|
||||
int carry = 0;
|
||||
for (int i = 0; i < srcLen; i++) {
|
||||
int val = source[i];
|
||||
result[i] = (val << 1) | carry;
|
||||
carry = val >>> 31;
|
||||
}
|
||||
if (carry != 0) {
|
||||
result[srcLen] = carry;
|
||||
}
|
||||
}
|
||||
|
||||
static TBigInteger shiftLeftOneBit(TBigInteger source) {
|
||||
int srcLen = source.numberLength;
|
||||
int resLen = srcLen + 1;
|
||||
int resDigits[] = new int[resLen];
|
||||
shiftLeftOneBit(resDigits, source.digits, srcLen);
|
||||
TBigInteger result = new TBigInteger(source.sign, resLen, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @see TBigInteger#shiftRight(int) */
|
||||
static TBigInteger shiftRight(TBigInteger source, int count) {
|
||||
int intCount = count >> 5; // count of integers
|
||||
count &= 31; // count of remaining bits
|
||||
if (intCount >= source.numberLength) {
|
||||
return ((source.sign < 0) ? TBigInteger.MINUS_ONE : TBigInteger.ZERO);
|
||||
}
|
||||
int i;
|
||||
int resLength = source.numberLength - intCount;
|
||||
int resDigits[] = new int[resLength + 1];
|
||||
|
||||
shiftRight(resDigits, resLength, source.digits, intCount, count);
|
||||
if (source.sign < 0) {
|
||||
// Checking if the dropped bits are zeros (the remainder equals to
|
||||
// 0)
|
||||
for (i = 0; (i < intCount) && (source.digits[i] == 0); i++) {
|
||||
// do nothing
|
||||
}
|
||||
// If the remainder is not zero, add 1 to the result
|
||||
if ((i < intCount) || ((count > 0) && ((source.digits[i] << (32 - count)) != 0))) {
|
||||
for (i = 0; (i < resLength) && (resDigits[i] == -1); i++) {
|
||||
resDigits[i] = 0;
|
||||
}
|
||||
if (i == resLength) {
|
||||
resLength++;
|
||||
}
|
||||
resDigits[i]++;
|
||||
}
|
||||
}
|
||||
TBigInteger result = new TBigInteger(source.sign, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code val >>= count} where {@code val} is a positive number.
|
||||
*/
|
||||
static void inplaceShiftRight(TBigInteger val, int count) {
|
||||
int sign = val.signum();
|
||||
if (count == 0 || val.signum() == 0)
|
||||
return;
|
||||
int intCount = count >> 5; // count of integers
|
||||
val.numberLength -= intCount;
|
||||
if (!shiftRight(val.digits, val.numberLength, val.digits, intCount, count & 31) && sign < 0) {
|
||||
// remainder not zero: add one to the result
|
||||
int i;
|
||||
for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
|
||||
val.digits[i] = 0;
|
||||
}
|
||||
if (i == val.numberLength) {
|
||||
val.numberLength++;
|
||||
}
|
||||
val.digits[i]++;
|
||||
}
|
||||
val.cutOffLeadingZeroes();
|
||||
val.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts right an array of integers. Total shift distance in bits is
|
||||
* intCount * 32 + count.
|
||||
*
|
||||
* @param result
|
||||
* the destination array
|
||||
* @param resultLen
|
||||
* the destination array's length
|
||||
* @param source
|
||||
* the source array
|
||||
* @param intCount
|
||||
* the number of elements to be shifted
|
||||
* @param count
|
||||
* the number of bits to be shifted
|
||||
* @return dropped bit's are all zero (i.e. remaider is zero)
|
||||
*/
|
||||
static boolean shiftRight(int result[], int resultLen, int source[], int intCount, int count) {
|
||||
int i;
|
||||
boolean allZero = true;
|
||||
for (i = 0; i < intCount; i++) {
|
||||
allZero &= source[i] == 0;
|
||||
}
|
||||
if (count == 0) {
|
||||
System.arraycopy(source, intCount, result, 0, resultLen);
|
||||
i = resultLen;
|
||||
} else {
|
||||
int leftShiftCount = 32 - count;
|
||||
|
||||
allZero &= (source[i] << leftShiftCount) == 0;
|
||||
for (i = 0; i < resultLen - 1; i++) {
|
||||
result[i] = (source[i + intCount] >>> count) | (source[i + intCount + 1] << leftShiftCount);
|
||||
}
|
||||
result[i] = (source[i + intCount] >>> count);
|
||||
i++;
|
||||
}
|
||||
|
||||
return allZero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a flipBit on the BigInteger, returning a BigInteger with the the
|
||||
* specified bit flipped.
|
||||
*
|
||||
* @param intCount
|
||||
* : the index of the element of the digits array where the
|
||||
* operation will be performed
|
||||
* @param bitNumber
|
||||
* : the bit's position in the intCount element
|
||||
*/
|
||||
static TBigInteger flipBit(TBigInteger val, int n) {
|
||||
int resSign = (val.sign == 0) ? 1 : val.sign;
|
||||
int intCount = n >> 5;
|
||||
int bitN = n & 31;
|
||||
int resLength = Math.max(intCount + 1, val.numberLength) + 1;
|
||||
int resDigits[] = new int[resLength];
|
||||
int i;
|
||||
|
||||
int bitNumber = 1 << bitN;
|
||||
System.arraycopy(val.digits, 0, resDigits, 0, val.numberLength);
|
||||
|
||||
if (val.sign < 0) {
|
||||
if (intCount >= val.numberLength) {
|
||||
resDigits[intCount] = bitNumber;
|
||||
} else {
|
||||
// val.sign<0 y intCount < val.numberLength
|
||||
int firstNonZeroDigit = val.getFirstNonzeroDigit();
|
||||
if (intCount > firstNonZeroDigit) {
|
||||
resDigits[intCount] ^= bitNumber;
|
||||
} else if (intCount < firstNonZeroDigit) {
|
||||
resDigits[intCount] = -bitNumber;
|
||||
for (i = intCount + 1; i < firstNonZeroDigit; i++) {
|
||||
resDigits[i] = -1;
|
||||
}
|
||||
resDigits[i] = resDigits[i]--;
|
||||
} else {
|
||||
i = intCount;
|
||||
resDigits[i] = -((-resDigits[intCount]) ^ bitNumber);
|
||||
if (resDigits[i] == 0) {
|
||||
for (i++; resDigits[i] == -1; i++) {
|
||||
resDigits[i] = 0;
|
||||
}
|
||||
resDigits[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {// case where val is positive
|
||||
resDigits[intCount] ^= bitNumber;
|
||||
}
|
||||
TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* Static library that provides {@link TBigInteger} base conversion from/to any
|
||||
* integer represented in an {@link java.lang.String} Object.
|
||||
*/
|
||||
class TConversion {
|
||||
|
||||
/** Just to denote that this class can't be instantiated */
|
||||
private TConversion() {}
|
||||
|
||||
/**
|
||||
* Holds the maximal exponent for each radix, so that radix<sup>digitFitInInt[radix]</sup>
|
||||
* fit in an {@code int} (32 bits).
|
||||
*/
|
||||
static final int[] digitFitInInt = { -1, -1, 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,
|
||||
7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5 };
|
||||
|
||||
/**
|
||||
* bigRadices values are precomputed maximal powers of radices (integer
|
||||
* numbers from 2 to 36) that fit into unsigned int (32 bits). bigRadices[0] =
|
||||
* 2 ^ 31, bigRadices[8] = 10 ^ 9, etc.
|
||||
*/
|
||||
|
||||
static final int bigRadices[] = { -2147483648, 1162261467, 1073741824, 1220703125, 362797056, 1977326743,
|
||||
1073741824, 387420489, 1000000000, 214358881, 429981696, 815730721, 1475789056, 170859375, 268435456,
|
||||
410338673, 612220032, 893871739, 1280000000, 1801088541, 113379904, 148035889, 191102976, 244140625,
|
||||
308915776, 387420489, 481890304, 594823321, 729000000, 887503681, 1073741824, 1291467969, 1544804416,
|
||||
1838265625, 60466176 };
|
||||
|
||||
|
||||
/** @see TBigInteger#toString(int) */
|
||||
static String bigInteger2String(TBigInteger val, int radix) {
|
||||
int sign = val.sign;
|
||||
int numberLength = val.numberLength;
|
||||
int digits[] = val.digits;
|
||||
|
||||
if (sign == 0) {
|
||||
return "0";
|
||||
}
|
||||
if (numberLength == 1) {
|
||||
int highDigit = digits[numberLength - 1];
|
||||
long v = highDigit & 0xFFFFFFFFL;
|
||||
if (sign < 0) {
|
||||
v = -v;
|
||||
}
|
||||
return Long.toString(v, radix);
|
||||
}
|
||||
if (radix == 10 || radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
|
||||
return val.toString();
|
||||
}
|
||||
double bitsForRadixDigit;
|
||||
bitsForRadixDigit = Math.log(radix) / Math.log(2);
|
||||
int resLengthInChars = (int) (val.abs().bitLength() / bitsForRadixDigit + ((sign < 0) ? 1 : 0)) + 1;
|
||||
|
||||
char result[] = new char[resLengthInChars];
|
||||
int currentChar = resLengthInChars;
|
||||
int resDigit;
|
||||
if (radix != 16) {
|
||||
int temp[] = new int[numberLength];
|
||||
System.arraycopy(digits, 0, temp, 0, numberLength);
|
||||
int tempLen = numberLength;
|
||||
int charsPerInt = digitFitInInt[radix];
|
||||
int i;
|
||||
// get the maximal power of radix that fits in int
|
||||
int bigRadix = bigRadices[radix - 2];
|
||||
while (true) {
|
||||
// divide the array of digits by bigRadix and convert remainders
|
||||
// to characters collecting them in the char array
|
||||
resDigit = TDivision.divideArrayByInt(temp, temp, tempLen, bigRadix);
|
||||
int previous = currentChar;
|
||||
do {
|
||||
result[--currentChar] = Character.forDigit(resDigit % radix, radix);
|
||||
} while (((resDigit /= radix) != 0) && (currentChar != 0));
|
||||
int delta = charsPerInt - previous + currentChar;
|
||||
for (i = 0; i < delta && currentChar > 0; i++) {
|
||||
result[--currentChar] = '0';
|
||||
}
|
||||
for (i = tempLen - 1; (i > 0) && (temp[i] == 0); i--) {
|
||||
// do nothing
|
||||
}
|
||||
tempLen = i + 1;
|
||||
if ((tempLen == 1) && (temp[0] == 0)) { // the quotient is 0
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// radix == 16
|
||||
for (int i = 0; i < numberLength; i++) {
|
||||
for (int j = 0; (j < 8) && (currentChar > 0); j++) {
|
||||
resDigit = digits[i] >> (j << 2) & 0xf;
|
||||
result[--currentChar] = Character.forDigit(resDigit, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (result[currentChar] == '0') {
|
||||
currentChar++;
|
||||
}
|
||||
if (sign == -1) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the correspondent {@code String} representation of {@code val}
|
||||
* being scaled by {@code scale}.
|
||||
*
|
||||
* @see TBigInteger#toString()
|
||||
* @see TBigDecimal#toString()
|
||||
*/
|
||||
static String toDecimalScaledString(TBigInteger val, int scale) {
|
||||
int sign = val.sign;
|
||||
int numberLength = val.numberLength;
|
||||
int digits[] = val.digits;
|
||||
int resLengthInChars;
|
||||
int currentChar;
|
||||
char result[];
|
||||
|
||||
if (sign == 0) {
|
||||
switch (scale) {
|
||||
case 0:
|
||||
return "0";
|
||||
case 1:
|
||||
return "0.0";
|
||||
case 2:
|
||||
return "0.00";
|
||||
case 3:
|
||||
return "0.000";
|
||||
case 4:
|
||||
return "0.0000";
|
||||
case 5:
|
||||
return "0.00000";
|
||||
case 6:
|
||||
return "0.000000";
|
||||
default:
|
||||
StringBuilder result1 = new StringBuilder();
|
||||
if (scale < 0) {
|
||||
result1.append("0E+");
|
||||
} else {
|
||||
result1.append("0E");
|
||||
}
|
||||
result1.append(-scale);
|
||||
return result1.toString();
|
||||
}
|
||||
}
|
||||
// one 32-bit unsigned value may contains 10 decimal digits
|
||||
resLengthInChars = numberLength * 10 + 1 + 7;
|
||||
// Explanation why +1+7:
|
||||
// +1 - one char for sign if needed.
|
||||
// +7 - For "special case 2" (see below) we have 7 free chars for
|
||||
// inserting necessary scaled digits.
|
||||
result = new char[resLengthInChars + 1];
|
||||
// allocated [resLengthInChars+1] characters.
|
||||
// a free latest character may be used for "special case 1" (see
|
||||
// below)
|
||||
currentChar = resLengthInChars;
|
||||
if (numberLength == 1) {
|
||||
int highDigit = digits[0];
|
||||
if (highDigit < 0) {
|
||||
long v = highDigit & 0xFFFFFFFFL;
|
||||
do {
|
||||
long prev = v;
|
||||
v /= 10;
|
||||
result[--currentChar] = (char) (0x0030 + ((int) (prev - v * 10)));
|
||||
} while (v != 0);
|
||||
} else {
|
||||
int v = highDigit;
|
||||
do {
|
||||
int prev = v;
|
||||
v /= 10;
|
||||
result[--currentChar] = (char) (0x0030 + (prev - v * 10));
|
||||
} while (v != 0);
|
||||
}
|
||||
} else {
|
||||
int temp[] = new int[numberLength];
|
||||
int tempLen = numberLength;
|
||||
System.arraycopy(digits, 0, temp, 0, tempLen);
|
||||
BIG_LOOP: while (true) {
|
||||
// divide the array of digits by bigRadix and convert
|
||||
// remainders
|
||||
// to characters collecting them in the char array
|
||||
long result11 = 0;
|
||||
for (int i1 = tempLen - 1; i1 >= 0; i1--) {
|
||||
long temp1 = (result11 << 32) + (temp[i1] & 0xFFFFFFFFL);
|
||||
long res = divideLongByBillion(temp1);
|
||||
temp[i1] = (int) res;
|
||||
result11 = (int) (res >> 32);
|
||||
}
|
||||
int resDigit = (int) result11;
|
||||
int previous = currentChar;
|
||||
do {
|
||||
result[--currentChar] = (char) (0x0030 + (resDigit % 10));
|
||||
} while ((resDigit /= 10) != 0 && currentChar != 0);
|
||||
int delta = 9 - previous + currentChar;
|
||||
for (int i = 0; (i < delta) && (currentChar > 0); i++) {
|
||||
result[--currentChar] = '0';
|
||||
}
|
||||
int j = tempLen - 1;
|
||||
for (; temp[j] == 0; j--) {
|
||||
if (j == 0) { // means temp[0] == 0
|
||||
break BIG_LOOP;
|
||||
}
|
||||
}
|
||||
tempLen = j + 1;
|
||||
}
|
||||
while (result[currentChar] == '0') {
|
||||
currentChar++;
|
||||
}
|
||||
}
|
||||
boolean negNumber = (sign < 0);
|
||||
int exponent = resLengthInChars - currentChar - scale - 1;
|
||||
if (scale == 0) {
|
||||
if (negNumber) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
if ((scale > 0) && (exponent >= -6)) {
|
||||
if (exponent >= 0) {
|
||||
// special case 1
|
||||
int insertPoint = currentChar + exponent;
|
||||
for (int j = resLengthInChars - 1; j >= insertPoint; j--) {
|
||||
result[j + 1] = result[j];
|
||||
}
|
||||
result[++insertPoint] = '.';
|
||||
if (negNumber) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar + 1);
|
||||
}
|
||||
// special case 2
|
||||
for (int j = 2; j < -exponent + 1; j++) {
|
||||
result[--currentChar] = '0';
|
||||
}
|
||||
result[--currentChar] = '.';
|
||||
result[--currentChar] = '0';
|
||||
if (negNumber) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
int startPoint = currentChar + 1;
|
||||
int endPoint = resLengthInChars;
|
||||
StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
|
||||
if (negNumber) {
|
||||
result1.append('-');
|
||||
}
|
||||
if (endPoint - startPoint >= 1) {
|
||||
result1.append(result[currentChar]);
|
||||
result1.append('.');
|
||||
result1.append(result, currentChar + 1, resLengthInChars - currentChar - 1);
|
||||
} else {
|
||||
result1.append(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
result1.append('E');
|
||||
if (exponent > 0) {
|
||||
result1.append('+');
|
||||
}
|
||||
result1.append(Integer.toString(exponent));
|
||||
return result1.toString();
|
||||
}
|
||||
|
||||
/* can process only 32-bit numbers */
|
||||
static String toDecimalScaledString(long value, int scale) {
|
||||
int resLengthInChars;
|
||||
int currentChar;
|
||||
char result[];
|
||||
boolean negNumber = value < 0;
|
||||
if(negNumber) {
|
||||
value = -value;
|
||||
}
|
||||
if (value == 0) {
|
||||
switch (scale) {
|
||||
case 0: return "0";
|
||||
case 1: return "0.0";
|
||||
case 2: return "0.00";
|
||||
case 3: return "0.000";
|
||||
case 4: return "0.0000";
|
||||
case 5: return "0.00000";
|
||||
case 6: return "0.000000";
|
||||
default:
|
||||
StringBuilder result1 = new StringBuilder();
|
||||
if (scale < 0) {
|
||||
result1.append("0E+");
|
||||
} else {
|
||||
result1.append("0E");
|
||||
}
|
||||
result1.append((scale == Integer.MIN_VALUE) ? "2147483648" : Integer.toString(-scale));
|
||||
return result1.toString();
|
||||
}
|
||||
}
|
||||
// one 32-bit unsigned value may contains 10 decimal digits
|
||||
resLengthInChars = 18;
|
||||
// Explanation why +1+7:
|
||||
// +1 - one char for sign if needed.
|
||||
// +7 - For "special case 2" (see below) we have 7 free chars for
|
||||
// inserting necessary scaled digits.
|
||||
result = new char[resLengthInChars+1];
|
||||
// Allocated [resLengthInChars+1] characters.
|
||||
// a free latest character may be used for "special case 1" (see below)
|
||||
currentChar = resLengthInChars;
|
||||
long v = value;
|
||||
do {
|
||||
long prev = v;
|
||||
v /= 10;
|
||||
result[--currentChar] = (char) (0x0030 + (prev - v * 10));
|
||||
} while (v != 0);
|
||||
|
||||
long exponent = (long)resLengthInChars - (long)currentChar - scale - 1L;
|
||||
if (scale == 0) {
|
||||
if (negNumber) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
if (scale > 0 && exponent >= -6) {
|
||||
if (exponent >= 0) {
|
||||
// special case 1
|
||||
int insertPoint = currentChar + (int)exponent;
|
||||
for(int j = resLengthInChars - 1; j >= insertPoint; j--) {
|
||||
result[j + 1] = result[j];
|
||||
}
|
||||
result[++insertPoint] = '.';
|
||||
if (negNumber) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar + 1);
|
||||
}
|
||||
// special case 2
|
||||
for (int j = 2; j < -exponent + 1; j++) {
|
||||
result[--currentChar] = '0';
|
||||
}
|
||||
result[--currentChar] = '.';
|
||||
result[--currentChar] = '0';
|
||||
if (negNumber) {
|
||||
result[--currentChar] = '-';
|
||||
}
|
||||
return new String(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
int startPoint = currentChar + 1;
|
||||
int endPoint = resLengthInChars;
|
||||
StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
|
||||
if (negNumber) {
|
||||
result1.append('-');
|
||||
}
|
||||
if (endPoint - startPoint >= 1) {
|
||||
result1.append(result[currentChar]);
|
||||
result1.append('.');
|
||||
result1.append(result, currentChar+1, resLengthInChars - currentChar-1);
|
||||
} else {
|
||||
result1.append(result, currentChar, resLengthInChars - currentChar);
|
||||
}
|
||||
result1.append('E');
|
||||
if (exponent > 0) {
|
||||
result1.append('+');
|
||||
}
|
||||
result1.append(Long.toString(exponent));
|
||||
return result1.toString();
|
||||
}
|
||||
|
||||
static long divideLongByBillion(long a) {
|
||||
long quot;
|
||||
long rem;
|
||||
|
||||
if (a >= 0) {
|
||||
long bLong = 1000000000L;
|
||||
quot = (a / bLong);
|
||||
rem = (a % bLong);
|
||||
} else {
|
||||
/*
|
||||
* Make the dividend positive shifting it right by 1 bit then get
|
||||
* the quotient an remainder and correct them properly
|
||||
*/
|
||||
long aPos = a >>> 1;
|
||||
long bPos = 1000000000L >>> 1;
|
||||
quot = aPos / bPos;
|
||||
rem = aPos % bPos;
|
||||
// double the remainder and add 1 if 'a' is odd
|
||||
rem = (rem << 1) + (a & 1);
|
||||
}
|
||||
return (rem << 32) | (quot & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
/** @see TBigInteger#doubleValue() */
|
||||
static double bigInteger2Double(TBigInteger val) {
|
||||
// val.bitLength() < 64
|
||||
if ((val.numberLength < 2) || ((val.numberLength == 2) && (val.digits[1] > 0))) {
|
||||
return val.longValue();
|
||||
}
|
||||
// val.bitLength() >= 33 * 32 > 1024
|
||||
if (val.numberLength > 32) {
|
||||
return val.sign > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
int bitLen = val.abs().bitLength();
|
||||
long exponent = bitLen - 1;
|
||||
int delta = bitLen - 54;
|
||||
// We need 54 top bits from this, the 53th bit is always 1 in lVal.
|
||||
long lVal = val.abs().shiftRight(delta).longValue();
|
||||
/*
|
||||
* Take 53 bits from lVal to mantissa. The least significant bit is
|
||||
* needed for rounding.
|
||||
*/
|
||||
long mantissa = lVal & 0x1FFFFFFFFFFFFFL;
|
||||
if (exponent == 1023) {
|
||||
if (mantissa == 0X1FFFFFFFFFFFFFL) {
|
||||
return val.sign > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
if (mantissa == 0x1FFFFFFFFFFFFEL) {
|
||||
return val.sign > 0 ? Double.MAX_VALUE : -Double.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
// Round the mantissa
|
||||
if ((mantissa & 1) == 1 && (mantissa & 2) == 2 || TBitLevel.nonZeroDroppedBits(delta, val.digits)) {
|
||||
mantissa += 2;
|
||||
}
|
||||
mantissa >>= 1; // drop the rounding bit
|
||||
long resSign = (val.sign < 0) ? 0x8000000000000000L : 0;
|
||||
exponent = ((1023 + exponent) << 52) & 0x7FF0000000000000L;
|
||||
long result = resSign | exponent | mantissa;
|
||||
return Double.longBitsToDouble(result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,952 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* Static library that provides all operations related with division and modular
|
||||
* arithmetic to {@link TBigInteger}. Some methods are provided in both mutable
|
||||
* and immutable way. There are several variants provided listed below:
|
||||
*
|
||||
* <ul type="circle">
|
||||
* <li><b>Division</b>
|
||||
* <ul type="circle">
|
||||
* <li>{@link TBigInteger} division and remainder by {@link TBigInteger}.</li>
|
||||
* <li>{@link TBigInteger} division and remainder by {@code int}.</li>
|
||||
* <li><i>gcd</i> between {@link TBigInteger} numbers.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>Modular arithmetic </b>
|
||||
* <ul type="circle">
|
||||
* <li>Modular exponentiation between {@link TBigInteger} numbers.</li>
|
||||
* <li>Modular inverse of a {@link TBigInteger} numbers.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
class TDivision {
|
||||
|
||||
/**
|
||||
* Divides the array 'a' by the array 'b' and gets the quotient and the
|
||||
* remainder. Implements the Knuth's division algorithm. See D. Knuth, The
|
||||
* Art of Computer Programming, vol. 2. Steps D1-D8 correspond the steps in
|
||||
* the algorithm description.
|
||||
*
|
||||
* @param quot
|
||||
* the quotient
|
||||
* @param quotLength
|
||||
* the quotient's length
|
||||
* @param a
|
||||
* the dividend
|
||||
* @param aLength
|
||||
* the dividend's length
|
||||
* @param b
|
||||
* the divisor
|
||||
* @param bLength
|
||||
* the divisor's length
|
||||
* @return the remainder
|
||||
*/
|
||||
static int[] divide(int quot[], int quotLength, int a[], int aLength, int b[], int bLength) {
|
||||
|
||||
int normA[] = new int[aLength + 1]; // the normalized dividend
|
||||
// an extra byte is needed for correct shift
|
||||
int normB[] = new int[bLength + 1]; // the normalized divisor;
|
||||
int normBLength = bLength;
|
||||
/*
|
||||
* Step D1: normalize a and b and put the results to a1 and b1 the
|
||||
* normalized divisor's first digit must be >= 2^31
|
||||
*/
|
||||
int divisorShift = Integer.numberOfLeadingZeros(b[bLength - 1]);
|
||||
if (divisorShift != 0) {
|
||||
TBitLevel.shiftLeft(normB, b, 0, divisorShift);
|
||||
TBitLevel.shiftLeft(normA, a, 0, divisorShift);
|
||||
} else {
|
||||
System.arraycopy(a, 0, normA, 0, aLength);
|
||||
System.arraycopy(b, 0, normB, 0, bLength);
|
||||
}
|
||||
int firstDivisorDigit = normB[normBLength - 1];
|
||||
// Step D2: set the quotient index
|
||||
int i = quotLength - 1;
|
||||
int j = aLength;
|
||||
|
||||
while (i >= 0) {
|
||||
// Step D3: calculate a guess digit guessDigit
|
||||
int guessDigit = 0;
|
||||
if (normA[j] == firstDivisorDigit) {
|
||||
// set guessDigit to the largest unsigned int value
|
||||
guessDigit = -1;
|
||||
} else {
|
||||
long product = (((normA[j] & 0xffffffffL) << 32) + (normA[j - 1] & 0xffffffffL));
|
||||
long res = TDivision.divideLongByInt(product, firstDivisorDigit);
|
||||
guessDigit = (int) res; // the quotient of divideLongByInt
|
||||
int rem = (int) (res >> 32); // the remainder of
|
||||
// divideLongByInt
|
||||
// decrease guessDigit by 1 while leftHand > rightHand
|
||||
if (guessDigit != 0) {
|
||||
long leftHand = 0;
|
||||
long rightHand = 0;
|
||||
boolean rOverflowed = false;
|
||||
guessDigit++; // to have the proper value in the loop
|
||||
// below
|
||||
do {
|
||||
guessDigit--;
|
||||
if (rOverflowed) {
|
||||
break;
|
||||
}
|
||||
// leftHand always fits in an unsigned long
|
||||
leftHand = (guessDigit & 0xffffffffL) * (normB[normBLength - 2] & 0xffffffffL);
|
||||
/*
|
||||
* rightHand can overflow; in this case the loop
|
||||
* condition will be true in the next step of the loop
|
||||
*/
|
||||
rightHand = ((long) rem << 32) + (normA[j - 2] & 0xffffffffL);
|
||||
long longR = (rem & 0xffffffffL) + (firstDivisorDigit & 0xffffffffL);
|
||||
/*
|
||||
* checks that longR does not fit in an unsigned int;
|
||||
* this ensures that rightHand will overflow unsigned
|
||||
* long in the next step
|
||||
*/
|
||||
if (Integer.numberOfLeadingZeros((int) (longR >>> 32)) < 32) {
|
||||
rOverflowed = true;
|
||||
} else {
|
||||
rem = (int) longR;
|
||||
}
|
||||
} while (((leftHand ^ 0x8000000000000000L) > (rightHand ^ 0x8000000000000000L)));
|
||||
}
|
||||
}
|
||||
// Step D4: multiply normB by guessDigit and subtract the production
|
||||
// from normA.
|
||||
if (guessDigit != 0) {
|
||||
int borrow = TDivision.multiplyAndSubtract(normA, j - normBLength, normB, normBLength, guessDigit);
|
||||
// Step D5: check the borrow
|
||||
if (borrow != 0) {
|
||||
// Step D6: compensating addition
|
||||
guessDigit--;
|
||||
long carry = 0;
|
||||
for (int k = 0; k < normBLength; k++) {
|
||||
carry += (normA[j - normBLength + k] & 0xffffffffL) + (normB[k] & 0xffffffffL);
|
||||
normA[j - normBLength + k] = (int) carry;
|
||||
carry >>>= 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (quot != null) {
|
||||
quot[i] = guessDigit;
|
||||
}
|
||||
// Step D7
|
||||
j--;
|
||||
i--;
|
||||
}
|
||||
/*
|
||||
* Step D8: we got the remainder in normA. Denormalize it id needed
|
||||
*/
|
||||
if (divisorShift != 0) {
|
||||
// reuse normB
|
||||
TBitLevel.shiftRight(normB, normBLength, normA, 0, divisorShift);
|
||||
return normB;
|
||||
}
|
||||
System.arraycopy(normA, 0, normB, 0, bLength);
|
||||
return normA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides an array by an integer value. Implements the Knuth's division
|
||||
* algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
|
||||
*
|
||||
* @param dest
|
||||
* the quotient
|
||||
* @param src
|
||||
* the dividend
|
||||
* @param srcLength
|
||||
* the length of the dividend
|
||||
* @param divisor
|
||||
* the divisor
|
||||
* @return remainder
|
||||
*/
|
||||
static int divideArrayByInt(int dest[], int src[], final int srcLength, final int divisor) {
|
||||
|
||||
long rem = 0;
|
||||
long bLong = divisor & 0xffffffffL;
|
||||
|
||||
for (int i = srcLength - 1; i >= 0; i--) {
|
||||
long temp = (rem << 32) | (src[i] & 0xffffffffL);
|
||||
long quot;
|
||||
if (temp >= 0) {
|
||||
quot = (temp / bLong);
|
||||
rem = (temp % bLong);
|
||||
} else {
|
||||
/*
|
||||
* make the dividend positive shifting it right by 1 bit then
|
||||
* get the quotient an remainder and correct them properly
|
||||
*/
|
||||
long aPos = temp >>> 1;
|
||||
long bPos = divisor >>> 1;
|
||||
quot = aPos / bPos;
|
||||
rem = aPos % bPos;
|
||||
// double the remainder and add 1 if a is odd
|
||||
rem = (rem << 1) + (temp & 1);
|
||||
if ((divisor & 1) != 0) {
|
||||
// the divisor is odd
|
||||
if (quot <= rem) {
|
||||
rem -= quot;
|
||||
} else {
|
||||
if (quot - rem <= bLong) {
|
||||
rem += bLong - quot;
|
||||
quot -= 1;
|
||||
} else {
|
||||
rem += (bLong << 1) - quot;
|
||||
quot -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dest[i] = (int) (quot & 0xffffffffL);
|
||||
}
|
||||
return (int) rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides an array by an integer value. Implements the Knuth's division
|
||||
* algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
|
||||
*
|
||||
* @param src
|
||||
* the dividend
|
||||
* @param srcLength
|
||||
* the length of the dividend
|
||||
* @param divisor
|
||||
* the divisor
|
||||
* @return remainder
|
||||
*/
|
||||
static int remainderArrayByInt(int src[], final int srcLength, final int divisor) {
|
||||
|
||||
long result = 0;
|
||||
|
||||
for (int i = srcLength - 1; i >= 0; i--) {
|
||||
long temp = (result << 32) + (src[i] & 0xffffffffL);
|
||||
long res = divideLongByInt(temp, divisor);
|
||||
result = (int) (res >> 32);
|
||||
}
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides a <code>BigInteger</code> by a signed <code>int</code> and
|
||||
* returns the remainder.
|
||||
*
|
||||
* @param dividend
|
||||
* the BigInteger to be divided. Must be non-negative.
|
||||
* @param divisor
|
||||
* a signed int
|
||||
* @return divide % divisor
|
||||
*/
|
||||
static int remainder(TBigInteger dividend, int divisor) {
|
||||
return remainderArrayByInt(dividend.digits, dividend.numberLength, divisor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides an unsigned long a by an unsigned int b. It is supposed that the
|
||||
* most significant bit of b is set to 1, i.e. b < 0
|
||||
*
|
||||
* @param a
|
||||
* the dividend
|
||||
* @param b
|
||||
* the divisor
|
||||
* @return the long value containing the unsigned integer remainder in the
|
||||
* left half and the unsigned integer quotient in the right half
|
||||
*/
|
||||
static long divideLongByInt(long a, int b) {
|
||||
long quot;
|
||||
long rem;
|
||||
long bLong = b & 0xffffffffL;
|
||||
|
||||
if (a >= 0) {
|
||||
quot = (a / bLong);
|
||||
rem = (a % bLong);
|
||||
} else {
|
||||
/*
|
||||
* Make the dividend positive shifting it right by 1 bit then get
|
||||
* the quotient an remainder and correct them properly
|
||||
*/
|
||||
long aPos = a >>> 1;
|
||||
long bPos = b >>> 1;
|
||||
quot = aPos / bPos;
|
||||
rem = aPos % bPos;
|
||||
// double the remainder and add 1 if a is odd
|
||||
rem = (rem << 1) + (a & 1);
|
||||
if ((b & 1) != 0) { // the divisor is odd
|
||||
if (quot <= rem) {
|
||||
rem -= quot;
|
||||
} else {
|
||||
if (quot - rem <= bLong) {
|
||||
rem += bLong - quot;
|
||||
quot -= 1;
|
||||
} else {
|
||||
rem += (bLong << 1) - quot;
|
||||
quot -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (rem << 32) | (quot & 0xffffffffL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the quotient and the remainder after a division by an
|
||||
* {@code int} number.
|
||||
*
|
||||
* @return an array of the form {@code [quotient, remainder]}.
|
||||
*/
|
||||
static TBigInteger[] divideAndRemainderByInteger(TBigInteger val, int divisor, int divisorSign) {
|
||||
// res[0] is a quotient and res[1] is a remainder:
|
||||
int[] valDigits = val.digits;
|
||||
int valLen = val.numberLength;
|
||||
int valSign = val.sign;
|
||||
if (valLen == 1) {
|
||||
long a = (valDigits[0] & 0xffffffffL);
|
||||
long b = (divisor & 0xffffffffL);
|
||||
long quo = a / b;
|
||||
long rem = a % b;
|
||||
if (valSign != divisorSign) {
|
||||
quo = -quo;
|
||||
}
|
||||
if (valSign < 0) {
|
||||
rem = -rem;
|
||||
}
|
||||
return new TBigInteger[] { TBigInteger.valueOf(quo), TBigInteger.valueOf(rem) };
|
||||
}
|
||||
int quotientLength = valLen;
|
||||
int quotientSign = ((valSign == divisorSign) ? 1 : -1);
|
||||
int quotientDigits[] = new int[quotientLength];
|
||||
int remainderDigits[];
|
||||
remainderDigits = new int[] { TDivision.divideArrayByInt(quotientDigits, valDigits, valLen, divisor) };
|
||||
TBigInteger result0 = new TBigInteger(quotientSign, quotientLength, quotientDigits);
|
||||
TBigInteger result1 = new TBigInteger(valSign, 1, remainderDigits);
|
||||
result0.cutOffLeadingZeroes();
|
||||
result1.cutOffLeadingZeroes();
|
||||
return new TBigInteger[] { result0, result1 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies an array by int and subtracts it from a subarray of another
|
||||
* array.
|
||||
*
|
||||
* @param a
|
||||
* the array to subtract from
|
||||
* @param start
|
||||
* the start element of the subarray of a
|
||||
* @param b
|
||||
* the array to be multiplied and subtracted
|
||||
* @param bLen
|
||||
* the length of b
|
||||
* @param c
|
||||
* the multiplier of b
|
||||
* @return the carry element of subtraction
|
||||
*/
|
||||
static int multiplyAndSubtract(int a[], int start, int b[], int bLen, int c) {
|
||||
long carry0 = 0;
|
||||
long carry1 = 0;
|
||||
|
||||
for (int i = 0; i < bLen; i++) {
|
||||
carry0 = TMultiplication.unsignedMultAddAdd(b[i], c, (int) carry0, 0);
|
||||
carry1 = (a[start + i] & 0xffffffffL) - (carry0 & 0xffffffffL) + carry1;
|
||||
a[start + i] = (int) carry1;
|
||||
carry1 >>= 32; // -1 or 0
|
||||
carry0 >>>= 32;
|
||||
}
|
||||
|
||||
carry1 = (a[start + bLen] & 0xffffffffL) - carry0 + carry1;
|
||||
a[start + bLen] = (int) carry1;
|
||||
return (int) (carry1 >> 32); // -1 or 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param m
|
||||
* a positive modulus Return the greatest common divisor of op1
|
||||
* and op2,
|
||||
*
|
||||
* @param op1
|
||||
* must be greater than zero
|
||||
* @param op2
|
||||
* must be greater than zero
|
||||
* @see TBigInteger#gcd(TBigInteger)
|
||||
* @return {@code GCD(op1, op2)}
|
||||
*/
|
||||
static TBigInteger gcdBinary(TBigInteger op1, TBigInteger op2) {
|
||||
// PRE: (op1 > 0) and (op2 > 0)
|
||||
|
||||
/*
|
||||
* Divide both number the maximal possible times by 2 without rounding
|
||||
* gcd(2*a, 2*b) = 2 * gcd(a,b)
|
||||
*/
|
||||
int lsb1 = op1.getLowestSetBit();
|
||||
int lsb2 = op2.getLowestSetBit();
|
||||
int pow2Count = Math.min(lsb1, lsb2);
|
||||
|
||||
TBitLevel.inplaceShiftRight(op1, lsb1);
|
||||
TBitLevel.inplaceShiftRight(op2, lsb2);
|
||||
|
||||
TBigInteger swap;
|
||||
// I want op2 > op1
|
||||
if (op1.compareTo(op2) == TBigInteger.GREATER) {
|
||||
swap = op1;
|
||||
op1 = op2;
|
||||
op2 = swap;
|
||||
}
|
||||
|
||||
do { // INV: op2 >= op1 && both are odd unless op1 = 0
|
||||
|
||||
// Optimization for small operands
|
||||
// (op2.bitLength() < 64) implies by INV (op1.bitLength() < 64)
|
||||
if ((op2.numberLength == 1) || ((op2.numberLength == 2) && (op2.digits[1] > 0))) {
|
||||
op2 = TBigInteger.valueOf(TDivision.gcdBinary(op1.longValue(), op2.longValue()));
|
||||
break;
|
||||
}
|
||||
|
||||
// Implements one step of the Euclidean algorithm
|
||||
// To reduce one operand if it's much smaller than the other one
|
||||
if (op2.numberLength > op1.numberLength * 1.2) {
|
||||
op2 = op2.remainder(op1);
|
||||
if (op2.signum() != 0) {
|
||||
TBitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
|
||||
}
|
||||
} else {
|
||||
|
||||
// Use Knuth's algorithm of successive subtract and shifting
|
||||
do {
|
||||
TElementary.inplaceSubtract(op2, op1); // both are odd
|
||||
TBitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
|
||||
} while (op2.compareTo(op1) >= TBigInteger.EQUALS);
|
||||
}
|
||||
// now op1 >= op2
|
||||
swap = op2;
|
||||
op2 = op1;
|
||||
op1 = swap;
|
||||
} while (op1.sign != 0);
|
||||
return op2.shiftLeft(pow2Count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the same as {@link #gcdBinary(TBigInteger, TBigInteger)}, but
|
||||
* with numbers of 63 bits, represented in positives values of {@code long}
|
||||
* type.
|
||||
*
|
||||
* @param op1
|
||||
* a positive number
|
||||
* @param op2
|
||||
* a positive number
|
||||
* @see #gcdBinary(TBigInteger, TBigInteger)
|
||||
* @return <code>GCD(op1, op2)</code>
|
||||
*/
|
||||
static long gcdBinary(long op1, long op2) {
|
||||
// PRE: (op1 > 0) and (op2 > 0)
|
||||
int lsb1 = Long.numberOfTrailingZeros(op1);
|
||||
int lsb2 = Long.numberOfTrailingZeros(op2);
|
||||
int pow2Count = Math.min(lsb1, lsb2);
|
||||
|
||||
if (lsb1 != 0) {
|
||||
op1 >>>= lsb1;
|
||||
}
|
||||
if (lsb2 != 0) {
|
||||
op2 >>>= lsb2;
|
||||
}
|
||||
do {
|
||||
if (op1 >= op2) {
|
||||
op1 -= op2;
|
||||
op1 >>>= Long.numberOfTrailingZeros(op1);
|
||||
} else {
|
||||
op2 -= op1;
|
||||
op2 >>>= Long.numberOfTrailingZeros(op2);
|
||||
}
|
||||
} while (op1 != 0);
|
||||
return (op2 << pow2Count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a.modInverse(p) Based on: Savas, E; Koc, C "The Montgomery
|
||||
* Modular Inverse - Revised"
|
||||
*/
|
||||
static TBigInteger modInverseMontgomery(TBigInteger a, TBigInteger p) {
|
||||
|
||||
if (a.sign == 0) {
|
||||
// ZERO hasn't inverse
|
||||
throw new ArithmeticException("BigInteger not invertible");
|
||||
}
|
||||
|
||||
if (!p.testBit(0)) {
|
||||
// montgomery inverse require even modulo
|
||||
return modInverseHars(a, p);
|
||||
}
|
||||
|
||||
int m = p.numberLength * 32;
|
||||
// PRE: a \in [1, p - 1]
|
||||
TBigInteger u, v, r, s;
|
||||
u = p.copy(); // make copy to use inplace method
|
||||
v = a.copy();
|
||||
int max = Math.max(v.numberLength, u.numberLength);
|
||||
r = new TBigInteger(1, 1, new int[max + 1]);
|
||||
s = new TBigInteger(1, 1, new int[max + 1]);
|
||||
s.digits[0] = 1;
|
||||
// s == 1 && v == 0
|
||||
|
||||
int k = 0;
|
||||
|
||||
int lsbu = u.getLowestSetBit();
|
||||
int lsbv = v.getLowestSetBit();
|
||||
int toShift;
|
||||
|
||||
if (lsbu > lsbv) {
|
||||
TBitLevel.inplaceShiftRight(u, lsbu);
|
||||
TBitLevel.inplaceShiftRight(v, lsbv);
|
||||
TBitLevel.inplaceShiftLeft(r, lsbv);
|
||||
k += lsbu - lsbv;
|
||||
} else {
|
||||
TBitLevel.inplaceShiftRight(u, lsbu);
|
||||
TBitLevel.inplaceShiftRight(v, lsbv);
|
||||
TBitLevel.inplaceShiftLeft(s, lsbu);
|
||||
k += lsbv - lsbu;
|
||||
}
|
||||
|
||||
r.sign = 1;
|
||||
while (v.signum() > 0) {
|
||||
// INV v >= 0, u >= 0, v odd, u odd (except last iteration when v is
|
||||
// even (0))
|
||||
|
||||
while (u.compareTo(v) > TBigInteger.EQUALS) {
|
||||
TElementary.inplaceSubtract(u, v);
|
||||
toShift = u.getLowestSetBit();
|
||||
TBitLevel.inplaceShiftRight(u, toShift);
|
||||
TElementary.inplaceAdd(r, s);
|
||||
TBitLevel.inplaceShiftLeft(s, toShift);
|
||||
k += toShift;
|
||||
}
|
||||
|
||||
while (u.compareTo(v) <= TBigInteger.EQUALS) {
|
||||
TElementary.inplaceSubtract(v, u);
|
||||
if (v.signum() == 0)
|
||||
break;
|
||||
toShift = v.getLowestSetBit();
|
||||
TBitLevel.inplaceShiftRight(v, toShift);
|
||||
TElementary.inplaceAdd(s, r);
|
||||
TBitLevel.inplaceShiftLeft(r, toShift);
|
||||
k += toShift;
|
||||
}
|
||||
}
|
||||
if (!u.isOne()) {
|
||||
throw new ArithmeticException("BigInteger not invertible.");
|
||||
}
|
||||
if (r.compareTo(p) >= TBigInteger.EQUALS) {
|
||||
TElementary.inplaceSubtract(r, p);
|
||||
}
|
||||
|
||||
r = p.subtract(r);
|
||||
|
||||
// Have pair: ((BigInteger)r, (Integer)k) where r == a^(-1) * 2^k mod
|
||||
// (module)
|
||||
int n1 = calcN(p);
|
||||
if (k > m) {
|
||||
r = monPro(r, TBigInteger.ONE, p, n1);
|
||||
k = k - m;
|
||||
}
|
||||
|
||||
r = monPro(r, TBigInteger.getPowerOfTwo(m - k), p, n1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the first digit of the inverse
|
||||
*/
|
||||
private static int calcN(TBigInteger a) {
|
||||
long m0 = a.digits[0] & 0xFFFFFFFFL;
|
||||
long n2 = 1L; // this is a'[0]
|
||||
long powerOfTwo = 2L;
|
||||
do {
|
||||
if (((m0 * n2) & powerOfTwo) != 0) {
|
||||
n2 |= powerOfTwo;
|
||||
}
|
||||
powerOfTwo <<= 1;
|
||||
} while (powerOfTwo < 0x100000000L);
|
||||
n2 = -n2;
|
||||
return (int) (n2 & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
static TBigInteger squareAndMultiply(TBigInteger x2, TBigInteger a2, TBigInteger exponent, TBigInteger modulus,
|
||||
int n2) {
|
||||
TBigInteger res = x2;
|
||||
for (int i = exponent.bitLength() - 1; i >= 0; i--) {
|
||||
res = monPro(res, res, modulus, n2);
|
||||
if (TBitLevel.testBit(exponent, i)) {
|
||||
res = monPro(res, a2, modulus, n2);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the "Shifting Euclidean modular inverse algorithm". "Laszlo
|
||||
* Hars - Modular Inverse Algorithms Without Multiplications for
|
||||
* Cryptographic Applications"
|
||||
*
|
||||
* @see TBigInteger#modInverse(TBigInteger)
|
||||
* @param a
|
||||
* a positive number
|
||||
* @param m
|
||||
* a positive modulus
|
||||
*/
|
||||
static TBigInteger modInverseHars(TBigInteger a, TBigInteger m) {
|
||||
// PRE: (a > 0) and (m > 0)
|
||||
TBigInteger u, v, r, s, temp;
|
||||
// u = MAX(a,m), v = MIN(a,m)
|
||||
if (a.compareTo(m) == TBigInteger.LESS) {
|
||||
u = m;
|
||||
v = a;
|
||||
r = TBigInteger.ZERO;
|
||||
s = TBigInteger.ONE;
|
||||
} else {
|
||||
v = m;
|
||||
u = a;
|
||||
s = TBigInteger.ZERO;
|
||||
r = TBigInteger.ONE;
|
||||
}
|
||||
int uLen = u.bitLength();
|
||||
int vLen = v.bitLength();
|
||||
int f = uLen - vLen;
|
||||
|
||||
while (vLen > 1) {
|
||||
if (u.sign == v.sign) {
|
||||
u = u.subtract(v.shiftLeft(f));
|
||||
r = r.subtract(s.shiftLeft(f));
|
||||
} else {
|
||||
u = u.add(v.shiftLeft(f));
|
||||
r = r.add(s.shiftLeft(f));
|
||||
}
|
||||
uLen = u.abs().bitLength();
|
||||
vLen = v.abs().bitLength();
|
||||
f = uLen - vLen;
|
||||
if (f < 0) {
|
||||
// SWAP(u,v)
|
||||
temp = u;
|
||||
u = v;
|
||||
v = temp;
|
||||
// SWAP(r,s)
|
||||
temp = r;
|
||||
r = s;
|
||||
s = temp;
|
||||
|
||||
f = -f;
|
||||
vLen = uLen;
|
||||
}
|
||||
}
|
||||
if (v.sign == 0) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
if (v.sign < 0) {
|
||||
s = s.negate();
|
||||
}
|
||||
if (s.compareTo(m) == TBigInteger.GREATER) {
|
||||
return s.subtract(m);
|
||||
}
|
||||
if (s.sign < 0) {
|
||||
return s.add(m);
|
||||
}
|
||||
return s; // a^(-1) mod m
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the Montgomery modular exponentiation based in <i>The sliding
|
||||
* windows algorithm and the MongomeryReduction</i>.
|
||||
*
|
||||
* @ar.org.fitc.ref
|
||||
* "A. Menezes,P. van Oorschot, S. Vanstone - Handbook of Applied Cryptography"
|
||||
* ;
|
||||
*
|
||||
* @see #oddModPow(BigInteger, BigInteger, BigInteger)
|
||||
*/
|
||||
static TBigInteger slidingWindow(TBigInteger x2, TBigInteger a2, TBigInteger exponent, TBigInteger modulus, int n2) {
|
||||
// fill odd low pows of a2
|
||||
TBigInteger pows[] = new TBigInteger[8];
|
||||
TBigInteger res = x2;
|
||||
int lowexp;
|
||||
TBigInteger x3;
|
||||
int acc3;
|
||||
pows[0] = a2;
|
||||
|
||||
x3 = monPro(a2, a2, modulus, n2);
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
pows[i] = monPro(pows[i - 1], x3, modulus, n2);
|
||||
}
|
||||
|
||||
for (int i = exponent.bitLength() - 1; i >= 0; i--) {
|
||||
if (TBitLevel.testBit(exponent, i)) {
|
||||
lowexp = 1;
|
||||
acc3 = i;
|
||||
|
||||
for (int j = Math.max(i - 3, 0); j <= i - 1; j++) {
|
||||
if (TBitLevel.testBit(exponent, j)) {
|
||||
if (j < acc3) {
|
||||
acc3 = j;
|
||||
lowexp = (lowexp << (i - j)) ^ 1;
|
||||
} else {
|
||||
lowexp = lowexp ^ (1 << (j - acc3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = acc3; j <= i; j++) {
|
||||
res = monPro(res, res, modulus, n2);
|
||||
}
|
||||
res = monPro(pows[(lowexp - 1) >> 1], res, modulus, n2);
|
||||
i = acc3;
|
||||
} else {
|
||||
res = monPro(res, res, modulus, n2);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs modular exponentiation using the Montgomery Reduction. It
|
||||
* requires that all parameters be positive and the modulus be odd. >
|
||||
*
|
||||
* @see TBigInteger#modPow(TBigInteger, TBigInteger)
|
||||
* @see #monPro(TBigInteger, TBigInteger, TBigInteger, int)
|
||||
* @see #slidingWindow(TBigInteger, TBigInteger, TBigInteger, TBigInteger,
|
||||
* int)
|
||||
* @see #squareAndMultiply(TBigInteger, TBigInteger, TBigInteger,
|
||||
* TBigInteger, int)
|
||||
*/
|
||||
static TBigInteger oddModPow(TBigInteger base, TBigInteger exponent, TBigInteger modulus) {
|
||||
// PRE: (base > 0), (exponent > 0), (modulus > 0) and (odd modulus)
|
||||
int k = (modulus.numberLength << 5); // r = 2^k
|
||||
// n-residue of base [base * r (mod modulus)]
|
||||
TBigInteger a2 = base.shiftLeft(k).mod(modulus);
|
||||
// n-residue of base [1 * r (mod modulus)]
|
||||
TBigInteger x2 = TBigInteger.getPowerOfTwo(k).mod(modulus);
|
||||
TBigInteger res;
|
||||
// Compute (modulus[0]^(-1)) (mod 2^32) for odd modulus
|
||||
|
||||
int n2 = calcN(modulus);
|
||||
if (modulus.numberLength == 1) {
|
||||
res = squareAndMultiply(x2, a2, exponent, modulus, n2);
|
||||
} else {
|
||||
res = slidingWindow(x2, a2, exponent, modulus, n2);
|
||||
}
|
||||
|
||||
return monPro(res, TBigInteger.ONE, modulus, n2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs modular exponentiation using the Montgomery Reduction. It
|
||||
* requires that all parameters be positive and the modulus be even. Based
|
||||
* <i>The square and multiply algorithm and the Montgomery Reduction C. K.
|
||||
* Koc - Montgomery Reduction with Even Modulus</i>. The square and multiply
|
||||
* algorithm and the Montgomery Reduction.
|
||||
*
|
||||
* @ar.org.fitc.ref "C. K. Koc - Montgomery Reduction with Even Modulus"
|
||||
* @see TBigInteger#modPow(TBigInteger, TBigInteger)
|
||||
*/
|
||||
static TBigInteger evenModPow(TBigInteger base, TBigInteger exponent, TBigInteger modulus) {
|
||||
// PRE: (base > 0), (exponent > 0), (modulus > 0) and (modulus even)
|
||||
// STEP 1: Obtain the factorization 'modulus'= q * 2^j.
|
||||
int j = modulus.getLowestSetBit();
|
||||
TBigInteger q = modulus.shiftRight(j);
|
||||
|
||||
// STEP 2: Compute x1 := base^exponent (mod q).
|
||||
TBigInteger x1 = oddModPow(base, exponent, q);
|
||||
|
||||
// STEP 3: Compute x2 := base^exponent (mod 2^j).
|
||||
TBigInteger x2 = pow2ModPow(base, exponent, j);
|
||||
|
||||
// STEP 4: Compute q^(-1) (mod 2^j) and y := (x2-x1) * q^(-1) (mod 2^j)
|
||||
TBigInteger qInv = modPow2Inverse(q, j);
|
||||
TBigInteger y = (x2.subtract(x1)).multiply(qInv);
|
||||
inplaceModPow2(y, j);
|
||||
if (y.sign < 0) {
|
||||
y = y.add(TBigInteger.getPowerOfTwo(j));
|
||||
}
|
||||
// STEP 5: Compute and return: x1 + q * y
|
||||
return x1.add(q.multiply(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* It requires that all parameters be positive.
|
||||
*
|
||||
* @return {@code base<sup>exponent</sup> mod (2<sup>j</sup>)}.
|
||||
* @see TBigInteger#modPow(TBigInteger, TBigInteger)
|
||||
*/
|
||||
static TBigInteger pow2ModPow(TBigInteger base, TBigInteger exponent, int j) {
|
||||
// PRE: (base > 0), (exponent > 0) and (j > 0)
|
||||
TBigInteger res = TBigInteger.ONE;
|
||||
TBigInteger e = exponent.copy();
|
||||
TBigInteger baseMod2toN = base.copy();
|
||||
TBigInteger res2;
|
||||
/*
|
||||
* If 'base' is odd then it's coprime with 2^j and phi(2^j) = 2^(j-1);
|
||||
* so we can reduce reduce the exponent (mod 2^(j-1)).
|
||||
*/
|
||||
if (base.testBit(0)) {
|
||||
inplaceModPow2(e, j - 1);
|
||||
}
|
||||
inplaceModPow2(baseMod2toN, j);
|
||||
|
||||
for (int i = e.bitLength() - 1; i >= 0; i--) {
|
||||
res2 = res.copy();
|
||||
inplaceModPow2(res2, j);
|
||||
res = res.multiply(res2);
|
||||
if (TBitLevel.testBit(e, i)) {
|
||||
res = res.multiply(baseMod2toN);
|
||||
inplaceModPow2(res, j);
|
||||
}
|
||||
}
|
||||
inplaceModPow2(res, j);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void monReduction(int[] res, TBigInteger modulus, int n2) {
|
||||
|
||||
/* res + m*modulus_digits */
|
||||
int[] modulus_digits = modulus.digits;
|
||||
int modulusLen = modulus.numberLength;
|
||||
long outerCarry = 0;
|
||||
|
||||
for (int i = 0; i < modulusLen; i++) {
|
||||
long innnerCarry = 0;
|
||||
int m = (int) TMultiplication.unsignedMultAddAdd(res[i], n2, 0, 0);
|
||||
for (int j = 0; j < modulusLen; j++) {
|
||||
innnerCarry = TMultiplication.unsignedMultAddAdd(m, modulus_digits[j], res[i + j], (int) innnerCarry);
|
||||
res[i + j] = (int) innnerCarry;
|
||||
innnerCarry >>>= 32;
|
||||
}
|
||||
|
||||
outerCarry += (res[i + modulusLen] & 0xFFFFFFFFL) + innnerCarry;
|
||||
res[i + modulusLen] = (int) outerCarry;
|
||||
outerCarry >>>= 32;
|
||||
}
|
||||
|
||||
res[modulusLen << 1] = (int) outerCarry;
|
||||
|
||||
/* res / r */
|
||||
for (int j = 0; j < modulusLen + 1; j++) {
|
||||
res[j] = res[j + modulusLen];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the Montgomery Product of two integers represented by
|
||||
* {@code int} arrays. The arrays are supposed in <i>little endian</i>
|
||||
* notation.
|
||||
*
|
||||
* @param a
|
||||
* The first factor of the product.
|
||||
* @param b
|
||||
* The second factor of the product.
|
||||
* @param modulus
|
||||
* The modulus of the operations. Z<sub>modulus</sub>.
|
||||
* @param n2
|
||||
* The digit modulus'[0].
|
||||
* @ar.org.fitc.ref "C. K. Koc - Analyzing and Comparing Montgomery
|
||||
* Multiplication Algorithms"
|
||||
* @see #modPowOdd(TBigInteger, TBigInteger, TBigInteger)
|
||||
*/
|
||||
static TBigInteger monPro(TBigInteger a, TBigInteger b, TBigInteger modulus, int n2) {
|
||||
int modulusLen = modulus.numberLength;
|
||||
int res[] = new int[(modulusLen << 1) + 1];
|
||||
TMultiplication.multArraysPAP(a.digits, Math.min(modulusLen, a.numberLength), b.digits,
|
||||
Math.min(modulusLen, b.numberLength), res);
|
||||
monReduction(res, modulus, n2);
|
||||
return finalSubtraction(res, modulus);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the final reduction of the Montgomery algorithm.
|
||||
*
|
||||
* @see monPro(BigInteger, BigInteger, BigInteger, long)
|
||||
* @see monSquare(BigInteger, BigInteger, long)
|
||||
*/
|
||||
static TBigInteger finalSubtraction(int res[], TBigInteger modulus) {
|
||||
|
||||
// skipping leading zeros
|
||||
int modulusLen = modulus.numberLength;
|
||||
boolean doSub = res[modulusLen] != 0;
|
||||
if (!doSub) {
|
||||
int modulusDigits[] = modulus.digits;
|
||||
doSub = true;
|
||||
for (int i = modulusLen - 1; i >= 0; i--) {
|
||||
if (res[i] != modulusDigits[i]) {
|
||||
doSub = (res[i] != 0) && ((res[i] & 0xFFFFFFFFL) > (modulusDigits[i] & 0xFFFFFFFFL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, modulusLen + 1, res);
|
||||
|
||||
// if (res >= modulusDigits) compute (res - modulusDigits)
|
||||
if (doSub) {
|
||||
TElementary.inplaceSubtract(result, modulus);
|
||||
}
|
||||
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x
|
||||
* an odd positive number.
|
||||
* @param n
|
||||
* the exponent by which 2 is raised.
|
||||
* @return {@code x<sup>-1</sup> (mod 2<sup>n</sup>)}.
|
||||
*/
|
||||
static TBigInteger modPow2Inverse(TBigInteger x, int n) {
|
||||
// PRE: (x > 0), (x is odd), and (n > 0)
|
||||
TBigInteger y = new TBigInteger(1, new int[1 << n]);
|
||||
y.numberLength = 1;
|
||||
y.digits[0] = 1;
|
||||
y.sign = 1;
|
||||
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (TBitLevel.testBit(x.multiply(y), i)) {
|
||||
// Adding 2^i to y (setting the i-th bit)
|
||||
y.digits[i >> 5] |= (1 << (i & 31));
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code x = x mod (2<sup>n</sup>)}.
|
||||
*
|
||||
* @param x
|
||||
* a positive number, it will store the result.
|
||||
* @param n
|
||||
* a positive exponent of {@code 2}.
|
||||
*/
|
||||
static void inplaceModPow2(TBigInteger x, int n) {
|
||||
// PRE: (x > 0) and (n >= 0)
|
||||
int fd = n >> 5;
|
||||
int leadingZeros;
|
||||
|
||||
if ((x.numberLength < fd) || (x.bitLength() <= n)) {
|
||||
return;
|
||||
}
|
||||
leadingZeros = 32 - (n & 31);
|
||||
x.numberLength = fd + 1;
|
||||
x.digits[fd] &= (leadingZeros < 32) ? (-1 >>> leadingZeros) : 0;
|
||||
x.cutOffLeadingZeroes();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* Static library that provides the basic arithmetic mutable operations for
|
||||
* {@link TBigInteger}. The operations provided are listed below.
|
||||
* <ul type="circle">
|
||||
* <li>Addition.</li>
|
||||
* <li>Subtraction.</li>
|
||||
* <li>Comparison.</li>
|
||||
* </ul>
|
||||
* In addition to this, some <i><b>Inplace</b></i> (mutable) methods are
|
||||
* provided.
|
||||
*/
|
||||
class TElementary {
|
||||
|
||||
/** Just to denote that this class can't be instantiated */
|
||||
private TElementary() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two arrays. All elements are treated as unsigned integers. The
|
||||
* magnitude is the bit chain of elements in big-endian order.
|
||||
*
|
||||
* @param a
|
||||
* the first array
|
||||
* @param b
|
||||
* the second array
|
||||
* @param size
|
||||
* the size of arrays
|
||||
* @return 1 if a > b, -1 if a < b, 0 if a == b
|
||||
*/
|
||||
static int compareArrays(final int[] a, final int[] b, final int size) {
|
||||
int i;
|
||||
for (i = size - 1; (i >= 0) && (a[i] == b[i]); i--) {
|
||||
// do nothing
|
||||
}
|
||||
return ((i < 0) ? TBigInteger.EQUALS : (a[i] & 0xFFFFFFFFL) < (b[i] & 0xFFFFFFFFL) ? TBigInteger.LESS
|
||||
: TBigInteger.GREATER);
|
||||
}
|
||||
|
||||
/** @see TBigInteger#add(TBigInteger) */
|
||||
static TBigInteger add(TBigInteger op1, TBigInteger op2) {
|
||||
int resDigits[];
|
||||
int resSign;
|
||||
int op1Sign = op1.sign;
|
||||
int op2Sign = op2.sign;
|
||||
|
||||
if (op1Sign == 0) {
|
||||
return op2;
|
||||
}
|
||||
if (op2Sign == 0) {
|
||||
return op1;
|
||||
}
|
||||
int op1Len = op1.numberLength;
|
||||
int op2Len = op2.numberLength;
|
||||
|
||||
if (op1Len + op2Len == 2) {
|
||||
long a = (op1.digits[0] & 0xFFFFFFFFL);
|
||||
long b = (op2.digits[0] & 0xFFFFFFFFL);
|
||||
long res;
|
||||
int valueLo;
|
||||
int valueHi;
|
||||
|
||||
if (op1Sign == op2Sign) {
|
||||
res = a + b;
|
||||
valueLo = (int) res;
|
||||
valueHi = (int) (res >>> 32);
|
||||
return ((valueHi == 0) ? new TBigInteger(op1Sign, valueLo) : new TBigInteger(op1Sign, 2, new int[] {
|
||||
valueLo, valueHi }));
|
||||
}
|
||||
return TBigInteger.valueOf((op1Sign < 0) ? (b - a) : (a - b));
|
||||
} else if (op1Sign == op2Sign) {
|
||||
resSign = op1Sign;
|
||||
// an augend should not be shorter than addend
|
||||
resDigits = (op1Len >= op2Len) ? add(op1.digits, op1Len, op2.digits, op2Len) : add(op2.digits, op2Len,
|
||||
op1.digits, op1Len);
|
||||
} else { // signs are different
|
||||
int cmp = ((op1Len != op2Len) ? ((op1Len > op2Len) ? 1 : -1)
|
||||
: compareArrays(op1.digits, op2.digits, op1Len));
|
||||
|
||||
if (cmp == TBigInteger.EQUALS) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
// a minuend should not be shorter than subtrahend
|
||||
if (cmp == TBigInteger.GREATER) {
|
||||
resSign = op1Sign;
|
||||
resDigits = subtract(op1.digits, op1Len, op2.digits, op2Len);
|
||||
} else {
|
||||
resSign = op2Sign;
|
||||
resDigits = subtract(op2.digits, op2Len, op1.digits, op1Len);
|
||||
}
|
||||
}
|
||||
TBigInteger res = new TBigInteger(resSign, resDigits.length, resDigits);
|
||||
res.cutOffLeadingZeroes();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code res = a + b}.
|
||||
*/
|
||||
private static void add(int res[], int a[], int aSize, int b[], int bSize) {
|
||||
// PRE: a.length < max(aSize, bSize)
|
||||
|
||||
int i;
|
||||
long carry = (a[0] & 0xFFFFFFFFL) + (b[0] & 0xFFFFFFFFL);
|
||||
|
||||
res[0] = (int) carry;
|
||||
carry >>= 32;
|
||||
|
||||
if (aSize >= bSize) {
|
||||
for (i = 1; i < bSize; i++) {
|
||||
carry += (a[i] & 0xFFFFFFFFL) + (b[i] & 0xFFFFFFFFL);
|
||||
res[i] = (int) carry;
|
||||
carry >>= 32;
|
||||
}
|
||||
for (; i < aSize; i++) {
|
||||
carry += a[i] & 0xFFFFFFFFL;
|
||||
res[i] = (int) carry;
|
||||
carry >>= 32;
|
||||
}
|
||||
} else {
|
||||
for (i = 1; i < aSize; i++) {
|
||||
carry += (a[i] & 0xFFFFFFFFL) + (b[i] & 0xFFFFFFFFL);
|
||||
res[i] = (int) carry;
|
||||
carry >>= 32;
|
||||
}
|
||||
for (; i < bSize; i++) {
|
||||
carry += b[i] & 0xFFFFFFFFL;
|
||||
res[i] = (int) carry;
|
||||
carry >>= 32;
|
||||
}
|
||||
}
|
||||
if (carry != 0) {
|
||||
res[i] = (int) carry;
|
||||
}
|
||||
}
|
||||
|
||||
/** @see TBigInteger#subtract(TBigInteger) */
|
||||
static TBigInteger subtract(TBigInteger op1, TBigInteger op2) {
|
||||
int resSign;
|
||||
int resDigits[];
|
||||
int op1Sign = op1.sign;
|
||||
int op2Sign = op2.sign;
|
||||
|
||||
if (op2Sign == 0) {
|
||||
return op1;
|
||||
}
|
||||
if (op1Sign == 0) {
|
||||
return op2.negate();
|
||||
}
|
||||
int op1Len = op1.numberLength;
|
||||
int op2Len = op2.numberLength;
|
||||
if (op1Len + op2Len == 2) {
|
||||
long a = (op1.digits[0] & 0xFFFFFFFFL);
|
||||
long b = (op2.digits[0] & 0xFFFFFFFFL);
|
||||
if (op1Sign < 0) {
|
||||
a = -a;
|
||||
}
|
||||
if (op2Sign < 0) {
|
||||
b = -b;
|
||||
}
|
||||
return TBigInteger.valueOf(a - b);
|
||||
}
|
||||
int cmp = ((op1Len != op2Len) ? ((op1Len > op2Len) ? 1 : -1) : TElementary.compareArrays(op1.digits, op2.digits,
|
||||
op1Len));
|
||||
|
||||
if (cmp == TBigInteger.LESS) {
|
||||
resSign = -op2Sign;
|
||||
resDigits = (op1Sign == op2Sign) ? subtract(op2.digits, op2Len, op1.digits, op1Len) : add(op2.digits,
|
||||
op2Len, op1.digits, op1Len);
|
||||
} else {
|
||||
resSign = op1Sign;
|
||||
if (op1Sign == op2Sign) {
|
||||
if (cmp == TBigInteger.EQUALS) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
resDigits = subtract(op1.digits, op1Len, op2.digits, op2Len);
|
||||
} else {
|
||||
resDigits = add(op1.digits, op1Len, op2.digits, op2Len);
|
||||
}
|
||||
}
|
||||
TBigInteger res = new TBigInteger(resSign, resDigits.length, resDigits);
|
||||
res.cutOffLeadingZeroes();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code res = a - b}. It is assumed the magnitude of a is not
|
||||
* less than the magnitude of b.
|
||||
*/
|
||||
private static void subtract(int res[], int a[], int aSize, int b[], int bSize) {
|
||||
// PRE: a[] >= b[]
|
||||
int i;
|
||||
long borrow = 0;
|
||||
|
||||
for (i = 0; i < bSize; i++) {
|
||||
borrow += (a[i] & 0xFFFFFFFFL) - (b[i] & 0xFFFFFFFFL);
|
||||
res[i] = (int) borrow;
|
||||
borrow >>= 32; // -1 or 0
|
||||
}
|
||||
for (; i < aSize; i++) {
|
||||
borrow += a[i] & 0xFFFFFFFFL;
|
||||
res[i] = (int) borrow;
|
||||
borrow >>= 32; // -1 or 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Addss the value represented by {@code b} to the value represented by
|
||||
* {@code a}. It is assumed the magnitude of a is not less than the
|
||||
* magnitude of b.
|
||||
*
|
||||
* @return {@code a + b}
|
||||
*/
|
||||
private static int[] add(int a[], int aSize, int b[], int bSize) {
|
||||
// PRE: a[] >= b[]
|
||||
int res[] = new int[aSize + 1];
|
||||
add(res, a, aSize, b, bSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code op1 += op2}. {@code op1} must have enough place to store
|
||||
* the result (i.e. {@code op1.bitLength() >= op2.bitLength()}). Both should
|
||||
* be positive (i.e. {@code op1 >= op2}).
|
||||
*
|
||||
* @param op1
|
||||
* the input minuend, and the output result.
|
||||
* @param op2
|
||||
* the addend
|
||||
*/
|
||||
static void inplaceAdd(TBigInteger op1, TBigInteger op2) {
|
||||
// PRE: op1 >= op2 > 0
|
||||
add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
op1.numberLength = Math.min(Math.max(op1.numberLength, op2.numberLength) + 1, op1.digits.length);
|
||||
op1.cutOffLeadingZeroes();
|
||||
op1.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an integer value to the array of integers remembering carry.
|
||||
*
|
||||
* @return a possible generated carry (0 or 1)
|
||||
*/
|
||||
static int inplaceAdd(int a[], final int aSize, final int addend) {
|
||||
long carry = addend & 0xFFFFFFFFL;
|
||||
|
||||
for (int i = 0; (carry != 0) && (i < aSize); i++) {
|
||||
carry += a[i] & 0xFFFFFFFFL;
|
||||
a[i] = (int) carry;
|
||||
carry >>= 32;
|
||||
}
|
||||
return (int) carry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs: {@code op1 += addend}. The number must to have place to hold a
|
||||
* possible carry.
|
||||
*/
|
||||
static void inplaceAdd(TBigInteger op1, final int addend) {
|
||||
int carry = inplaceAdd(op1.digits, op1.numberLength, addend);
|
||||
if (carry == 1) {
|
||||
op1.digits[op1.numberLength] = 1;
|
||||
op1.numberLength++;
|
||||
}
|
||||
op1.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code op1 -= op2}. {@code op1} must have enough place to store
|
||||
* the result (i.e. {@code op1.bitLength() >= op2.bitLength()}). Both should
|
||||
* be positive (what implies that {@code op1 >= op2}).
|
||||
*
|
||||
* @param op1
|
||||
* the input minuend, and the output result.
|
||||
* @param op2
|
||||
* the subtrahend
|
||||
*/
|
||||
static void inplaceSubtract(TBigInteger op1, TBigInteger op2) {
|
||||
// PRE: op1 >= op2 > 0
|
||||
subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
op1.cutOffLeadingZeroes();
|
||||
op1.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code res = b - a}
|
||||
*/
|
||||
private static void inverseSubtract(int res[], int a[], int aSize, int b[], int bSize) {
|
||||
int i;
|
||||
long borrow = 0;
|
||||
if (aSize < bSize) {
|
||||
for (i = 0; i < aSize; i++) {
|
||||
borrow += (b[i] & 0xFFFFFFFFL) - (a[i] & 0xFFFFFFFFL);
|
||||
res[i] = (int) borrow;
|
||||
borrow >>= 32; // -1 or 0
|
||||
}
|
||||
for (; i < bSize; i++) {
|
||||
borrow += b[i] & 0xFFFFFFFFL;
|
||||
res[i] = (int) borrow;
|
||||
borrow >>= 32; // -1 or 0
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < bSize; i++) {
|
||||
borrow += (b[i] & 0xFFFFFFFFL) - (a[i] & 0xFFFFFFFFL);
|
||||
res[i] = (int) borrow;
|
||||
borrow >>= 32; // -1 or 0
|
||||
}
|
||||
for (; i < aSize; i++) {
|
||||
borrow -= a[i] & 0xFFFFFFFFL;
|
||||
res[i] = (int) borrow;
|
||||
borrow >>= 32; // -1 or 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts the value represented by {@code b} from the value represented
|
||||
* by {@code a}. It is assumed the magnitude of a is not less than the
|
||||
* magnitude of b.
|
||||
*
|
||||
* @return {@code a - b}
|
||||
*/
|
||||
private static int[] subtract(int a[], int aSize, int b[], int bSize) {
|
||||
// PRE: a[] >= b[]
|
||||
int res[] = new int[aSize];
|
||||
subtract(res, a, aSize, b, bSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as
|
||||
*
|
||||
* @link #inplaceSubtract(BigInteger, BigInteger), but without the
|
||||
* restriction of non-positive values
|
||||
* @param op1
|
||||
* should have enough space to save the result
|
||||
* @param op2
|
||||
*/
|
||||
static void completeInPlaceSubtract(TBigInteger op1, TBigInteger op2) {
|
||||
int resultSign = op1.compareTo(op2);
|
||||
if (op1.sign == 0) {
|
||||
System.arraycopy(op2.digits, 0, op1.digits, 0, op2.numberLength);
|
||||
op1.sign = -op2.sign;
|
||||
} else if (op1.sign != op2.sign) {
|
||||
add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
op1.sign = resultSign;
|
||||
} else {
|
||||
int sign = unsignedArraysCompare(op1.digits, op2.digits, op1.numberLength, op2.numberLength);
|
||||
if (sign > 0) {
|
||||
subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
// op1.sign remains equal
|
||||
} else {
|
||||
inverseSubtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
op1.sign = -op1.sign;
|
||||
}
|
||||
}
|
||||
op1.numberLength = Math.max(op1.numberLength, op2.numberLength) + 1;
|
||||
op1.cutOffLeadingZeroes();
|
||||
op1.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as @link #inplaceAdd(BigInteger, BigInteger), but without the
|
||||
* restriction of non-positive values
|
||||
*
|
||||
* @param op1
|
||||
* any number
|
||||
* @param op2
|
||||
* any number
|
||||
*/
|
||||
static void completeInPlaceAdd(TBigInteger op1, TBigInteger op2) {
|
||||
if (op1.sign == 0)
|
||||
System.arraycopy(op2.digits, 0, op1.digits, 0, op2.numberLength);
|
||||
else if (op2.sign == 0)
|
||||
return;
|
||||
else if (op1.sign == op2.sign)
|
||||
add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
else {
|
||||
int sign = unsignedArraysCompare(op1.digits, op2.digits, op1.numberLength, op2.numberLength);
|
||||
if (sign > 0)
|
||||
subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
else {
|
||||
inverseSubtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
|
||||
op1.sign = -op1.sign;
|
||||
}
|
||||
}
|
||||
op1.numberLength = Math.max(op1.numberLength, op2.numberLength) + 1;
|
||||
op1.cutOffLeadingZeroes();
|
||||
op1.unCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two arrays, representing unsigned integer in little-endian
|
||||
* order. Returns +1,0,-1 if a is - respective - greater, equal or lesser
|
||||
* then b
|
||||
*/
|
||||
private static int unsignedArraysCompare(int[] a, int[] b, int aSize, int bSize) {
|
||||
if (aSize > bSize)
|
||||
return 1;
|
||||
else if (aSize < bSize)
|
||||
return -1;
|
||||
|
||||
else {
|
||||
int i;
|
||||
for (i = aSize - 1; i >= 0 && a[i] == b[i]; i--) {
|
||||
// do nothing
|
||||
}
|
||||
return i < 0 ? TBigInteger.EQUALS : ((a[i] & 0xFFFFFFFFL) < (b[i] & 0xFFFFFFFFL) ? TBigInteger.LESS
|
||||
: TBigInteger.GREATER);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,808 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* The library implements some logical operations over {@code BigInteger}. The
|
||||
* operations provided are listed below.
|
||||
* <ul type="circle">
|
||||
* <li>not</li>
|
||||
* <li>and</li>
|
||||
* <li>andNot</li>
|
||||
* <li>or</li>
|
||||
* <li>xor</li>
|
||||
* </ul>
|
||||
*/
|
||||
class TLogical {
|
||||
|
||||
/** Just to denote that this class can't be instantiated. */
|
||||
|
||||
private TLogical() {}
|
||||
|
||||
|
||||
/** @see TBigInteger#not() */
|
||||
static TBigInteger not(TBigInteger val) {
|
||||
if (val.sign == 0) {
|
||||
return TBigInteger.MINUS_ONE;
|
||||
}
|
||||
if (val.equals(TBigInteger.MINUS_ONE)) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
int resDigits[] = new int[val.numberLength + 1];
|
||||
int i;
|
||||
|
||||
if (val.sign > 0) {
|
||||
// ~val = -val + 1
|
||||
if (val.digits[val.numberLength - 1] != -1) {
|
||||
for (i = 0; val.digits[i] == -1; i++) {
|
||||
// do nothing
|
||||
}
|
||||
} else {
|
||||
for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (i == val.numberLength) {
|
||||
resDigits[i] = 1;
|
||||
return new TBigInteger(-val.sign, i + 1, resDigits);
|
||||
}
|
||||
}
|
||||
// Here a carry 1 was generated
|
||||
} else {// (val.sign < 0)
|
||||
// ~val = -val - 1
|
||||
for (i = 0; val.digits[i] == 0; i++) {
|
||||
resDigits[i] = -1;
|
||||
}
|
||||
// Here a borrow -1 was generated
|
||||
}
|
||||
// Now, the carry/borrow can be absorbed
|
||||
resDigits[i] = val.digits[i] + val.sign;
|
||||
// Copying the remaining unchanged digit
|
||||
for (i++; i < val.numberLength; i++) {
|
||||
resDigits[i] = val.digits[i];
|
||||
}
|
||||
return new TBigInteger(-val.sign, i, resDigits);
|
||||
}
|
||||
|
||||
/** @see TBigInteger#and(TBigInteger) */
|
||||
static TBigInteger and(TBigInteger val, TBigInteger that) {
|
||||
if (that.sign == 0 || val.sign == 0) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
if (that.equals(TBigInteger.MINUS_ONE)){
|
||||
return val;
|
||||
}
|
||||
if (val.equals(TBigInteger.MINUS_ONE)) {
|
||||
return that;
|
||||
}
|
||||
|
||||
if (val.sign > 0) {
|
||||
if (that.sign > 0) {
|
||||
return andPositive(val, that);
|
||||
} else {
|
||||
return andDiffSigns(val, that);
|
||||
}
|
||||
} else {
|
||||
if (that.sign > 0) {
|
||||
return andDiffSigns(that, val);
|
||||
} else if (val.numberLength > that.numberLength) {
|
||||
return andNegative(val, that);
|
||||
} else {
|
||||
return andNegative(that, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return sign = 1, magnitude = val.magnitude & that.magnitude*/
|
||||
static TBigInteger andPositive(TBigInteger val, TBigInteger that) {
|
||||
// PRE: both arguments are positive
|
||||
int resLength = Math.min(val.numberLength, that.numberLength);
|
||||
int i = Math.max(val.getFirstNonzeroDigit(), that.getFirstNonzeroDigit());
|
||||
|
||||
if (i >= resLength) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
|
||||
int resDigits[] = new int[resLength];
|
||||
for ( ; i < resLength; i++) {
|
||||
resDigits[i] = val.digits[i] & that.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = positive.magnitude & magnitude = -negative.magnitude */
|
||||
static TBigInteger andDiffSigns(TBigInteger positive, TBigInteger negative) {
|
||||
// PRE: positive is positive and negative is negative
|
||||
int iPos = positive.getFirstNonzeroDigit();
|
||||
int iNeg = negative.getFirstNonzeroDigit();
|
||||
|
||||
// Look if the trailing zeros of the negative will "blank" all
|
||||
// the positive digits
|
||||
if (iNeg >= positive.numberLength) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
int resLength = positive.numberLength;
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
// Must start from max(iPos, iNeg)
|
||||
int i = Math.max(iPos, iNeg);
|
||||
if (i == iNeg) {
|
||||
resDigits[i] = -negative.digits[i] & positive.digits[i];
|
||||
i++;
|
||||
}
|
||||
int limit = Math.min(negative.numberLength, positive.numberLength);
|
||||
for ( ; i < limit; i++) {
|
||||
resDigits[i] = ~negative.digits[i] & positive.digits[i];
|
||||
}
|
||||
// if the negative was shorter must copy the remaining digits
|
||||
// from positive
|
||||
if (i >= negative.numberLength) {
|
||||
for ( ; i < positive.numberLength; i++) {
|
||||
resDigits[i] = positive.digits[i];
|
||||
}
|
||||
} // else positive ended and must "copy" virtual 0's, do nothing then
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = -1, magnitude = -(-longer.magnitude & -shorter.magnitude)*/
|
||||
static TBigInteger andNegative(TBigInteger longer, TBigInteger shorter) {
|
||||
// PRE: longer and shorter are negative
|
||||
// PRE: longer has at least as many digits as shorter
|
||||
int iLonger = longer.getFirstNonzeroDigit();
|
||||
int iShorter = shorter.getFirstNonzeroDigit();
|
||||
|
||||
// Does shorter matter?
|
||||
if (iLonger >= shorter.numberLength) {
|
||||
return longer;
|
||||
}
|
||||
|
||||
int resLength;
|
||||
int resDigits[];
|
||||
int i = Math.max(iShorter, iLonger);
|
||||
int digit;
|
||||
if (iShorter > iLonger) {
|
||||
digit = -shorter.digits[i] & ~longer.digits[i];
|
||||
} else if (iShorter < iLonger) {
|
||||
digit = ~shorter.digits[i] & -longer.digits[i];
|
||||
} else {
|
||||
digit = -shorter.digits[i] & -longer.digits[i];
|
||||
}
|
||||
if (digit == 0) {
|
||||
for (i++; i < shorter.numberLength && (digit = ~(longer.digits[i] | shorter.digits[i])) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (digit == 0) {
|
||||
// shorter has only the remaining virtual sign bits
|
||||
for ( ; i < longer.numberLength && (digit = ~longer.digits[i]) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (digit == 0) {
|
||||
resLength = longer.numberLength + 1;
|
||||
resDigits = new int[resLength];
|
||||
resDigits[resLength - 1] = 1;
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
resLength = longer.numberLength;
|
||||
resDigits = new int[resLength];
|
||||
resDigits[i] = -digit;
|
||||
for (i++; i < shorter.numberLength; i++){
|
||||
// resDigits[i] = ~(~longer.digits[i] & ~shorter.digits[i];)
|
||||
resDigits[i] = longer.digits[i] | shorter.digits[i];
|
||||
}
|
||||
// shorter has only the remaining virtual sign bits
|
||||
for( ; i < longer.numberLength; i++){
|
||||
resDigits[i] = longer.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @see TBigInteger#andNot(TBigInteger) */
|
||||
static TBigInteger andNot(TBigInteger val, TBigInteger that) {
|
||||
if (that.sign == 0 ) {
|
||||
return val;
|
||||
}
|
||||
if (val.sign == 0) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
if (val.equals(TBigInteger.MINUS_ONE)) {
|
||||
return that.not();
|
||||
}
|
||||
if (that.equals(TBigInteger.MINUS_ONE)){
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
|
||||
//if val == that, return 0
|
||||
|
||||
if (val.sign > 0) {
|
||||
if (that.sign > 0) {
|
||||
return andNotPositive(val, that);
|
||||
} else {
|
||||
return andNotPositiveNegative(val, that);
|
||||
}
|
||||
} else {
|
||||
if (that.sign > 0) {
|
||||
return andNotNegativePositive(val, that);
|
||||
} else {
|
||||
return andNotNegative(val, that);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return sign = 1, magnitude = val.magnitude & ~that.magnitude*/
|
||||
static TBigInteger andNotPositive(TBigInteger val, TBigInteger that) {
|
||||
// PRE: both arguments are positive
|
||||
int resDigits[] = new int[val.numberLength];
|
||||
|
||||
int limit = Math.min(val.numberLength, that.numberLength);
|
||||
int i;
|
||||
for (i = val.getFirstNonzeroDigit(); i < limit; i++) {
|
||||
resDigits[i] = val.digits[i] & ~that.digits[i];
|
||||
}
|
||||
for ( ; i < val.numberLength; i++) {
|
||||
resDigits[i] = val.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, val.numberLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = 1, magnitude = positive.magnitude & ~(-negative.magnitude)*/
|
||||
static TBigInteger andNotPositiveNegative(TBigInteger positive, TBigInteger negative) {
|
||||
// PRE: positive > 0 && negative < 0
|
||||
int iNeg = negative.getFirstNonzeroDigit();
|
||||
int iPos = positive.getFirstNonzeroDigit();
|
||||
|
||||
if (iNeg >= positive.numberLength) {
|
||||
return positive;
|
||||
}
|
||||
|
||||
int resLength = Math.min(positive.numberLength, negative.numberLength);
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
// Always start from first non zero of positive
|
||||
int i = iPos;
|
||||
for ( ; i < iNeg; i++) {
|
||||
// resDigits[i] = positive.digits[i] & -1 (~0)
|
||||
resDigits[i] = positive.digits[i];
|
||||
}
|
||||
if (i == iNeg) {
|
||||
resDigits[i] = positive.digits[i] & (negative.digits[i] - 1);
|
||||
i++;
|
||||
}
|
||||
for ( ; i < resLength; i++) {
|
||||
// resDigits[i] = positive.digits[i] & ~(~negative.digits[i]);
|
||||
resDigits[i] = positive.digits[i] & negative.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = -1, magnitude = -(-negative.magnitude & ~positive.magnitude)*/
|
||||
static TBigInteger andNotNegativePositive(TBigInteger negative, TBigInteger positive) {
|
||||
// PRE: negative < 0 && positive > 0
|
||||
int resLength;
|
||||
int resDigits[];
|
||||
int limit;
|
||||
int digit;
|
||||
|
||||
int iNeg = negative.getFirstNonzeroDigit();
|
||||
int iPos = positive.getFirstNonzeroDigit();
|
||||
|
||||
if (iNeg >= positive.numberLength) {
|
||||
return negative;
|
||||
}
|
||||
|
||||
resLength = Math.max(negative.numberLength, positive.numberLength);
|
||||
int i = iNeg;
|
||||
if (iPos > iNeg) {
|
||||
resDigits = new int[resLength];
|
||||
limit = Math.min(negative.numberLength, iPos);
|
||||
for ( ; i < limit; i++) {
|
||||
// 1st case: resDigits [i] = -(-negative.digits[i] & (~0))
|
||||
// otherwise: resDigits[i] = ~(~negative.digits[i] & ~0) ;
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
if (i == negative.numberLength) {
|
||||
for (i = iPos; i < positive.numberLength; i++) {
|
||||
// resDigits[i] = ~(~positive.digits[i] & -1);
|
||||
resDigits[i] = positive.digits[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
digit = -negative.digits[i] & ~positive.digits[i];
|
||||
if (digit == 0) {
|
||||
limit = Math.min(positive.numberLength, negative.numberLength);
|
||||
for (i++; i < limit && (digit = ~(negative.digits[i] | positive.digits[i])) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (digit == 0) {
|
||||
// the shorter has only the remaining virtual sign bits
|
||||
for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (digit == 0) {
|
||||
resLength++;
|
||||
resDigits = new int[resLength];
|
||||
resDigits[resLength - 1] = 1;
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
resDigits = new int[resLength];
|
||||
resDigits[i] = -digit;
|
||||
i++;
|
||||
}
|
||||
|
||||
limit = Math.min(positive.numberLength, negative.numberLength);
|
||||
for ( ; i < limit; i++) {
|
||||
//resDigits[i] = ~(~negative.digits[i] & ~positive.digits[i]);
|
||||
resDigits[i] = negative.digits[i] | positive.digits[i];
|
||||
}
|
||||
// Actually one of the next two cycles will be executed
|
||||
for ( ; i < negative.numberLength; i++) {
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
for ( ; i < positive.numberLength; i++) {
|
||||
resDigits[i] = positive.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = 1, magnitude = -val.magnitude & ~(-that.magnitude)*/
|
||||
static TBigInteger andNotNegative(TBigInteger val, TBigInteger that) {
|
||||
// PRE: val < 0 && that < 0
|
||||
int iVal = val.getFirstNonzeroDigit();
|
||||
int iThat = that.getFirstNonzeroDigit();
|
||||
|
||||
if (iVal >= that.numberLength) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
|
||||
int resLength = that.numberLength;
|
||||
int resDigits[] = new int[resLength];
|
||||
int limit;
|
||||
int i = iVal;
|
||||
if (iVal < iThat) {
|
||||
// resDigits[i] = -val.digits[i] & -1;
|
||||
resDigits[i] = -val.digits[i];
|
||||
limit = Math.min(val.numberLength, iThat);
|
||||
for (i++; i < limit; i++) {
|
||||
// resDigits[i] = ~val.digits[i] & -1;
|
||||
resDigits[i] = ~val.digits[i];
|
||||
}
|
||||
if (i == val.numberLength) {
|
||||
for ( ; i < iThat; i++) {
|
||||
// resDigits[i] = -1 & -1;
|
||||
resDigits[i] = -1;
|
||||
}
|
||||
// resDigits[i] = -1 & ~-that.digits[i];
|
||||
resDigits[i] = that.digits[i] - 1;
|
||||
} else {
|
||||
// resDigits[i] = ~val.digits[i] & ~-that.digits[i];
|
||||
resDigits[i] = ~val.digits[i] & (that.digits[i] - 1);
|
||||
}
|
||||
} else if (iThat < iVal ) {
|
||||
// resDigits[i] = -val.digits[i] & ~~that.digits[i];
|
||||
resDigits[i] = -val.digits[i] & that.digits[i];
|
||||
} else {
|
||||
// resDigits[i] = -val.digits[i] & ~-that.digits[i];
|
||||
resDigits[i] = -val.digits[i] & (that.digits[i] - 1);
|
||||
}
|
||||
|
||||
limit = Math.min(val.numberLength, that.numberLength);
|
||||
for (i++; i < limit; i++) {
|
||||
// resDigits[i] = ~val.digits[i] & ~~that.digits[i];
|
||||
resDigits[i] = ~val.digits[i] & that.digits[i];
|
||||
}
|
||||
for ( ; i < that.numberLength; i++) {
|
||||
// resDigits[i] = -1 & ~~that.digits[i];
|
||||
resDigits[i] = that.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @see TBigInteger#or(TBigInteger) */
|
||||
static TBigInteger or(TBigInteger val, TBigInteger that) {
|
||||
if (that.equals(TBigInteger.MINUS_ONE) || val.equals(TBigInteger.MINUS_ONE)) {
|
||||
return TBigInteger.MINUS_ONE;
|
||||
}
|
||||
if (that.sign == 0) {
|
||||
return val;
|
||||
}
|
||||
if (val.sign == 0) {
|
||||
return that;
|
||||
}
|
||||
|
||||
if (val.sign > 0) {
|
||||
if (that.sign > 0) {
|
||||
if (val.numberLength > that.numberLength) {
|
||||
return orPositive(val, that);
|
||||
} else {
|
||||
return orPositive(that, val);
|
||||
}
|
||||
} else {
|
||||
return orDiffSigns(val, that);
|
||||
}
|
||||
} else {
|
||||
if (that.sign > 0) {
|
||||
return orDiffSigns(that, val);
|
||||
} else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
|
||||
return orNegative(that, val);
|
||||
} else {
|
||||
return orNegative(val, that);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return sign = 1, magnitude = longer.magnitude | shorter.magnitude*/
|
||||
static TBigInteger orPositive(TBigInteger longer, TBigInteger shorter) {
|
||||
// PRE: longer and shorter are positive;
|
||||
// PRE: longer has at least as many digits as shorter
|
||||
int resLength = longer.numberLength;
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
|
||||
for (i = 0; i < shorter.numberLength; i++) {
|
||||
resDigits[i] = longer.digits[i] | shorter.digits[i];
|
||||
}
|
||||
for ( ; i < resLength; i++) {
|
||||
resDigits[i] = longer.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = -1, magnitude = -(-val.magnitude | -that.magnitude) */
|
||||
static TBigInteger orNegative(TBigInteger val, TBigInteger that){
|
||||
// PRE: val and that are negative;
|
||||
// PRE: val has at least as many trailing zeros digits as that
|
||||
int iThat = that.getFirstNonzeroDigit();
|
||||
int iVal = val.getFirstNonzeroDigit();
|
||||
int i;
|
||||
|
||||
if (iVal >= that.numberLength) {
|
||||
return that;
|
||||
}else if (iThat >= val.numberLength) {
|
||||
return val;
|
||||
}
|
||||
|
||||
int resLength = Math.min(val.numberLength, that.numberLength);
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
//Looking for the first non-zero digit of the result
|
||||
if (iThat == iVal) {
|
||||
resDigits[iVal] = -(-val.digits[iVal] | -that.digits[iVal]);
|
||||
i = iVal;
|
||||
} else {
|
||||
for (i = iThat; i < iVal; i++) {
|
||||
resDigits[i] = that.digits[i];
|
||||
}
|
||||
resDigits[i] = that.digits[i] & (val.digits[i] - 1);
|
||||
}
|
||||
|
||||
for (i++; i < resLength; i++) {
|
||||
resDigits[i] = val.digits[i] & that.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = -1, magnitude = -(positive.magnitude | -negative.magnitude) */
|
||||
static TBigInteger orDiffSigns(TBigInteger positive, TBigInteger negative){
|
||||
// Jumping over the least significant zero bits
|
||||
int iNeg = negative.getFirstNonzeroDigit();
|
||||
int iPos = positive.getFirstNonzeroDigit();
|
||||
int i;
|
||||
int limit;
|
||||
|
||||
// Look if the trailing zeros of the positive will "copy" all
|
||||
// the negative digits
|
||||
if (iPos >= negative.numberLength) {
|
||||
return negative;
|
||||
}
|
||||
int resLength = negative.numberLength;
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
if (iNeg < iPos ) {
|
||||
// We know for sure that this will
|
||||
// be the first non zero digit in the result
|
||||
for (i = iNeg; i < iPos; i++) {
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
} else if (iPos < iNeg) {
|
||||
i = iPos;
|
||||
resDigits[i] = -positive.digits[i];
|
||||
limit = Math.min(positive.numberLength, iNeg);
|
||||
for(i++; i < limit; i++ ) {
|
||||
resDigits[i] = ~positive.digits[i];
|
||||
}
|
||||
if (i != positive.numberLength) {
|
||||
resDigits[i] = ~(-negative.digits[i] | positive.digits[i]);
|
||||
} else{
|
||||
for (; i<iNeg; i++) {
|
||||
resDigits[i] = -1;
|
||||
}
|
||||
// resDigits[i] = ~(-negative.digits[i] | 0);
|
||||
resDigits[i] = negative.digits[i] - 1;
|
||||
}
|
||||
i++;
|
||||
} else {// iNeg == iPos
|
||||
// Applying two complement to negative and to result
|
||||
i = iPos;
|
||||
resDigits[i] = -(-negative.digits[i] | positive.digits[i]);
|
||||
i++;
|
||||
}
|
||||
limit = Math.min(negative.numberLength, positive.numberLength);
|
||||
for (; i < limit; i++) {
|
||||
// Applying two complement to negative and to result
|
||||
// resDigits[i] = ~(~negative.digits[i] | positive.digits[i] );
|
||||
resDigits[i] = negative.digits[i] & ~positive.digits[i];
|
||||
}
|
||||
for( ; i < negative.numberLength; i++) {
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @see TBigInteger#xor(TBigInteger) */
|
||||
static TBigInteger xor(TBigInteger val, TBigInteger that) {
|
||||
if (that.sign == 0) {
|
||||
return val;
|
||||
}
|
||||
if (val.sign == 0) {
|
||||
return that;
|
||||
}
|
||||
if (that.equals(TBigInteger.MINUS_ONE)) {
|
||||
return val.not();
|
||||
}
|
||||
if (val.equals(TBigInteger.MINUS_ONE)) {
|
||||
return that.not();
|
||||
}
|
||||
|
||||
if (val.sign > 0) {
|
||||
if (that.sign > 0) {
|
||||
if (val.numberLength > that.numberLength) {
|
||||
return xorPositive(val, that);
|
||||
} else {
|
||||
return xorPositive(that, val);
|
||||
}
|
||||
} else {
|
||||
return xorDiffSigns(val, that);
|
||||
}
|
||||
} else {
|
||||
if (that.sign > 0) {
|
||||
return xorDiffSigns(that, val);
|
||||
} else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
|
||||
return xorNegative(that, val);
|
||||
} else {
|
||||
return xorNegative(val, that);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return sign = 0, magnitude = longer.magnitude | shorter.magnitude */
|
||||
static TBigInteger xorPositive(TBigInteger longer, TBigInteger shorter) {
|
||||
// PRE: longer and shorter are positive;
|
||||
// PRE: longer has at least as many digits as shorter
|
||||
int resLength = longer.numberLength;
|
||||
int resDigits[] = new int[resLength];
|
||||
int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
|
||||
for ( ; i < shorter.numberLength; i++) {
|
||||
resDigits[i] = longer.digits[i] ^ shorter.digits[i];
|
||||
}
|
||||
for( ; i < longer.numberLength; i++ ){
|
||||
resDigits[i] = longer.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = 0, magnitude = -val.magnitude ^ -that.magnitude */
|
||||
static TBigInteger xorNegative(TBigInteger val, TBigInteger that){
|
||||
// PRE: val and that are negative
|
||||
// PRE: val has at least as many trailing zero digits as that
|
||||
int resLength = Math.max(val.numberLength, that.numberLength);
|
||||
int resDigits[] = new int[resLength];
|
||||
int iVal = val.getFirstNonzeroDigit();
|
||||
int iThat = that.getFirstNonzeroDigit();
|
||||
int i = iThat;
|
||||
int limit;
|
||||
|
||||
|
||||
if (iVal == iThat) {
|
||||
resDigits[i] = -val.digits[i] ^ -that.digits[i];
|
||||
} else {
|
||||
resDigits[i] = -that.digits[i];
|
||||
limit = Math.min(that.numberLength, iVal);
|
||||
for (i++; i < limit; i++) {
|
||||
resDigits[i] = ~that.digits[i];
|
||||
}
|
||||
// Remains digits in that?
|
||||
if (i == that.numberLength) {
|
||||
//Jumping over the remaining zero to the first non one
|
||||
for ( ;i < iVal; i++) {
|
||||
//resDigits[i] = 0 ^ -1;
|
||||
resDigits[i] = -1;
|
||||
}
|
||||
//resDigits[i] = -val.digits[i] ^ -1;
|
||||
resDigits[i] = val.digits[i] - 1;
|
||||
} else {
|
||||
resDigits[i] = -val.digits[i] ^ ~that.digits[i];
|
||||
}
|
||||
}
|
||||
|
||||
limit = Math.min(val.numberLength, that.numberLength);
|
||||
//Perform ^ between that al val until that ends
|
||||
for (i++; i < limit; i++) {
|
||||
//resDigits[i] = ~val.digits[i] ^ ~that.digits[i];
|
||||
resDigits[i] = val.digits[i] ^ that.digits[i];
|
||||
}
|
||||
//Perform ^ between val digits and -1 until val ends
|
||||
for ( ; i < val.numberLength; i++) {
|
||||
//resDigits[i] = ~val.digits[i] ^ -1 ;
|
||||
resDigits[i] = val.digits[i] ;
|
||||
}
|
||||
for ( ; i < that.numberLength; i++) {
|
||||
//resDigits[i] = -1 ^ ~that.digits[i] ;
|
||||
resDigits[i] = that.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @return sign = 1, magnitude = -(positive.magnitude ^ -negative.magnitude)*/
|
||||
static TBigInteger xorDiffSigns(TBigInteger positive, TBigInteger negative){
|
||||
int resLength = Math.max(negative.numberLength, positive.numberLength);
|
||||
int resDigits[];
|
||||
int iNeg = negative.getFirstNonzeroDigit();
|
||||
int iPos = positive.getFirstNonzeroDigit();
|
||||
int i;
|
||||
int limit;
|
||||
|
||||
//The first
|
||||
if (iNeg < iPos) {
|
||||
resDigits = new int[resLength];
|
||||
i = iNeg;
|
||||
//resDigits[i] = -(-negative.digits[i]);
|
||||
resDigits[i] = negative.digits[i];
|
||||
limit = Math.min(negative.numberLength, iPos);
|
||||
//Skip the positive digits while they are zeros
|
||||
for (i++; i < limit; i++) {
|
||||
//resDigits[i] = ~(~negative.digits[i]);
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
//if the negative has no more elements, must fill the
|
||||
//result with the remaining digits of the positive
|
||||
if (i == negative.numberLength) {
|
||||
for ( ; i < positive.numberLength; i++) {
|
||||
//resDigits[i] = ~(positive.digits[i] ^ -1) -> ~(~positive.digits[i])
|
||||
resDigits[i] = positive.digits[i];
|
||||
}
|
||||
}
|
||||
} else if (iPos < iNeg) {
|
||||
resDigits = new int[resLength];
|
||||
i = iPos;
|
||||
//Applying two complement to the first non-zero digit of the result
|
||||
resDigits[i] = -positive.digits[i];
|
||||
limit = Math.min(positive.numberLength, iNeg);
|
||||
for (i++; i < limit; i++) {
|
||||
//Continue applying two complement the result
|
||||
resDigits[i] = ~positive.digits[i];
|
||||
}
|
||||
//When the first non-zero digit of the negative is reached, must apply
|
||||
//two complement (arithmetic negation) to it, and then operate
|
||||
if (i == iNeg) {
|
||||
resDigits[i] = ~(positive.digits[i] ^ -negative.digits[i]);
|
||||
i++;
|
||||
} else {
|
||||
//if the positive has no more elements must fill the remaining digits with
|
||||
//the negative ones
|
||||
for ( ; i < iNeg; i++) {
|
||||
// resDigits[i] = ~(0 ^ 0)
|
||||
resDigits[i] = -1;
|
||||
}
|
||||
for ( ; i < negative.numberLength; i++) {
|
||||
//resDigits[i] = ~(~negative.digits[i] ^ 0)
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int digit;
|
||||
//The first non-zero digit of the positive and negative are the same
|
||||
i = iNeg;
|
||||
digit = positive.digits[i] ^ -negative.digits[i];
|
||||
if (digit == 0) {
|
||||
limit = Math.min(positive.numberLength, negative.numberLength);
|
||||
for (i++; i < limit && (digit = positive.digits[i] ^ ~negative.digits[i]) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (digit == 0) {
|
||||
// shorter has only the remaining virtual sign bits
|
||||
for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++) {
|
||||
// do nothing
|
||||
}
|
||||
if (digit == 0) {
|
||||
resLength = resLength + 1;
|
||||
resDigits = new int[resLength];
|
||||
resDigits[resLength - 1] = 1;
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
resDigits = new int[resLength];
|
||||
resDigits[i] = -digit;
|
||||
i++;
|
||||
}
|
||||
|
||||
limit = Math.min(negative.numberLength, positive.numberLength);
|
||||
for ( ; i < limit; i++) {
|
||||
resDigits[i] = ~(~negative.digits[i] ^ positive.digits[i]);
|
||||
}
|
||||
for ( ; i < positive.numberLength; i++) {
|
||||
// resDigits[i] = ~(positive.digits[i] ^ -1)
|
||||
resDigits[i] = positive.digits[i];
|
||||
}
|
||||
for ( ; i < negative.numberLength; i++) {
|
||||
// resDigits[i] = ~(0 ^ ~negative.digits[i])
|
||||
resDigits[i] = negative.digits[i];
|
||||
}
|
||||
|
||||
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Immutable objects describing settings such as rounding mode and digit
|
||||
* precision for the numerical operations provided by class {@link TBigDecimal}.
|
||||
*/
|
||||
public final class TMathContext implements Serializable {
|
||||
|
||||
/**
|
||||
* A {@code MathContext} which corresponds to the IEEE 754r quadruple
|
||||
* decimal precision format: 34 digit precision and
|
||||
* {@link TRoundingMode#HALF_EVEN} rounding.
|
||||
*/
|
||||
public static final TMathContext DECIMAL128 = new TMathContext(34,
|
||||
TRoundingMode.HALF_EVEN);
|
||||
|
||||
/**
|
||||
* A {@code MathContext} which corresponds to the IEEE 754r single decimal
|
||||
* precision format: 7 digit precision and {@link TRoundingMode#HALF_EVEN}
|
||||
* rounding.
|
||||
*/
|
||||
public static final TMathContext DECIMAL32 = new TMathContext(7,
|
||||
TRoundingMode.HALF_EVEN);
|
||||
|
||||
/**
|
||||
* A {@code MathContext} which corresponds to the IEEE 754r double decimal
|
||||
* precision format: 16 digit precision and {@link TRoundingMode#HALF_EVEN}
|
||||
* rounding.
|
||||
*/
|
||||
public static final TMathContext DECIMAL64 = new TMathContext(16,
|
||||
TRoundingMode.HALF_EVEN);
|
||||
|
||||
/**
|
||||
* A {@code MathContext} for unlimited precision with
|
||||
* {@link TRoundingMode#HALF_UP} rounding.
|
||||
*/
|
||||
public static final TMathContext UNLIMITED = new TMathContext(0,
|
||||
TRoundingMode.HALF_UP);
|
||||
|
||||
/** This is the serialVersionUID used by the sun implementation */
|
||||
private static final long serialVersionUID = 5579720004786848255L;
|
||||
|
||||
/**
|
||||
* The number of digits to be used for an operation; results are rounded to
|
||||
* this precision.
|
||||
*/
|
||||
private int precision;
|
||||
|
||||
/**
|
||||
* A {@code RoundingMode} object which specifies the algorithm to be used
|
||||
* for rounding.
|
||||
*/
|
||||
private TRoundingMode roundingMode;
|
||||
|
||||
/**
|
||||
* An array of {@code char} containing: {@code
|
||||
* 'p','r','e','c','i','s','i','o','n','='}. It's used to improve the
|
||||
* methods related to {@code String} conversion.
|
||||
*
|
||||
* @see #MathContext(String)
|
||||
* @see #toString()
|
||||
*/
|
||||
private final static char[] chPrecision = { 'p', 'r', 'e', 'c', 'i', 's',
|
||||
'i', 'o', 'n', '=' };
|
||||
|
||||
/**
|
||||
* An array of {@code char} containing: {@code
|
||||
* 'r','o','u','n','d','i','n','g','M','o','d','e','='}. It's used to
|
||||
* improve the methods related to {@code String} conversion.
|
||||
*
|
||||
* @see #MathContext(String)
|
||||
* @see #toString()
|
||||
*/
|
||||
private final static char[] chRoundingMode = { 'r', 'o', 'u', 'n', 'd',
|
||||
'i', 'n', 'g', 'M', 'o', 'd', 'e', '=' };
|
||||
|
||||
/**
|
||||
* Constructs a new {@code MathContext} with the specified precision and
|
||||
* with the rounding mode {@link TRoundingMode#HALF_UP HALF_UP}. If the
|
||||
* precision passed is zero, then this implies that the computations have to
|
||||
* be performed exact, the rounding mode in this case is irrelevant.
|
||||
*
|
||||
* @param precision
|
||||
* the precision for the new {@code MathContext}.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code precision < 0}.
|
||||
*/
|
||||
public TMathContext(int precision) {
|
||||
this(precision, TRoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code MathContext} with the specified precision and
|
||||
* with the specified rounding mode. If the precision passed is zero, then
|
||||
* this implies that the computations have to be performed exact, the
|
||||
* rounding mode in this case is irrelevant.
|
||||
*
|
||||
* @param precision
|
||||
* the precision for the new {@code MathContext}.
|
||||
* @param roundingMode
|
||||
* the rounding mode for the new {@code MathContext}.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code precision < 0}.
|
||||
* @throws NullPointerException
|
||||
* if {@code roundingMode} is {@code null}.
|
||||
*/
|
||||
public TMathContext(int precision, TRoundingMode roundingMode) {
|
||||
if (precision < 0) {
|
||||
throw new IllegalArgumentException("Digits < 0");
|
||||
}
|
||||
if (roundingMode == null) {
|
||||
throw new NullPointerException("null RoundingMode");
|
||||
}
|
||||
this.precision = precision;
|
||||
this.roundingMode = roundingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code MathContext} from a string. The string has to
|
||||
* specify the precision and the rounding mode to be used and has to follow
|
||||
* the following syntax: "precision=<precision> roundingMode=<roundingMode>"
|
||||
* This is the same form as the one returned by the {@link #toString}
|
||||
* method.
|
||||
*
|
||||
* @param val
|
||||
* a string describing the precision and rounding mode for the
|
||||
* new {@code MathContext}.
|
||||
* @throws IllegalArgumentException
|
||||
* if the string is not in the correct format or if the
|
||||
* precision specified is < 0.
|
||||
*/
|
||||
public TMathContext(String val) {
|
||||
char[] charVal = val.toCharArray();
|
||||
int i; // Index of charVal
|
||||
int j; // Index of chRoundingMode
|
||||
int digit; // It will contain the digit parsed
|
||||
|
||||
if ((charVal.length < 27) || (charVal.length > 45)) {
|
||||
throw new IllegalArgumentException("bad string format");
|
||||
}
|
||||
// Parsing "precision=" String
|
||||
for (i = 0; (i < chPrecision.length) && (charVal[i] == chPrecision[i]); i++) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (i < chPrecision.length) {
|
||||
throw new IllegalArgumentException("bad string format");
|
||||
}
|
||||
// Parsing the value for "precision="...
|
||||
digit = Character.digit(charVal[i], 10);
|
||||
if (digit == -1) {
|
||||
throw new IllegalArgumentException("bad string format");
|
||||
}
|
||||
this.precision = this.precision * 10 + digit;
|
||||
i++;
|
||||
|
||||
do {
|
||||
digit = Character.digit(charVal[i], 10);
|
||||
if (digit == -1) {
|
||||
if (charVal[i] == ' ') {
|
||||
// It parsed all the digits
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
// It isn't a valid digit, and isn't a white space
|
||||
throw new IllegalArgumentException("bad string format");
|
||||
}
|
||||
// Accumulating the value parsed
|
||||
this.precision = this.precision * 10 + digit;
|
||||
if (this.precision < 0) {
|
||||
throw new IllegalArgumentException("bad string format");
|
||||
}
|
||||
i++;
|
||||
} while (true);
|
||||
// Parsing "roundingMode="
|
||||
for (j = 0; (j < chRoundingMode.length) && (charVal[i] == chRoundingMode[j]); i++, j++) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (j < chRoundingMode.length) {
|
||||
throw new IllegalArgumentException("bad string format");
|
||||
}
|
||||
// Parsing the value for "roundingMode"...
|
||||
this.roundingMode = TRoundingMode.valueOf(String.valueOf(charVal, i, charVal.length - i));
|
||||
}
|
||||
|
||||
/* Public Methods */
|
||||
|
||||
/**
|
||||
* Returns the precision. The precision is the number of digits used for an
|
||||
* operation. Results are rounded to this precision. The precision is
|
||||
* guaranteed to be non negative. If the precision is zero, then the
|
||||
* computations have to be performed exact, results are not rounded in this
|
||||
* case.
|
||||
*
|
||||
* @return the precision.
|
||||
*/
|
||||
public int getPrecision() {
|
||||
return precision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rounding mode. The rounding mode is the strategy to be used
|
||||
* to round results.
|
||||
* <p>
|
||||
* The rounding mode is one of
|
||||
* {@link TRoundingMode#UP},
|
||||
* {@link TRoundingMode#DOWN},
|
||||
* {@link TRoundingMode#CEILING},
|
||||
* {@link TRoundingMode#FLOOR},
|
||||
* {@link TRoundingMode#HALF_UP},
|
||||
* {@link TRoundingMode#HALF_DOWN},
|
||||
* {@link TRoundingMode#HALF_EVEN}, or
|
||||
* {@link TRoundingMode#UNNECESSARY}.
|
||||
*
|
||||
* @return the rounding mode.
|
||||
*/
|
||||
public TRoundingMode getRoundingMode() {
|
||||
return roundingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if x is a {@code MathContext} with the same precision
|
||||
* setting and the same rounding mode as this {@code MathContext} instance.
|
||||
*
|
||||
* @param x
|
||||
* object to be compared.
|
||||
* @return {@code true} if this {@code MathContext} instance is equal to the
|
||||
* {@code x} argument; {@code false} otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object x) {
|
||||
return ((x instanceof TMathContext)
|
||||
&& (((TMathContext) x).getPrecision() == precision) && (((TMathContext) x)
|
||||
.getRoundingMode() == roundingMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code for this {@code MathContext} instance.
|
||||
*
|
||||
* @return the hash code for this {@code MathContext}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Make place for the necessary bits to represent 8 rounding modes
|
||||
return ((precision << 3) | roundingMode.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation for this {@code MathContext} instance.
|
||||
* The string has the form
|
||||
* {@code
|
||||
* "precision=<precision> roundingMode=<roundingMode>"
|
||||
* } where {@code <precision>} is an integer describing the number
|
||||
* of digits used for operations and {@code <roundingMode>} is the
|
||||
* string representation of the rounding mode.
|
||||
*
|
||||
* @return a string representation for this {@code MathContext} instance
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(45);
|
||||
|
||||
sb.append(chPrecision);
|
||||
sb.append(precision);
|
||||
sb.append(' ');
|
||||
sb.append(chRoundingMode);
|
||||
sb.append(roundingMode);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* Static library that provides all multiplication of {@link TBigInteger} methods.
|
||||
*/
|
||||
class TMultiplication {
|
||||
|
||||
/** Just to denote that this class can't be instantiated. */
|
||||
private TMultiplication() {}
|
||||
|
||||
/**
|
||||
* Break point in digits (number of {@code int} elements)
|
||||
* between Karatsuba and Pencil and Paper multiply.
|
||||
*/
|
||||
static final int whenUseKaratsuba = 63; // an heuristic value
|
||||
|
||||
/**
|
||||
* An array with powers of ten that fit in the type {@code int}.
|
||||
* ({@code 10^0,10^1,...,10^9})
|
||||
*/
|
||||
static final int tenPows[] = {
|
||||
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
|
||||
};
|
||||
|
||||
/**
|
||||
* An array with powers of five that fit in the type {@code int}.
|
||||
* ({@code 5^0,5^1,...,5^13})
|
||||
*/
|
||||
static final int fivePows[] = {
|
||||
1, 5, 25, 125, 625, 3125, 15625, 78125, 390625,
|
||||
1953125, 9765625, 48828125, 244140625, 1220703125
|
||||
};
|
||||
|
||||
/**
|
||||
* An array with the first powers of ten in {@code BigInteger} version.
|
||||
* ({@code 10^0,10^1,...,10^31})
|
||||
*/
|
||||
static final TBigInteger[] bigTenPows = new TBigInteger[32];
|
||||
|
||||
/**
|
||||
* An array with the first powers of five in {@code BigInteger} version.
|
||||
* ({@code 5^0,5^1,...,5^31})
|
||||
*/
|
||||
static final TBigInteger bigFivePows[] = new TBigInteger[32];
|
||||
|
||||
|
||||
|
||||
static {
|
||||
int i;
|
||||
long fivePow = 1L;
|
||||
|
||||
for (i = 0; i <= 18; i++) {
|
||||
bigFivePows[i] = TBigInteger.valueOf(fivePow);
|
||||
bigTenPows[i] = TBigInteger.valueOf(fivePow << i);
|
||||
fivePow *= 5;
|
||||
}
|
||||
for (; i < bigTenPows.length; i++) {
|
||||
bigFivePows[i] = bigFivePows[i - 1].multiply(bigFivePows[1]);
|
||||
bigTenPows[i] = bigTenPows[i - 1].multiply(TBigInteger.TEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a multiplication of two BigInteger and hides the algorithm used.
|
||||
* @see TBigInteger#multiply(TBigInteger)
|
||||
*/
|
||||
static TBigInteger multiply(TBigInteger x, TBigInteger y) {
|
||||
return karatsuba(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the multiplication with the Karatsuba's algorithm.
|
||||
* <b>Karatsuba's algorithm:</b>
|
||||
*<tt>
|
||||
* u = u<sub>1</sub> * B + u<sub>0</sub><br>
|
||||
* v = v<sub>1</sub> * B + v<sub>0</sub><br>
|
||||
*
|
||||
*
|
||||
* u*v = (u<sub>1</sub> * v<sub>1</sub>) * B<sub>2</sub> + ((u<sub>1</sub> - u<sub>0</sub>) * (v<sub>0</sub> - v<sub>1</sub>) + u<sub>1</sub> * v<sub>1</sub> +
|
||||
* u<sub>0</sub> * v<sub>0</sub> ) * B + u<sub>0</sub> * v<sub>0</sub><br>
|
||||
*</tt>
|
||||
* @param op1 first factor of the product
|
||||
* @param op2 second factor of the product
|
||||
* @return {@code op1 * op2}
|
||||
* @see #multiply(TBigInteger, TBigInteger)
|
||||
*/
|
||||
static TBigInteger karatsuba(TBigInteger op1, TBigInteger op2) {
|
||||
TBigInteger temp;
|
||||
if (op2.numberLength > op1.numberLength) {
|
||||
temp = op1;
|
||||
op1 = op2;
|
||||
op2 = temp;
|
||||
}
|
||||
if (op2.numberLength < whenUseKaratsuba) {
|
||||
return multiplyPAP(op1, op2);
|
||||
}
|
||||
/* Karatsuba: u = u1*B + u0
|
||||
* v = v1*B + v0
|
||||
* u*v = (u1*v1)*B^2 + ((u1-u0)*(v0-v1) + u1*v1 + u0*v0)*B + u0*v0
|
||||
*/
|
||||
// ndiv2 = (op1.numberLength / 2) * 32
|
||||
int ndiv2 = (op1.numberLength & 0xFFFFFFFE) << 4;
|
||||
TBigInteger upperOp1 = op1.shiftRight(ndiv2);
|
||||
TBigInteger upperOp2 = op2.shiftRight(ndiv2);
|
||||
TBigInteger lowerOp1 = op1.subtract(upperOp1.shiftLeft(ndiv2));
|
||||
TBigInteger lowerOp2 = op2.subtract(upperOp2.shiftLeft(ndiv2));
|
||||
|
||||
TBigInteger upper = karatsuba(upperOp1, upperOp2);
|
||||
TBigInteger lower = karatsuba(lowerOp1, lowerOp2);
|
||||
TBigInteger middle = karatsuba( upperOp1.subtract(lowerOp1),
|
||||
lowerOp2.subtract(upperOp2));
|
||||
middle = middle.add(upper).add(lower);
|
||||
middle = middle.shiftLeft(ndiv2);
|
||||
upper = upper.shiftLeft(ndiv2 << 1);
|
||||
|
||||
return upper.add(middle).add(lower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two BigIntegers.
|
||||
* Implements traditional scholar algorithm described by Knuth.
|
||||
*
|
||||
* <br><tt>
|
||||
* <table border="0">
|
||||
* <tbody>
|
||||
*
|
||||
*
|
||||
* <tr>
|
||||
* <td align="center">A=</td>
|
||||
* <td>a<sub>3</sub></td>
|
||||
* <td>a<sub>2</sub></td>
|
||||
* <td>a<sub>1</sub></td>
|
||||
* <td>a<sub>0</sub></td>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
*
|
||||
*<tr>
|
||||
* <td align="center">B=</td>
|
||||
* <td></td>
|
||||
* <td>b<sub>2</sub></td>
|
||||
* <td>b<sub>1</sub></td>
|
||||
* <td>b<sub>1</sub></td>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* <td>b<sub>0</sub>*a<sub>3</sub></td>
|
||||
* <td>b<sub>0</sub>*a<sub>2</sub></td>
|
||||
* <td>b<sub>0</sub>*a<sub>1</sub></td>
|
||||
* <td>b<sub>0</sub>*a<sub>0</sub></td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* <td>b<sub>1</sub>*a<sub>3</sub></td>
|
||||
* <td>b<sub>1</sub>*a<sub>2</sub></td>
|
||||
* <td>b<sub>1</sub>*a1</td>
|
||||
* <td>b<sub>1</sub>*a0</td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>+</td>
|
||||
* <td>b<sub>2</sub>*a<sub>3</sub></td>
|
||||
* <td>b<sub>2</sub>*a<sub>2</sub></td>
|
||||
* <td>b<sub>2</sub>*a<sub>1</sub></td>
|
||||
* <td>b<sub>2</sub>*a<sub>0</sub></td>
|
||||
* </tr>
|
||||
*
|
||||
*<tr>
|
||||
* <td></td>
|
||||
*<td>______</td>
|
||||
* <td>______</td>
|
||||
* <td>______</td>
|
||||
* <td>______</td>
|
||||
* <td>______</td>
|
||||
* <td>______</td>
|
||||
*</tr>
|
||||
*
|
||||
* <tr>
|
||||
*
|
||||
* <td align="center">A*B=R=</td>
|
||||
* <td align="center">r<sub>5</sub></td>
|
||||
* <td align="center">r<sub>4</sub></td>
|
||||
* <td align="center">r<sub>3</sub></td>
|
||||
* <td align="center">r<sub>2</sub></td>
|
||||
* <td align="center">r<sub>1</sub></td>
|
||||
* <td align="center">r<sub>0</sub></td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
*
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
*</tt>
|
||||
*
|
||||
* @param op1 first factor of the multiplication {@code op1 >= 0}
|
||||
* @param op2 second factor of the multiplication {@code op2 >= 0}
|
||||
* @return a {@code BigInteger} of value {@code op1 * op2}
|
||||
*/
|
||||
static TBigInteger multiplyPAP(TBigInteger a, TBigInteger b) {
|
||||
// PRE: a >= b
|
||||
int aLen = a.numberLength;
|
||||
int bLen = b.numberLength;
|
||||
int resLength = aLen + bLen;
|
||||
int resSign = (a.sign != b.sign) ? -1 : 1;
|
||||
// A special case when both numbers don't exceed int
|
||||
if (resLength == 2) {
|
||||
long val = unsignedMultAddAdd(a.digits[0], b.digits[0], 0, 0);
|
||||
int valueLo = (int)val;
|
||||
int valueHi = (int)(val >>> 32);
|
||||
return ((valueHi == 0)
|
||||
? new TBigInteger(resSign, valueLo)
|
||||
: new TBigInteger(resSign, 2, new int[]{valueLo, valueHi}));
|
||||
}
|
||||
int[] aDigits = a.digits;
|
||||
int[] bDigits = b.digits;
|
||||
int resDigits[] = new int[resLength];
|
||||
// Common case
|
||||
multArraysPAP(aDigits, aLen, bDigits, bLen, resDigits);
|
||||
TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void multArraysPAP(int[] aDigits, int aLen, int[] bDigits, int bLen, int[] resDigits) {
|
||||
if(aLen == 0 || bLen == 0) return;
|
||||
|
||||
if(aLen == 1) {
|
||||
resDigits[bLen] = multiplyByInt(resDigits, bDigits, bLen, aDigits[0]);
|
||||
} else if(bLen == 1) {
|
||||
resDigits[aLen] = multiplyByInt(resDigits, aDigits, aLen, bDigits[0]);
|
||||
} else {
|
||||
multPAP(aDigits, bDigits, resDigits, aLen, bLen);
|
||||
}
|
||||
}
|
||||
|
||||
static void multPAP(int a[], int b[], int t[], int aLen, int bLen) {
|
||||
if(a == b && aLen == bLen) {
|
||||
square(a, aLen, t);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < aLen; i++){
|
||||
long carry = 0;
|
||||
int aI = a[i];
|
||||
for (int j = 0; j < bLen; j++){
|
||||
carry = unsignedMultAddAdd(aI, b[j], t[i+j], (int)carry);
|
||||
t[i+j] = (int) carry;
|
||||
carry >>>= 32;
|
||||
}
|
||||
t[i+bLen] = (int) carry;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies an array of integers by an integer value
|
||||
* and saves the result in {@code res}.
|
||||
* @param a the array of integers
|
||||
* @param aSize the number of elements of intArray to be multiplied
|
||||
* @param factor the multiplier
|
||||
* @return the top digit of production
|
||||
*/
|
||||
private static int multiplyByInt(int res[], int a[], final int aSize, final int factor) {
|
||||
long carry = 0;
|
||||
for (int i = 0; i < aSize; i++) {
|
||||
carry = unsignedMultAddAdd(a[i], factor, (int)carry, 0);
|
||||
res[i] = (int)carry;
|
||||
carry >>>= 32;
|
||||
}
|
||||
return (int)carry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiplies an array of integers by an integer value.
|
||||
* @param a the array of integers
|
||||
* @param aSize the number of elements of intArray to be multiplied
|
||||
* @param factor the multiplier
|
||||
* @return the top digit of production
|
||||
*/
|
||||
static int multiplyByInt(int a[], final int aSize, final int factor) {
|
||||
return multiplyByInt(a, a, aSize, factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies a number by a positive integer.
|
||||
* @param val an arbitrary {@code BigInteger}
|
||||
* @param factor a positive {@code int} number
|
||||
* @return {@code val * factor}
|
||||
*/
|
||||
static TBigInteger multiplyByPositiveInt(TBigInteger val, int factor) {
|
||||
int resSign = val.sign;
|
||||
if (resSign == 0) {
|
||||
return TBigInteger.ZERO;
|
||||
}
|
||||
int aNumberLength = val.numberLength;
|
||||
int[] aDigits = val.digits;
|
||||
|
||||
if (aNumberLength == 1) {
|
||||
long res = unsignedMultAddAdd(aDigits[0], factor, 0, 0);
|
||||
int resLo = (int)res;
|
||||
int resHi = (int)(res >>> 32);
|
||||
return ((resHi == 0)
|
||||
? new TBigInteger(resSign, resLo)
|
||||
: new TBigInteger(resSign, 2, new int[]{resLo, resHi}));
|
||||
}
|
||||
// Common case
|
||||
int resLength = aNumberLength + 1;
|
||||
int resDigits[] = new int[resLength];
|
||||
|
||||
resDigits[aNumberLength] = multiplyByInt(resDigits, aDigits, aNumberLength, factor);
|
||||
TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
|
||||
result.cutOffLeadingZeroes();
|
||||
return result;
|
||||
}
|
||||
|
||||
static TBigInteger pow(TBigInteger base, int exponent) {
|
||||
// PRE: exp > 0
|
||||
TBigInteger res = TBigInteger.ONE;
|
||||
TBigInteger acc = base;
|
||||
|
||||
for (; exponent > 1; exponent >>= 1) {
|
||||
if ((exponent & 1) != 0) {
|
||||
// if odd, multiply one more time by acc
|
||||
res = res.multiply(acc);
|
||||
}
|
||||
// acc = base^(2^i)
|
||||
//a limit where karatsuba performs a faster square than the square algorithm
|
||||
if ( acc.numberLength == 1 ){
|
||||
acc = acc.multiply(acc); // square
|
||||
}
|
||||
else{
|
||||
acc = new TBigInteger(1, square(acc.digits, acc.numberLength, new int [acc.numberLength<<1]));
|
||||
}
|
||||
}
|
||||
// exponent == 1, multiply one more time
|
||||
res = res.multiply(acc);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a<sup>2</sup>
|
||||
* @param a The number to square.
|
||||
* @param aLen The length of the number to square.
|
||||
*/
|
||||
static int[] square(int[] a, int aLen, int[] res) {
|
||||
long carry;
|
||||
|
||||
for(int i = 0; i < aLen; i++){
|
||||
carry = 0;
|
||||
for (int j = i+1; j < aLen; j++){
|
||||
carry = unsignedMultAddAdd(a[i], a[j], res[i+j], (int)carry);
|
||||
res[i+j] = (int) carry;
|
||||
carry >>>= 32;
|
||||
}
|
||||
res[i+aLen] = (int) carry;
|
||||
}
|
||||
|
||||
TBitLevel.shiftLeftOneBit(res, res, aLen << 1);
|
||||
|
||||
carry = 0;
|
||||
for(int i = 0, index = 0; i < aLen; i++, index++){
|
||||
carry = unsignedMultAddAdd(a[i], a[i], res[index],(int)carry);
|
||||
res[index] = (int) carry;
|
||||
carry >>>= 32;
|
||||
index++;
|
||||
carry += res[index] & 0xFFFFFFFFL;
|
||||
res[index] = (int)carry;
|
||||
carry >>>= 32;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies a number by a power of ten.
|
||||
* This method is used in {@code BigDecimal} class.
|
||||
* @param val the number to be multiplied
|
||||
* @param exp a positive {@code long} exponent
|
||||
* @return {@code val * 10<sup>exp</sup>}
|
||||
*/
|
||||
static TBigInteger multiplyByTenPow(TBigInteger val, long exp) {
|
||||
// PRE: exp >= 0
|
||||
return ((exp < tenPows.length)
|
||||
? multiplyByPositiveInt(val, tenPows[(int)exp])
|
||||
: val.multiply(powerOf10(exp)));
|
||||
}
|
||||
|
||||
/**
|
||||
* It calculates a power of ten, which exponent could be out of 32-bit range.
|
||||
* Note that internally this method will be used in the worst case with
|
||||
* an exponent equals to: {@code Integer.MAX_VALUE - Integer.MIN_VALUE}.
|
||||
* @param exp the exponent of power of ten, it must be positive.
|
||||
* @return a {@code BigInteger} with value {@code 10<sup>exp</sup>}.
|
||||
*/
|
||||
static TBigInteger powerOf10(long exp) {
|
||||
// PRE: exp >= 0
|
||||
int intExp = (int)exp;
|
||||
// "SMALL POWERS"
|
||||
if (exp < bigTenPows.length) {
|
||||
// The largest power that fit in 'long' type
|
||||
return bigTenPows[intExp];
|
||||
} else if (exp <= 50) {
|
||||
// To calculate: 10^exp
|
||||
return TBigInteger.TEN.pow(intExp);
|
||||
} else if (exp <= 1000) {
|
||||
// To calculate: 5^exp * 2^exp
|
||||
return bigFivePows[1].pow(intExp).shiftLeft(intExp);
|
||||
}
|
||||
// "LARGE POWERS"
|
||||
/*
|
||||
* To check if there is free memory to allocate a BigInteger of the
|
||||
* estimated size, measured in bytes: 1 + [exp / log10(2)]
|
||||
*/
|
||||
long byteArraySize = 1 + (long)(exp / 2.4082399653118496);
|
||||
|
||||
if (byteArraySize > 1000000) {
|
||||
throw new ArithmeticException("power of ten too big");
|
||||
}
|
||||
if (exp <= Integer.MAX_VALUE) {
|
||||
// To calculate: 5^exp * 2^exp
|
||||
return bigFivePows[1].pow(intExp).shiftLeft(intExp);
|
||||
}
|
||||
/*
|
||||
* "HUGE POWERS"
|
||||
*
|
||||
* This branch probably won't be executed since the power of ten is too
|
||||
* big.
|
||||
*/
|
||||
// To calculate: 5^exp
|
||||
TBigInteger powerOfFive = bigFivePows[1].pow(Integer.MAX_VALUE);
|
||||
TBigInteger res = powerOfFive;
|
||||
long longExp = exp - Integer.MAX_VALUE;
|
||||
|
||||
intExp = (int)(exp % Integer.MAX_VALUE);
|
||||
while (longExp > Integer.MAX_VALUE) {
|
||||
res = res.multiply(powerOfFive);
|
||||
longExp -= Integer.MAX_VALUE;
|
||||
}
|
||||
res = res.multiply(bigFivePows[1].pow(intExp));
|
||||
// To calculate: 5^exp << exp
|
||||
res = res.shiftLeft(Integer.MAX_VALUE);
|
||||
longExp = exp - Integer.MAX_VALUE;
|
||||
while (longExp > Integer.MAX_VALUE) {
|
||||
res = res.shiftLeft(Integer.MAX_VALUE);
|
||||
longExp -= Integer.MAX_VALUE;
|
||||
}
|
||||
res = res.shiftLeft(intExp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies a number by a power of five.
|
||||
* This method is used in {@code BigDecimal} class.
|
||||
* @param val the number to be multiplied
|
||||
* @param exp a positive {@code int} exponent
|
||||
* @return {@code val * 5<sup>exp</sup>}
|
||||
*/
|
||||
static TBigInteger multiplyByFivePow(TBigInteger val, int exp) {
|
||||
// PRE: exp >= 0
|
||||
if (exp < fivePows.length) {
|
||||
return multiplyByPositiveInt(val, fivePows[exp]);
|
||||
} else if (exp < bigFivePows.length) {
|
||||
return val.multiply(bigFivePows[exp]);
|
||||
} else {// Large powers of five
|
||||
return val.multiply(bigFivePows[1].pow(exp));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the value unsigned ((uint)a*(uint)b + (uint)c + (uint)d). This
|
||||
* method could improve the readability and performance of the code.
|
||||
*
|
||||
* @param a
|
||||
* parameter 1
|
||||
* @param b
|
||||
* parameter 2
|
||||
* @param c
|
||||
* parameter 3
|
||||
* @param d
|
||||
* parameter 4
|
||||
* @return value of expression
|
||||
*/
|
||||
static long unsignedMultAddAdd(int a, int b, int c, int d) {
|
||||
return (a & 0xFFFFFFFFL) * (b & 0xFFFFFFFFL) + (c & 0xFFFFFFFFL) + (d & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Provides primality probabilistic methods.
|
||||
*/
|
||||
class TPrimality {
|
||||
|
||||
/** Just to denote that this class can't be instantiated. */
|
||||
private TPrimality() {
|
||||
}
|
||||
|
||||
/** All prime numbers with bit length lesser than 10 bits. */
|
||||
private static final int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
||||
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
|
||||
191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
|
||||
311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
|
||||
439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
|
||||
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
|
||||
709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
|
||||
857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
|
||||
1009, 1013, 1019, 1021 };
|
||||
|
||||
/** All {@code BigInteger} prime numbers with bit length lesser than 8 bits. */
|
||||
private static final TBigInteger BIprimes[] = new TBigInteger[primes.length];
|
||||
|
||||
/**
|
||||
* It encodes how many iterations of Miller-Rabin test are need to get an
|
||||
* error bound not greater than {@code 2<sup>(-100)</sup>}. For example: for
|
||||
* a {@code 1000}-bit number we need {@code 4} iterations, since
|
||||
* {@code BITS[3] < 1000 <= BITS[4]}.
|
||||
*/
|
||||
private static final int[] BITS = { 0, 0, 1854, 1233, 927, 747, 627, 543, 480, 431, 393, 361, 335, 314, 295, 279,
|
||||
265, 253, 242, 232, 223, 216, 181, 169, 158, 150, 145, 140, 136, 132, 127, 123, 119, 114, 110, 105, 101,
|
||||
96, 92, 87, 83, 78, 73, 69, 64, 59, 54, 49, 44, 38, 32, 26, 1 };
|
||||
|
||||
/**
|
||||
* It encodes how many i-bit primes there are in the table for
|
||||
* {@code i=2,...,10}. For example {@code offsetPrimes[6]} says that from
|
||||
* index {@code 11} exists {@code 7} consecutive {@code 6}-bit prime numbers
|
||||
* in the array.
|
||||
*/
|
||||
private static final int[][] offsetPrimes = { null, null, { 0, 2 }, { 2, 2 }, { 4, 2 }, { 6, 5 }, { 11, 7 },
|
||||
{ 18, 13 }, { 31, 23 }, { 54, 43 }, { 97, 75 } };
|
||||
|
||||
static {// To initialize the dual table of BigInteger primes
|
||||
for (int i = 0; i < primes.length; i++) {
|
||||
BIprimes[i] = TBigInteger.valueOf(primes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It uses the sieve of Eratosthenes to discard several composite numbers in
|
||||
* some appropriate range (at the moment {@code [this, this + 1024]}). After
|
||||
* this process it applies the Miller-Rabin test to the numbers that were
|
||||
* not discarded in the sieve.
|
||||
*
|
||||
* @see TBigInteger#nextProbablePrime()
|
||||
* @see #millerRabin(TBigInteger, int)
|
||||
*/
|
||||
static TBigInteger nextProbablePrime(TBigInteger n) {
|
||||
// PRE: n >= 0
|
||||
int i, j;
|
||||
int certainty;
|
||||
int gapSize = 1024; // for searching of the next probable prime number
|
||||
int modules[] = new int[primes.length];
|
||||
boolean isDivisible[] = new boolean[gapSize];
|
||||
TBigInteger startPoint;
|
||||
TBigInteger probPrime;
|
||||
// If n < "last prime of table" searches next prime in the table
|
||||
if ((n.numberLength == 1) && (n.digits[0] >= 0) && (n.digits[0] < primes[primes.length - 1])) {
|
||||
for (i = 0; n.digits[0] >= primes[i]; i++) {
|
||||
// do nothing
|
||||
}
|
||||
return BIprimes[i];
|
||||
}
|
||||
/*
|
||||
* Creates a "N" enough big to hold the next probable prime Note that: N
|
||||
* < "next prime" < 2*N
|
||||
*/
|
||||
startPoint = new TBigInteger(1, n.numberLength, new int[n.numberLength + 1]);
|
||||
System.arraycopy(n.digits, 0, startPoint.digits, 0, n.numberLength);
|
||||
// To fix N to the "next odd number"
|
||||
if (n.testBit(0)) {
|
||||
TElementary.inplaceAdd(startPoint, 2);
|
||||
} else {
|
||||
startPoint.digits[0] |= 1;
|
||||
}
|
||||
// To set the improved certainly of Miller-Rabin
|
||||
j = startPoint.bitLength();
|
||||
for (certainty = 2; j < BITS[certainty]; certainty++) {
|
||||
// do nothing
|
||||
}
|
||||
// To calculate modules: N mod p1, N mod p2, ... for first primes.
|
||||
for (i = 0; i < primes.length; i++) {
|
||||
modules[i] = TDivision.remainder(startPoint, primes[i]) - gapSize;
|
||||
}
|
||||
while (true) {
|
||||
// At this point, all numbers in the gap are initialized as
|
||||
// probably primes
|
||||
Arrays.fill(isDivisible, false);
|
||||
// To discard multiples of first primes
|
||||
for (i = 0; i < primes.length; i++) {
|
||||
modules[i] = (modules[i] + gapSize) % primes[i];
|
||||
j = (modules[i] == 0) ? 0 : (primes[i] - modules[i]);
|
||||
for (; j < gapSize; j += primes[i]) {
|
||||
isDivisible[j] = true;
|
||||
}
|
||||
}
|
||||
// To execute Miller-Rabin for non-divisible numbers by all first
|
||||
// primes
|
||||
for (j = 0; j < gapSize; j++) {
|
||||
if (!isDivisible[j]) {
|
||||
probPrime = startPoint.copy();
|
||||
TElementary.inplaceAdd(probPrime, j);
|
||||
|
||||
if (millerRabin(probPrime, certainty)) {
|
||||
return probPrime;
|
||||
}
|
||||
}
|
||||
}
|
||||
TElementary.inplaceAdd(startPoint, gapSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A random number is generated until a probable prime number is found.
|
||||
*
|
||||
* @see TBigInteger#BigInteger(int,int,Random)
|
||||
* @see TBigInteger#probablePrime(int,Random)
|
||||
* @see #isProbablePrime(TBigInteger, int)
|
||||
*/
|
||||
static TBigInteger consBigInteger(int bitLength, int certainty, Random rnd) {
|
||||
// PRE: bitLength >= 2;
|
||||
// For small numbers get a random prime from the prime table
|
||||
if (bitLength <= 10) {
|
||||
int rp[] = offsetPrimes[bitLength];
|
||||
return BIprimes[rp[0] + rnd.nextInt(rp[1])];
|
||||
}
|
||||
int shiftCount = (-bitLength) & 31;
|
||||
int last = (bitLength + 31) >> 5;
|
||||
TBigInteger n = new TBigInteger(1, last, new int[last]);
|
||||
|
||||
last--;
|
||||
do {// To fill the array with random integers
|
||||
for (int i = 0; i < n.numberLength; i++) {
|
||||
n.digits[i] = rnd.nextInt();
|
||||
}
|
||||
// To fix to the correct bitLength
|
||||
n.digits[last] |= 0x80000000;
|
||||
n.digits[last] >>>= shiftCount;
|
||||
// To create an odd number
|
||||
n.digits[0] |= 1;
|
||||
} while (!isProbablePrime(n, certainty));
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TBigInteger#isProbablePrime(int)
|
||||
* @see #millerRabin(TBigInteger, int)
|
||||
* @ar.org.fitc.ref Optimizations: "A. Menezes - Handbook of applied
|
||||
* Cryptography, Chapter 4".
|
||||
*/
|
||||
static boolean isProbablePrime(TBigInteger n, int certainty) {
|
||||
// PRE: n >= 0;
|
||||
if ((certainty <= 0) || ((n.numberLength == 1) && (n.digits[0] == 2))) {
|
||||
return true;
|
||||
}
|
||||
// To discard all even numbers
|
||||
if (!n.testBit(0)) {
|
||||
return false;
|
||||
}
|
||||
// To check if 'n' exists in the table (it fit in 10 bits)
|
||||
if ((n.numberLength == 1) && ((n.digits[0] & 0XFFFFFC00) == 0)) {
|
||||
return (Arrays.binarySearch(primes, n.digits[0]) >= 0);
|
||||
}
|
||||
// To check if 'n' is divisible by some prime of the table
|
||||
for (int i = 1; i < primes.length; i++) {
|
||||
if (TDivision.remainderArrayByInt(n.digits, n.numberLength, primes[i]) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// To set the number of iterations necessary for Miller-Rabin test
|
||||
int i;
|
||||
int bitLength = n.bitLength();
|
||||
|
||||
for (i = 2; bitLength < BITS[i]; i++) {
|
||||
// do nothing
|
||||
}
|
||||
certainty = Math.min(i, 1 + ((certainty - 1) >> 1));
|
||||
|
||||
return millerRabin(n, certainty);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Miller-Rabin primality test.
|
||||
*
|
||||
* @param n
|
||||
* the input number to be tested.
|
||||
* @param t
|
||||
* the number of trials.
|
||||
* @return {@code false} if the number is definitely compose, otherwise
|
||||
* {@code true} with probability {@code 1 - 4<sup>(-t)</sup>}.
|
||||
* @ar.org.fitc.ref "D. Knuth, The Art of Computer Programming Vo.2, Section
|
||||
* 4.5.4., Algorithm P"
|
||||
*/
|
||||
private static boolean millerRabin(TBigInteger n, int t) {
|
||||
// PRE: n >= 0, t >= 0
|
||||
TBigInteger x; // x := UNIFORM{2...n-1}
|
||||
TBigInteger y; // y := x^(q * 2^j) mod n
|
||||
TBigInteger n_minus_1 = n.subtract(TBigInteger.ONE); // n-1
|
||||
int bitLength = n_minus_1.bitLength(); // ~ log2(n-1)
|
||||
// (q,k) such that: n-1 = q * 2^k and q is odd
|
||||
int k = n_minus_1.getLowestSetBit();
|
||||
TBigInteger q = n_minus_1.shiftRight(k);
|
||||
Random rnd = new Random();
|
||||
|
||||
for (int i = 0; i < t; i++) {
|
||||
// To generate a witness 'x', first it use the primes of table
|
||||
if (i < primes.length) {
|
||||
x = BIprimes[i];
|
||||
} else {/*
|
||||
* It generates random witness only if it's necesssary. Note
|
||||
* that all methods would call Miller-Rabin with t <= 50 so
|
||||
* this part is only to do more robust the algorithm
|
||||
*/
|
||||
do {
|
||||
x = new TBigInteger(bitLength, rnd);
|
||||
} while ((x.compareTo(n) >= TBigInteger.EQUALS) || (x.sign == 0) || x.isOne());
|
||||
}
|
||||
y = x.modPow(q, n);
|
||||
if (y.isOne() || y.equals(n_minus_1)) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 1; j < k; j++) {
|
||||
if (y.equals(n_minus_1)) {
|
||||
continue;
|
||||
}
|
||||
y = y.multiply(y).mod(n);
|
||||
if (y.isOne()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!y.equals(n_minus_1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.math;
|
||||
|
||||
/**
|
||||
* Specifies the rounding behavior for operations whose results cannot be
|
||||
* represented exactly.
|
||||
*/
|
||||
public enum TRoundingMode {
|
||||
|
||||
/**
|
||||
* Rounding mode where positive values are rounded towards positive infinity
|
||||
* and negative values towards negative infinity.
|
||||
* <br>
|
||||
* Rule: {@code x.round().abs() >= x.abs()}
|
||||
*/
|
||||
UP(TBigDecimal.ROUND_UP),
|
||||
|
||||
/**
|
||||
* Rounding mode where the values are rounded towards zero.
|
||||
* <br>
|
||||
* Rule: {@code x.round().abs() <= x.abs()}
|
||||
*/
|
||||
DOWN(TBigDecimal.ROUND_DOWN),
|
||||
|
||||
/**
|
||||
* Rounding mode to round towards positive infinity. For positive values
|
||||
* this rounding mode behaves as {@link #UP}, for negative values as
|
||||
* {@link #DOWN}.
|
||||
* <br>
|
||||
* Rule: {@code x.round() >= x}
|
||||
*/
|
||||
CEILING(TBigDecimal.ROUND_CEILING),
|
||||
|
||||
/**
|
||||
* Rounding mode to round towards negative infinity. For positive values
|
||||
* this rounding mode behaves as {@link #DOWN}, for negative values as
|
||||
* {@link #UP}.
|
||||
* <br>
|
||||
* Rule: {@code x.round() <= x}
|
||||
*/
|
||||
FLOOR(TBigDecimal.ROUND_FLOOR),
|
||||
|
||||
/**
|
||||
* Rounding mode where values are rounded towards the nearest neighbor. Ties
|
||||
* are broken by rounding up.
|
||||
*/
|
||||
HALF_UP(TBigDecimal.ROUND_HALF_UP),
|
||||
|
||||
/**
|
||||
* Rounding mode where values are rounded towards the nearest neighbor. Ties
|
||||
* are broken by rounding down.
|
||||
*/
|
||||
HALF_DOWN(TBigDecimal.ROUND_HALF_DOWN),
|
||||
|
||||
/**
|
||||
* Rounding mode where values are rounded towards the nearest neighbor. Ties
|
||||
* are broken by rounding to the even neighbor.
|
||||
*/
|
||||
HALF_EVEN(TBigDecimal.ROUND_HALF_EVEN),
|
||||
|
||||
/**
|
||||
* Rounding mode where the rounding operations throws an ArithmeticException
|
||||
* for the case that rounding is necessary, i.e. for the case that the value
|
||||
* cannot be represented exactly.
|
||||
*/
|
||||
UNNECESSARY(TBigDecimal.ROUND_UNNECESSARY);
|
||||
|
||||
/** The old constant of <code>BigDecimal</code>. */
|
||||
@SuppressWarnings("unused")
|
||||
private final int bigDecimalRM;
|
||||
|
||||
/** It sets the old constant. */
|
||||
TRoundingMode(int rm) {
|
||||
bigDecimalRM = rm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts rounding mode constants from class {@code BigDecimal} into
|
||||
* {@code RoundingMode} values.
|
||||
*
|
||||
* @param mode
|
||||
* rounding mode constant as defined in class {@code BigDecimal}
|
||||
* @return corresponding rounding mode object
|
||||
*/
|
||||
public static TRoundingMode valueOf(int mode) {
|
||||
switch (mode) {
|
||||
case TBigDecimal.ROUND_CEILING:
|
||||
return CEILING;
|
||||
case TBigDecimal.ROUND_DOWN:
|
||||
return DOWN;
|
||||
case TBigDecimal.ROUND_FLOOR:
|
||||
return FLOOR;
|
||||
case TBigDecimal.ROUND_HALF_DOWN:
|
||||
return HALF_DOWN;
|
||||
case TBigDecimal.ROUND_HALF_EVEN:
|
||||
return HALF_EVEN;
|
||||
case TBigDecimal.ROUND_HALF_UP:
|
||||
return HALF_UP;
|
||||
case TBigDecimal.ROUND_UNNECESSARY:
|
||||
return UNNECESSARY;
|
||||
case TBigDecimal.ROUND_UP:
|
||||
return UP;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid rounding mode");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.text;
|
||||
|
||||
public class TAnnotation {
|
||||
private Object value;
|
||||
|
||||
public TAnnotation(Object attribute) {
|
||||
value = attribute;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + "[value=" + value + ']';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.text;
|
||||
|
||||
import org.teavm.classlib.java.io.TSerializable;
|
||||
import org.teavm.classlib.java.util.TMap;
|
||||
import org.teavm.classlib.java.util.TSet;
|
||||
|
||||
public interface TAttributedCharacterIterator extends TCharacterIterator {
|
||||
|
||||
public static class Attribute implements TSerializable {
|
||||
public static final Attribute INPUT_METHOD_SEGMENT = new Attribute(
|
||||
"input_method_segment");
|
||||
|
||||
public static final Attribute LANGUAGE = new Attribute("language");
|
||||
|
||||
public static final Attribute READING = new Attribute("reading");
|
||||
|
||||
private String name;
|
||||
|
||||
protected Attribute(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object object) {
|
||||
return this == object;
|
||||
}
|
||||
|
||||
protected String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + '(' + getName() + ')';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of attributes present in the {@code
|
||||
* AttributedCharacterIterator}. An empty set is returned if no attributes
|
||||
* were defined.
|
||||
*
|
||||
* @return a set of attribute keys; may be empty.
|
||||
*/
|
||||
public TSet<Attribute> getAllAttributeKeys();
|
||||
|
||||
/**
|
||||
* Returns the value stored in the attribute for the current character. If
|
||||
* the attribute was not defined then {@code null} is returned.
|
||||
*
|
||||
* @param attribute the attribute for which the value should be returned.
|
||||
* @return the value of the requested attribute for the current character or
|
||||
* {@code null} if it was not defined.
|
||||
*/
|
||||
public Object getAttribute(Attribute attribute);
|
||||
|
||||
/**
|
||||
* Returns a map of all attributes of the current character. If no
|
||||
* attributes were defined for the current character then an empty map is
|
||||
* returned.
|
||||
*
|
||||
* @return a map of all attributes for the current character or an empty
|
||||
* map.
|
||||
*/
|
||||
public TMap<Attribute, Object> getAttributes();
|
||||
|
||||
/**
|
||||
* Returns the index of the last character in the run having the same
|
||||
* attributes as the current character.
|
||||
*
|
||||
* @return the index of the last character of the current run.
|
||||
*/
|
||||
public int getRunLimit();
|
||||
|
||||
/**
|
||||
* Returns the index of the last character in the run that has the same
|
||||
* attribute value for the given attribute as the current character.
|
||||
*
|
||||
* @param attribute
|
||||
* the attribute which the run is based on.
|
||||
* @return the index of the last character of the current run.
|
||||
*/
|
||||
public int getRunLimit(Attribute attribute);
|
||||
|
||||
/**
|
||||
* Returns the index of the last character in the run that has the same
|
||||
* attribute values for the attributes in the set as the current character.
|
||||
*
|
||||
* @param attributes
|
||||
* the set of attributes which the run is based on.
|
||||
* @return the index of the last character of the current run.
|
||||
*/
|
||||
public int getRunLimit(TSet<? extends Attribute> attributes);
|
||||
|
||||
/**
|
||||
* Returns the index of the first character in the run that has the same
|
||||
* attributes as the current character.
|
||||
*
|
||||
* @return the index of the last character of the current run.
|
||||
*/
|
||||
public int getRunStart();
|
||||
|
||||
/**
|
||||
* Returns the index of the first character in the run that has the same
|
||||
* attribute value for the given attribute as the current character.
|
||||
*
|
||||
* @param attribute
|
||||
* the attribute which the run is based on.
|
||||
* @return the index of the last character of the current run.
|
||||
*/
|
||||
public int getRunStart(Attribute attribute);
|
||||
|
||||
/**
|
||||
* Returns the index of the first character in the run that has the same
|
||||
* attribute values for the attributes in the set as the current character.
|
||||
*
|
||||
* @param attributes
|
||||
* the set of attributes which the run is based on.
|
||||
* @return the index of the last character of the current run.
|
||||
*/
|
||||
public int getRunStart(TSet<? extends Attribute> attributes);
|
||||
}
|
|
@ -0,0 +1,630 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.text;
|
||||
|
||||
import org.teavm.classlib.java.text.TAttributedCharacterIterator.Attribute;
|
||||
import org.teavm.classlib.java.util.*;
|
||||
|
||||
public class TAttributedString {
|
||||
|
||||
String text;
|
||||
|
||||
TMap<TAttributedCharacterIterator.Attribute, TList<Range>> attributeMap;
|
||||
|
||||
static class Range {
|
||||
int start;
|
||||
|
||||
int end;
|
||||
|
||||
Object value;
|
||||
|
||||
Range(int s, int e, Object v) {
|
||||
start = s;
|
||||
end = e;
|
||||
value = v;
|
||||
}
|
||||
}
|
||||
|
||||
static class AttributedIterator implements TAttributedCharacterIterator {
|
||||
|
||||
private int begin, end, offset;
|
||||
|
||||
private TAttributedString attrString;
|
||||
|
||||
private THashSet<Attribute> attributesAllowed;
|
||||
|
||||
AttributedIterator(TAttributedString attrString) {
|
||||
this.attrString = attrString;
|
||||
begin = 0;
|
||||
end = attrString.text.length();
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
AttributedIterator(TAttributedString attrString, TAttributedCharacterIterator.Attribute[] attributes, int begin,
|
||||
int end) {
|
||||
if (begin < 0 || end > attrString.text.length() || begin > end) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.begin = begin;
|
||||
this.end = end;
|
||||
offset = begin;
|
||||
this.attrString = attrString;
|
||||
if (attributes != null) {
|
||||
THashSet<Attribute> set = new THashSet<>((attributes.length * 4 / 3) + 1);
|
||||
for (int i = attributes.length; --i >= 0;) {
|
||||
set.add(attributes[i]);
|
||||
}
|
||||
attributesAllowed = set;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object clone() {
|
||||
try {
|
||||
AttributedIterator clone = (AttributedIterator) super.clone();
|
||||
if (attributesAllowed != null) {
|
||||
clone.attributesAllowed = (THashSet<Attribute>) attributesAllowed.clone();
|
||||
}
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char current() {
|
||||
if (offset == end) {
|
||||
return DONE;
|
||||
}
|
||||
return attrString.text.charAt(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char first() {
|
||||
if (begin == end) {
|
||||
return DONE;
|
||||
}
|
||||
offset = begin;
|
||||
return attrString.text.charAt(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBeginIndex() {
|
||||
return begin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndIndex() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private boolean inRange(Range range) {
|
||||
if (!(range.value instanceof TAnnotation)) {
|
||||
return true;
|
||||
}
|
||||
return range.start >= begin && range.start < end && range.end > begin && range.end <= end;
|
||||
}
|
||||
|
||||
private boolean inRange(TList<Range> ranges) {
|
||||
TIterator<Range> it = ranges.iterator();
|
||||
while (it.hasNext()) {
|
||||
Range range = it.next();
|
||||
if (range.start >= begin && range.start < end) {
|
||||
return !(range.value instanceof TAnnotation) || (range.end > begin && range.end <= end);
|
||||
} else if (range.end > begin && range.end <= end) {
|
||||
return !(range.value instanceof TAnnotation) || (range.start >= begin && range.start < end);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TSet<AttributedIterator.Attribute> getAllAttributeKeys() {
|
||||
if (begin == 0 && end == attrString.text.length() && attributesAllowed == null) {
|
||||
return attrString.attributeMap.keySet();
|
||||
}
|
||||
|
||||
TSet<AttributedIterator.Attribute> result = new THashSet<>((attrString.attributeMap.size() * 4 / 3) + 1);
|
||||
TIterator<TMap.Entry<Attribute, TList<Range>>> it = attrString.attributeMap.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
TMap.Entry<Attribute, TList<Range>> entry = it.next();
|
||||
if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) {
|
||||
TList<Range> ranges = entry.getValue();
|
||||
if (inRange(ranges)) {
|
||||
result.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object currentValue(TList<Range> ranges) {
|
||||
TIterator<Range> it = ranges.iterator();
|
||||
while (it.hasNext()) {
|
||||
Range range = it.next();
|
||||
if (offset >= range.start && offset < range.end) {
|
||||
return inRange(range) ? range.value : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(TAttributedCharacterIterator.Attribute attribute) {
|
||||
if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
|
||||
return null;
|
||||
}
|
||||
TArrayList<Range> ranges = (TArrayList<Range>) attrString.attributeMap.get(attribute);
|
||||
if (ranges == null) {
|
||||
return null;
|
||||
}
|
||||
return currentValue(ranges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TMap<Attribute, Object> getAttributes() {
|
||||
TMap<Attribute, Object> result = new THashMap<>((attrString.attributeMap.size() * 4 / 3) + 1);
|
||||
TIterator<TMap.Entry<Attribute, TList<Range>>> it = attrString.attributeMap.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
TMap.Entry<Attribute, TList<Range>> entry = it.next();
|
||||
if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) {
|
||||
Object value = currentValue(entry.getValue());
|
||||
if (value != null) {
|
||||
result.put(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRunLimit() {
|
||||
return getRunLimit(getAllAttributeKeys());
|
||||
}
|
||||
|
||||
private int runLimit(TList<Range> ranges) {
|
||||
int result = end;
|
||||
TListIterator<Range> it = ranges.listIterator(ranges.size());
|
||||
while (it.hasPrevious()) {
|
||||
Range range = it.previous();
|
||||
if (range.end <= begin) {
|
||||
break;
|
||||
}
|
||||
if (offset >= range.start && offset < range.end) {
|
||||
return inRange(range) ? range.end : result;
|
||||
} else if (offset >= range.end) {
|
||||
break;
|
||||
}
|
||||
result = range.start;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRunLimit(TAttributedCharacterIterator.Attribute attribute) {
|
||||
if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
|
||||
return end;
|
||||
}
|
||||
TArrayList<Range> ranges = (TArrayList<Range>) attrString.attributeMap.get(attribute);
|
||||
if (ranges == null) {
|
||||
return end;
|
||||
}
|
||||
return runLimit(ranges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRunLimit(TSet<? extends Attribute> attributes) {
|
||||
int limit = end;
|
||||
TIterator<? extends Attribute> it = attributes.iterator();
|
||||
while (it.hasNext()) {
|
||||
TAttributedCharacterIterator.Attribute attribute = it.next();
|
||||
int newLimit = getRunLimit(attribute);
|
||||
if (newLimit < limit) {
|
||||
limit = newLimit;
|
||||
}
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRunStart() {
|
||||
return getRunStart(getAllAttributeKeys());
|
||||
}
|
||||
|
||||
private int runStart(TList<Range> ranges) {
|
||||
int result = begin;
|
||||
TIterator<Range> it = ranges.iterator();
|
||||
while (it.hasNext()) {
|
||||
Range range = it.next();
|
||||
if (range.start >= end) {
|
||||
break;
|
||||
}
|
||||
if (offset >= range.start && offset < range.end) {
|
||||
return inRange(range) ? range.start : result;
|
||||
} else if (offset < range.start) {
|
||||
break;
|
||||
}
|
||||
result = range.end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRunStart(TAttributedCharacterIterator.Attribute attribute) {
|
||||
if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
|
||||
return begin;
|
||||
}
|
||||
TArrayList<Range> ranges = (TArrayList<Range>) attrString.attributeMap.get(attribute);
|
||||
if (ranges == null) {
|
||||
return begin;
|
||||
}
|
||||
return runStart(ranges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRunStart(TSet<? extends Attribute> attributes) {
|
||||
int start = begin;
|
||||
TIterator<? extends Attribute> it = attributes.iterator();
|
||||
while (it.hasNext()) {
|
||||
TAttributedCharacterIterator.Attribute attribute = it.next();
|
||||
int newStart = getRunStart(attribute);
|
||||
if (newStart > start) {
|
||||
start = newStart;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char last() {
|
||||
if (begin == end) {
|
||||
return DONE;
|
||||
}
|
||||
offset = end - 1;
|
||||
return attrString.text.charAt(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char next() {
|
||||
if (offset >= (end - 1)) {
|
||||
offset = end;
|
||||
return DONE;
|
||||
}
|
||||
return attrString.text.charAt(++offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char previous() {
|
||||
if (offset == begin) {
|
||||
return DONE;
|
||||
}
|
||||
return attrString.text.charAt(--offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char setIndex(int location) {
|
||||
if (location < begin || location > end) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
offset = location;
|
||||
if (offset == end) {
|
||||
return DONE;
|
||||
}
|
||||
return attrString.text.charAt(offset);
|
||||
}
|
||||
}
|
||||
|
||||
public TAttributedString(TAttributedCharacterIterator iterator) {
|
||||
if (iterator.getBeginIndex() > iterator.getEndIndex()) {
|
||||
throw new IllegalArgumentException("Invalid substring range");
|
||||
}
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = iterator.getBeginIndex(); i < iterator.getEndIndex(); i++) {
|
||||
buffer.append(iterator.current());
|
||||
iterator.next();
|
||||
}
|
||||
text = buffer.toString();
|
||||
TSet<TAttributedCharacterIterator.Attribute> attributes = iterator.getAllAttributeKeys();
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
|
||||
|
||||
TIterator<Attribute> it = attributes.iterator();
|
||||
while (it.hasNext()) {
|
||||
TAttributedCharacterIterator.Attribute attribute = it.next();
|
||||
iterator.setIndex(0);
|
||||
while (iterator.current() != TCharacterIterator.DONE) {
|
||||
int start = iterator.getRunStart(attribute);
|
||||
int limit = iterator.getRunLimit(attribute);
|
||||
Object value = iterator.getAttribute(attribute);
|
||||
if (value != null) {
|
||||
addAttribute(attribute, value, start, limit);
|
||||
}
|
||||
iterator.setIndex(limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TAttributedString(TAttributedCharacterIterator iterator, int start, int end, TSet<Attribute> attributes) {
|
||||
if (start < iterator.getBeginIndex() || end > iterator.getEndIndex() || start > end) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
iterator.setIndex(start);
|
||||
while (iterator.getIndex() < end) {
|
||||
buffer.append(iterator.current());
|
||||
iterator.next();
|
||||
}
|
||||
text = buffer.toString();
|
||||
attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
|
||||
|
||||
TIterator<Attribute> it = attributes.iterator();
|
||||
while (it.hasNext()) {
|
||||
TAttributedCharacterIterator.Attribute attribute = it.next();
|
||||
iterator.setIndex(start);
|
||||
while (iterator.getIndex() < end) {
|
||||
Object value = iterator.getAttribute(attribute);
|
||||
int runStart = iterator.getRunStart(attribute);
|
||||
int limit = iterator.getRunLimit(attribute);
|
||||
if ((value instanceof TAnnotation && runStart >= start && limit <= end) ||
|
||||
(value != null && !(value instanceof TAnnotation))) {
|
||||
addAttribute(attribute, value, (runStart < start ? start : runStart) - start, (limit > end ? end
|
||||
: limit) - start);
|
||||
}
|
||||
iterator.setIndex(limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TAttributedString(TAttributedCharacterIterator iterator, int start, int end) {
|
||||
this(iterator, start, end, iterator.getAllAttributeKeys());
|
||||
}
|
||||
|
||||
public TAttributedString(TAttributedCharacterIterator iterator, int start, int end,
|
||||
TAttributedCharacterIterator.Attribute[] attributes) {
|
||||
this(iterator, start, end, new THashSet<>(TArrays.asList(attributes)));
|
||||
}
|
||||
|
||||
public TAttributedString(String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
text = value;
|
||||
attributeMap = new THashMap<>(11);
|
||||
}
|
||||
|
||||
public TAttributedString(String value, TMap<? extends TAttributedCharacterIterator.Attribute, ?> attributes) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (value.length() == 0 && !attributes.isEmpty()) {
|
||||
throw new IllegalArgumentException("Cannot add attributes to empty string");
|
||||
}
|
||||
text = value;
|
||||
attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
|
||||
TIterator<?> it = attributes.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) it.next();
|
||||
TArrayList<Range> ranges = new TArrayList<>(1);
|
||||
ranges.add(new Range(0, text.length(), entry.getValue()));
|
||||
attributeMap.put((TAttributedCharacterIterator.Attribute) entry.getKey(), ranges);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a given attribute to this string.
|
||||
*
|
||||
* @param attribute
|
||||
* the attribute that will be applied to this string.
|
||||
* @param value
|
||||
* the value of the attribute that will be applied to this
|
||||
* string.
|
||||
* @throws IllegalArgumentException
|
||||
* if the length of this attributed string is 0.
|
||||
* @throws NullPointerException
|
||||
* if {@code attribute} is {@code null}.
|
||||
*/
|
||||
public void addAttribute(TAttributedCharacterIterator.Attribute attribute, Object value) {
|
||||
if (null == attribute) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (text.length() == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
TList<Range> ranges = attributeMap.get(attribute);
|
||||
if (ranges == null) {
|
||||
ranges = new TArrayList<>(1);
|
||||
attributeMap.put(attribute, ranges);
|
||||
} else {
|
||||
ranges.clear();
|
||||
}
|
||||
ranges.add(new Range(0, text.length(), value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a given attribute to the given range of this string.
|
||||
*
|
||||
* @param attribute
|
||||
* the attribute that will be applied to this string.
|
||||
* @param value
|
||||
* the value of the attribute that will be applied to this
|
||||
* string.
|
||||
* @param start
|
||||
* the start of the range where the attribute will be applied.
|
||||
* @param end
|
||||
* the end of the range where the attribute will be applied.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code start < 0}, {@code end} is greater than the length
|
||||
* of this string, or if {@code start >= end}.
|
||||
* @throws NullPointerException
|
||||
* if {@code attribute} is {@code null}.
|
||||
*/
|
||||
public void addAttribute(TAttributedCharacterIterator.Attribute attribute, Object value, int start, int end) {
|
||||
if (null == attribute) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (start < 0 || end > text.length() || start >= end) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TList<Range> ranges = attributeMap.get(attribute);
|
||||
if (ranges == null) {
|
||||
ranges = new TArrayList<>(1);
|
||||
ranges.add(new Range(start, end, value));
|
||||
attributeMap.put(attribute, ranges);
|
||||
return;
|
||||
}
|
||||
TListIterator<Range> it = ranges.listIterator();
|
||||
while (it.hasNext()) {
|
||||
Range range = it.next();
|
||||
if (end <= range.start) {
|
||||
it.previous();
|
||||
break;
|
||||
} else if (start < range.end || (start == range.end && value.equals(range.value))) {
|
||||
Range r1 = null, r3;
|
||||
it.remove();
|
||||
r1 = new Range(range.start, start, range.value);
|
||||
r3 = new Range(end, range.end, range.value);
|
||||
|
||||
while (end > range.end && it.hasNext()) {
|
||||
range = it.next();
|
||||
if (end <= range.end) {
|
||||
if (end > range.start || (end == range.start && value.equals(range.value))) {
|
||||
it.remove();
|
||||
r3 = new Range(end, range.end, range.value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (value.equals(r1.value)) {
|
||||
if (value.equals(r3.value)) {
|
||||
it.add(new Range(r1.start < start ? r1.start : start, r3.end > end ? r3.end : end, r1.value));
|
||||
} else {
|
||||
it.add(new Range(r1.start < start ? r1.start : start, end, r1.value));
|
||||
if (r3.start < r3.end) {
|
||||
it.add(r3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value.equals(r3.value)) {
|
||||
if (r1.start < r1.end) {
|
||||
it.add(r1);
|
||||
}
|
||||
it.add(new Range(start, r3.end > end ? r3.end : end, r3.value));
|
||||
} else {
|
||||
if (r1.start < r1.end) {
|
||||
it.add(r1);
|
||||
}
|
||||
it.add(new Range(start, end, value));
|
||||
if (r3.start < r3.end) {
|
||||
it.add(r3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
it.add(new Range(start, end, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a given set of attributes to the given range of the string.
|
||||
*
|
||||
* @param attributes
|
||||
* the set of attributes that will be applied to this string.
|
||||
* @param start
|
||||
* the start of the range where the attribute will be applied.
|
||||
* @param end
|
||||
* the end of the range where the attribute will be applied.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code start < 0}, {@code end} is greater than the length
|
||||
* of this string, or if {@code start >= end}.
|
||||
*/
|
||||
public void addAttributes(TMap<? extends TAttributedCharacterIterator.Attribute, ?> attributes, int start, int end) {
|
||||
TIterator<?> it = attributes.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) it.next();
|
||||
addAttribute((TAttributedCharacterIterator.Attribute) entry.getKey(), entry.getValue(), start, end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code AttributedCharacterIterator} that gives access to the
|
||||
* complete content of this attributed string.
|
||||
*
|
||||
* @return the newly created {@code AttributedCharacterIterator}.
|
||||
*/
|
||||
public TAttributedCharacterIterator getIterator() {
|
||||
return new AttributedIterator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code AttributedCharacterIterator} that gives access to the
|
||||
* complete content of this attributed string. Only attributes contained in
|
||||
* {@code attributes} are available from this iterator if they are defined
|
||||
* for this text.
|
||||
*
|
||||
* @param attributes
|
||||
* the array containing attributes that will be in the new
|
||||
* iterator if they are defined for this text.
|
||||
* @return the newly created {@code AttributedCharacterIterator}.
|
||||
*/
|
||||
public TAttributedCharacterIterator getIterator(TAttributedCharacterIterator.Attribute[] attributes) {
|
||||
return new AttributedIterator(this, attributes, 0, text.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code AttributedCharacterIterator} that gives access to the
|
||||
* contents of this attributed string starting at index {@code start} up to
|
||||
* index {@code end}. Only attributes contained in {@code attributes} are
|
||||
* available from this iterator if they are defined for this text.
|
||||
*
|
||||
* @param attributes
|
||||
* the array containing attributes that will be in the new
|
||||
* iterator if they are defined for this text.
|
||||
* @param start
|
||||
* the start index of the iterator on the underlying text.
|
||||
* @param end
|
||||
* the end index of the iterator on the underlying text.
|
||||
* @return the newly created {@code AttributedCharacterIterator}.
|
||||
*/
|
||||
public TAttributedCharacterIterator getIterator(TAttributedCharacterIterator.Attribute[] attributes, int start,
|
||||
int end) {
|
||||
return new AttributedIterator(this, attributes, start, end);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.text;
|
||||
|
||||
public interface TCharacterIterator extends Cloneable {
|
||||
public static final char DONE = '\uffff';
|
||||
|
||||
public Object clone();
|
||||
|
||||
public char current();
|
||||
|
||||
public char first();
|
||||
|
||||
public int getBeginIndex();
|
||||
|
||||
public int getEndIndex();
|
||||
|
||||
public int getIndex();
|
||||
|
||||
public char last();
|
||||
|
||||
public char next();
|
||||
|
||||
public char previous();
|
||||
|
||||
public char setIndex(int location);
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.teavm.classlib.java.text;
|
||||
|
||||
import org.teavm.classlib.impl.unicode.CLDRHelper;
|
||||
import org.teavm.classlib.impl.unicode.DateFormatCollection;
|
||||
import org.teavm.classlib.java.util.*;
|
||||
|
||||
public abstract class TDateFormat extends TFormat {
|
||||
protected TCalendar calendar;
|
||||
public final static int DEFAULT = 2;
|
||||
public final static int FULL = 0;
|
||||
public final static int LONG = 1;
|
||||
public final static int MEDIUM = 2;
|
||||
public final static int SHORT = 3;
|
||||
public final static int ERA_FIELD = 0;
|
||||
public final static int YEAR_FIELD = 1;
|
||||
public final static int MONTH_FIELD = 2;
|
||||
public final static int DATE_FIELD = 3;
|
||||
public final static int HOUR_OF_DAY1_FIELD = 4;
|
||||
public final static int HOUR_OF_DAY0_FIELD = 5;
|
||||
public final static int MINUTE_FIELD = 6;
|
||||
public final static int SECOND_FIELD = 7;
|
||||
public final static int MILLISECOND_FIELD = 8;
|
||||
public final static int DAY_OF_WEEK_FIELD = 9;
|
||||
public final static int DAY_OF_YEAR_FIELD = 10;
|
||||
public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
|
||||
public final static int WEEK_OF_YEAR_FIELD = 12;
|
||||
public final static int WEEK_OF_MONTH_FIELD = 13;
|
||||
public final static int AM_PM_FIELD = 14;
|
||||
public final static int HOUR1_FIELD = 15;
|
||||
public final static int HOUR0_FIELD = 16;
|
||||
public final static int TIMEZONE_FIELD = 17;
|
||||
|
||||
protected TDateFormat() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
TDateFormat clone = (TDateFormat) super.clone();
|
||||
clone.calendar = (TCalendar) calendar.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
if (!(object instanceof TDateFormat)) {
|
||||
return false;
|
||||
}
|
||||
TDateFormat dateFormat = (TDateFormat) object;
|
||||
return calendar.getFirstDayOfWeek() == dateFormat.calendar.getFirstDayOfWeek() &&
|
||||
calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar.getMinimalDaysInFirstWeek() &&
|
||||
calendar.isLenient() == dateFormat.calendar.isLenient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final StringBuffer format(Object object, StringBuffer buffer, TFieldPosition field) {
|
||||
if (object instanceof TDate) {
|
||||
return format((TDate) object, buffer, field);
|
||||
}
|
||||
if (object instanceof Number) {
|
||||
return format(new TDate(((Number) object).longValue()), buffer, field);
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
public final String format(TDate date) {
|
||||
return format(date, new StringBuffer(), new TFieldPosition(0)).toString();
|
||||
}
|
||||
|
||||
public abstract StringBuffer format(TDate date, StringBuffer buffer, TFieldPosition field);
|
||||
|
||||
public static TLocale[] getAvailableLocales() {
|
||||
return TLocale.getAvailableLocales();
|
||||
}
|
||||
|
||||
public TCalendar getCalendar() {
|
||||
return calendar;
|
||||
}
|
||||
|
||||
public static TDateFormat getDateInstance() {
|
||||
return getDateInstance(DEFAULT);
|
||||
}
|
||||
|
||||
public static TDateFormat getDateInstance(int style) {
|
||||
return getDateInstance(style, TLocale.getDefault());
|
||||
}
|
||||
|
||||
public static TDateFormat getDateInstance(int style, TLocale locale) {
|
||||
return new TSimpleDateFormat(getDateFormatString(style, locale), locale);
|
||||
}
|
||||
|
||||
private static String getDateFormatString(int style, TLocale locale) {
|
||||
DateFormatCollection formats = CLDRHelper.resolveDateFormats(locale.getLanguage(), locale.getCountry());
|
||||
switch (style) {
|
||||
case SHORT:
|
||||
return formats.getShortFormat();
|
||||
case MEDIUM:
|
||||
return formats.getMediumFormat();
|
||||
case LONG:
|
||||
return formats.getLongFormat();
|
||||
case FULL:
|
||||
return formats.getFullFormat();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown style: " + style);
|
||||
}
|
||||
}
|
||||
|
||||
public static TDateFormat getDateTimeInstance() {
|
||||
return getDateTimeInstance(DEFAULT, DEFAULT);
|
||||
}
|
||||
|
||||
public static TDateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
|
||||
return getDateTimeInstance(dateStyle, timeStyle, TLocale.getDefault());
|
||||
}
|
||||
|
||||
public static TDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TLocale locale) {
|
||||
String pattern = getDateTimeFormatString(Math.max(dateStyle, timeStyle), locale);
|
||||
pattern = pattern.replace("{0}", getTimeFormatString(dateStyle, locale))
|
||||
.replace("{1}", getDateFormatString(timeStyle, locale));
|
||||
return new TSimpleDateFormat(pattern, locale);
|
||||
}
|
||||
|
||||
public static String getDateTimeFormatString(int style, TLocale locale) {
|
||||
DateFormatCollection formats = CLDRHelper.resolveDateTimeFormats(locale.getLanguage(), locale.getCountry());
|
||||
switch (style) {
|
||||
case SHORT:
|
||||
return formats.getShortFormat();
|
||||
case MEDIUM:
|
||||
return formats.getMediumFormat();
|
||||
case LONG:
|
||||
return formats.getLongFormat();
|
||||
case FULL:
|
||||
return formats.getFullFormat();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown style: " + style);
|
||||
}
|
||||
}
|
||||
|
||||
public final static TDateFormat getInstance() {
|
||||
return getDateTimeInstance(SHORT, SHORT);
|
||||
}
|
||||
|
||||
static String getStyleName(int style) {
|
||||
String styleName;
|
||||
switch (style) {
|
||||
case SHORT:
|
||||
styleName = "SHORT";
|
||||
break;
|
||||
case MEDIUM:
|
||||
styleName = "MEDIUM";
|
||||
break;
|
||||
case LONG:
|
||||
styleName = "LONG";
|
||||
break;
|
||||
case FULL:
|
||||
styleName = "FULL";
|
||||
break;
|
||||
default:
|
||||
styleName = "";
|
||||
}
|
||||
return styleName;
|
||||
}
|
||||
|
||||
public final static TDateFormat getTimeInstance() {
|
||||
return getTimeInstance(DEFAULT);
|
||||
}
|
||||
|
||||
public static TDateFormat getTimeInstance(int style) {
|
||||
return getTimeInstance(style, TLocale.getDefault());
|
||||
}
|
||||
|
||||
public static TDateFormat getTimeInstance(int style, TLocale locale) {
|
||||
return new TSimpleDateFormat(getTimeFormatString(style, locale), locale);
|
||||
}
|
||||
|
||||
private static String getTimeFormatString(int style, TLocale locale) {
|
||||
DateFormatCollection formats = CLDRHelper.resolveTimeFormats(locale.getLanguage(), locale.getCountry());
|
||||
switch (style) {
|
||||
case SHORT:
|
||||
return formats.getShortFormat();
|
||||
case MEDIUM:
|
||||
return formats.getMediumFormat();
|
||||
case LONG:
|
||||
return formats.getLongFormat();
|
||||
case FULL:
|
||||
return formats.getFullFormat();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown style: " + style);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return calendar.getFirstDayOfWeek() + calendar.getMinimalDaysInFirstWeek() +
|
||||
(calendar.isLenient() ? 1231 : 1237);
|
||||
}
|
||||
|
||||
public boolean isLenient() {
|
||||
return calendar.isLenient();
|
||||
}
|
||||
|
||||
public TDate parse(String string) throws TParseException {
|
||||
TParsePosition position = new TParsePosition(0);
|
||||
TDate date = parse(string, position);
|
||||
if (position.getIndex() == 0) {
|
||||
throw new TParseException("Unparseable date" + string, position.getErrorIndex());
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
public abstract TDate parse(String string, TParsePosition position);
|
||||
|
||||
@Override
|
||||
public Object parseObject(String string, TParsePosition position) {
|
||||
return parse(string, position);
|
||||
}
|
||||
|
||||
public void setCalendar(TCalendar cal) {
|
||||
calendar = cal;
|
||||
}
|
||||
|
||||
public void setLenient(boolean value) {
|
||||
calendar.setLenient(value);
|
||||
}
|
||||
|
||||
public static class Field extends TFormat.Field {
|
||||
private static THashMap<Integer, Field> table = new THashMap<>();
|
||||
public final static Field ERA = new Field("era", TCalendar.ERA);
|
||||
public final static Field YEAR = new Field("year", TCalendar.YEAR);
|
||||
public final static Field MONTH = new Field("month", TCalendar.MONTH);
|
||||
public final static Field HOUR_OF_DAY0 = new Field("hour of day", TCalendar.HOUR_OF_DAY);
|
||||
public final static Field HOUR_OF_DAY1 = new Field("hour of day 1", -1);
|
||||
public final static Field MINUTE = new Field("minute", TCalendar.MINUTE);
|
||||
public final static Field SECOND = new Field("second", TCalendar.SECOND);
|
||||
public final static Field MILLISECOND = new Field("millisecond", TCalendar.MILLISECOND);
|
||||
public final static Field DAY_OF_WEEK = new Field("day of week", TCalendar.DAY_OF_WEEK);
|
||||
public final static Field DAY_OF_MONTH = new Field("day of month", TCalendar.DAY_OF_MONTH);
|
||||
public final static Field DAY_OF_YEAR = new Field("day of year", TCalendar.DAY_OF_YEAR);
|
||||
public final static Field DAY_OF_WEEK_IN_MONTH = new Field("day of week in month",
|
||||
TCalendar.DAY_OF_WEEK_IN_MONTH);
|
||||
public final static Field WEEK_OF_YEAR = new Field("week of year", TCalendar.WEEK_OF_YEAR);
|
||||
public final static Field WEEK_OF_MONTH = new Field("week of month", TCalendar.WEEK_OF_MONTH);
|
||||
public final static Field AM_PM = new Field("am pm", TCalendar.AM_PM);
|
||||
public final static Field HOUR0 = new Field("hour", TCalendar.HOUR);
|
||||
public final static Field HOUR1 = new Field("hour 1", -1);
|
||||
public final static Field TIME_ZONE = new Field("time zone", -1);
|
||||
private int calendarField = -1;
|
||||
|
||||
protected Field(String fieldName, int calendarField) {
|
||||
super(fieldName);
|
||||
this.calendarField = calendarField;
|
||||
if (calendarField != -1 && table.get(new Integer(calendarField)) == null) {
|
||||
table.put(new Integer(calendarField), this);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCalendarField() {
|
||||
return calendarField;
|
||||
}
|
||||
|
||||
public static Field ofCalendarField(int calendarField) {
|
||||
if (calendarField < 0 || calendarField >= TCalendar.FIELD_COUNT) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return table.get(new Integer(calendarField));
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user