1.1 --- a/.hgtags Tue Feb 11 10:48:24 2014 +0100
1.2 +++ b/.hgtags Tue Feb 11 13:31:42 2014 +0100
1.3 @@ -11,3 +11,4 @@
1.4 23572dc719bd630817d11eaabdee4565f63ef8e1 release-0.7.1
1.5 56abd247f421febd8b2c5e59d666968692e11555 release-0.7.2
1.6 a83e16b8b825399bb21461e578c32d86982e4ed3 release-0.8
1.7 +1b30bba2b38db83c7a232039cfd2eaf7e0e25f3f release-0.8.1
2.1 --- a/dew/nbactions.xml Tue Feb 11 10:48:24 2014 +0100
2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2.3 @@ -1,52 +0,0 @@
2.4 -<?xml version="1.0" encoding="UTF-8"?>
2.5 -<!--
2.6 -
2.7 - Back 2 Browser Bytecode Translator
2.8 - Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
2.9 -
2.10 - This program is free software: you can redistribute it and/or modify
2.11 - it under the terms of the GNU General Public License as published by
2.12 - the Free Software Foundation, version 2 of the License.
2.13 -
2.14 - This program is distributed in the hope that it will be useful,
2.15 - but WITHOUT ANY WARRANTY; without even the implied warranty of
2.16 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.17 - GNU General Public License for more details.
2.18 -
2.19 - You should have received a copy of the GNU General Public License
2.20 - along with this program. Look for COPYING file in the top folder.
2.21 - If not, see http://opensource.org/licenses/GPL-2.0.
2.22 -
2.23 --->
2.24 -<actions>
2.25 - <action>
2.26 - <actionName>run</actionName>
2.27 - <goals>
2.28 - <goal>process-classes</goal>
2.29 - <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
2.30 - </goals>
2.31 - </action>
2.32 - <action>
2.33 - <actionName>debug</actionName>
2.34 - <goals>
2.35 - <goal>process-classes</goal>
2.36 - <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
2.37 - </goals>
2.38 - <properties>
2.39 - <exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath org.apidesign.bck2brwsr.dew.Dew</exec.args>
2.40 - <exec.executable>java</exec.executable>
2.41 - <jpda.listen>true</jpda.listen>
2.42 - </properties>
2.43 - </action>
2.44 - <action>
2.45 - <actionName>profile</actionName>
2.46 - <goals>
2.47 - <goal>process-classes</goal>
2.48 - <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
2.49 - </goals>
2.50 - <properties>
2.51 - <exec.args>${profiler.args} -classpath %classpath org.apidesign.bck2brwsr.dew.Dew</exec.args>
2.52 - <exec.executable>${profiler.java}</exec.executable>
2.53 - </properties>
2.54 - </action>
2.55 - </actions>
3.1 --- a/dew/pom.xml Tue Feb 11 10:48:24 2014 +0100
3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
3.3 @@ -1,91 +0,0 @@
3.4 -<?xml version="1.0"?>
3.5 -<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">
3.6 - <modelVersion>4.0.0</modelVersion>
3.7 - <parent>
3.8 - <groupId>org.apidesign</groupId>
3.9 - <artifactId>bck2brwsr</artifactId>
3.10 - <version>0.9-SNAPSHOT</version>
3.11 - </parent>
3.12 - <groupId>org.apidesign.bck2brwsr</groupId>
3.13 - <artifactId>dew</artifactId>
3.14 - <version>0.9-SNAPSHOT</version>
3.15 - <name>Development Environment for Web</name>
3.16 - <url>http://maven.apache.org</url>
3.17 - <build>
3.18 - <plugins>
3.19 - <plugin>
3.20 - <groupId>org.apache.maven.plugins</groupId>
3.21 - <artifactId>maven-compiler-plugin</artifactId>
3.22 - <version>2.3.2</version>
3.23 - <configuration>
3.24 - <source>1.7</source>
3.25 - <target>1.7</target>
3.26 - </configuration>
3.27 - </plugin>
3.28 - <plugin>
3.29 - <groupId>org.codehaus.mojo</groupId>
3.30 - <artifactId>exec-maven-plugin</artifactId>
3.31 - <version>1.2.1</version>
3.32 - <executions>
3.33 - <execution>
3.34 - <goals>
3.35 - <goal>exec</goal>
3.36 - </goals>
3.37 - </execution>
3.38 - </executions>
3.39 - <configuration>
3.40 - <executable>java</executable>
3.41 - <arguments>
3.42 - <argument>-classpath</argument>
3.43 - <classpath />
3.44 - <argument>org.apidesign.bck2brwsr.dew.Dew</argument>
3.45 - </arguments>
3.46 - </configuration>
3.47 - </plugin>
3.48 - <plugin>
3.49 - <groupId>org.apache.maven.plugins</groupId>
3.50 - <artifactId>maven-deploy-plugin</artifactId>
3.51 - <version>2.7</version>
3.52 - <configuration>
3.53 - <skip>true</skip>
3.54 - </configuration>
3.55 - </plugin>
3.56 - </plugins>
3.57 - </build>
3.58 - <properties>
3.59 - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3.60 - </properties>
3.61 - <dependencies>
3.62 - <dependency>
3.63 - <groupId>org.glassfish.grizzly</groupId>
3.64 - <artifactId>grizzly-http-server</artifactId>
3.65 - <version>2.2.19</version>
3.66 - </dependency>
3.67 - <dependency>
3.68 - <groupId>${project.groupId}</groupId>
3.69 - <artifactId>vm4brwsr</artifactId>
3.70 - <version>${project.version}</version>
3.71 - </dependency>
3.72 - <dependency>
3.73 - <groupId>org.json</groupId>
3.74 - <artifactId>json</artifactId>
3.75 - <version>20090211</version>
3.76 - </dependency>
3.77 - <dependency>
3.78 - <groupId>org.testng</groupId>
3.79 - <artifactId>testng</artifactId>
3.80 - <scope>test</scope>
3.81 - <exclusions>
3.82 - <exclusion>
3.83 - <artifactId>junit</artifactId>
3.84 - <groupId>junit</groupId>
3.85 - </exclusion>
3.86 - </exclusions>
3.87 - </dependency>
3.88 - <dependency>
3.89 - <groupId>${project.groupId}</groupId>
3.90 - <artifactId>javaquery.api</artifactId>
3.91 - <version>${project.version}</version>
3.92 - </dependency>
3.93 - </dependencies>
3.94 -</project>
4.1 --- a/dew/src/main/java/org/apidesign/bck2brwsr/dew/Compile.java Tue Feb 11 10:48:24 2014 +0100
4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
4.3 @@ -1,203 +0,0 @@
4.4 -/**
4.5 - * Back 2 Browser Bytecode Translator
4.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.7 - *
4.8 - * This program is free software: you can redistribute it and/or modify
4.9 - * it under the terms of the GNU General Public License as published by
4.10 - * the Free Software Foundation, version 2 of the License.
4.11 - *
4.12 - * This program is distributed in the hope that it will be useful,
4.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.15 - * GNU General Public License for more details.
4.16 - *
4.17 - * You should have received a copy of the GNU General Public License
4.18 - * along with this program. Look for COPYING file in the top folder.
4.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
4.20 - */
4.21 -package org.apidesign.bck2brwsr.dew;
4.22 -
4.23 -import java.io.ByteArrayInputStream;
4.24 -import java.io.ByteArrayOutputStream;
4.25 -import java.io.IOException;
4.26 -import java.io.InputStream;
4.27 -import java.io.OutputStream;
4.28 -import java.net.URI;
4.29 -import java.net.URISyntaxException;
4.30 -import java.util.ArrayList;
4.31 -import java.util.Arrays;
4.32 -import java.util.HashMap;
4.33 -import java.util.List;
4.34 -import java.util.Map;
4.35 -import java.util.regex.Matcher;
4.36 -import java.util.regex.Pattern;
4.37 -import javax.tools.Diagnostic;
4.38 -import javax.tools.DiagnosticListener;
4.39 -import javax.tools.FileObject;
4.40 -import javax.tools.ForwardingJavaFileManager;
4.41 -import javax.tools.JavaFileManager;
4.42 -import javax.tools.JavaFileObject;
4.43 -import javax.tools.JavaFileObject.Kind;
4.44 -import javax.tools.SimpleJavaFileObject;
4.45 -import javax.tools.StandardJavaFileManager;
4.46 -import javax.tools.StandardLocation;
4.47 -import javax.tools.ToolProvider;
4.48 -
4.49 -/**
4.50 - *
4.51 - * @author Jaroslav Tulach <jtulach@netbeans.org>
4.52 - */
4.53 -final class Compile implements DiagnosticListener<JavaFileObject> {
4.54 - private final List<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<>();
4.55 - private final Map<String, byte[]> classes;
4.56 - private final String pkg;
4.57 - private final String cls;
4.58 - private final String html;
4.59 -
4.60 - private Compile(String html, String code) throws IOException {
4.61 - this.pkg = findPkg(code);
4.62 - this.cls = findCls(code);
4.63 - this.html = html;
4.64 - classes = compile(html, code);
4.65 - }
4.66 -
4.67 - /** Performs compilation of given HTML page and associated Java code
4.68 - */
4.69 - public static Compile create(String html, String code) throws IOException {
4.70 - return new Compile(html, code);
4.71 - }
4.72 -
4.73 - /** Checks for given class among compiled resources */
4.74 - public byte[] get(String res) {
4.75 - return classes.get(res);
4.76 - }
4.77 -
4.78 - /** Obtains errors created during compilation.
4.79 - */
4.80 - public List<Diagnostic<? extends JavaFileObject>> getErrors() {
4.81 - List<Diagnostic<? extends JavaFileObject>> err = new ArrayList<>();
4.82 - for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
4.83 - if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
4.84 - err.add(diagnostic);
4.85 - }
4.86 - }
4.87 - return err;
4.88 - }
4.89 -
4.90 - private Map<String, byte[]> compile(final String html, final String code) throws IOException {
4.91 - StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
4.92 -
4.93 - final Map<String, ByteArrayOutputStream> class2BAOS = new HashMap<>();
4.94 -
4.95 - JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
4.96 - @Override
4.97 - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
4.98 - return code;
4.99 - }
4.100 - };
4.101 - final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
4.102 - @Override
4.103 - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
4.104 - return html;
4.105 - }
4.106 -
4.107 - @Override
4.108 - public InputStream openInputStream() throws IOException {
4.109 - return new ByteArrayInputStream(html.getBytes());
4.110 - }
4.111 - };
4.112 -
4.113 - final URI scratch;
4.114 - try {
4.115 - scratch = new URI("mem://mem3");
4.116 - } catch (URISyntaxException ex) {
4.117 - throw new IOException(ex);
4.118 - }
4.119 -
4.120 - JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
4.121 - @Override
4.122 - public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
4.123 - if (kind == Kind.CLASS) {
4.124 - final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
4.125 -
4.126 - class2BAOS.put(className.replace('.', '/') + ".class", buffer);
4.127 - return new SimpleJavaFileObject(sibling.toUri(), kind) {
4.128 - @Override
4.129 - public OutputStream openOutputStream() throws IOException {
4.130 - return buffer;
4.131 - }
4.132 - };
4.133 - }
4.134 -
4.135 - if (kind == Kind.SOURCE) {
4.136 - return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) {
4.137 - private final ByteArrayOutputStream data = new ByteArrayOutputStream();
4.138 - @Override
4.139 - public OutputStream openOutputStream() throws IOException {
4.140 - return data;
4.141 - }
4.142 -
4.143 - @Override
4.144 - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
4.145 - data.close();
4.146 - return new String(data.toByteArray());
4.147 - }
4.148 - };
4.149 - }
4.150 -
4.151 - throw new IllegalStateException();
4.152 - }
4.153 -
4.154 - @Override
4.155 - public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
4.156 - if (location == StandardLocation.SOURCE_PATH) {
4.157 - if (packageName.equals(pkg)) {
4.158 - return htmlFile;
4.159 - }
4.160 - }
4.161 -
4.162 - return null;
4.163 - }
4.164 -
4.165 - };
4.166 -
4.167 - ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call();
4.168 -
4.169 - Map<String, byte[]> result = new HashMap<>();
4.170 -
4.171 - for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
4.172 - result.put(e.getKey(), e.getValue().toByteArray());
4.173 - }
4.174 -
4.175 - return result;
4.176 - }
4.177 -
4.178 -
4.179 - @Override
4.180 - public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
4.181 - errors.add(diagnostic);
4.182 - }
4.183 - private static String findPkg(String java) throws IOException {
4.184 - Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
4.185 - Matcher m = p.matcher(java);
4.186 - if (!m.find()) {
4.187 - throw new IOException("Can't find package declaration in the java file");
4.188 - }
4.189 - String pkg = m.group(1);
4.190 - return pkg;
4.191 - }
4.192 - private static String findCls(String java) throws IOException {
4.193 - Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
4.194 - Matcher m = p.matcher(java);
4.195 - if (!m.find()) {
4.196 - throw new IOException("Can't find package declaration in the java file");
4.197 - }
4.198 - String cls = m.group(1);
4.199 - return cls;
4.200 - }
4.201 -
4.202 - String getHtml() {
4.203 - String fqn = "'" + pkg + '.' + cls + "'";
4.204 - return html.replace("'${fqn}'", fqn);
4.205 - }
4.206 -}
5.1 --- a/dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java Tue Feb 11 10:48:24 2014 +0100
5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
5.3 @@ -1,138 +0,0 @@
5.4 -/**
5.5 - * Back 2 Browser Bytecode Translator
5.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5.7 - *
5.8 - * This program is free software: you can redistribute it and/or modify
5.9 - * it under the terms of the GNU General Public License as published by
5.10 - * the Free Software Foundation, version 2 of the License.
5.11 - *
5.12 - * This program is distributed in the hope that it will be useful,
5.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.15 - * GNU General Public License for more details.
5.16 - *
5.17 - * You should have received a copy of the GNU General Public License
5.18 - * along with this program. Look for COPYING file in the top folder.
5.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
5.20 - */
5.21 -package org.apidesign.bck2brwsr.dew;
5.22 -
5.23 -import java.io.ByteArrayInputStream;
5.24 -import java.io.IOException;
5.25 -import java.io.InputStream;
5.26 -import java.io.InputStreamReader;
5.27 -import java.io.OutputStream;
5.28 -import java.util.List;
5.29 -import java.util.Locale;
5.30 -import javax.tools.Diagnostic;
5.31 -import javax.tools.JavaFileObject;
5.32 -import org.apidesign.vm4brwsr.Bck2Brwsr;
5.33 -import org.glassfish.grizzly.http.Method;
5.34 -import org.glassfish.grizzly.http.server.HttpHandler;
5.35 -import org.glassfish.grizzly.http.server.HttpServer;
5.36 -import org.glassfish.grizzly.http.server.Request;
5.37 -import org.glassfish.grizzly.http.server.Response;
5.38 -import org.glassfish.grizzly.http.util.HttpStatus;
5.39 -import org.json.JSONArray;
5.40 -import org.json.JSONObject;
5.41 -import org.json.JSONTokener;
5.42 -
5.43 -/**
5.44 - *
5.45 - * @author phrebejk
5.46 - */
5.47 -final class Dew extends HttpHandler implements Bck2Brwsr.Resources {
5.48 - private Compile data;
5.49 -
5.50 - public static void main(String... args) throws Exception {
5.51 - DewLauncher l = new DewLauncher(null);
5.52 - l.addClassLoader(DewLauncher.class.getClassLoader());
5.53 - final Dew dew = new Dew();
5.54 - HttpServer s = l.initServer(dew);
5.55 - s.getServerConfiguration().addHttpHandler(dew, "/dew/");
5.56 - l.launchServerAndBrwsr(s, "/dew/");
5.57 - System.in.read();
5.58 - }
5.59 -
5.60 - @Override
5.61 - public void service(Request request, Response response) throws Exception {
5.62 -
5.63 - if ( request.getMethod() == Method.POST ) {
5.64 - InputStream is = request.getInputStream();
5.65 - JSONTokener tok = new JSONTokener(new InputStreamReader(is));
5.66 - JSONObject obj = new JSONObject(tok);
5.67 - String tmpHtml = obj.getString("html");
5.68 - String tmpJava = obj.getString("java");
5.69 -
5.70 - Compile res = Compile.create(tmpHtml, tmpJava);
5.71 - List<Diagnostic<? extends JavaFileObject>> err = res.getErrors();
5.72 - if (err.isEmpty()) {
5.73 - data = res;
5.74 - response.getOutputStream().write("[]".getBytes());
5.75 - response.setStatus(HttpStatus.OK_200);
5.76 - } else {
5.77 -
5.78 - JSONArray errors = new JSONArray();
5.79 -
5.80 - for (Diagnostic<? extends JavaFileObject> d : err) {
5.81 - JSONObject e = new JSONObject();
5.82 - e.put("col", d.getColumnNumber());
5.83 - e.put("line", d.getLineNumber());
5.84 - e.put("kind", d.getKind().toString());
5.85 - e.put("msg", d.getMessage(Locale.ENGLISH));
5.86 - errors.put(e);
5.87 - }
5.88 -
5.89 - errors.write(response.getWriter());
5.90 - response.setStatus(HttpStatus.PRECONDITION_FAILED_412);
5.91 - }
5.92 -
5.93 - return;
5.94 - }
5.95 -
5.96 - String r = request.getHttpHandlerPath();
5.97 - if (r == null || r.equals("/")) {
5.98 - r = "index.html";
5.99 - }
5.100 - if (r.equals("/result.html")) {
5.101 - response.setContentType("text/html");
5.102 - if (data != null) {
5.103 - response.getOutputBuffer().write(data.getHtml());
5.104 - }
5.105 - response.setStatus(HttpStatus.OK_200);
5.106 - return;
5.107 - }
5.108 -
5.109 - if (r.startsWith("/")) {
5.110 - r = r.substring(1);
5.111 - }
5.112 -
5.113 - if (r.endsWith(".html") || r.endsWith(".xhtml")) {
5.114 - response.setContentType("text/html");
5.115 - }
5.116 - OutputStream os = response.getOutputStream();
5.117 - try (InputStream is = Dew.class.getResourceAsStream(r) ) {
5.118 - copyStream(is, os, request.getRequestURL().toString() );
5.119 - } catch (IOException ex) {
5.120 - response.setDetailMessage(ex.getLocalizedMessage());
5.121 - response.setError();
5.122 - response.setStatus(404);
5.123 - }
5.124 - }
5.125 -
5.126 - static void copyStream(InputStream is, OutputStream os, String baseURL) throws IOException {
5.127 - for (;;) {
5.128 - int ch = is.read();
5.129 - if (ch == -1) {
5.130 - break;
5.131 - }
5.132 - os.write(ch);
5.133 - }
5.134 - }
5.135 -
5.136 - @Override
5.137 - public InputStream get(String r) throws IOException {
5.138 - byte[] arr = data == null ? null : data.get(r);
5.139 - return arr == null ? null : new ByteArrayInputStream(arr);
5.140 - }
5.141 -}
6.1 --- a/dew/src/main/java/org/apidesign/bck2brwsr/dew/DewLauncher.java Tue Feb 11 10:48:24 2014 +0100
6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
6.3 @@ -1,203 +0,0 @@
6.4 -/**
6.5 - * Back 2 Browser Bytecode Translator
6.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.7 - *
6.8 - * This program is free software: you can redistribute it and/or modify
6.9 - * it under the terms of the GNU General Public License as published by
6.10 - * the Free Software Foundation, version 2 of the License.
6.11 - *
6.12 - * This program is distributed in the hope that it will be useful,
6.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.15 - * GNU General Public License for more details.
6.16 - *
6.17 - * You should have received a copy of the GNU General Public License
6.18 - * along with this program. Look for COPYING file in the top folder.
6.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
6.20 - */
6.21 -package org.apidesign.bck2brwsr.dew;
6.22 -
6.23 -import java.io.IOException;
6.24 -import java.io.InputStream;
6.25 -import java.io.Writer;
6.26 -import java.net.URI;
6.27 -import java.net.URISyntaxException;
6.28 -import java.net.URL;
6.29 -import java.util.Arrays;
6.30 -import java.util.Enumeration;
6.31 -import java.util.LinkedHashSet;
6.32 -import java.util.Set;
6.33 -import java.util.logging.Level;
6.34 -import java.util.logging.Logger;
6.35 -import org.apidesign.vm4brwsr.Bck2Brwsr;
6.36 -import org.glassfish.grizzly.PortRange;
6.37 -import org.glassfish.grizzly.http.server.HttpHandler;
6.38 -import org.glassfish.grizzly.http.server.HttpServer;
6.39 -import org.glassfish.grizzly.http.server.NetworkListener;
6.40 -import org.glassfish.grizzly.http.server.Request;
6.41 -import org.glassfish.grizzly.http.server.Response;
6.42 -import org.glassfish.grizzly.http.server.ServerConfiguration;
6.43 -
6.44 -/**
6.45 - * Lightweight server to launch dew - the Development Environment for Web.
6.46 - */
6.47 -final class DewLauncher {
6.48 - private static final Logger LOG = Logger.getLogger(DewLauncher.class.getName());
6.49 - private Set<ClassLoader> loaders = new LinkedHashSet<>();
6.50 - private Set<Bck2Brwsr.Resources> xRes = new LinkedHashSet<>();
6.51 - private final Res resources = new Res();
6.52 - private final String cmd;
6.53 -
6.54 - public DewLauncher(String cmd) {
6.55 - this.cmd = cmd;
6.56 - }
6.57 -
6.58 - public void addClassLoader(ClassLoader url) {
6.59 - this.loaders.add(url);
6.60 - }
6.61 -
6.62 - final HttpServer initServer(Bck2Brwsr.Resources... extraResources) throws IOException {
6.63 - xRes.addAll(Arrays.asList(extraResources));
6.64 -
6.65 - HttpServer s = HttpServer.createSimpleServer(".", new PortRange(8080, 65535));
6.66 -
6.67 - final ServerConfiguration conf = s.getServerConfiguration();
6.68 - conf.addHttpHandler(new VM(resources), "/bck2brwsr.js");
6.69 - conf.addHttpHandler(new VMInit(), "/vm.js");
6.70 - conf.addHttpHandler(new Classes(resources), "/classes/");
6.71 - return s;
6.72 - }
6.73 -
6.74 - final Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
6.75 - server.start();
6.76 - NetworkListener listener = server.getListeners().iterator().next();
6.77 - int port = listener.getPort();
6.78 -
6.79 - URI uri = new URI("http://localhost:" + port + page);
6.80 - LOG.log(Level.INFO, "Showing {0}", uri);
6.81 - if (cmd == null) {
6.82 - try {
6.83 - LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] {
6.84 - System.getProperty("java.vm.name"),
6.85 - System.getProperty("java.vm.vendor"),
6.86 - System.getProperty("java.vm.version"),
6.87 - });
6.88 - java.awt.Desktop.getDesktop().browse(uri);
6.89 - LOG.log(Level.INFO, "Desktop.browse successfully finished");
6.90 - return null;
6.91 - } catch (UnsupportedOperationException ex) {
6.92 - LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage());
6.93 - LOG.log(Level.FINE, null, ex);
6.94 - }
6.95 - }
6.96 - {
6.97 - String cmdName = cmd == null ? "xdg-open" : cmd;
6.98 - String[] cmdArr = {
6.99 - cmdName, uri.toString()
6.100 - };
6.101 - LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr));
6.102 - final Process process = Runtime.getRuntime().exec(cmdArr);
6.103 - return new Object[] { process, null };
6.104 - }
6.105 - }
6.106 -
6.107 - private class Res implements Bck2Brwsr.Resources {
6.108 - @Override
6.109 - public InputStream get(String resource) throws IOException {
6.110 - for (ClassLoader l : loaders) {
6.111 - URL u = null;
6.112 - Enumeration<URL> en = l.getResources(resource);
6.113 - while (en.hasMoreElements()) {
6.114 - u = en.nextElement();
6.115 - }
6.116 - if (u != null) {
6.117 - return u.openStream();
6.118 - }
6.119 - }
6.120 - for (Bck2Brwsr.Resources r : xRes) {
6.121 - InputStream is = r.get(resource);
6.122 - if (is != null) {
6.123 - return is;
6.124 - }
6.125 - }
6.126 - throw new IOException("Can't find " + resource);
6.127 - }
6.128 - }
6.129 -
6.130 - private static class VM extends HttpHandler {
6.131 - private final String bck2brwsr;
6.132 -
6.133 - public VM(Res loader) throws IOException {
6.134 - StringBuilder sb = new StringBuilder();
6.135 - Bck2Brwsr.generate(sb, loader);
6.136 - this.bck2brwsr = sb.toString();
6.137 - }
6.138 -
6.139 - @Override
6.140 - public void service(Request request, Response response) throws Exception {
6.141 - response.setCharacterEncoding("UTF-8");
6.142 - response.setContentType("text/javascript");
6.143 - response.getWriter().write(bck2brwsr);
6.144 - }
6.145 - }
6.146 - private static class VMInit extends HttpHandler {
6.147 - public VMInit() {
6.148 - }
6.149 -
6.150 - @Override
6.151 - public void service(Request request, Response response) throws Exception {
6.152 - response.setCharacterEncoding("UTF-8");
6.153 - response.setContentType("text/javascript");
6.154 - response.getWriter().append(
6.155 - "function ldCls(res) {\n"
6.156 - + " var request = new XMLHttpRequest();\n"
6.157 - + " request.open('GET', '/classes/' + res, false);\n"
6.158 - + " request.send();\n"
6.159 - + " var arr = eval('(' + request.responseText + ')');\n"
6.160 - + " return arr;\n"
6.161 - + "}\n"
6.162 - + "var vm = new bck2brwsr(ldCls);\n");
6.163 - }
6.164 - }
6.165 -
6.166 - private static class Classes extends HttpHandler {
6.167 - private final Res loader;
6.168 -
6.169 - public Classes(Res loader) {
6.170 - this.loader = loader;
6.171 - }
6.172 -
6.173 - @Override
6.174 - public void service(Request request, Response response) throws Exception {
6.175 - String res = request.getHttpHandlerPath();
6.176 - if (res.startsWith("/")) {
6.177 - res = res.substring(1);
6.178 - }
6.179 - try (InputStream is = loader.get(res)) {
6.180 - response.setContentType("text/javascript");
6.181 - Writer w = response.getWriter();
6.182 - w.append("[");
6.183 - for (int i = 0;; i++) {
6.184 - int b = is.read();
6.185 - if (b == -1) {
6.186 - break;
6.187 - }
6.188 - if (i > 0) {
6.189 - w.append(", ");
6.190 - }
6.191 - if (i % 20 == 0) {
6.192 - w.write("\n");
6.193 - }
6.194 - if (b > 127) {
6.195 - b = b - 256;
6.196 - }
6.197 - w.append(Integer.toString(b));
6.198 - }
6.199 - w.append("\n]");
6.200 - } catch (IOException ex) {
6.201 - response.setError();
6.202 - response.setDetailMessage(ex.getMessage());
6.203 - }
6.204 - }
6.205 - }
6.206 -}
7.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/css/app.css Tue Feb 11 10:48:24 2014 +0100
7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
7.3 @@ -1,54 +0,0 @@
7.4 -/* app css stylesheet */
7.5 -.code-editor, .mono-font, .CodeMirror {
7.6 - font-family: "Inconsolata","Monaco","Consolas","Andale Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace;
7.7 - font-size: 13px;
7.8 - line-height: 15px;
7.9 -}
7.10 -
7.11 -.CodeMirror {
7.12 - border: 1px solid #d9edf7;
7.13 - height: 300px;
7.14 -}
7.15 -
7.16 -.CodeMirror-scroll {
7.17 - overflow-y: auto;
7.18 - overflow-x: auto;
7.19 -}
7.20 -
7.21 -.error-hover:hover {
7.22 - text-decoration: underline;
7.23 - cursor: pointer;
7.24 -}
7.25 -
7.26 -.ic-html5 {
7.27 - display: inline-block;
7.28 - height: 20px;
7.29 - width: 20px;
7.30 - vertical-align: text-bottom;
7.31 - background-repeat: no-repeat;
7.32 - background-image: url("../img/html5.png");
7.33 -}
7.34 -
7.35 -.ic-java {
7.36 - display: inline-block;
7.37 - height: 20px;
7.38 - width: 20px;
7.39 - vertical-align: text-bottom;
7.40 - background-repeat: no-repeat;
7.41 - background-image: url("../img/java.png");
7.42 -
7.43 -}
7.44 -
7.45 -.issues {
7.46 - width: 16px;
7.47 -}
7.48 -
7.49 -.issue {
7.50 - height: 16px;
7.51 - width: 16px;
7.52 - vertical-align: middle;
7.53 - background-repeat: no-repeat;
7.54 - background-image: url("../img/error.png");
7.55 - /* color: #822; */
7.56 -}
7.57 -
8.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/css/bootstrap-combined.min.css Tue Feb 11 10:48:24 2014 +0100
8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
8.3 @@ -1,18 +0,0 @@
8.4 -/*!
8.5 - * Bootstrap v2.2.2
8.6 - *
8.7 - * Copyright 2012 Twitter, Inc
8.8 - * Licensed under the Apache License v2.0
8.9 - * http://www.apache.org/licenses/LICENSE-2.0
8.10 - *
8.11 - * Designed and built with all the love in the world @twitter by @mdo and @fat.
8.12 - */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover{color:#808080}.text-warning{color:#c09853}a.text-warning:hover{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover{color:#2d6987}.text-success{color:#468847}a.text-success:hover{color:#356635}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret{border-top-color:#555;border-bottom-color:#555}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media .pull-left{margin-right:10px}.media .pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}
8.13 -/*!
8.14 - * Bootstrap Responsive v2.2.2
8.15 - *
8.16 - * Copyright 2012 Twitter, Inc
8.17 - * Licensed under the Apache License v2.0
8.18 - * http://www.apache.org/licenses/LICENSE-2.0
8.19 - *
8.20 - * Designed and built with all the love in the world @twitter by @mdo and @fat.
8.21 - */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
9.1 Binary file dew/src/main/resources/org/apidesign/bck2brwsr/dew/img/error.png has changed
10.1 Binary file dew/src/main/resources/org/apidesign/bck2brwsr/dew/img/glyphicons-halflings-white.png has changed
11.1 Binary file dew/src/main/resources/org/apidesign/bck2brwsr/dew/img/glyphicons-halflings.png has changed
12.1 Binary file dew/src/main/resources/org/apidesign/bck2brwsr/dew/img/html5.png has changed
13.1 Binary file dew/src/main/resources/org/apidesign/bck2brwsr/dew/img/java.png has changed
14.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/index.html Tue Feb 11 10:48:24 2014 +0100
14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
14.3 @@ -1,98 +0,0 @@
14.4 -<!--
14.5 -To change this template, choose Tools | Templates
14.6 -and open the template in the editor.
14.7 --->
14.8 -<!DOCTYPE html>
14.9 -<html lang="en" ng-app="bck2brwsr" ng-controller="DevCtrl">
14.10 - <head>
14.11 - <title>Back2Browser - DEW</title>
14.12 - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
14.13 - <meta name="viewport" content="width=device-width, initial-scale=1.0">
14.14 - <link rel="stylesheet" href="css/bootstrap-combined.min.css"/>
14.15 - <link rel="stylesheet" href="js/codemirror/codemirror.css">
14.16 - <link rel="stylesheet" href="js/codemirror/theme/elegant.css"/>
14.17 - <link rel="stylesheet" href="css/app.css"/>
14.18 - </head>
14.19 - <body>
14.20 -
14.21 - <div class="navbar navbar-fixed-top" style="width: 100%">
14.22 - <div class="navbar-inner" style="padding-left: 12px; padding-right: 12px;">
14.23 - <!-- a class="brand" style="font-size: 100%"><span class="text-info"><b>Java and HTML5</b></span><small>- Together at Last!</small></a-->
14.24 - <form class="navbar-form pull-right">
14.25 - <!-- select class="span2"></select -->
14.26 - <button ng-click="post()" class="btn btn-warning">Rebuild</button>
14.27 - </form>
14.28 - <!-- ul class="nav">
14.29 - <li><select class="btn-small" type="text"></select></li>
14.30 - </ul -->
14.31 - <!-- form class="form form-horizontal pull-right">
14.32 - <button class="btn btn-warning btn-small pull-right top" ng-click="post()">Rebuild</button>
14.33 - </form -->
14.34 - <!-- ul class="nav pull-right">
14.35 -
14.36 - </ul-->
14.37 - </div>
14.38 - </div>
14.39 -
14.40 - <div class="container-fluid">
14.41 -
14.42 - <div style="height: 4em;"> </div>
14.43 -
14.44 - <div class="row-fluid">
14.45 - <div class="span6" style="margin-bottom: 10px;">
14.46 - <table class="table table-condensed" style="margin-bottom: 2px">
14.47 - <tr><td><i class="ic-html5"></i> HTML5</td></tr>
14.48 - </table>
14.49 - <div>
14.50 - <textarea ui-codemirror='{ lineNumbers : true, mode : "xml", theme : "elegant", matchBrackets : true, lineWrapping : true }' ng-model="html"></textarea>
14.51 - <div class="alert alert-error" ng-show="doc.modelError">
14.52 - <small>{{doc.modelError.toString()}}</small>
14.53 - </div>
14.54 - </div>
14.55 - </div>
14.56 -
14.57 - <div class="span6">
14.58 - <table class="table table-condensed" style="margin-bottom: 2px">
14.59 - <tr><td><i class="ic-java"></i> Java</td></tr>
14.60 - </table>
14.61 - <div>
14.62 - <textarea id="editorJava" ui-codemirror='{ lineNumbers : true, mode : "text/x-java", theme : "elegant", matchBrackets : true, lineWrapping : true, gutters: ["CodeMirror-linenumbers", "issues"] }' ng-model="java"></textarea>
14.63 - <div class="alert alert-error" ng-show="doc.modelError">
14.64 - <small>{{doc.modelError.toString()}}</small>
14.65 - </div>
14.66 - </div>
14.67 - </div>
14.68 -
14.69 - </div>
14.70 -
14.71 - <table class="table table-condensed">
14.72 - <tr ng-click="gotoError(e.line, e.col)" ng-repeat="e in errors" ng-class="errorClass(e.kind)">
14.73 - <td style="text-align: right">{{e.line}}</td>
14.74 - <td>:</td>
14.75 - <td style="text-align: left">{{e.col}}</td>
14.76 - <td width="100%" class="text-error error-hover">{{e.msg}} <i class="icon-play"/></td>
14.77 - </tr>
14.78 - </table>
14.79 -
14.80 -
14.81 - <div> </div>
14.82 -
14.83 - <ul class="nav nav-tabs">
14.84 - <li ng-class="'active'"><a href="#">Result</a></li>
14.85 - </ul>
14.86 -
14.87 -
14.88 - <!-- button class="btn" ng-click="reload()">Reload</button -->
14.89 - <iframe id="result" frameborder="0" scrolling="yes" width="100%" style="height: 1000px; overflow: auto; border: 1px solid #DFDFDF;" src="result.html">
14.90 - <p>Your browser does not support iframes.</p>
14.91 - </iframe>
14.92 -
14.93 - </div>
14.94 -
14.95 - <script src="js/angular/angular.min.js"></script>
14.96 - <script src="js/codemirror/codemirror.js"></script>
14.97 - <script src="js/codemirror/mode/xml.js"></script>
14.98 - <script src="js/codemirror/mode/clike.js"></script>
14.99 - <script src="js/app.js"></script>
14.100 - </body>
14.101 -</html>
15.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/angular/angular.min.js Tue Feb 11 10:48:24 2014 +0100
15.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
15.3 @@ -1,159 +0,0 @@
15.4 -/*
15.5 - AngularJS v1.0.3
15.6 - (c) 2010-2012 Google, Inc. http://angularjs.org
15.7 - License: MIT
15.8 -*/
15.9 -(function(U,ca,p){'use strict';function m(b,a,c){var d;if(b)if(N(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==m)b.forEach(a,c);else if(L(b)&&wa(b.length))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function lb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function ec(b,a,c){for(var d=lb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}
15.10 -function mb(b){return function(a,c){b(c,a)}}function xa(){for(var b=Z.length,a;b;){b--;a=Z[b].charCodeAt(0);if(a==57)return Z[b]="A",Z.join("");if(a==90)Z[b]="0";else return Z[b]=String.fromCharCode(a+1),Z.join("")}Z.unshift("0");return Z.join("")}function x(b){m(arguments,function(a){a!==b&&m(a,function(a,d){b[d]=a})});return b}function G(b){return parseInt(b,10)}function ya(b,a){return x(new (x(function(){},{prototype:b})),a)}function D(){}function ma(b){return b}function I(b){return function(){return b}}
15.11 -function t(b){return typeof b=="undefined"}function v(b){return typeof b!="undefined"}function L(b){return b!=null&&typeof b=="object"}function F(b){return typeof b=="string"}function wa(b){return typeof b=="number"}function na(b){return Sa.apply(b)=="[object Date]"}function J(b){return Sa.apply(b)=="[object Array]"}function N(b){return typeof b=="function"}function oa(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function R(b){return F(b)?b.replace(/^\s*/,"").replace(/\s*$/,""):b}function fc(b){return b&&
15.12 -(b.nodeName||b.bind&&b.find)}function Ta(b,a,c){var d=[];m(b,function(b,g,i){d.push(a.call(c,b,g,i))});return d}function gc(b,a){var c=0,d;if(J(b)||F(b))return b.length;else if(L(b))for(d in b)(!a||b.hasOwnProperty(d))&&c++;return c}function za(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Ua(b,a){var c=za(b,a);c>=0&&b.splice(c,1);return a}function V(b,a){if(oa(b)||b&&b.$evalAsync&&b.$watch)throw B("Can't copy Window or Scope");if(a){if(b===
15.13 -a)throw B("Can't copy equivalent objects or arrays");if(J(b)){for(;a.length;)a.pop();for(var c=0;c<b.length;c++)a.push(V(b[c]))}else for(c in m(a,function(b,c){delete a[c]}),b)a[c]=V(b[c])}else(a=b)&&(J(b)?a=V(b,[]):na(b)?a=new Date(b.getTime()):L(b)&&(a=V(b,{})));return a}function hc(b,a){var a=a||{},c;for(c in b)b.hasOwnProperty(c)&&c.substr(0,2)!=="$$"&&(a[c]=b[c]);return a}function ha(b,a){if(b===a)return!0;if(b===null||a===null)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&
15.14 -c=="object")if(J(b)){if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ha(b[d],a[d]))return!1;return!0}}else if(na(b))return na(a)&&b.getTime()==a.getTime();else{if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||oa(b)||oa(a))return!1;c={};for(d in b){if(d.charAt(0)!=="$"&&!N(b[d])&&!ha(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c[d]&&d.charAt(0)!=="$"&&!N(a[d]))return!1;return!0}return!1}function Va(b,a){var c=arguments.length>2?ia.call(arguments,2):[];return N(a)&&!(a instanceof RegExp)?c.length?
15.15 -function(){return arguments.length?a.apply(b,c.concat(ia.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function ic(b,a){var c=a;/^\$+/.test(b)?c=p:oa(a)?c="$WINDOW":a&&ca===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function da(b,a){return JSON.stringify(b,ic,a?" ":null)}function nb(b){return F(b)?JSON.parse(b):b}function Wa(b){b&&b.length!==0?(b=E(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;
15.16 -return b}function pa(b){b=u(b).clone();try{b.html("")}catch(a){}return u("<div>").append(b).html().match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+E(b)})}function Xa(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=v(c[1])?decodeURIComponent(c[1]):!0)});return a}function ob(b){var a=[];m(b,function(b,d){a.push(Ya(d,!0)+(b===!0?"":"="+Ya(b,!0)))});return a.length?a.join("&"):""}function Za(b){return Ya(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,
15.17 -"=").replace(/%2B/gi,"+")}function Ya(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function jc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,i=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;m(i,function(a){i[a]=!0;c(ca.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(m(b.querySelectorAll("."+a),c),m(b.querySelectorAll("."+a+"\\:"),c),m(b.querySelectorAll("["+
15.18 -a+"]"),c))});m(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):m(a.attributes,function(b){if(!e&&i[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function pb(b,a){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=qb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,i){a.$apply(function(){b.data("$injector",i);c(b)(a)})}]);return c}function $a(b,a){a=a||"_";return b.replace(kc,
15.19 -function(b,d){return(d?a:"")+b.toLowerCase()})}function qa(b,a,c){if(!b)throw new B("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function ra(b,a,c){c&&J(b)&&(b=b[b.length-1]);qa(N(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function lc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,
15.20 -d,e){return function(){b[e||"push"]([c,d,arguments]);return j}}if(!e)throw B("No module: "+d);var b=[],c=[],k=a("$injector","invoke"),j={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:k,run:function(a){c.push(a);
15.21 -return this}};g&&k(g);return j})}})}function rb(b){return b.replace(mc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(nc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,i,f,h,k,j,l;b.length;){i=b.shift();f=0;for(h=i.length;f<h;f++){k=u(i[f]);c?k.triggerHandler("$destroy"):c=!c;j=0;for(e=(l=k.children()).length,k=e;j<k;j++)b.push(ja(l[j]))}}return d.apply(this,arguments)}var d=ja.fn[b],d=d.$original||d;c.$original=d;ja.fn[b]=c}function Q(b){if(b instanceof Q)return b;if(!(this instanceof
15.22 -Q)){if(F(b)&&b.charAt(0)!="<")throw B("selectors not implemented");return new Q(b)}if(F(b)){var a=ca.createElement("div");a.innerHTML="<div> </div>"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}else bb(this,b)}function cb(b){return b.cloneNode(!0)}function sa(b){sb(b);for(var a=0,b=b.childNodes||[];a<b.length;a++)sa(b[a])}function tb(b,a,c){var d=$(b,"events");$(b,"handle")&&(t(a)?m(d,function(a,c){db(b,c,a);delete d[c]}):t(c)?(db(b,a,d[a]),delete d[a]):Ua(d[a],c))}function sb(b){var a=
15.23 -b[Aa],c=Ba[a];c&&(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),tb(b)),delete Ba[a],b[Aa]=p)}function $(b,a,c){var d=b[Aa],d=Ba[d||-1];if(v(c))d||(b[Aa]=d=++oc,d=Ba[d]={}),d[a]=c;else return d&&d[a]}function ub(b,a,c){var d=$(b,"data"),e=v(c),g=!e&&v(a),i=g&&!L(a);!d&&!i&&$(b,"data",d={});if(e)d[a]=c;else if(g)if(i)return d&&d[a];else x(d,a);else return d}function Ca(b,a){return(" "+b.className+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" ")>-1}function vb(b,a){a&&m(a.split(" "),function(a){b.className=
15.24 -R((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+R(a)+" "," "))})}function wb(b,a){a&&m(a.split(" "),function(a){if(!Ca(b,a))b.className=R(b.className+" "+R(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&v(a.length)&&!oa(a)?a:[a],c=0;c<a.length;c++)b.push(a[c])}function xb(b,a){return Da(b,"$"+(a||"ngController")+"Controller")}function Da(b,a,c){b=u(b);for(b[0].nodeType==9&&(b=b.find("html"));b.length;){if(c=b.data(a))return c;b=b.parent()}}function yb(b,a){var c=Ea[a.toLowerCase()];
15.25 -return c&&zb[b.nodeName]&&c}function pc(b,a){var c=function(c,e){if(!c.preventDefault)c.preventDefault=function(){c.returnValue=!1};if(!c.stopPropagation)c.stopPropagation=function(){c.cancelBubble=!0};if(!c.target)c.target=c.srcElement||ca;if(t(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented};m(a[e||c.type],function(a){a.call(b,c)});aa<=8?(c.preventDefault=null,
15.26 -c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function ga(b){var a=typeof b,c;if(a=="object"&&b!==null)if(typeof(c=b.$$hashKey)=="function")c=b.$$hashKey();else{if(c===p)c=b.$$hashKey=xa()}else c=b;return a+":"+c}function Fa(b){m(b,this.put,this)}function eb(){}function Ab(b){var a,c;if(typeof b=="function"){if(!(a=b.$inject))a=[],c=b.toString().replace(qc,""),c=c.match(rc),m(c[1].split(sc),function(b){b.replace(tc,
15.27 -function(b,c,d){a.push(d)})}),b.$inject=a}else J(b)?(c=b.length-1,ra(b[c],"fn"),a=b.slice(0,c)):ra(b,"fn",!0);return a}function qb(b){function a(a){return function(b,c){if(L(b))m(b,mb(a));else return a(b,c)}}function c(a,b){N(b)&&(b=l.instantiate(b));if(!b.$get)throw B("Provider "+a+" must define $get factory method.");return j[a+f]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[];m(a,function(a){if(!k.get(a))if(k.put(a,!0),F(a)){var c=ta(a);b=b.concat(e(c.requires)).concat(c._runBlocks);
15.28 -try{for(var d=c._invokeQueue,c=0,f=d.length;c<f;c++){var h=d[c],g=h[0]=="$injector"?l:l.get(h[0]);g[h[1]].apply(g,h[2])}}catch(n){throw n.message&&(n.message+=" from "+a),n;}}else if(N(a))try{b.push(l.invoke(a))}catch(i){throw i.message&&(i.message+=" from "+a),i;}else if(J(a))try{b.push(l.invoke(a))}catch(j){throw j.message&&(j.message+=" from "+String(a[a.length-1])),j;}else ra(a,"module")});return b}function g(a,b){function c(d){if(typeof d!=="string")throw B("Service name expected");if(a.hasOwnProperty(d)){if(a[d]===
15.29 -i)throw B("Circular dependency: "+h.join(" <- "));return a[d]}else try{return h.unshift(d),a[d]=i,a[d]=b(d)}finally{h.shift()}}function d(a,b,e){var f=[],k=Ab(a),g,n,i;n=0;for(g=k.length;n<g;n++)i=k[n],f.push(e&&e.hasOwnProperty(i)?e[i]:c(i,h));a.$inject||(a=a[g]);switch(b?-1:f.length){case 0:return a();case 1:return a(f[0]);case 2:return a(f[0],f[1]);case 3:return a(f[0],f[1],f[2]);case 4:return a(f[0],f[1],f[2],f[3]);case 5:return a(f[0],f[1],f[2],f[3],f[4]);case 6:return a(f[0],f[1],f[2],f[3],
15.30 -f[4],f[5]);case 7:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6]);case 8:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7]);case 9:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]);case 10:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9]);default:return a.apply(b,f)}}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(J(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return L(e)?e:c},get:c,annotate:Ab}}var i={},f="Provider",h=[],k=new Fa,j={$provide:{provider:a(c),
15.31 -factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,I(b))}),constant:a(function(a,b){j[a]=b;o[a]=b}),decorator:function(a,b){var c=l.get(a+f),d=c.$get;c.$get=function(){var a=r.invoke(d,c);return r.invoke(b,null,{$delegate:a})}}}},l=g(j,function(){throw B("Unknown provider: "+h.join(" <- "));}),o={},r=o.$injector=g(o,function(a){a=l.get(a+f);return r.invoke(a.$get,a)});m(e(b),function(a){r.invoke(a||D)});return r}function uc(){var b=
15.32 -!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;m(a,function(a){!b&&E(a.nodeName)==="a"&&(b=a)});return b}function g(){var b=c.hash(),d;b?(d=i.getElementById(b))?d.scrollIntoView():(d=e(i.getElementsByName(b)))?d.scrollIntoView():b==="top"&&a.scrollTo(0,0):a.scrollTo(0,0)}var i=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(g)});return g}]}function vc(b,a,c,d){function e(a){try{a.apply(null,
15.33 -ia.call(arguments,1))}finally{if(n--,n===0)for(;w.length;)try{w.pop()()}catch(b){c.error(b)}}}function g(a,b){(function ea(){m(q,function(a){a()});s=b(ea,a)})()}function i(){O!=f.url()&&(O=f.url(),m(A,function(a){a(f.url())}))}var f=this,h=a[0],k=b.location,j=b.history,l=b.setTimeout,o=b.clearTimeout,r={};f.isMock=!1;var n=0,w=[];f.$$completeOutstandingRequest=e;f.$$incOutstandingRequestCount=function(){n++};f.notifyWhenNoOutstandingRequests=function(a){m(q,function(a){a()});n===0?a():w.push(a)};
15.34 -var q=[],s;f.addPollFn=function(a){t(s)&&g(100,l);q.push(a);return a};var O=k.href,C=a.find("base");f.url=function(a,b){if(a){if(O!=a)return O=a,d.history?b?j.replaceState(null,"",a):(j.pushState(null,"",a),C.attr("href",C.attr("href"))):b?k.replace(a):k.href=a,f}else return k.href.replace(/%27/g,"'")};var A=[],K=!1;f.onUrlChange=function(a){K||(d.history&&u(b).bind("popstate",i),d.hashchange?u(b).bind("hashchange",i):f.addPollFn(i),K=!0);A.push(a);return a};f.baseHref=function(){var a=C.attr("href");
15.35 -return a?a.replace(/^https?\:\/\/[^\/]*/,""):a};var W={},y="",M=f.baseHref();f.cookies=function(a,b){var d,e,f,k;if(a)if(b===p)h.cookie=escape(a)+"=;path="+M+";expires=Thu, 01 Jan 1970 00:00:00 GMT";else{if(F(b))d=(h.cookie=escape(a)+"="+escape(b)+";path="+M).length+1,d>4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"),W.length>20&&c.warn("Cookie '"+a+"' possibly not set or overflowed because too many cookies were already set ("+W.length+
15.36 -" > 20 )")}else{if(h.cookie!==y){y=h.cookie;d=y.split("; ");W={};for(f=0;f<d.length;f++)e=d[f],k=e.indexOf("="),k>0&&(W[unescape(e.substring(0,k))]=unescape(e.substring(k+1)))}return W}};f.defer=function(a,b){var c;n++;c=l(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};f.defer.cancel=function(a){return r[a]?(delete r[a],o(a),e(D),!0):!1}}function wc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new vc(b,d,a,c)}]}function xc(){this.$get=function(){function b(b,
15.37 -d){function e(a){if(a!=l){if(o){if(o==a)o=a.n}else o=a;g(a.n,a.p);g(a,l);l=a;l.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw B("cacheId "+b+" taken");var i=0,f=x({},d,{id:b}),h={},k=d&&d.capacity||Number.MAX_VALUE,j={},l=null,o=null;return a[b]={put:function(a,b){var c=j[a]||(j[a]={key:a});e(c);t(b)||(a in h||i++,h[a]=b,i>k&&this.remove(o.key))},get:function(a){var b=j[a];if(b)return e(b),h[a]},remove:function(a){var b=j[a];if(b){if(b==l)l=b.p;if(b==o)o=b.n;g(b.n,b.p);delete j[a];
15.38 -delete h[a];i--}},removeAll:function(){h={};i=0;j={};l=o=null},destroy:function(){j=f=h=null;delete a[b]},info:function(){return x({},f,{size:i})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function yc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Bb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ";
15.39 -this.directive=function f(d,e){F(d)?(qa(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];m(a[d],function(a){try{var f=b.invoke(a);if(N(f))f={compile:I(f)};else if(!f.compile&&f.link)f.compile=I(f.link);f.priority=f.priority||0;f.name=f.name||d;f.require=f.require||f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(k){c(k)}});return e}])),a[d].push(e)):m(d,mb(f));return this};this.$get=["$injector","$interpolate","$exceptionHandler",
15.40 -"$http","$templateCache","$parse","$controller","$rootScope",function(b,h,k,j,l,o,r,n){function w(a,b,c){a instanceof u||(a=u(a));m(a,function(b,c){b.nodeType==3&&(a[c]=u(b).wrap("<span></span>").parent()[0])});var d=s(a,b,a,c);return function(b,c){qa(b,"scope");var e=c?ua.clone.call(a):a;e.data("$scope",b);q(e,"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function q(a,b){try{a.addClass(b)}catch(c){}}function s(a,b,c,d){function e(a,c,d,k){for(var g,h,j,n,o,l=0,r=0,q=f.length;l<q;r++)j=c[r],g=f[l++],
15.41 -h=f[l++],g?(g.scope?(n=a.$new(L(g.scope)),u(j).data("$scope",n)):n=a,(o=g.transclude)||!k&&b?g(h,n,j,d,function(b){return function(c){var d=a.$new();return b(d,c).bind("$destroy",Va(d,d.$destroy))}}(o||b)):g(h,n,j,p,k)):h&&h(a,j.childNodes,p,k)}for(var f=[],k,g,h,j=0;j<a.length;j++)g=new ea,k=O(a[j],[],g,d),g=(k=k.length?C(k,a[j],g,b,c):null)&&k.terminal||!a[j].childNodes.length?null:s(a[j].childNodes,k?k.transclude:b),f.push(k),f.push(g),h=h||k||g;return h?e:null}function O(a,b,c,f){var k=c.$attr,
15.42 -g;switch(a.nodeType){case 1:A(b,fa(Cb(a).toLowerCase()),"E",f);var h,j,n;g=a.attributes;for(var o=0,l=g&&g.length;o<l;o++)if(h=g[o],h.specified)j=h.name,n=fa(j.toLowerCase()),k[n]=j,c[n]=h=R(aa&&j=="href"?decodeURIComponent(a.getAttribute(j,2)):h.value),yb(a,n)&&(c[n]=!0),X(a,b,h,n),A(b,n,"A",f);a=a.className;if(F(a)&&a!=="")for(;g=e.exec(a);)n=fa(g[2]),A(b,n,"C",f)&&(c[n]=R(g[3])),a=a.substr(g.index+g[0].length);break;case 3:H(b,a.nodeValue);break;case 8:try{if(g=d.exec(a.nodeValue))n=fa(g[1]),A(b,
15.43 -n,"M",f)&&(c[n]=R(g[2]))}catch(r){}}b.sort(y);return b}function C(a,b,c,d,e){function f(a,b){if(a)a.require=z.require,l.push(a);if(b)b.require=z.require,ba.push(b)}function h(a,b){var c,d="data",e=!1;if(F(a)){for(;(c=a.charAt(0))=="^"||c=="?";)a=a.substr(1),c=="^"&&(d="inheritedData"),e=e||c=="?";c=b[d]("$"+a+"Controller");if(!c&&!e)throw B("No controller: "+a);}else J(a)&&(c=[],m(a,function(a){c.push(h(a,b))}));return c}function j(a,d,e,f,g){var n,q,w,K,s;n=b===e?c:hc(c,new ea(u(e),c.$attr));q=n.$$element;
15.44 -if(C){var zc=/^\s*([@=&])\s*(\w*)\s*$/,O=d.$parent||d;m(C.scope,function(a,b){var c=a.match(zc)||[],e=c[2]||b,f,g,k;switch(c[1]){case "@":n.$observe(e,function(a){d[b]=a});n.$$observers[e].$$scope=O;break;case "=":g=o(n[e]);k=g.assign||function(){f=d[b]=g(O);throw B(Db+n[e]+" (directive: "+C.name+")");};f=d[b]=g(O);d.$watch(function(){var a=g(O);a!==d[b]&&(a!==f?f=d[b]=a:k(O,a=f=d[b]));return a});break;case "&":g=o(n[e]);d[b]=function(a){return g(O,a)};break;default:throw B("Invalid isolate scope definition for directive "+
15.45 -C.name+": "+a);}})}t&&m(t,function(a){var b={$scope:d,$element:q,$attrs:n,$transclude:g};s=a.controller;s=="@"&&(s=n[a.name]);q.data("$"+a.name+"Controller",r(s,b))});f=0;for(w=l.length;f<w;f++)try{K=l[f],K(d,q,n,K.require&&h(K.require,q))}catch(y){k(y,pa(q))}a&&a(d,e.childNodes,p,g);f=0;for(w=ba.length;f<w;f++)try{K=ba[f],K(d,q,n,K.require&&h(K.require,q))}catch(Ha){k(Ha,pa(q))}}for(var n=-Number.MAX_VALUE,l=[],ba=[],s=null,C=null,A=null,y=c.$$element=u(b),z,H,X,D,v=d,t,x,Y,E=0,G=a.length;E<G;E++){z=
15.46 -a[E];X=p;if(n>z.priority)break;if(Y=z.scope)M("isolated scope",C,z,y),L(Y)&&(q(y,"ng-isolate-scope"),C=z),q(y,"ng-scope"),s=s||z;H=z.name;if(Y=z.controller)t=t||{},M("'"+H+"' controller",t[H],z,y),t[H]=z;if(Y=z.transclude)M("transclusion",D,z,y),D=z,n=z.priority,Y=="element"?(X=u(b),y=c.$$element=u("<\!-- "+H+": "+c[H]+" --\>"),b=y[0],Ga(e,u(X[0]),b),v=w(X,d,n)):(X=u(cb(b)).contents(),y.html(""),v=w(X,d));if(Y=z.template)if(M("template",A,z,y),A=z,Y=Ha(Y),z.replace){X=u("<div>"+R(Y)+"</div>").contents();
15.47 -b=X[0];if(X.length!=1||b.nodeType!==1)throw new B(g+Y);Ga(e,y,b);H={$attr:{}};a=a.concat(O(b,a.splice(E+1,a.length-(E+1)),H));K(c,H);G=a.length}else y.html(Y);if(z.templateUrl)M("template",A,z,y),A=z,j=W(a.splice(E,a.length-E),j,y,c,e,z.replace,v),G=a.length;else if(z.compile)try{x=z.compile(y,c,v),N(x)?f(null,x):x&&f(x.pre,x.post)}catch(I){k(I,pa(y))}if(z.terminal)j.terminal=!0,n=Math.max(n,z.priority)}j.scope=s&&s.scope;j.transclude=D&&v;return j}function A(d,e,g,h){var j=!1;if(a.hasOwnProperty(e))for(var n,
15.48 -e=b.get(e+c),o=0,l=e.length;o<l;o++)try{if(n=e[o],(h===p||h>n.priority)&&n.restrict.indexOf(g)!=-1)d.push(n),j=!0}catch(r){k(r)}return j}function K(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b,function(b,f){f=="class"?(q(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):f=="style"?e.attr("style",e.attr("style")+";"+b):f.charAt(0)!="$"&&!a.hasOwnProperty(f)&&(a[f]=b,d[f]=c[f])})}function W(a,b,c,d,e,
15.49 -f,k){var h=[],n,o,r=c[0],q=a.shift(),w=x({},q,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");j.get(q.templateUrl,{cache:l}).success(function(j){var l,q,j=Ha(j);if(f){q=u("<div>"+R(j)+"</div>").contents();l=q[0];if(q.length!=1||l.nodeType!==1)throw new B(g+j);j={$attr:{}};Ga(e,c,l);O(l,a,j);K(d,j)}else l=r,c.html(j);a.unshift(w);n=C(a,c,d,k);for(o=s(c.contents(),k);h.length;){var ba=h.pop(),j=h.pop();q=h.pop();var y=h.pop(),m=l;q!==r&&(m=cb(l),Ga(j,u(q),m));n(function(){b(o,
15.50 -y,m,e,ba)},y,m,e,ba)}h=null}).error(function(a,b,c,d){throw B("Failed to load template: "+d.url);});return function(a,c,d,e,f){h?(h.push(c),h.push(d),h.push(e),h.push(f)):n(function(){b(o,c,d,e,f)},c,d,e,f)}}function y(a,b){return b.priority-a.priority}function M(a,b,c,d){if(b)throw B("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+pa(d));}function H(a,b){var c=h(b,!0);c&&a.push({priority:0,compile:I(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);q(d.data("$binding",
15.51 -e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function X(a,b,c,d){var e=h(c,!0);e&&b.push({priority:100,compile:I(function(a,b,c){b=c.$$observers||(c.$$observers={});d==="class"&&(e=h(c[d],!0));c[d]=p;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,a)})})})}function Ga(a,b,c){var d=b[0],e=d.parentNode,f,g;if(a){f=0;for(g=a.length;f<g;f++)if(a[f]==d){a[f]=c;break}}e&&e.replaceChild(c,d);c[u.expando]=d[u.expando];b[0]=c}var ea=
15.52 -function(a,b){this.$$element=a;this.$attr=b||{}};ea.prototype={$normalize:fa,$set:function(a,b,c,d){var e=yb(this.$$element[0],a),f=this.$$observers;e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=$a(a,"-"));c!==!1&&(b===null||b===p?this.$$element.removeAttr(d):this.$$element.attr(d,b));f&&m(f[a],function(a){try{a(b)}catch(c){k(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);n.$evalAsync(function(){e.$$inter||
15.53 -b(c[a])});return b}};var D=h.startSymbol(),ba=h.endSymbol(),Ha=D=="{{"||ba=="}}"?ma:function(a){return a.replace(/\{\{/g,D).replace(/}}/g,ba)};return w}]}function fa(b){return rb(b.replace(Ac,""))}function Bc(){var b={};this.register=function(a,c){L(a)?x(b,a):b[a]=c};this.$get=["$injector","$window",function(a,c){return function(d,e){if(F(d)){var g=d,d=b.hasOwnProperty(g)?b[g]:fb(e.$scope,g,!0)||fb(c,g,!0);ra(d,g,!0)}return a.instantiate(d,e)}}]}function Cc(){this.$get=["$window",function(b){return u(b.document)}]}
15.54 -function Dc(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Ec(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse",function(c){function d(d,f){for(var h,k,j=0,l=[],o=d.length,r=!1,n=[];j<o;)(h=d.indexOf(b,j))!=-1&&(k=d.indexOf(a,h+e))!=-1?(j!=h&&l.push(d.substring(j,h)),l.push(j=c(r=d.substring(h+e,k))),j.exp=r,j=k+g,r=!0):(j!=o&&l.push(d.substring(j)),j=o);if(!(o=
15.55 -l.length))l.push(""),o=1;if(!f||r)return n.length=o,j=function(a){for(var b=0,c=o,d;b<c;b++){if(typeof(d=l[b])=="function")d=d(a),d==null||d==p?d="":typeof d!="string"&&(d=da(d));n[b]=d}return n.join("")},j.exp=d,j.parts=l,j}var e=b.length,g=a.length;d.startSymbol=function(){return b};d.endSymbol=function(){return a};return d}]}function Eb(b){for(var b=b.split("/"),a=b.length;a--;)b[a]=Za(b[a]);return b.join("/")}function va(b,a){var c=Fb.exec(b),c={protocol:c[1],host:c[3],port:G(c[5])||Gb[c[1]]||
15.56 -null,path:c[6]||"/",search:c[8],hash:c[10]};if(a)a.$$protocol=c.protocol,a.$$host=c.host,a.$$port=c.port;return c}function ka(b,a,c){return b+"://"+a+(c==Gb[b]?"":":"+c)}function Fc(b,a,c){var d=va(b);return decodeURIComponent(d.path)!=a||t(d.hash)||d.hash.indexOf(c)!==0?b:ka(d.protocol,d.host,d.port)+a.substr(0,a.lastIndexOf("/"))+d.hash.substr(c.length)}function Gc(b,a,c){var d=va(b);if(decodeURIComponent(d.path)==a)return b;else{var e=d.search&&"?"+d.search||"",g=d.hash&&"#"+d.hash||"",i=a.substr(0,
15.57 -a.lastIndexOf("/")),f=d.path.substr(i.length);if(d.path.indexOf(i)!==0)throw B('Invalid url "'+b+'", missing path prefix "'+i+'" !');return ka(d.protocol,d.host,d.port)+a+"#"+c+f+e+g}}function gb(b,a,c){a=a||"";this.$$parse=function(b){var c=va(b,this);if(c.path.indexOf(a)!==0)throw B('Invalid url "'+b+'", missing path prefix "'+a+'" !');this.$$path=decodeURIComponent(c.path.substr(a.length));this.$$search=Xa(c.search);this.$$hash=c.hash&&decodeURIComponent(c.hash)||"";this.$$compose()};this.$$compose=
15.58 -function(){var b=ob(this.$$search),c=this.$$hash?"#"+Za(this.$$hash):"";this.$$url=Eb(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=ka(this.$$protocol,this.$$host,this.$$port)+a+this.$$url};this.$$rewriteAppUrl=function(a){if(a.indexOf(c)==0)return a};this.$$parse(b)}function Ia(b,a,c){var d;this.$$parse=function(b){var c=va(b,this);if(c.hash&&c.hash.indexOf(a)!==0)throw B('Invalid url "'+b+'", missing hash prefix "'+a+'" !');d=c.path+(c.search?"?"+c.search:"");c=Hc.exec((c.hash||"").substr(a.length));
15.59 -this.$$path=c[1]?(c[1].charAt(0)=="/"?"":"/")+decodeURIComponent(c[1]):"";this.$$search=Xa(c[3]);this.$$hash=c[5]&&decodeURIComponent(c[5])||"";this.$$compose()};this.$$compose=function(){var b=ob(this.$$search),c=this.$$hash?"#"+Za(this.$$hash):"";this.$$url=Eb(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=ka(this.$$protocol,this.$$host,this.$$port)+d+(this.$$url?"#"+a+this.$$url:"")};this.$$rewriteAppUrl=function(a){if(a.indexOf(c)==0)return a};this.$$parse(b)}function Hb(b,a,c,d){Ia.apply(this,arguments);
15.60 -this.$$rewriteAppUrl=function(b){if(b.indexOf(c)==0)return c+d+"#"+a+b.substr(c.length)}}function Ja(b){return function(){return this[b]}}function Ib(b,a){return function(c){if(t(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Ic(){var b="",a=!1;this.hashPrefix=function(a){return v(a)?(b=a,this):b};this.html5Mode=function(b){return v(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function i(a){c.$broadcast("$locationChangeSuccess",
15.61 -f.absUrl(),a)}var f,h,k,j=d.url(),l=va(j);a?(h=d.baseHref()||"/",k=h.substr(0,h.lastIndexOf("/")),l=ka(l.protocol,l.host,l.port)+k+"/",f=e.history?new gb(Fc(j,h,b),k,l):new Hb(Gc(j,h,b),b,l,h.substr(k.length+1))):(l=ka(l.protocol,l.host,l.port)+(l.path||"")+(l.search?"?"+l.search:"")+"#"+b+"/",f=new Ia(j,b,l));g.bind("click",function(a){if(!a.ctrlKey&&!(a.metaKey||a.which==2)){for(var b=u(a.target);E(b[0].nodeName)!=="a";)if(b[0]===g[0]||!(b=b.parent())[0])return;var d=b.prop("href"),e=f.$$rewriteAppUrl(d);
15.62 -d&&!b.attr("target")&&e&&(f.$$parse(e),c.$apply(),a.preventDefault(),U.angular["ff-684208-preventDefault"]=!0)}});f.absUrl()!=j&&d.url(f.absUrl(),!0);d.onUrlChange(function(a){f.absUrl()!=a&&(c.$evalAsync(function(){var b=f.absUrl();f.$$parse(a);i(b)}),c.$$phase||c.$digest())});var o=0;c.$watch(function(){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=
15.63 -!1;return o});return f}]}function Jc(){this.$get=["$window",function(b){function a(a){a instanceof B&&(a.stack?a=a.message&&a.stack.indexOf(a.message)===-1?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function c(c){var e=b.console||{},g=e[c]||e.log||D;return g.apply?function(){var b=[];m(arguments,function(c){b.push(a(c))});return g.apply(e,b)}:function(a,b){g(a,b)}}return{log:c("log"),warn:c("warn"),info:c("info"),error:c("error")}}]}function Kc(b,
15.64 -a){function c(a){return a.indexOf(q)!=-1}function d(){return n+1<b.length?b.charAt(n+1):!1}function e(a){return"0"<=a&&a<="9"}function g(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"||a=="\u00a0"}function i(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"}function f(a){return a=="-"||a=="+"||e(a)}function h(a,c,d){d=d||n;throw B("Lexer Error: "+a+" at column"+(v(c)?"s "+c+"-"+n+" ["+b.substring(c,d)+"]":" "+d)+" in expression ["+b+"].");}function k(){for(var a="",c=n;n<b.length;){var k=
15.65 -E(b.charAt(n));if(k=="."||e(k))a+=k;else{var g=d();if(k=="e"&&f(g))a+=k;else if(f(k)&&g&&e(g)&&a.charAt(a.length-1)=="e")a+=k;else if(f(k)&&(!g||!e(g))&&a.charAt(a.length-1)=="e")h("Invalid exponent");else break}n++}a*=1;o.push({index:c,text:a,json:!0,fn:function(){return a}})}function j(){for(var c="",d=n,f,k,h;n<b.length;){var j=b.charAt(n);if(j=="."||i(j)||e(j))j=="."&&(f=n),c+=j;else break;n++}if(f)for(k=n;k<b.length;){j=b.charAt(k);if(j=="("){h=c.substr(f-d+1);c=c.substr(0,f-d);n=k;break}if(g(j))k++;
15.66 -else break}d={index:d,text:c};if(Ka.hasOwnProperty(c))d.fn=d.json=Ka[c];else{var l=Jb(c,a);d.fn=x(function(a,b){return l(a,b)},{assign:function(a,b){return Kb(a,c,b)}})}o.push(d);h&&(o.push({index:f,text:".",json:!1}),o.push({index:f+1,text:h,json:!1}))}function l(a){var c=n;n++;for(var d="",e=a,f=!1;n<b.length;){var k=b.charAt(n);e+=k;if(f)k=="u"?(k=b.substring(n+1,n+5),k.match(/[\da-f]{4}/i)||h("Invalid unicode escape [\\u"+k+"]"),n+=4,d+=String.fromCharCode(parseInt(k,16))):(f=Lc[k],d+=f?f:k),
15.67 -f=!1;else if(k=="\\")f=!0;else if(k==a){n++;o.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}else d+=k;n++}h("Unterminated quote",c)}for(var o=[],r,n=0,w=[],q,s=":";n<b.length;){q=b.charAt(n);if(c("\"'"))l(q);else if(e(q)||c(".")&&e(d()))k();else if(i(q)){if(j(),"{,".indexOf(s)!=-1&&w[0]=="{"&&(r=o[o.length-1]))r.json=r.text.indexOf(".")==-1}else if(c("(){}[].,;:"))o.push({index:n,text:q,json:":[,".indexOf(s)!=-1&&c("{[")||c("}]:,")}),c("{[")&&w.unshift(q),c("}]")&&w.shift(),
15.68 -n++;else if(g(q)){n++;continue}else{var m=q+d(),C=Ka[q],A=Ka[m];A?(o.push({index:n,text:m,fn:A}),n+=2):C?(o.push({index:n,text:q,fn:C,json:"[,:".indexOf(s)!=-1&&c("+-")}),n+=1):h("Unexpected next character ",n,n+1)}s=q}return o}function Mc(b,a,c,d){function e(a,c){throw B("Syntax Error: Token '"+c.text+"' "+a+" at column "+(c.index+1)+" of the expression ["+b+"] starting at ["+b.substring(c.index)+"].");}function g(){if(M.length===0)throw B("Unexpected end of expression: "+b);return M[0]}function i(a,
15.69 -b,c,d){if(M.length>0){var e=M[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=i(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),M.shift(),b):!1}function h(a){f(a)||e("is unexpected, expecting ["+a+"]",i())}function k(a,b){return function(c,d){return a(c,d,b)}}function j(a,b,c){return function(d,e){return b(d,e,a,c)}}function l(){for(var a=[];;)if(M.length>0&&!i("}",")",";","]")&&a.push(v()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,
15.70 -e=0;e<a.length;e++){var f=a[e];f&&(d=f(b,c))}return d}}function o(){for(var a=f(),b=c(a.text),d=[];;)if(a=f(":"))d.push(H());else{var e=function(a,c,e){for(var e=[e],f=0;f<d.length;f++)e.push(d[f](a,c));return b.apply(a,e)};return function(){return e}}}function r(){for(var a=n(),b;;)if(b=f("||"))a=j(a,b.fn,n());else return a}function n(){var a=w(),b;if(b=f("&&"))a=j(a,b.fn,n());return a}function w(){var a=q(),b;if(b=f("==","!="))a=j(a,b.fn,w());return a}function q(){var a;a=s();for(var b;b=f("+",
15.71 -"-");)a=j(a,b.fn,s());if(b=f("<",">","<=",">="))a=j(a,b.fn,q());return a}function s(){for(var a=m(),b;b=f("*","/","%");)a=j(a,b.fn,m());return a}function m(){var a;return f("+")?C():(a=f("-"))?j(W,a.fn,m()):(a=f("!"))?k(a.fn,m()):C()}function C(){var a;if(f("("))a=v(),h(")");else if(f("["))a=A();else if(f("{"))a=K();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=u(a,c),c=null):b.text==="["?(c=a,a=ea(a)):b.text==="."?(c=a,a=t(a)):e("IMPOSSIBLE");
15.72 -return a}function A(){var a=[];if(g().text!="]"){do a.push(H());while(f(","))}h("]");return function(b,c){for(var d=[],e=0;e<a.length;e++)d.push(a[e](b,c));return d}}function K(){var a=[];if(g().text!="}"){do{var b=f(),b=b.string||b.text;h(":");var c=H();a.push({key:b,value:c})}while(f(","))}h("}");return function(b,c){for(var d={},e=0;e<a.length;e++){var f=a[e],k=f.value(b,c);d[f.key]=k}return d}}var W=I(0),y,M=Kc(b,d),H=function(){var a=r(),c,d;return(d=f("="))?(a.assign||e("implies assignment but ["+
15.73 -b.substring(0,d.index)+"] can not be assigned to",d),c=r(),function(b,d){return a.assign(b,c(b,d),d)}):a},u=function(a,b){var c=[];if(g().text!=")"){do c.push(H());while(f(","))}h(")");return function(d,e){for(var f=[],k=b?b(d,e):d,h=0;h<c.length;h++)f.push(c[h](d,e));h=a(d,e)||D;return h.apply?h.apply(k,f):h(f[0],f[1],f[2],f[3],f[4])}},t=function(a){var b=f().text,c=Jb(b,d);return x(function(b,d){return c(a(b,d),d)},{assign:function(c,d,e){return Kb(a(c,e),b,d)}})},ea=function(a){var b=H();h("]");
15.74 -return x(function(c,d){var e=a(c,d),f=b(c,d),k;if(!e)return p;if((e=e[f])&&e.then){k=e;if(!("$$v"in e))k.$$v=p,k.then(function(a){k.$$v=a});e=e.$$v}return e},{assign:function(c,d,e){return a(c,e)[b(c,e)]=d}})},v=function(){for(var a=H(),b;;)if(b=f("|"))a=j(a,b.fn,o());else return a};a?(H=r,u=t=ea=v=function(){e("is not valid json",{text:b,index:0})},y=C()):y=l();M.length!==0&&e("is an unexpected token",M[0]);return y}function Kb(b,a,c){for(var a=a.split("."),d=0;a.length>1;d++){var e=a.shift(),g=
15.75 -b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]=c}function fb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,i=0;i<g;i++)d=a[i],b&&(b=(e=b)[d]);return!c&&N(b)?Va(e,b):b}function Lb(b,a,c,d,e){return function(g,i){var f=i&&i.hasOwnProperty(b)?i:g,h;if(f===null||f===p)return f;if((f=f[b])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!a||f===null||f===p)return f;if((f=f[a])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!c||f===
15.76 -null||f===p)return f;if((f=f[c])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!d||f===null||f===p)return f;if((f=f[d])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!e||f===null||f===p)return f;if((f=f[e])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}return f}}function Jb(b,a){if(hb.hasOwnProperty(b))return hb[b];var c=b.split("."),d=c.length,e;if(a)e=d<6?Lb(c[0],c[1],c[2],c[3],c[4]):function(a,b){var e=0,
15.77 -k;do k=Lb(c[e++],c[e++],c[e++],c[e++],c[e++])(a,b),b=p,a=k;while(e<d);return k};else{var g="var l, fn, p;\n";m(c,function(a,b){g+="if(s === null || s === undefined) return s;\nl=s;\ns="+(b?"s":'((k&&k.hasOwnProperty("'+a+'"))?k:s)')+'["'+a+'"];\nif (s && s.then) {\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n'});g+="return s;";e=Function("s","k",g);e.toString=function(){return g}}return hb[b]=e}function Nc(){var b={};this.$get=["$filter","$sniffer",
15.78 -function(a,c){return function(d){switch(typeof d){case "string":return b.hasOwnProperty(d)?b[d]:b[d]=Mc(d,!1,a,c.csp);case "function":return d;default:return D}}}]}function Oc(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return Pc(function(a){b.$evalAsync(a)},a)}]}function Pc(b,a){function c(a){return a}function d(a){return i(a)}var e=function(){var f=[],h,k;return k={resolve:function(a){if(f){var c=f;f=p;h=g(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],h.then(a[0],
15.79 -a[1])})}},reject:function(a){k.resolve(i(a))},promise:{then:function(b,k){var g=e(),i=function(d){try{g.resolve((b||c)(d))}catch(e){a(e),g.reject(e)}},n=function(b){try{g.resolve((k||d)(b))}catch(c){a(c),g.reject(c)}};f?f.push([i,n]):h.then(i,n);return g.promise}}}},g=function(a){return a&&a.then?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},i=function(a){return{then:function(c,k){var g=e();b(function(){g.resolve((k||d)(a))});return g.promise}}};return{defer:e,reject:i,
15.80 -when:function(f,h,k){var j=e(),l,o=function(b){try{return(h||c)(b)}catch(d){return a(d),i(d)}},r=function(b){try{return(k||d)(b)}catch(c){return a(c),i(c)}};b(function(){g(f).then(function(a){l||(l=!0,j.resolve(g(a).then(o,r)))},function(a){l||(l=!0,j.resolve(r(a)))})});return j.promise},all:function(a){var b=e(),c=a.length,d=[];c?m(a,function(a,e){g(a).then(function(a){e in d||(d[e]=a,--c||b.resolve(d))},function(a){e in d||b.reject(a)})}):b.resolve(d);return b.promise}}}function Qc(){var b={};this.when=
15.81 -function(a,c){b[a]=x({reloadOnSearch:!0},c);if(a){var d=a[a.length-1]=="/"?a.substr(0,a.length-1):a+"/";b[d]={redirectTo:a}}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache",function(a,c,d,e,g,i,f){function h(){var b=k(),h=r.current;if(b&&h&&b.$route===h.$route&&ha(b.pathParams,h.pathParams)&&!b.reloadOnSearch&&!o)h.params=b.params,V(h.params,d),a.$broadcast("$routeUpdate",h);else if(b||
15.82 -h)o=!1,a.$broadcast("$routeChangeStart",b,h),(r.current=b)&&b.redirectTo&&(F(b.redirectTo)?c.path(j(b.redirectTo,b.params)).search(b.params).replace():c.url(b.redirectTo(b.pathParams,c.path(),c.search())).replace()),e.when(b).then(function(){if(b){var a=[],c=[],d;m(b.resolve||{},function(b,d){a.push(d);c.push(F(b)?g.get(b):g.invoke(b))});if(!v(d=b.template))if(v(d=b.templateUrl))d=i.get(d,{cache:f}).then(function(a){return a.data});v(d)&&(a.push("$template"),c.push(d));return e.all(c).then(function(b){var c=
15.83 -{};m(b,function(b,d){c[a[d]]=b});return c})}}).then(function(c){if(b==r.current){if(b)b.locals=c,V(b.params,d);a.$broadcast("$routeChangeSuccess",b,h)}},function(c){b==r.current&&a.$broadcast("$routeChangeError",b,h,c)})}function k(){var a,d;m(b,function(b,e){if(!d&&(a=l(c.path(),e)))d=ya(b,{params:x({},c.search(),a),pathParams:a}),d.$route=b});return d||b[null]&&ya(b[null],{params:{},pathParams:{}})}function j(a,b){var c=[];m((a||"").split(":"),function(a,d){if(d==0)c.push(a);else{var e=a.match(/(\w+)(.*)/),
15.84 -f=e[1];c.push(b[f]);c.push(e[2]||"");delete b[f]}});return c.join("")}var l=function(a,b){var c="^"+b.replace(/([\.\\\(\)\^\$])/g,"\\$1")+"$",d=[],e={};m(b.split(/\W/),function(a){if(a){var b=RegExp(":"+a+"([\\W])");c.match(b)&&(c=c.replace(b,"([^\\/]*)$1"),d.push(a))}});var f=a.match(RegExp(c));f&&m(d,function(a,b){e[a]=f[b+1]});return f?e:null},o=!1,r={routes:b,reload:function(){o=!0;a.$evalAsync(h)}};a.$on("$locationChangeSuccess",h);return r}]}function Rc(){this.$get=I({})}function Sc(){var b=
15.85 -10;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse",function(a,c,d){function e(){this.$id=xa();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$asyncQueue=[];this.$$listeners={}}function g(a){if(h.$$phase)throw B(h.$$phase+" already in progress");h.$$phase=a}function i(a,b){var c=d(a);ra(c,b);return c}function f(){}e.prototype={$new:function(a){if(N(a))throw B("API-CHANGE: Use $controller to instantiate controllers.");
15.86 -a?(a=new e,a.$root=this.$root):(a=function(){},a.prototype=this,a=new a,a.$id=xa());a["this"]=a;a.$$listeners={};a.$parent=this;a.$$asyncQueue=[];a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,c){var d=i(a,"watch"),e=this.$$watchers,g={fn:b,last:f,get:d,exp:a,eq:!!c};if(!N(b)){var h=i(b||D,"listener");g.fn=function(a,b,
15.87 -c){h(c)}}if(!e)e=this.$$watchers=[];e.unshift(g);return function(){Ua(e,g)}},$digest:function(){var a,d,e,i,r,n,m,q=b,s,p=[],C,A;g("$digest");do{m=!1;s=this;do{for(r=s.$$asyncQueue;r.length;)try{s.$eval(r.shift())}catch(K){c(K)}if(i=s.$$watchers)for(n=i.length;n--;)try{if(a=i[n],(d=a.get(s))!==(e=a.last)&&!(a.eq?ha(d,e):typeof d=="number"&&typeof e=="number"&&isNaN(d)&&isNaN(e)))m=!0,a.last=a.eq?V(d):d,a.fn(d,e===f?d:e,s),q<5&&(C=4-q,p[C]||(p[C]=[]),A=N(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):
15.88 -a.exp,A+="; newVal: "+da(d)+"; oldVal: "+da(e),p[C].push(A))}catch(W){c(W)}if(!(i=s.$$childHead||s!==this&&s.$$nextSibling))for(;s!==this&&!(i=s.$$nextSibling);)s=s.$parent}while(s=i);if(m&&!q--)throw h.$$phase=null,B(b+" $digest() iterations reached. Aborting!\nWatchers fired in the last 5 iterations: "+da(p));}while(m||r.length);h.$$phase=null},$destroy:function(){if(h!=this){var a=this.$parent;this.$broadcast("$destroy");if(a.$$childHead==this)a.$$childHead=this.$$nextSibling;if(a.$$childTail==
15.89 -this)a.$$childTail=this.$$prevSibling;if(this.$$prevSibling)this.$$prevSibling.$$nextSibling=this.$$nextSibling;if(this.$$nextSibling)this.$$nextSibling.$$prevSibling=this.$$prevSibling;this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null}},$eval:function(a,b){return d(a)(this,b)},$evalAsync:function(a){this.$$asyncQueue.push(a)},$apply:function(a){try{return g("$apply"),this.$eval(a)}catch(b){c(b)}finally{h.$$phase=null;try{h.$digest()}catch(d){throw c(d),d;}}},
15.90 -$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);return function(){c[za(c,b)]=null}},$emit:function(a,b){var d=[],e,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},i=[h].concat(ia.call(arguments,1)),m,p;do{e=f.$$listeners[a]||d;h.currentScope=f;m=0;for(p=e.length;m<p;m++)if(e[m])try{if(e[m].apply(null,i),g)return h}catch(C){c(C)}else e.splice(m,1),m--,p--;f=f.$parent}while(f);
15.91 -return h},$broadcast:function(a,b){var d=this,e=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ia.call(arguments,1)),h,i;do{d=e;f.currentScope=d;e=d.$$listeners[a]||[];h=0;for(i=e.length;h<i;h++)if(e[h])try{e[h].apply(null,g)}catch(m){c(m)}else e.splice(h,1),h--,i--;if(!(e=d.$$childHead||d!==this&&d.$$nextSibling))for(;d!==this&&!(e=d.$$nextSibling);)d=d.$parent}while(d=e);return f}};var h=new e;return h}]}function Tc(){this.$get=
15.92 -["$window",function(b){var a={},c=G((/android (\d+)/.exec(E(b.navigator.userAgent))||[])[1]);return{history:!(!b.history||!b.history.pushState||c<4),hashchange:"onhashchange"in b&&(!b.document.documentMode||b.document.documentMode>7),hasEvent:function(c){if(c=="input"&&aa==9)return!1;if(t(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Uc(){this.$get=I(U)}function Mb(b){var a={},c,d,e;if(!b)return a;m(b.split("\n"),function(b){e=b.indexOf(":");c=E(R(b.substr(0,
15.93 -e)));d=R(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Nb(b){var a=L(b)?b:p;return function(c){a||(a=Mb(b));return c?a[E(c)]||null:a}}function Ob(b,a,c){if(N(c))return c(b,a);m(c,function(c){b=c(b,a)});return b}function Vc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){F(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=nb(d,!0)));return d}],transformRequest:[function(a){return L(a)&&Sa.apply(a)!=="[object File]"?da(a):a}],
15.94 -headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"},put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,h,k,j){function l(a){function c(a){var b=x({},a,{data:Ob(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:k.reject(b)}a.method=la(a.method);var e=a.transformRequest||
15.95 -d.transformRequest,f=a.transformResponse||d.transformResponse,h=d.headers,h=x({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},h.common,h[E(a.method)],a.headers),e=Ob(a.data,Nb(h),e),g;t(a.data)&&delete h["Content-Type"];g=o(a,e,h);g=g.then(c,c);m(w,function(a){g=a(g)});g.success=function(b){g.then(function(c){b(c.data,c.status,c.headers,a)});return g};g.error=function(b){g.then(null,function(c){b(c.data,c.status,c.headers,a)});return g};return g}function o(b,c,d){function e(a,b,c){m&&(200<=a&&a<300?m.put(w,
15.96 -[a,b,Mb(c)]):m.remove(w));f(b,a,c);h.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?j.resolve:j.reject)({data:a,status:c,headers:Nb(d),config:b})}function i(){var a=za(l.pendingRequests,b);a!==-1&&l.pendingRequests.splice(a,1)}var j=k.defer(),o=j.promise,m,p,w=r(b.url,b.params);l.pendingRequests.push(b);o.then(i,i);b.cache&&b.method=="GET"&&(m=L(b.cache)?b.cache:n);if(m)if(p=m.get(w))if(p.then)return p.then(i,i),p;else J(p)?f(p[1],p[0],V(p[2])):f(p,200,{});else m.put(w,o);p||a(b.method,
15.97 -w,c,e,d,b.timeout,b.withCredentials);return o}function r(a,b){if(!b)return a;var c=[];ec(b,function(a,b){a==null||a==p||(L(a)&&(a=da(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var n=c("$http"),w=[];m(e,function(a){w.push(F(a)?j.get(a):j.invoke(a))});l.pendingRequests=[];(function(a){m(arguments,function(a){l[a]=function(b,c){return l(x(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){l[a]=
15.98 -function(b,c,d){return l(x(d||{},{method:a,url:b,data:c}))}})})("post","put");l.defaults=d;return l}]}function Wc(){this.$get=["$browser","$window","$document",function(b,a,c){return Xc(b,Yc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Xc(b,a,c,d,e,g){function i(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;aa?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror=
15.99 -d;e.body.appendChild(c)}return function(e,h,k,j,l,o,r){function n(a,c,d,e){c=(h.match(Fb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(D)}b.$$incOutstandingRequestCount();h=h||b.url();if(E(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};i(h.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?n(j,200,d[p].data):n(j,-2);delete d[p]})}else{var q=new a;q.open(e,h,!0);m(l,function(a,b){a&&q.setRequestHeader(b,a)});
15.100 -var s;q.onreadystatechange=function(){q.readyState==4&&n(j,s||q.status,q.responseText,q.getAllResponseHeaders())};if(r)q.withCredentials=!0;q.send(k||"");o>0&&c(function(){s=-1;q.abort()},o)}}}function Zc(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},
15.101 -DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",
15.102 -shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function $c(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,h){var k=c.defer(),j=k.promise,l=v(h)&&!h,f=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}l||b.$apply()},f),h=function(){delete g[j.$$timeoutId]};j.$$timeoutId=f;g[f]=k;j.then(h,h);return j}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):
15.103 -!1};return e}]}function Pb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Qb);a("date",Rb);a("filter",ad);a("json",bd);a("limitTo",cd);a("lowercase",dd);a("number",Sb);a("orderBy",Tb);a("uppercase",ed)}function ad(){return function(b,a){if(!(b instanceof Array))return b;var c=[];c.check=function(a){for(var b=0;b<c.length;b++)if(!c[b](a))return!1;return!0};var d=function(a,b){if(b.charAt(0)===
15.104 -"!")return!d(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return(""+a).toLowerCase().indexOf(b)>-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;c<a.length;c++)if(d(a[c],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var e in a)e=="$"?function(){var b=(""+a[e]).toLowerCase();b&&c.push(function(a){return d(a,b)})}():function(){var b=e,f=
15.105 -(""+a[e]).toLowerCase();f&&c.push(function(a){return d(fb(a,b),f)})}();break;case "function":c.push(a);break;default:return b}for(var g=[],i=0;i<b.length;i++){var f=b[i];c.check(f)&&g.push(f)}return g}}function Qb(b){var a=b.NUMBER_FORMATS;return function(b,d){if(t(d))d=a.CURRENCY_SYM;return Ub(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Sb(b){var a=b.NUMBER_FORMATS;return function(b,d){return Ub(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Ub(b,a,c,d,e){if(isNaN(b)||
15.106 -!isFinite(b))return"";var g=b<0,b=Math.abs(b),i=b+"",f="",h=[],k=!1;if(i.indexOf("e")!==-1){var j=i.match(/([\d\.]+)e(-?)(\d+)/);j&&j[2]=="-"&&j[3]>e+1?i="0":(f=i,k=!0)}if(!k){i=(i.split(Vb)[1]||"").length;t(e)&&(e=Math.min(Math.max(a.minFrac,i),a.maxFrac));var i=Math.pow(10,e),b=Math.round(b*i)/i,b=(""+b).split(Vb),i=b[0],b=b[1]||"",k=0,j=a.lgSize,l=a.gSize;if(i.length>=j+l)for(var k=i.length-j,o=0;o<k;o++)(k-o)%l===0&&o!==0&&(f+=c),f+=i.charAt(o);for(o=k;o<i.length;o++)(i.length-o)%j===0&&o!==0&&
15.107 -(f+=c),f+=i.charAt(o);for(;b.length<e;)b+="0";e&&(f+=d+b.substr(0,e))}h.push(g?a.negPre:a.posPre);h.push(f);h.push(g?a.negSuf:a.posSuf);return h.join("")}function ib(b,a,c){var d="";b<0&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function P(b,a,c,d){return function(e){e=e["get"+b]();if(c>0||e>-c)e+=c;e===0&&c==-12&&(e=12);return ib(e,a,d)}}function La(b,a){return function(c,d){var e=c["get"+b](),g=la(a?"SHORT"+b:b);return d[g][e]}}function Rb(b){function a(a){var b;
15.108 -if(b=a.match(c)){var a=new Date(0),g=0,i=0;b[9]&&(g=G(b[9]+b[10]),i=G(b[9]+b[11]));a.setUTCFullYear(G(b[1]),G(b[2])-1,G(b[3]));a.setUTCHours(G(b[4]||0)-g,G(b[5]||0)-i,G(b[6]||0),G(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var g="",i=[],f,h,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;F(c)&&(c=fd.test(c)?G(c):a(c));wa(c)&&(c=new Date(c));if(!na(c))return c;for(;e;)(h=gd.exec(e))?(i=i.concat(ia.call(h,
15.109 -1)),e=i.pop()):(i.push(e),e=null);m(i,function(a){f=hd[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function bd(){return function(b){return da(b,!0)}}function cd(){return function(b,a){if(!(b instanceof Array))return b;var a=G(a),c=[],d,e;if(!b||!(b instanceof Array))return c;a>b.length?a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Tb(b){return function(a,c,d){function e(a,b){return Wa(b)?
15.110 -function(b,c){return a(c,b)}:a}if(!(a instanceof Array))return a;if(!c)return a;for(var c=J(c)?c:[c],c=Ta(c,function(a){var c=!1,d=a||ma;if(F(a)){if(a.charAt(0)=="+"||a.charAt(0)=="-")c=a.charAt(0)=="-",a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),f=typeof c,g=typeof e;f==g?(f=="string"&&(c=c.toLowerCase()),f=="string"&&(e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=f<g?-1:1;return c},c)}),g=[],i=0;i<a.length;i++)g.push(a[i]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=
15.111 -c[d](a,b);if(e!==0)return e}return 0},d))}}function S(b){N(b)&&(b={link:b});b.restrict=b.restrict||"AC";return I(b)}function Wb(b,a){function c(a,c){c=c?"-"+$a(c,"-"):"";b.removeClass((a?Ma:Na)+c).addClass((a?Na:Ma)+c)}var d=this,e=b.parent().controller("form")||Oa,g=0,i=d.$error={};d.$name=a.name;d.$dirty=!1;d.$pristine=!0;d.$valid=!0;d.$invalid=!1;e.$addControl(d);b.addClass(Pa);c(!0);d.$addControl=function(a){a.$name&&!d.hasOwnProperty(a.$name)&&(d[a.$name]=a)};d.$removeControl=function(a){a.$name&&
15.112 -d[a.$name]===a&&delete d[a.$name];m(i,function(b,c){d.$setValidity(c,!0,a)})};d.$setValidity=function(a,b,k){var j=i[a];if(b){if(j&&(Ua(j,k),!j.length)){g--;if(!g)c(b),d.$valid=!0,d.$invalid=!1;i[a]=!1;c(!0,a);e.$setValidity(a,!0,d)}}else{g||c(b);if(j){if(za(j,k)!=-1)return}else i[a]=j=[],g++,c(!1,a),e.$setValidity(a,!1,d);j.push(k);d.$valid=!1;d.$invalid=!0}};d.$setDirty=function(){b.removeClass(Pa).addClass(Xb);d.$dirty=!0;d.$pristine=!1;e.$setDirty()}}function T(b){return t(b)||b===""||b===null||
15.113 -b!==b}function Qa(b,a,c,d,e,g){var i=function(){var c=R(a.val());d.$viewValue!==c&&b.$apply(function(){d.$setViewValue(c)})};if(e.hasEvent("input"))a.bind("input",i);else{var f;a.bind("keydown",function(a){a=a.keyCode;a===91||15<a&&a<19||37<=a&&a<=40||f||(f=g.defer(function(){i();f=null}))});a.bind("change",i)}d.$render=function(){a.val(T(d.$viewValue)?"":d.$viewValue)};var h=c.ngPattern,k=function(a,b){return T(b)||a.test(b)?(d.$setValidity("pattern",!0),b):(d.$setValidity("pattern",!1),p)};h&&(h.match(/^\/(.*)\/$/)?
15.114 -(h=RegExp(h.substr(1,h.length-2)),e=function(a){return k(h,a)}):e=function(a){var c=b.$eval(h);if(!c||!c.test)throw new B("Expected "+h+" to be a RegExp but was "+c);return k(c,a)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var j=G(c.ngMinlength),e=function(a){return!T(a)&&a.length<j?(d.$setValidity("minlength",!1),p):(d.$setValidity("minlength",!0),a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var l=G(c.ngMaxlength),c=function(a){return!T(a)&&a.length>l?(d.$setValidity("maxlength",
15.115 -!1),p):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function jb(b,a){b="ngClass"+b;return S(function(c,d,e){function g(b,d){if(a===!0||c.$index%2===a)d&&b!==d&&i(d),f(b)}function i(a){L(a)&&!J(a)&&(a=Ta(a,function(a,b){if(a)return b}));d.removeClass(J(a)?a.join(" "):a)}function f(a){L(a)&&!J(a)&&(a=Ta(a,function(a,b){if(a)return b}));a&&d.addClass(J(a)?a.join(" "):a)}c.$watch(e[b],g,!0);e.$observe("class",function(){var a=c.$eval(e[b]);g(a,a)});b!=="ngClass"&&c.$watch("$index",
15.116 -function(d,g){var j=d%2;j!==g%2&&(j==a?f(c.$eval(e[b])):i(c.$eval(e[b])))})})}var E=function(b){return F(b)?b.toLowerCase():b},la=function(b){return F(b)?b.toUpperCase():b},B=U.Error,aa=G((/msie (\d+)/.exec(E(navigator.userAgent))||[])[1]),u,ja,ia=[].slice,Ra=[].push,Sa=Object.prototype.toString,Yb=U.angular||(U.angular={}),ta,Cb,Z=["0","0","0"];D.$inject=[];ma.$inject=[];Cb=aa<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?la(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?
15.117 -b.nodeName:b[0].nodeName};var kc=/[A-Z]/g,id={full:"1.0.3",major:1,minor:0,dot:3,codeName:"bouncy-thunder"},Ba=Q.cache={},Aa=Q.expando="ng-"+(new Date).getTime(),oc=1,Zb=U.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},db=U.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},mc=/([\:\-\_]+(.))/g,nc=/^moz([A-Z])/,ua=Q.prototype={ready:function(b){function a(){c||(c=!0,b())}
15.118 -var c=!1;this.bind("DOMContentLoaded",a);Q(U).bind("load",a)},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+b])},length:0,push:Ra,sort:[].sort,splice:[].splice},Ea={};m("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ea[E(b)]=b});var zb={};m("input,select,option,textarea,button,form".split(","),function(b){zb[la(b)]=!0});m({data:ub,inheritedData:Da,scope:function(b){return Da(b,
15.119 -"$scope")},controller:xb,injector:function(b){return Da(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ca,css:function(b,a,c){a=rb(a);if(v(c))b.style[a]=c;else{var d;aa<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];aa<=8&&(d=d===""?p:d);return d}},attr:function(b,a,c){var d=E(a);if(Ea[d])if(v(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||D).specified?d:p;else if(v(c))b.setAttribute(a,
15.120 -c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?p:b},prop:function(b,a,c){if(v(c))b[a]=c;else return b[a]},text:x(aa<9?function(b,a){if(b.nodeType==1){if(t(a))return b.innerText;b.innerText=a}else{if(t(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(t(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(t(a))return b.value;b.value=a},html:function(b,a){if(t(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)sa(d[c]);b.innerHTML=a}},function(b,
15.121 -a){Q.prototype[a]=function(a,d){var e,g;if((b.length==2&&b!==Ca&&b!==xb?a:d)===p)if(L(a)){for(e=0;e<this.length;e++)if(b===ub)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}else{if(this.length)return b(this[0],a,d)}else{for(e=0;e<this.length;e++)b(this[e],a,d);return this}return b.$dv}});m({removeData:sb,dealoc:sa,bind:function a(c,d,e){var g=$(c,"events"),i=$(c,"handle");g||$(c,"events",g={});i||$(c,"handle",i=pc(c,g));m(d.split(" "),function(d){var h=g[d];if(!h){if(d=="mouseenter"||
15.122 -d=="mouseleave"){var k=0;g.mouseenter=[];g.mouseleave=[];a(c,"mouseover",function(a){k++;k==1&&i(a,"mouseenter")});a(c,"mouseout",function(a){k--;k==0&&i(a,"mouseleave")})}else Zb(c,d,i),g[d]=[];h=g[d]}h.push(e)})},unbind:tb,replaceWith:function(a,c){var d,e=a.parentNode;sa(a);m(new Q(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];m(a.childNodes,function(a){a.nodeName!="#text"&&c.push(a)});return c},contents:function(a){return a.childNodes},
15.123 -append:function(a,c){m(new Q(c),function(c){a.nodeType===1&&a.appendChild(c)})},prepend:function(a,c){if(a.nodeType===1){var d=a.firstChild;m(new Q(c),function(c){d?a.insertBefore(c,d):(a.appendChild(c),d=c)})}},wrap:function(a,c){var c=u(c)[0],d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){sa(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;m(new Q(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:wb,removeClass:vb,toggleClass:function(a,
15.124 -c,d){t(d)&&(d=!Ca(a,c));(d?wb:vb)(a,c)},parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},next:function(a){return a.nextSibling},find:function(a,c){return a.getElementsByTagName(c)},clone:cb,triggerHandler:function(a,c){var d=($(a,"events")||{})[c];m(d,function(c){c.call(a,null)})}},function(a,c){Q.prototype[c]=function(c,e){for(var g,i=0;i<this.length;i++)g==p?(g=a(this[i],c,e),g!==p&&(g=u(g))):bb(g,a(this[i],c,e));return g==p?this:g}});Fa.prototype={put:function(a,c){this[ga(a)]=
15.125 -c},get:function(a){return this[ga(a)]},remove:function(a){var c=this[a=ga(a)];delete this[a];return c}};eb.prototype={push:function(a,c){var d=this[a=ga(a)];d?d.push(c):this[a]=[c]},shift:function(a){var c=this[a=ga(a)];if(c)return c.length==1?(delete this[a],c[0]):c.shift()},peek:function(a){if(a=this[ga(a)])return a[0]}};var rc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,sc=/,/,tc=/^\s*(_?)(\S+?)\1\s*$/,qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Db="Non-assignable model expression: ";Bb.$inject=["$provide"];
15.126 -var Ac=/^(x[\:\-_]|data[\:\-_])/i,Fb=/^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,$b=/^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,Hc=$b,Gb={http:80,https:443,ftp:21};gb.prototype={$$replace:!1,absUrl:Ja("$$absUrl"),url:function(a,c){if(t(a))return this.$$url;var d=$b.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));if(d[2]||d[1])this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:Ja("$$protocol"),host:Ja("$$host"),port:Ja("$$port"),path:Ib("$$path",function(a){return a.charAt(0)==
15.127 -"/"?a:"/"+a}),search:function(a,c){if(t(a))return this.$$search;v(c)?c===null?delete this.$$search[a]:this.$$search[a]=c:this.$$search=F(a)?Xa(a):a;this.$$compose();return this},hash:Ib("$$hash",ma),replace:function(){this.$$replace=!0;return this}};Ia.prototype=ya(gb.prototype);Hb.prototype=ya(Ia.prototype);var Ka={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:D,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return v(d)?v(e)?d+e:d:v(e)?e:p},"-":function(a,
15.128 -c,d,e){d=d(a,c);e=e(a,c);return(v(d)?d:0)-(v(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":D,"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,
15.129 -c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Lc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},hb={},Yc=U.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw new B("This browser does not support XMLHttpRequest.");
15.130 -};Pb.$inject=["$provide"];Qb.$inject=["$locale"];Sb.$inject=["$locale"];var Vb=".",hd={yyyy:P("FullYear",4),yy:P("FullYear",2,0,!0),y:P("FullYear",1),MMMM:La("Month"),MMM:La("Month",!0),MM:P("Month",2,1),M:P("Month",1,1),dd:P("Date",2),d:P("Date",1),HH:P("Hours",2),H:P("Hours",1),hh:P("Hours",2,-12),h:P("Hours",1,-12),mm:P("Minutes",2),m:P("Minutes",1),ss:P("Seconds",2),s:P("Seconds",1),EEEE:La("Day"),EEE:La("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=a.getTimezoneOffset();
15.131 -return ib(a/60,2)+ib(Math.abs(a%60),2)}},gd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,fd=/^\d+$/;Rb.$inject=["$locale"];var dd=I(E),ed=I(la);Tb.$inject=["$parse"];var jd=I({restrict:"E",compile:function(a,c){c.href||c.$set("href","");return function(a,c){c.bind("click",function(a){if(!c.attr("href"))return a.preventDefault(),!1})}}}),kb={};m(Ea,function(a,c){var d=fa("ng-"+c);kb[d]=function(){return{priority:100,compile:function(){return function(a,g,i){a.$watch(i[d],
15.132 -function(a){i.$set(c,!!a)})}}}}});m(["src","href"],function(a){var c=fa("ng-"+a);kb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),aa&&e.prop(a,c))})}}}});var Oa={$addControl:D,$removeControl:D,$setValidity:D,$setDirty:D};Wb.$inject=["$element","$attrs","$scope"];var Ra=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E",controller:Wb,compile:function(){return{pre:function(a,d,i,f){if(!i.action){var h=function(a){a.preventDefault?
15.133 -a.preventDefault():a.returnValue=!1};Zb(d[0],"submit",h);d.bind("$destroy",function(){c(function(){db(d[0],"submit",h)},0,!1)})}var k=d.parent().controller("form"),j=i.name||i.ngForm;j&&(a[j]=f);k&&d.bind("$destroy",function(){k.$removeControl(f);j&&(a[j]=p);x(f,Oa)})}}}};return a?x(V(d),{restrict:"EAC"}):d}]},kd=Ra(),ld=Ra(!0),md=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,nd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,od=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,
15.134 -ac={text:Qa,number:function(a,c,d,e,g,i){Qa(a,c,d,e,g,i);e.$parsers.push(function(a){var c=T(a);return c||od.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),p)});e.$formatters.push(function(a){return T(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!T(a)&&a<f?(e.$setValidity("min",!1),p):(e.$setValidity("min",!0),a)};e.$parsers.push(a);e.$formatters.push(a)}if(d.max){var h=parseFloat(d.max),d=function(a){return!T(a)&&a>h?(e.$setValidity("max",
15.135 -!1),p):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return T(a)||wa(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),p)})},url:function(a,c,d,e,g,i){Qa(a,c,d,e,g,i);a=function(a){return T(a)||md.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,i){Qa(a,c,d,e,g,i);a=function(a){return T(a)||nd.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",
15.136 -!1),p)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){t(d.name)&&c.attr("name",xa());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,i=d.ngFalseValue;F(g)||(g=!0);F(i)||(i=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===
15.137 -g});e.$parsers.push(function(a){return a?g:i})},hidden:D,button:D,submit:D,reset:D},bc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,i){i&&(ac[E(g.type)]||ac.text)(d,e,g,i,c,a)}}}],Na="ng-valid",Ma="ng-invalid",Pa="ng-pristine",Xb="ng-dirty",pd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function i(a,c){c=c?"-"+$a(c,"-"):"";e.removeClass((a?Ma:Na)+c).addClass((a?Na:Ma)+c)}this.$modelValue=this.$viewValue=Number.NaN;
15.138 -this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var f=g(d.ngModel),h=f.assign;if(!h)throw B(Db+d.ngModel+" ("+pa(e)+")");this.$render=D;var k=e.inheritedData("$formController")||Oa,j=0,l=this.$error={};e.addClass(Pa);i(!0);this.$setValidity=function(a,c){if(l[a]!==!c){if(c){if(l[a]&&j--,!j)i(!0),this.$valid=!0,this.$invalid=!1}else i(!1),this.$invalid=!0,this.$valid=!1,j++;l[a]=!c;i(c,a);k.$setValidity(a,
15.139 -c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(Pa).addClass(Xb),k.$setDirty();m(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,h(a,d),m(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var o=this;a.$watch(function(){var c=f(a);if(o.$modelValue!==c){var d=o.$formatters,e=d.length;for(o.$modelValue=c;e--;)c=d[e](c);if(o.$viewValue!==c)o.$viewValue=c,o.$render()}})}],qd=function(){return{require:["ngModel",
15.140 -"^?form"],controller:pd,link:function(a,c,d,e){var g=e[0],i=e[1]||Oa;i.$addControl(g);c.bind("$destroy",function(){i.$removeControl(g)})}}},rd=I({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),cc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(T(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);
15.141 -d.$observe("required",function(){g(e.$viewValue)})}}}},sd=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){var c=[];a&&m(a.split(g),function(a){a&&c.push(R(a))});return c});e.$formatters.push(function(a){return J(a)?a.join(", "):p})}}},td=/^(true|false|\d+)$/,ud=function(){return{priority:100,compile:function(a,c){return td.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,
15.142 -c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},vd=S(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==p?"":a)})}),wd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],xd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,
15.143 -function(a){c.html(a||"")})}}],yd=jb("",!0),zd=jb("Odd",0),Ad=jb("Even",1),Bd=S({compile:function(a,c){c.$set("ngCloak",p);a.removeClass("ng-cloak")}}),Cd=[function(){return{scope:!0,controller:"@"}}],Dd=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],dc={};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "),function(a){var c=fa("ng-"+a);dc[c]=["$parse",function(d){return function(e,g,i){var f=d(i[c]);g.bind(E(a),function(a){e.$apply(function(){f(e,
15.144 -{$event:a})})})}}]});var Ed=S(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Fd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,i){var f=i.ngInclude||i.src,h=i.onload||"",k=i.autoscroll;return function(g,i){var o=0,m,n=function(){m&&(m.$destroy(),m=null);i.html("")};g.$watch(f,function(f){var p=++o;f?a.get(f,{cache:c}).success(function(a){p===o&&(m&&m.$destroy(),m=g.$new(),i.html(a),e(i.contents())(m),
15.145 -v(k)&&(!k||g.$eval(k))&&d(),m.$emit("$includeContentLoaded"),g.$eval(h))}).error(function(){p===o&&n()}):n()})}}}}],Gd=S({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Hd=S({terminal:!0,priority:1E3}),Id=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,i){var f=i.count,h=g.attr(i.$attr.when),k=i.offset||0,j=e.$eval(h),l={},o=c.startSymbol(),r=c.endSymbol();m(j,function(a,e){l[e]=c(a.replace(d,o+f+"-"+k+r))});e.$watch(function(){var c=
15.146 -parseFloat(e.$eval(f));return isNaN(c)?"":(j[c]||(c=a.pluralCat(c-k)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Jd=S({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,i){var f=i.ngRepeat,i=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),h,k,j;if(!i)throw B("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=i[1];h=i[2];i=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!i)throw B("'item' in 'item in collection' should be identifier or (key, value) but got '"+
15.147 -f+"'.");k=i[3]||i[1];j=i[2];var l=new eb;a.$watch(function(a){var e,f,i=a.$eval(h),m=gc(i,!0),p,u=new eb,C,A,v,t,y=c;if(J(i))v=i||[];else{v=[];for(C in i)i.hasOwnProperty(C)&&C.charAt(0)!="$"&&v.push(C);v.sort()}e=0;for(f=v.length;e<f;e++){C=i===v?e:v[e];A=i[C];if(t=l.shift(A)){p=t.scope;u.push(A,t);if(e!==t.index)t.index=e,y.after(t.element);y=t.element}else p=a.$new();p[k]=A;j&&(p[j]=C);p.$index=e;p.$first=e===0;p.$last=e===m-1;p.$middle=!(p.$first||p.$last);t||d(p,function(a){y.after(a);t={scope:p,
15.148 -element:y=a,index:e};u.push(A,t)})}for(C in l)if(l.hasOwnProperty(C))for(v=l[C];v.length;)A=v.pop(),A.element.remove(),A.scope.$destroy();l=u})}}}),Kd=S(function(a,c,d){a.$watch(d.ngShow,function(a){c.css("display",Wa(a)?"":"none")})}),Ld=S(function(a,c,d){a.$watch(d.ngHide,function(a){c.css("display",Wa(a)?"none":"")})}),Md=S(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&m(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Nd=I({restrict:"EA",compile:function(a,c){var d=c.ngSwitch||c.on,
15.149 -e={};a.data("ng-switch",e);return function(a,i){var f,h,k;a.$watch(d,function(d){h&&(k.$destroy(),h.remove(),h=k=null);if(f=e["!"+d]||e["?"])a.$eval(c.change),k=a.$new(),f(k,function(a){h=a;i.append(a)})})}}}),Od=S({transclude:"element",priority:500,compile:function(a,c,d){a=a.inheritedData("ng-switch");qa(a);a["!"+c.ngSwitchWhen]=d}}),Pd=S({transclude:"element",priority:500,compile:function(a,c,d){a=a.inheritedData("ng-switch");qa(a);a["?"]=d}}),Qd=S({controller:["$transclude","$element",function(a,
15.150 -c){a(function(a){c.append(a)})}]}),Rd=["$http","$templateCache","$route","$anchorScroll","$compile","$controller",function(a,c,d,e,g,i){return{restrict:"ECA",terminal:!0,link:function(a,c,k){function j(){var j=d.current&&d.current.locals,k=j&&j.$template;if(k){c.html(k);l&&(l.$destroy(),l=null);var k=g(c.contents()),p=d.current;l=p.scope=a.$new();if(p.controller)j.$scope=l,j=i(p.controller,j),c.contents().data("$ngControllerController",j);k(l);l.$emit("$viewContentLoaded");l.$eval(m);e()}else c.html(""),
15.151 -l&&(l.$destroy(),l=null)}var l,m=k.onload||"";a.$on("$routeChangeSuccess",j);j()}}}],Sd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){d.type=="text/ng-template"&&a.put(d.id,c[0].text)}}}],Td=I({terminal:!0}),Ud=["$compile","$parse",function(a,c){var d=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,e={$setViewValue:D};return{restrict:"E",require:["select",
15.152 -"?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var h=this,k={},j=e,l;h.databound=d.ngModel;h.init=function(a,c,d){j=a;l=d};h.addOption=function(c){k[c]=!0;j.$viewValue==c&&(a.val(c),l.parent()&&l.remove())};h.removeOption=function(a){this.hasOption(a)&&(delete k[a],j.$viewValue==a&&this.renderUnknownOption(a))};h.renderUnknownOption=function(c){c="? "+ga(c)+" ?";l.val(c);a.prepend(l);a.val(c);l.prop("selected",!0)};h.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",
15.153 -function(){h.renderUnknownOption=D})}],link:function(e,i,f,h){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(A.parent()&&A.remove(),c.val(a),a===""&&s.prop("selected",!0)):t(a)&&s?c.val(""):e.renderUnknownOption(a)};c.bind("change",function(){a.$apply(function(){A.parent()&&A.remove();d.$setViewValue(c.val())})})}function j(a,c,d){var e;d.$render=function(){var a=new Fa(d.$viewValue);m(c.children(),function(c){c.selected=v(a.get(c.value))})};a.$watch(function(){ha(e,d.$viewValue)||
15.154 -(e=V(d.$viewValue),d.$render())});c.bind("change",function(){a.$apply(function(){var a=[];m(c.children(),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function l(e,f,g){function h(){var a={"":[]},c=[""],d,i,s,t,u;s=g.$modelValue;t=r(e)||[];var y=l?lb(t):t,A,w,x;w={};u=!1;var z,B;if(n)u=new Fa(s);else if(s===null||q)a[""].push({selected:s===null,id:"",label:""}),u=!0;for(x=0;A=y.length,x<A;x++){w[k]=t[l?w[l]=y[x]:x];d=m(e,w)||"";if(!(i=a[d]))i=a[d]=[],c.push(d);n?d=u.remove(o(e,
15.155 -w))!=p:(d=s===o(e,w),u=u||d);z=j(e,w);z=z===p?"":z;i.push({id:l?y[x]:x,label:z,selected:d})}!n&&!u&&a[""].unshift({id:"?",label:"",selected:!0});w=0;for(y=c.length;w<y;w++){d=c[w];i=a[d];if(v.length<=w)s={element:C.clone().attr("label",d),label:i.label},t=[s],v.push(t),f.append(s.element);else if(t=v[w],s=t[0],s.label!=d)s.element.attr("label",s.label=d);z=null;x=0;for(A=i.length;x<A;x++)if(d=i[x],u=t[x+1]){z=u.element;if(u.label!==d.label)z.text(u.label=d.label);if(u.id!==d.id)z.val(u.id=d.id);if(u.element.selected!==
15.156 -d.selected)z.prop("selected",u.selected=d.selected)}else d.id===""&&q?B=q:(B=D.clone()).val(d.id).attr("selected",d.selected).text(d.label),t.push({element:B,label:d.label,id:d.id,selected:d.selected}),z?z.after(B):s.element.append(B),z=B;for(x++;t.length>x;)t.pop().element.remove()}for(;v.length>w;)v.pop()[0].element.remove()}var i;if(!(i=w.match(d)))throw B("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+w+"'.");var j=c(i[2]||i[1]),k=i[4]||
15.157 -i[6],l=i[5],m=c(i[3]||""),o=c(i[2]?i[1]:k),r=c(i[7]),v=[[{element:f,label:""}]];q&&(a(q)(e),q.removeClass("ng-scope"),q.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=r(e)||[],d={},h,i,j,m,q,s;if(n){i=[];m=0;for(s=v.length;m<s;m++){a=v[m];j=1;for(q=a.length;j<q;j++)if((h=a[j].element)[0].selected)h=h.val(),l&&(d[l]=h),d[k]=c[h],i.push(o(e,d))}}else h=f.val(),h=="?"?i=p:h==""?i=null:(d[k]=c[h],l&&(d[l]=h),i=o(e,d));g.$setViewValue(i)})});g.$render=h;e.$watch(h)}if(h[1]){for(var o=
15.158 -h[0],r=h[1],n=f.multiple,w=f.ngOptions,q=!1,s,D=u(ca.createElement("option")),C=u(ca.createElement("optgroup")),A=D.clone(),h=0,x=i.children(),E=x.length;h<E;h++)if(x[h].value==""){s=q=x.eq(h);break}o.init(r,q,A);if(n&&(f.required||f.ngRequired)){var y=function(a){r.$setValidity("required",!f.required||a&&a.length);return a};r.$parsers.push(y);r.$formatters.unshift(y);f.$observe("required",function(){y(r.$viewValue)})}w?l(e,i,r):n?j(e,i,r):k(e,i,r,o)}}}}],Vd=["$interpolate",function(a){var c={addOption:D,
15.159 -removeOption:D};return{restrict:"E",priority:100,compile:function(d,e){if(t(e.value)){var g=a(d.text(),!0);g||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),j=k.data("$selectController")||k.parent().data("$selectController");j&&j.databound?d.prop("selected",!1):j=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&j.removeOption(c);j.addOption(a)}):j.addOption(e.value);d.bind("$destroy",function(){j.removeOption(e.value)})}}}}],Wd=I({restrict:"E",terminal:!0});(ja=U.jQuery)?(u=
15.160 -ja,x(ja.fn,{scope:ua.scope,controller:ua.controller,injector:ua.injector,inheritedData:ua.inheritedData}),ab("remove",!0),ab("empty"),ab("html")):u=Q;Yb.element=u;(function(a){x(a,{bootstrap:pb,copy:V,extend:x,equals:ha,element:u,forEach:m,injector:qb,noop:D,bind:Va,toJson:da,fromJson:nb,identity:ma,isUndefined:t,isDefined:v,isString:F,isFunction:N,isObject:L,isNumber:wa,isElement:fc,isArray:J,version:id,isDate:na,lowercase:E,uppercase:la,callbacks:{counter:0}});ta=lc(U);try{ta("ngLocale")}catch(c){ta("ngLocale",
15.161 -[]).provider("$locale",Zc)}ta("ng",["ngLocale"],["$provide",function(a){a.provider("$compile",Bb).directive({a:jd,input:bc,textarea:bc,form:kd,script:Sd,select:Ud,style:Wd,option:Vd,ngBind:vd,ngBindHtmlUnsafe:xd,ngBindTemplate:wd,ngClass:yd,ngClassEven:Ad,ngClassOdd:zd,ngCsp:Dd,ngCloak:Bd,ngController:Cd,ngForm:ld,ngHide:Ld,ngInclude:Fd,ngInit:Gd,ngNonBindable:Hd,ngPluralize:Id,ngRepeat:Jd,ngShow:Kd,ngSubmit:Ed,ngStyle:Md,ngSwitch:Nd,ngSwitchWhen:Od,ngSwitchDefault:Pd,ngOptions:Td,ngView:Rd,ngTransclude:Qd,
15.162 -ngModel:qd,ngList:sd,ngChange:rd,required:cc,ngRequired:cc,ngValue:ud}).directive(kb).directive(dc);a.provider({$anchorScroll:uc,$browser:wc,$cacheFactory:xc,$controller:Bc,$document:Cc,$exceptionHandler:Dc,$filter:Pb,$interpolate:Ec,$http:Vc,$httpBackend:Wc,$location:Ic,$log:Jc,$parse:Nc,$route:Qc,$routeParams:Rc,$rootScope:Sc,$q:Oc,$sniffer:Tc,$templateCache:yc,$timeout:$c,$window:Uc})}])})(Yb);u(ca).ready(function(){jc(ca,pb)})})(window,document);angular.element(document).find("head").append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
16.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js Tue Feb 11 10:48:24 2014 +0100
16.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
16.3 @@ -1,224 +0,0 @@
16.4 -// 'use strict';
16.5 -
16.6 -// Declare app level module which depends on filters, and services
16.7 -angular.module('bck2brwsr', []).
16.8 - directive('uiCodemirror', ['$timeout', function($timeout) {
16.9 - 'use strict';
16.10 -
16.11 - var events = ["cursorActivity", "viewportChange", "gutterClick", "focus", "blur", "scroll", "update"];
16.12 - return {
16.13 - restrict: 'A',
16.14 - require: 'ngModel',
16.15 - link: function(scope, elm, attrs, ngModel) {
16.16 - var options, opts, onChange, deferCodeMirror, codeMirror, timeoutId, val;
16.17 -
16.18 - if (elm[0].type !== 'textarea') {
16.19 - throw new Error('uiCodemirror3 can only be applied to a textarea element');
16.20 - }
16.21 -
16.22 - options = /* uiConfig.codemirror || */ {};
16.23 - opts = angular.extend({}, options, scope.$eval(attrs.uiCodemirror));
16.24 -
16.25 - onChange = function(instance, changeObj) {
16.26 - val = instance.getValue();
16.27 - $timeout.cancel(timeoutId);
16.28 - timeoutId = $timeout(function() {
16.29 - ngModel.$setViewValue(val);
16.30 - }, 500);
16.31 - };
16.32 -
16.33 - deferCodeMirror = function() {
16.34 - codeMirror = CodeMirror.fromTextArea(elm[0], opts);
16.35 - elm[0].codeMirror = codeMirror;
16.36 - // codeMirror.on("change", onChange(opts.onChange));
16.37 - codeMirror.on("change", onChange);
16.38 -
16.39 - for (var i = 0, n = events.length, aEvent; i < n; ++i) {
16.40 - aEvent = opts["on" + events[i].charAt(0).toUpperCase() + events[i].slice(1)];
16.41 - if (aEvent === void 0)
16.42 - continue;
16.43 - if (typeof aEvent !== "function")
16.44 - continue;
16.45 -
16.46 - var bound = _.bind( aEvent, scope );
16.47 -
16.48 - codeMirror.on(events[i], bound);
16.49 - }
16.50 -
16.51 - // CodeMirror expects a string, so make sure it gets one.
16.52 - // This does not change the model.
16.53 - ngModel.$formatters.push(function(value) {
16.54 - if (angular.isUndefined(value) || value === null) {
16.55 - return '';
16.56 - }
16.57 - else if (angular.isObject(value) || angular.isArray(value)) {
16.58 - throw new Error('ui-codemirror cannot use an object or an array as a model');
16.59 - }
16.60 - return value;
16.61 - });
16.62 -
16.63 - // Override the ngModelController $render method, which is what gets called when the model is updated.
16.64 - // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
16.65 - ngModel.$render = function() {
16.66 - codeMirror.setValue(ngModel.$viewValue);
16.67 - };
16.68 -
16.69 - };
16.70 -
16.71 - $timeout(deferCodeMirror);
16.72 -
16.73 - }
16.74 - };
16.75 -}]);
16.76 -
16.77 -function DevCtrl( $scope, $http ) {
16.78 - var templateHtml =
16.79 -"<html><body>\n" +
16.80 -" <input data-bind=\"value: value, valueUpdate: 'afterkeydown'\" \n" +
16.81 -" value=\"0\" type=\"number\">\n" +
16.82 -" </input>\n" +
16.83 -" * <span data-bind=\"text: value\">0</span> \n" +
16.84 -" = <span data-bind=\"text: powerValue\">0</span>\n" +
16.85 -" <br/>\n" +
16.86 -" <button id='dupl'>Duplicate!</button>\n" +
16.87 -" <button id=\"clear\">Clear!</button>" +
16.88 -" <hr/>\n" +
16.89 -"\n" +
16.90 -"\n" +
16.91 -"\n" +
16.92 -"\n" +
16.93 -"\n" +
16.94 -"\n" +
16.95 -"\n" +
16.96 -"\n" +
16.97 -"\n" +
16.98 -"\n" +
16.99 -"\n" +
16.100 -"\n" +
16.101 -"\n" +
16.102 -"\n" +
16.103 -"\n" +
16.104 -"\n" +
16.105 -"\n" +
16.106 -"\n" +
16.107 -"\n" +
16.108 -"\n" +
16.109 -" <script src=\"/bck2brwsr.js\"></script>\n" +
16.110 -" <script type=\"text/javascript\">\n" +
16.111 -" function ldCls(res) {\n" +
16.112 -" var request = new XMLHttpRequest();\n" +
16.113 -" request.open('GET', '/classes/' + res, false);\n" +
16.114 -" request.send();\n" +
16.115 -" var arr = eval('(' + request.responseText + ')');\n" +
16.116 -" return arr;\n" +
16.117 -" }\n" +
16.118 -" var vm = bck2brwsr(ldCls);\n" +
16.119 -" vm.loadClass('${fqn}');\n" +
16.120 -" </script>\n" +
16.121 -"</body></html>";
16.122 - var templateJava =
16.123 -"package bck2brwsr.demo;\n" +
16.124 -"import org.apidesign.bck2brwsr.htmlpage.api.*;\n" +
16.125 -"import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;\n" +
16.126 -"\n" +
16.127 -"@Page(xhtml=\"index.html\", className=\"Index\", properties={\n" +
16.128 -" @Property(name=\"value\", type=int.class)\n" +
16.129 -"})\n" +
16.130 -"class YourFirstHTML5PageInRealLanguage {\n" +
16.131 -" static { new Index().applyBindings(); }\n" +
16.132 -" @On(event=CLICK, id=\"dupl\") static void duplicateValue(Index m) {\n" +
16.133 -" m.setValue(m.getValue() * 2);\n" +
16.134 -" }\n" +
16.135 -" @On(event=CLICK, id=\"clear\") static void zeroTheValue(Index m) {\n" +
16.136 -" m.setValue(0);;\n" +
16.137 -" }\n" +
16.138 -" @ComputedProperty static int powerValue(int value) {\n" +
16.139 -" return value * value;\n" +
16.140 -" }\n" +
16.141 -"}";
16.142 -
16.143 -
16.144 - $scope.makeMarker = function( editor, line ) {
16.145 - var marker = document.createElement("div");
16.146 - marker.innerHTML = " ";
16.147 - marker.className = "issue";
16.148 -
16.149 - var info = editor.lineInfo(line);
16.150 - editor.setGutterMarker(line, "issues", info.markers ? null : marker);
16.151 -
16.152 - return marker;
16.153 - };
16.154 -
16.155 -
16.156 - // Returns a function, that, as long as it continues to be invoked, will not
16.157 - // be triggered. The function will be called after it stops being called for
16.158 - // N milliseconds. If `immediate` is passed, trigger the function on the
16.159 - // leading edge, instead of the trailing.
16.160 - $scope.debounce = function(func, wait, immediate) {
16.161 - var timeout, result;
16.162 - return function() {
16.163 - var context = this, args = arguments;
16.164 - var later = function() {
16.165 - timeout = null;
16.166 - if (!immediate) result = func.apply(context, args);
16.167 - };
16.168 - var callNow = immediate && !timeout;
16.169 - clearTimeout(timeout);
16.170 - timeout = setTimeout(later, wait);
16.171 - if (callNow) result = func.apply(context, args);
16.172 - return result;
16.173 - };
16.174 - };
16.175 -
16.176 - $scope.reload = function() {
16.177 - $scope.errors = null;
16.178 - var frame = document.getElementById("result");
16.179 - frame.src = "result.html";
16.180 - frame.contentDocument.location.reload(true);
16.181 - frame.contentWindow.location.reload();
16.182 - document.getElementById("editorJava").codeMirror.clearGutter("issues");
16.183 - };
16.184 -
16.185 - $scope.fail = function( data ) {
16.186 - $scope.errors = eval( data );
16.187 - var editor = document.getElementById("editorJava").codeMirror;
16.188 - editor.clearGutter( "issues" );
16.189 -
16.190 - for( var i = 0; i < $scope.errors.length; i ++ ) {
16.191 - $scope.makeMarker( editor, $scope.errors[i].line - 1 );
16.192 - }
16.193 -
16.194 - };
16.195 -
16.196 - $scope.post = function() {
16.197 - return $http({url: ".",
16.198 - method: "POST",
16.199 - //headers: this.headers,
16.200 - data: { html : $scope.html, java : $scope.java}
16.201 - }).success( $scope.reload ).error( $scope.fail );
16.202 - };
16.203 -
16.204 - $scope.errorClass = function( kind ) {
16.205 - switch( kind ) {
16.206 - case "ERROR" :
16.207 - return "error";
16.208 - default :
16.209 - return "warning";
16.210 - }
16.211 - };
16.212 -
16.213 - $scope.gotoError = function( line, col ) {
16.214 - var editor = document.getElementById("editorJava").codeMirror;
16.215 - editor.setCursor({ line: line - 1, ch : col - 1 });
16.216 - editor.focus();
16.217 - };
16.218 -
16.219 - $scope.tab = "html";
16.220 - $scope.html= templateHtml;
16.221 - $scope.java = templateJava;
16.222 -
16.223 - $scope.$watch( "html", $scope.debounce( $scope.post, 2000 ) );
16.224 - $scope.$watch( "java", $scope.debounce( $scope.post, 2000 ) );
16.225 - $scope.post();
16.226 -
16.227 -}
17.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/codemirror.css Tue Feb 11 10:48:24 2014 +0100
17.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
17.3 @@ -1,239 +0,0 @@
17.4 -/* BASICS */
17.5 -
17.6 -.CodeMirror {
17.7 - /* Set height, width, borders, and global font properties here */
17.8 - font-family: monospace;
17.9 - height: 300px;
17.10 -}
17.11 -.CodeMirror-scroll {
17.12 - /* Set scrolling behaviour here */
17.13 - overflow: auto;
17.14 -}
17.15 -
17.16 -/* PADDING */
17.17 -
17.18 -.CodeMirror-lines {
17.19 - padding: 4px 0; /* Vertical padding around content */
17.20 -}
17.21 -.CodeMirror pre {
17.22 - padding: 0 4px; /* Horizontal padding of content */
17.23 -}
17.24 -
17.25 -.CodeMirror-scrollbar-filler {
17.26 - background-color: white; /* The little square between H and V scrollbars */
17.27 -}
17.28 -
17.29 -/* GUTTER */
17.30 -
17.31 -.CodeMirror-gutters {
17.32 - border-right: 1px solid #ddd;
17.33 - background-color: #f7f7f7;
17.34 -}
17.35 -.CodeMirror-linenumbers {}
17.36 -.CodeMirror-linenumber {
17.37 - padding: 0 3px 0 5px;
17.38 - min-width: 20px;
17.39 - text-align: right;
17.40 - color: #999;
17.41 -}
17.42 -
17.43 -/* CURSOR */
17.44 -
17.45 -.CodeMirror pre.CodeMirror-cursor {
17.46 - border-left: 1px solid black;
17.47 -}
17.48 -/* Shown when moving in bi-directional text */
17.49 -.CodeMirror pre.CodeMirror-secondarycursor {
17.50 - border-left: 1px solid silver;
17.51 -}
17.52 -.cm-keymap-fat-cursor pre.CodeMirror-cursor {
17.53 - width: auto;
17.54 - border: 0;
17.55 - background: transparent;
17.56 - background: rgba(0, 200, 0, .4);
17.57 - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
17.58 -}
17.59 -/* Kludge to turn off filter in ie9+, which also accepts rgba */
17.60 -.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
17.61 - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
17.62 -}
17.63 -/* Can style cursor different in overwrite (non-insert) mode */
17.64 -.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
17.65 -
17.66 -/* DEFAULT THEME */
17.67 -
17.68 -.cm-s-default .cm-keyword {color: #708;}
17.69 -.cm-s-default .cm-atom {color: #219;}
17.70 -.cm-s-default .cm-number {color: #164;}
17.71 -.cm-s-default .cm-def {color: #00f;}
17.72 -.cm-s-default .cm-variable {color: black;}
17.73 -.cm-s-default .cm-variable-2 {color: #05a;}
17.74 -.cm-s-default .cm-variable-3 {color: #085;}
17.75 -.cm-s-default .cm-property {color: black;}
17.76 -.cm-s-default .cm-operator {color: black;}
17.77 -.cm-s-default .cm-comment {color: #a50;}
17.78 -.cm-s-default .cm-string {color: #a11;}
17.79 -.cm-s-default .cm-string-2 {color: #f50;}
17.80 -.cm-s-default .cm-meta {color: #555;}
17.81 -.cm-s-default .cm-error {color: #f00;}
17.82 -.cm-s-default .cm-qualifier {color: #555;}
17.83 -.cm-s-default .cm-builtin {color: #30a;}
17.84 -.cm-s-default .cm-bracket {color: #997;}
17.85 -.cm-s-default .cm-tag {color: #170;}
17.86 -.cm-s-default .cm-attribute {color: #00c;}
17.87 -.cm-s-default .cm-header {color: blue;}
17.88 -.cm-s-default .cm-quote {color: #090;}
17.89 -.cm-s-default .cm-hr {color: #999;}
17.90 -.cm-s-default .cm-link {color: #00c;}
17.91 -
17.92 -.cm-negative {color: #d44;}
17.93 -.cm-positive {color: #292;}
17.94 -.cm-header, .cm-strong {font-weight: bold;}
17.95 -.cm-em {font-style: italic;}
17.96 -.cm-emstrong {font-style: italic; font-weight: bold;}
17.97 -.cm-link {text-decoration: underline;}
17.98 -
17.99 -.cm-invalidchar {color: #f00;}
17.100 -
17.101 -div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
17.102 -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
17.103 -
17.104 -/* STOP */
17.105 -
17.106 -/* The rest of this file contains styles related to the mechanics of
17.107 - the editor. You probably shouldn't touch them. */
17.108 -
17.109 -.CodeMirror {
17.110 - line-height: 1;
17.111 - position: relative;
17.112 - overflow: hidden;
17.113 -}
17.114 -
17.115 -.CodeMirror-scroll {
17.116 - /* 30px is the magic margin used to hide the element's real scrollbars */
17.117 - /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
17.118 - margin-bottom: -30px; margin-right: -30px;
17.119 - padding-bottom: 30px; padding-right: 30px;
17.120 - height: 100%;
17.121 - outline: none; /* Prevent dragging from highlighting the element */
17.122 - position: relative;
17.123 -}
17.124 -.CodeMirror-sizer {
17.125 - position: relative;
17.126 -}
17.127 -
17.128 -/* The fake, visible scrollbars. Used to force redraw during scrolling
17.129 - before actuall scrolling happens, thus preventing shaking and
17.130 - flickering artifacts. */
17.131 -.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
17.132 - position: absolute;
17.133 - z-index: 6;
17.134 - display: none;
17.135 -}
17.136 -.CodeMirror-vscrollbar {
17.137 - right: 0; top: 0;
17.138 - overflow-x: hidden;
17.139 - overflow-y: scroll;
17.140 -}
17.141 -.CodeMirror-hscrollbar {
17.142 - bottom: 0; left: 0;
17.143 - overflow-y: hidden;
17.144 - overflow-x: scroll;
17.145 -}
17.146 -.CodeMirror-scrollbar-filler {
17.147 - right: 0; bottom: 0;
17.148 - z-index: 6;
17.149 -}
17.150 -
17.151 -.CodeMirror-gutters {
17.152 - position: absolute; left: 0; top: 0;
17.153 - height: 100%;
17.154 - z-index: 3;
17.155 -}
17.156 -.CodeMirror-gutter {
17.157 - height: 100%;
17.158 - display: inline-block;
17.159 - /* Hack to make IE7 behave */
17.160 - *zoom:1;
17.161 - *display:inline;
17.162 -}
17.163 -.CodeMirror-gutter-elt {
17.164 - position: absolute;
17.165 - cursor: default;
17.166 - z-index: 4;
17.167 -}
17.168 -
17.169 -.CodeMirror-lines {
17.170 - cursor: text;
17.171 -}
17.172 -.CodeMirror pre {
17.173 - /* Reset some styles that the rest of the page might have set */
17.174 - -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
17.175 - border-width: 0;
17.176 - background: transparent;
17.177 - font-family: inherit;
17.178 - font-size: inherit;
17.179 - margin: 0;
17.180 - white-space: pre;
17.181 - word-wrap: normal;
17.182 - line-height: inherit;
17.183 - color: inherit;
17.184 - z-index: 2;
17.185 - position: relative;
17.186 - overflow: visible;
17.187 -}
17.188 -.CodeMirror-wrap pre {
17.189 - word-wrap: break-word;
17.190 - white-space: pre-wrap;
17.191 - word-break: normal;
17.192 -}
17.193 -.CodeMirror-linebackground {
17.194 - position: absolute;
17.195 - left: 0; right: 0; top: 0; bottom: 0;
17.196 - z-index: 0;
17.197 -}
17.198 -
17.199 -.CodeMirror-linewidget {
17.200 - position: relative;
17.201 - z-index: 2;
17.202 -}
17.203 -
17.204 -.CodeMirror-wrap .CodeMirror-scroll {
17.205 - overflow-x: hidden;
17.206 -}
17.207 -
17.208 -.CodeMirror-measure {
17.209 - position: absolute;
17.210 - width: 100%; height: 0px;
17.211 - overflow: hidden;
17.212 - visibility: hidden;
17.213 -}
17.214 -.CodeMirror-measure pre { position: static; }
17.215 -
17.216 -.CodeMirror pre.CodeMirror-cursor {
17.217 - position: absolute;
17.218 - visibility: hidden;
17.219 - border-right: none;
17.220 - width: 0;
17.221 -}
17.222 -.CodeMirror-focused pre.CodeMirror-cursor {
17.223 - visibility: visible;
17.224 -}
17.225 -
17.226 -.CodeMirror-selected { background: #d9d9d9; }
17.227 -.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
17.228 -
17.229 -.CodeMirror-searching {
17.230 - background: #ffa;
17.231 - background: rgba(255, 255, 0, .4);
17.232 -}
17.233 -
17.234 -/* IE7 hack to prevent it from returning funny offsetTops on the spans */
17.235 -.CodeMirror span { *vertical-align: text-bottom; }
17.236 -
17.237 -@media print {
17.238 - /* Hide the cursor when printing */
17.239 - .CodeMirror pre.CodeMirror-cursor {
17.240 - visibility: hidden;
17.241 - }
17.242 -}
18.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/codemirror.js Tue Feb 11 10:48:24 2014 +0100
18.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
18.3 @@ -1,4553 +0,0 @@
18.4 -// CodeMirror version 3.0
18.5 -//
18.6 -// CodeMirror is the only global var we claim
18.7 -window.CodeMirror = (function() {
18.8 - "use strict";
18.9 -
18.10 - // BROWSER SNIFFING
18.11 -
18.12 - // Crude, but necessary to handle a number of hard-to-feature-detect
18.13 - // bugs and behavior differences.
18.14 - var gecko = /gecko\/\d/i.test(navigator.userAgent);
18.15 - var ie = /MSIE \d/.test(navigator.userAgent);
18.16 - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
18.17 - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
18.18 - var webkit = /WebKit\//.test(navigator.userAgent);
18.19 - var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
18.20 - var chrome = /Chrome\//.test(navigator.userAgent);
18.21 - var opera = /Opera\//.test(navigator.userAgent);
18.22 - var safari = /Apple Computer/.test(navigator.vendor);
18.23 - var khtml = /KHTML\//.test(navigator.userAgent);
18.24 - var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
18.25 - var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
18.26 - var phantom = /PhantomJS/.test(navigator.userAgent);
18.27 -
18.28 - var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
18.29 - // This is woefully incomplete. Suggestions for alternative methods welcome.
18.30 - var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent);
18.31 - var mac = ios || /Mac/.test(navigator.platform);
18.32 -
18.33 - // Optimize some code when these features are not used
18.34 - var sawReadOnlySpans = false, sawCollapsedSpans = false;
18.35 -
18.36 - // CONSTRUCTOR
18.37 -
18.38 - function CodeMirror(place, options) {
18.39 - if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
18.40 -
18.41 - this.options = options = options || {};
18.42 - // Determine effective options based on given values and defaults.
18.43 - for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
18.44 - options[opt] = defaults[opt];
18.45 - setGuttersForLineNumbers(options);
18.46 -
18.47 - var display = this.display = makeDisplay(place);
18.48 - display.wrapper.CodeMirror = this;
18.49 - updateGutters(this);
18.50 - if (options.autofocus && !mobile) focusInput(this);
18.51 -
18.52 - this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
18.53 - this.nextOpId = 0;
18.54 - loadMode(this);
18.55 - themeChanged(this);
18.56 - if (options.lineWrapping)
18.57 - this.display.wrapper.className += " CodeMirror-wrap";
18.58 -
18.59 - // Initialize the content.
18.60 - this.setValue(options.value || "");
18.61 - // Override magic textarea content restore that IE sometimes does
18.62 - // on our hidden textarea on reload
18.63 - if (ie) setTimeout(bind(resetInput, this, true), 20);
18.64 - this.view.history = makeHistory();
18.65 -
18.66 - registerEventHandlers(this);
18.67 - // IE throws unspecified error in certain cases, when
18.68 - // trying to access activeElement before onload
18.69 - var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
18.70 - if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
18.71 - else onBlur(this);
18.72 -
18.73 - operation(this, function() {
18.74 - for (var opt in optionHandlers)
18.75 - if (optionHandlers.propertyIsEnumerable(opt))
18.76 - optionHandlers[opt](this, options[opt], Init);
18.77 - for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
18.78 - })();
18.79 - }
18.80 -
18.81 - // DISPLAY CONSTRUCTOR
18.82 -
18.83 - function makeDisplay(place) {
18.84 - var d = {};
18.85 - var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
18.86 - input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
18.87 - // Wraps and hides input textarea
18.88 - d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
18.89 - // The actual fake scrollbars.
18.90 - d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
18.91 - d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
18.92 - d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
18.93 - // DIVs containing the selection and the actual code
18.94 - d.lineDiv = elt("div");
18.95 - d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
18.96 - // Blinky cursor, and element used to ensure cursor fits at the end of a line
18.97 - d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor");
18.98 - // Secondary cursor, shown when on a 'jump' in bi-directional text
18.99 - d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
18.100 - // Used to measure text size
18.101 - d.measure = elt("div", null, "CodeMirror-measure");
18.102 - // Wraps everything that needs to exist inside the vertically-padded coordinate system
18.103 - d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
18.104 - null, "position: relative; outline: none");
18.105 - // Moved around its parent to cover visible view
18.106 - d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
18.107 - // Set to the height of the text, causes scrolling
18.108 - d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
18.109 - // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
18.110 - d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
18.111 - // Will contain the gutters, if any
18.112 - d.gutters = elt("div", null, "CodeMirror-gutters");
18.113 - d.lineGutter = null;
18.114 - // Helper element to properly size the gutter backgrounds
18.115 - var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
18.116 - // Provides scrolling
18.117 - d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
18.118 - d.scroller.setAttribute("tabIndex", "-1");
18.119 - // The element in which the editor lives.
18.120 - d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
18.121 - d.scrollbarFiller, d.scroller], "CodeMirror");
18.122 - // Work around IE7 z-index bug
18.123 - if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
18.124 - if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
18.125 -
18.126 - // Needed to hide big blue blinking cursor on Mobile Safari
18.127 - if (ios) input.style.width = "0px";
18.128 - if (!webkit) d.scroller.draggable = true;
18.129 - // Needed to handle Tab key in KHTML
18.130 - if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
18.131 - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
18.132 - else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
18.133 -
18.134 - // Current visible range (may be bigger than the view window).
18.135 - d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
18.136 -
18.137 - // Used to only resize the line number gutter when necessary (when
18.138 - // the amount of lines crosses a boundary that makes its width change)
18.139 - d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
18.140 - // See readInput and resetInput
18.141 - d.prevInput = "";
18.142 - // Set to true when a non-horizontal-scrolling widget is added. As
18.143 - // an optimization, widget aligning is skipped when d is false.
18.144 - d.alignWidgets = false;
18.145 - // Flag that indicates whether we currently expect input to appear
18.146 - // (after some event like 'keypress' or 'input') and are polling
18.147 - // intensively.
18.148 - d.pollingFast = false;
18.149 - // Self-resetting timeout for the poller
18.150 - d.poll = new Delayed();
18.151 - // True when a drag from the editor is active
18.152 - d.draggingText = false;
18.153 -
18.154 - d.cachedCharWidth = d.cachedTextHeight = null;
18.155 - d.measureLineCache = [];
18.156 - d.measureLineCachePos = 0;
18.157 -
18.158 - // Tracks when resetInput has punted to just putting a short
18.159 - // string instead of the (large) selection.
18.160 - d.inaccurateSelection = false;
18.161 -
18.162 - // Used to adjust overwrite behaviour when a paste has been
18.163 - // detected
18.164 - d.pasteIncoming = false;
18.165 -
18.166 - return d;
18.167 - }
18.168 -
18.169 - // VIEW CONSTRUCTOR
18.170 -
18.171 - function makeView(doc) {
18.172 - var selPos = {line: 0, ch: 0};
18.173 - return {
18.174 - doc: doc,
18.175 - // frontier is the point up to which the content has been parsed,
18.176 - frontier: 0, highlight: new Delayed(),
18.177 - sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
18.178 - scrollTop: 0, scrollLeft: 0,
18.179 - overwrite: false, focused: false,
18.180 - // Tracks the maximum line length so that
18.181 - // the horizontal scrollbar can be kept
18.182 - // static when scrolling.
18.183 - maxLine: getLine(doc, 0),
18.184 - maxLineLength: 0,
18.185 - maxLineChanged: false,
18.186 - suppressEdits: false,
18.187 - goalColumn: null,
18.188 - cantEdit: false,
18.189 - keyMaps: []
18.190 - };
18.191 - }
18.192 -
18.193 - // STATE UPDATES
18.194 -
18.195 - // Used to get the editor into a consistent state again when options change.
18.196 -
18.197 - function loadMode(cm) {
18.198 - var doc = cm.view.doc;
18.199 - cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
18.200 - doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
18.201 - cm.view.frontier = 0;
18.202 - startWorker(cm, 100);
18.203 - }
18.204 -
18.205 - function wrappingChanged(cm) {
18.206 - var doc = cm.view.doc, th = textHeight(cm.display);
18.207 - if (cm.options.lineWrapping) {
18.208 - cm.display.wrapper.className += " CodeMirror-wrap";
18.209 - var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
18.210 - doc.iter(0, doc.size, function(line) {
18.211 - if (line.height == 0) return;
18.212 - var guess = Math.ceil(line.text.length / perLine) || 1;
18.213 - if (guess != 1) updateLineHeight(line, guess * th);
18.214 - });
18.215 - cm.display.sizer.style.minWidth = "";
18.216 - } else {
18.217 - cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
18.218 - computeMaxLength(cm.view);
18.219 - doc.iter(0, doc.size, function(line) {
18.220 - if (line.height != 0) updateLineHeight(line, th);
18.221 - });
18.222 - }
18.223 - regChange(cm, 0, doc.size);
18.224 - clearCaches(cm);
18.225 - setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
18.226 - }
18.227 -
18.228 - function keyMapChanged(cm) {
18.229 - var style = keyMap[cm.options.keyMap].style;
18.230 - cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
18.231 - (style ? " cm-keymap-" + style : "");
18.232 - }
18.233 -
18.234 - function themeChanged(cm) {
18.235 - cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
18.236 - cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
18.237 - clearCaches(cm);
18.238 - }
18.239 -
18.240 - function guttersChanged(cm) {
18.241 - updateGutters(cm);
18.242 - updateDisplay(cm, true);
18.243 - }
18.244 -
18.245 - function updateGutters(cm) {
18.246 - var gutters = cm.display.gutters, specs = cm.options.gutters;
18.247 - removeChildren(gutters);
18.248 - for (var i = 0; i < specs.length; ++i) {
18.249 - var gutterClass = specs[i];
18.250 - var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
18.251 - if (gutterClass == "CodeMirror-linenumbers") {
18.252 - cm.display.lineGutter = gElt;
18.253 - gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
18.254 - }
18.255 - }
18.256 - gutters.style.display = i ? "" : "none";
18.257 - }
18.258 -
18.259 - function lineLength(doc, line) {
18.260 - if (line.height == 0) return 0;
18.261 - var len = line.text.length, merged, cur = line;
18.262 - while (merged = collapsedSpanAtStart(cur)) {
18.263 - var found = merged.find();
18.264 - cur = getLine(doc, found.from.line);
18.265 - len += found.from.ch - found.to.ch;
18.266 - }
18.267 - cur = line;
18.268 - while (merged = collapsedSpanAtEnd(cur)) {
18.269 - var found = merged.find();
18.270 - len -= cur.text.length - found.from.ch;
18.271 - cur = getLine(doc, found.to.line);
18.272 - len += cur.text.length - found.to.ch;
18.273 - }
18.274 - return len;
18.275 - }
18.276 -
18.277 - function computeMaxLength(view) {
18.278 - view.maxLine = getLine(view.doc, 0);
18.279 - view.maxLineLength = lineLength(view.doc, view.maxLine);
18.280 - view.maxLineChanged = true;
18.281 - view.doc.iter(1, view.doc.size, function(line) {
18.282 - var len = lineLength(view.doc, line);
18.283 - if (len > view.maxLineLength) {
18.284 - view.maxLineLength = len;
18.285 - view.maxLine = line;
18.286 - }
18.287 - });
18.288 - }
18.289 -
18.290 - // Make sure the gutters options contains the element
18.291 - // "CodeMirror-linenumbers" when the lineNumbers option is true.
18.292 - function setGuttersForLineNumbers(options) {
18.293 - var found = false;
18.294 - for (var i = 0; i < options.gutters.length; ++i) {
18.295 - if (options.gutters[i] == "CodeMirror-linenumbers") {
18.296 - if (options.lineNumbers) found = true;
18.297 - else options.gutters.splice(i--, 1);
18.298 - }
18.299 - }
18.300 - if (!found && options.lineNumbers)
18.301 - options.gutters.push("CodeMirror-linenumbers");
18.302 - }
18.303 -
18.304 - // SCROLLBARS
18.305 -
18.306 - // Re-synchronize the fake scrollbars with the actual size of the
18.307 - // content. Optionally force a scrollTop.
18.308 - function updateScrollbars(d /* display */, docHeight) {
18.309 - var totalHeight = docHeight + 2 * paddingTop(d);
18.310 - d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
18.311 - var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
18.312 - var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
18.313 - var needsV = scrollHeight > d.scroller.clientHeight;
18.314 - if (needsV) {
18.315 - d.scrollbarV.style.display = "block";
18.316 - d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
18.317 - d.scrollbarV.firstChild.style.height =
18.318 - (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
18.319 - } else d.scrollbarV.style.display = "";
18.320 - if (needsH) {
18.321 - d.scrollbarH.style.display = "block";
18.322 - d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
18.323 - d.scrollbarH.firstChild.style.width =
18.324 - (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
18.325 - } else d.scrollbarH.style.display = "";
18.326 - if (needsH && needsV) {
18.327 - d.scrollbarFiller.style.display = "block";
18.328 - d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
18.329 - } else d.scrollbarFiller.style.display = "";
18.330 -
18.331 - if (mac_geLion && scrollbarWidth(d.measure) === 0)
18.332 - d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
18.333 - }
18.334 -
18.335 - function visibleLines(display, doc, viewPort) {
18.336 - var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
18.337 - if (typeof viewPort == "number") top = viewPort;
18.338 - else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
18.339 - top = Math.floor(top - paddingTop(display));
18.340 - var bottom = Math.ceil(top + height);
18.341 - return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
18.342 - }
18.343 -
18.344 - // LINE NUMBERS
18.345 -
18.346 - function alignHorizontally(cm) {
18.347 - var display = cm.display;
18.348 - if (!display.alignWidgets && !display.gutters.firstChild) return;
18.349 - var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
18.350 - var gutterW = display.gutters.offsetWidth, l = comp + "px";
18.351 - for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
18.352 - for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
18.353 - }
18.354 - display.gutters.style.left = (comp + gutterW) + "px";
18.355 - }
18.356 -
18.357 - function maybeUpdateLineNumberWidth(cm) {
18.358 - if (!cm.options.lineNumbers) return false;
18.359 - var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
18.360 - if (last.length != display.lineNumChars) {
18.361 - var test = display.measure.appendChild(elt("div", [elt("div", last)],
18.362 - "CodeMirror-linenumber CodeMirror-gutter-elt"));
18.363 - var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
18.364 - display.lineGutter.style.width = "";
18.365 - display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
18.366 - display.lineNumWidth = display.lineNumInnerWidth + padding;
18.367 - display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
18.368 - display.lineGutter.style.width = display.lineNumWidth + "px";
18.369 - return true;
18.370 - }
18.371 - return false;
18.372 - }
18.373 -
18.374 - function lineNumberFor(options, i) {
18.375 - return String(options.lineNumberFormatter(i + options.firstLineNumber));
18.376 - }
18.377 - function compensateForHScroll(display) {
18.378 - return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
18.379 - }
18.380 -
18.381 - // DISPLAY DRAWING
18.382 -
18.383 - function updateDisplay(cm, changes, viewPort) {
18.384 - var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
18.385 - var updated = updateDisplayInner(cm, changes, viewPort);
18.386 - if (updated) {
18.387 - signalLater(cm, cm, "update", cm);
18.388 - if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
18.389 - signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
18.390 - }
18.391 - updateSelection(cm);
18.392 - updateScrollbars(cm.display, cm.view.doc.height);
18.393 -
18.394 - return updated;
18.395 - }
18.396 -
18.397 - // Uses a set of changes plus the current scroll position to
18.398 - // determine which DOM updates have to be made, and makes the
18.399 - // updates.
18.400 - function updateDisplayInner(cm, changes, viewPort) {
18.401 - var display = cm.display, doc = cm.view.doc;
18.402 - if (!display.wrapper.clientWidth) {
18.403 - display.showingFrom = display.showingTo = display.viewOffset = 0;
18.404 - return;
18.405 - }
18.406 -
18.407 - // Compute the new visible window
18.408 - // If scrollTop is specified, use that to determine which lines
18.409 - // to render instead of the current scrollbar position.
18.410 - var visible = visibleLines(display, doc, viewPort);
18.411 - // Bail out if the visible area is already rendered and nothing changed.
18.412 - if (changes !== true && changes.length == 0 &&
18.413 - visible.from > display.showingFrom && visible.to < display.showingTo)
18.414 - return;
18.415 -
18.416 - if (changes && maybeUpdateLineNumberWidth(cm))
18.417 - changes = true;
18.418 - display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";
18.419 -
18.420 - // When merged lines are present, the line that needs to be
18.421 - // redrawn might not be the one that was changed.
18.422 - if (changes !== true && sawCollapsedSpans)
18.423 - for (var i = 0; i < changes.length; ++i) {
18.424 - var ch = changes[i], merged;
18.425 - while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
18.426 - var from = merged.find().from.line;
18.427 - if (ch.diff) ch.diff -= ch.from - from;
18.428 - ch.from = from;
18.429 - }
18.430 - }
18.431 -
18.432 - // Used to determine which lines need their line numbers updated
18.433 - var positionsChangedFrom = changes === true ? 0 : Infinity;
18.434 - if (cm.options.lineNumbers && changes && changes !== true)
18.435 - for (var i = 0; i < changes.length; ++i)
18.436 - if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
18.437 -
18.438 - var from = Math.max(visible.from - cm.options.viewportMargin, 0);
18.439 - var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
18.440 - if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
18.441 - if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
18.442 - if (sawCollapsedSpans) {
18.443 - from = lineNo(visualLine(doc, getLine(doc, from)));
18.444 - while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
18.445 - }
18.446 -
18.447 - // Create a range of theoretically intact lines, and punch holes
18.448 - // in that using the change info.
18.449 - var intact = changes === true ? [] :
18.450 - computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
18.451 - // Clip off the parts that won't be visible
18.452 - var intactLines = 0;
18.453 - for (var i = 0; i < intact.length; ++i) {
18.454 - var range = intact[i];
18.455 - if (range.from < from) range.from = from;
18.456 - if (range.to > to) range.to = to;
18.457 - if (range.from >= range.to) intact.splice(i--, 1);
18.458 - else intactLines += range.to - range.from;
18.459 - }
18.460 - if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
18.461 - return;
18.462 - intact.sort(function(a, b) {return a.from - b.from;});
18.463 -
18.464 - if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
18.465 - patchDisplay(cm, from, to, intact, positionsChangedFrom);
18.466 - display.lineDiv.style.display = "";
18.467 -
18.468 - var different = from != display.showingFrom || to != display.showingTo ||
18.469 - display.lastSizeC != display.wrapper.clientHeight;
18.470 - // This is just a bogus formula that detects when the editor is
18.471 - // resized or the font size changes.
18.472 - if (different) display.lastSizeC = display.wrapper.clientHeight;
18.473 - display.showingFrom = from; display.showingTo = to;
18.474 - startWorker(cm, 100);
18.475 -
18.476 - var prevBottom = display.lineDiv.offsetTop;
18.477 - for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
18.478 - if (ie_lt8) {
18.479 - var bot = node.offsetTop + node.offsetHeight;
18.480 - height = bot - prevBottom;
18.481 - prevBottom = bot;
18.482 - } else {
18.483 - var box = node.getBoundingClientRect();
18.484 - height = box.bottom - box.top;
18.485 - }
18.486 - var diff = node.lineObj.height - height;
18.487 - if (height < 2) height = textHeight(display);
18.488 - if (diff > .001 || diff < -.001)
18.489 - updateLineHeight(node.lineObj, height);
18.490 - }
18.491 - display.viewOffset = heightAtLine(cm, getLine(doc, from));
18.492 - // Position the mover div to align with the current virtual scroll position
18.493 - display.mover.style.top = display.viewOffset + "px";
18.494 - return true;
18.495 - }
18.496 -
18.497 - function computeIntact(intact, changes) {
18.498 - for (var i = 0, l = changes.length || 0; i < l; ++i) {
18.499 - var change = changes[i], intact2 = [], diff = change.diff || 0;
18.500 - for (var j = 0, l2 = intact.length; j < l2; ++j) {
18.501 - var range = intact[j];
18.502 - if (change.to <= range.from && change.diff) {
18.503 - intact2.push({from: range.from + diff, to: range.to + diff});
18.504 - } else if (change.to <= range.from || change.from >= range.to) {
18.505 - intact2.push(range);
18.506 - } else {
18.507 - if (change.from > range.from)
18.508 - intact2.push({from: range.from, to: change.from});
18.509 - if (change.to < range.to)
18.510 - intact2.push({from: change.to + diff, to: range.to + diff});
18.511 - }
18.512 - }
18.513 - intact = intact2;
18.514 - }
18.515 - return intact;
18.516 - }
18.517 -
18.518 - function getDimensions(cm) {
18.519 - var d = cm.display, left = {}, width = {};
18.520 - for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
18.521 - left[cm.options.gutters[i]] = n.offsetLeft;
18.522 - width[cm.options.gutters[i]] = n.offsetWidth;
18.523 - }
18.524 - return {fixedPos: compensateForHScroll(d),
18.525 - gutterTotalWidth: d.gutters.offsetWidth,
18.526 - gutterLeft: left,
18.527 - gutterWidth: width,
18.528 - wrapperWidth: d.wrapper.clientWidth};
18.529 - }
18.530 -
18.531 - function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
18.532 - var dims = getDimensions(cm);
18.533 - var display = cm.display, lineNumbers = cm.options.lineNumbers;
18.534 - // IE does bad things to nodes when .innerHTML = "" is used on a parent
18.535 - // we still need widgets and markers intact to add back to the new content later
18.536 - if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget))
18.537 - removeChildren(display.lineDiv);
18.538 - var container = display.lineDiv, cur = container.firstChild;
18.539 -
18.540 - function rm(node) {
18.541 - var next = node.nextSibling;
18.542 - if (webkit && mac && cm.display.currentWheelTarget == node) {
18.543 - node.style.display = "none";
18.544 - node.lineObj = null;
18.545 - } else {
18.546 - container.removeChild(node);
18.547 - }
18.548 - return next;
18.549 - }
18.550 -
18.551 - var nextIntact = intact.shift(), lineNo = from;
18.552 - cm.view.doc.iter(from, to, function(line) {
18.553 - if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
18.554 - if (lineIsHidden(line)) {
18.555 - if (line.height != 0) updateLineHeight(line, 0);
18.556 - } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
18.557 - // This line is intact. Skip to the actual node. Update its
18.558 - // line number if needed.
18.559 - while (cur.lineObj != line) cur = rm(cur);
18.560 - if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
18.561 - setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
18.562 - cur = cur.nextSibling;
18.563 - } else {
18.564 - // This line needs to be generated.
18.565 - var lineNode = buildLineElement(cm, line, lineNo, dims);
18.566 - container.insertBefore(lineNode, cur);
18.567 - lineNode.lineObj = line;
18.568 - }
18.569 - ++lineNo;
18.570 - });
18.571 - while (cur) cur = rm(cur);
18.572 - }
18.573 -
18.574 - function buildLineElement(cm, line, lineNo, dims) {
18.575 - var lineElement = lineContent(cm, line);
18.576 - var markers = line.gutterMarkers, display = cm.display;
18.577 -
18.578 - if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
18.579 - (!line.widgets || !line.widgets.length)) return lineElement;
18.580 -
18.581 - // Lines with gutter elements or a background class need
18.582 - // to be wrapped again, and have the extra elements added
18.583 - // to the wrapper div
18.584 -
18.585 - var wrap = elt("div", null, line.wrapClass, "position: relative");
18.586 - if (cm.options.lineNumbers || markers) {
18.587 - var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
18.588 - dims.fixedPos + "px"));
18.589 - wrap.alignable = [gutterWrap];
18.590 - if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
18.591 - wrap.lineNumber = gutterWrap.appendChild(
18.592 - elt("div", lineNumberFor(cm.options, lineNo),
18.593 - "CodeMirror-linenumber CodeMirror-gutter-elt",
18.594 - "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
18.595 - + display.lineNumInnerWidth + "px"));
18.596 - if (markers)
18.597 - for (var k = 0; k < cm.options.gutters.length; ++k) {
18.598 - var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
18.599 - if (found)
18.600 - gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
18.601 - dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
18.602 - }
18.603 - }
18.604 - // Kludge to make sure the styled element lies behind the selection (by z-index)
18.605 - if (line.bgClass)
18.606 - wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
18.607 - wrap.appendChild(lineElement);
18.608 - if (line.widgets)
18.609 - for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
18.610 - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
18.611 - node.widget = widget;
18.612 - if (widget.noHScroll) {
18.613 - (wrap.alignable || (wrap.alignable = [])).push(node);
18.614 - var width = dims.wrapperWidth;
18.615 - node.style.left = dims.fixedPos + "px";
18.616 - if (!widget.coverGutter) {
18.617 - width -= dims.gutterTotalWidth;
18.618 - node.style.paddingLeft = dims.gutterTotalWidth + "px";
18.619 - }
18.620 - node.style.width = width + "px";
18.621 - }
18.622 - if (widget.coverGutter) {
18.623 - node.style.zIndex = 5;
18.624 - node.style.position = "relative";
18.625 - if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
18.626 - }
18.627 - if (widget.above)
18.628 - wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
18.629 - else
18.630 - wrap.appendChild(node);
18.631 - }
18.632 -
18.633 - if (ie_lt8) wrap.style.zIndex = 2;
18.634 - return wrap;
18.635 - }
18.636 -
18.637 - // SELECTION / CURSOR
18.638 -
18.639 - function updateSelection(cm) {
18.640 - var display = cm.display;
18.641 - var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
18.642 - if (collapsed || cm.options.showCursorWhenSelecting)
18.643 - updateSelectionCursor(cm);
18.644 - else
18.645 - display.cursor.style.display = display.otherCursor.style.display = "none";
18.646 - if (!collapsed)
18.647 - updateSelectionRange(cm);
18.648 - else
18.649 - display.selectionDiv.style.display = "none";
18.650 -
18.651 - // Move the hidden textarea near the cursor to prevent scrolling artifacts
18.652 - var headPos = cursorCoords(cm, cm.view.sel.head, "div");
18.653 - var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
18.654 - display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
18.655 - headPos.top + lineOff.top - wrapOff.top)) + "px";
18.656 - display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
18.657 - headPos.left + lineOff.left - wrapOff.left)) + "px";
18.658 - }
18.659 -
18.660 - // No selection, plain cursor
18.661 - function updateSelectionCursor(cm) {
18.662 - var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
18.663 - display.cursor.style.left = pos.left + "px";
18.664 - display.cursor.style.top = pos.top + "px";
18.665 - display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
18.666 - display.cursor.style.display = "";
18.667 -
18.668 - if (pos.other) {
18.669 - display.otherCursor.style.display = "";
18.670 - display.otherCursor.style.left = pos.other.left + "px";
18.671 - display.otherCursor.style.top = pos.other.top + "px";
18.672 - display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
18.673 - } else { display.otherCursor.style.display = "none"; }
18.674 - }
18.675 -
18.676 - // Highlight selection
18.677 - function updateSelectionRange(cm) {
18.678 - var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
18.679 - var fragment = document.createDocumentFragment();
18.680 - var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
18.681 -
18.682 - function add(left, top, width, bottom) {
18.683 - if (top < 0) top = 0;
18.684 - fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
18.685 - "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
18.686 - "px; height: " + (bottom - top) + "px"));
18.687 - }
18.688 -
18.689 - function drawForLine(line, fromArg, toArg, retTop) {
18.690 - var lineObj = getLine(doc, line);
18.691 - var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
18.692 - function coords(ch) {
18.693 - return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
18.694 - }
18.695 -
18.696 - iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
18.697 - var leftPos = coords(dir == "rtl" ? to - 1 : from);
18.698 - var rightPos = coords(dir == "rtl" ? from : to - 1);
18.699 - var left = leftPos.left, right = rightPos.right;
18.700 - if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
18.701 - add(left, leftPos.top, null, leftPos.bottom);
18.702 - left = pl;
18.703 - if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
18.704 - }
18.705 - if (toArg == null && to == lineLen) right = clientWidth;
18.706 - if (fromArg == null && from == 0) left = pl;
18.707 - rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
18.708 - if (left < pl + 1) left = pl;
18.709 - add(left, rightPos.top, right - left, rightPos.bottom);
18.710 - });
18.711 - return rVal;
18.712 - }
18.713 -
18.714 - if (sel.from.line == sel.to.line) {
18.715 - drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
18.716 - } else {
18.717 - var fromObj = getLine(doc, sel.from.line);
18.718 - var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
18.719 - while (merged = collapsedSpanAtEnd(cur)) {
18.720 - var found = merged.find();
18.721 - path.push(found.from.ch, found.to.line, found.to.ch);
18.722 - if (found.to.line == sel.to.line) {
18.723 - path.push(sel.to.ch);
18.724 - singleLine = true;
18.725 - break;
18.726 - }
18.727 - cur = getLine(doc, found.to.line);
18.728 - }
18.729 -
18.730 - // This is a single, merged line
18.731 - if (singleLine) {
18.732 - for (var i = 0; i < path.length; i += 3)
18.733 - drawForLine(path[i], path[i+1], path[i+2]);
18.734 - } else {
18.735 - var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
18.736 - if (sel.from.ch)
18.737 - // Draw the first line of selection.
18.738 - middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
18.739 - else
18.740 - // Simply include it in the middle block.
18.741 - middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
18.742 -
18.743 - if (!sel.to.ch)
18.744 - middleBot = heightAtLine(cm, toObj) - display.viewOffset;
18.745 - else
18.746 - middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
18.747 -
18.748 - if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
18.749 - }
18.750 - }
18.751 -
18.752 - removeChildrenAndAdd(display.selectionDiv, fragment);
18.753 - display.selectionDiv.style.display = "";
18.754 - }
18.755 -
18.756 - // Cursor-blinking
18.757 - function restartBlink(cm) {
18.758 - var display = cm.display;
18.759 - clearInterval(display.blinker);
18.760 - var on = true;
18.761 - display.cursor.style.visibility = display.otherCursor.style.visibility = "";
18.762 - display.blinker = setInterval(function() {
18.763 - if (!display.cursor.offsetHeight) return;
18.764 - display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
18.765 - }, cm.options.cursorBlinkRate);
18.766 - }
18.767 -
18.768 - // HIGHLIGHT WORKER
18.769 -
18.770 - function startWorker(cm, time) {
18.771 - if (cm.view.frontier < cm.display.showingTo)
18.772 - cm.view.highlight.set(time, bind(highlightWorker, cm));
18.773 - }
18.774 -
18.775 - function highlightWorker(cm) {
18.776 - var view = cm.view, doc = view.doc;
18.777 - if (view.frontier >= cm.display.showingTo) return;
18.778 - var end = +new Date + cm.options.workTime;
18.779 - var state = copyState(view.mode, getStateBefore(cm, view.frontier));
18.780 - var changed = [], prevChange;
18.781 - doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
18.782 - if (view.frontier >= cm.display.showingFrom) { // Visible
18.783 - if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) {
18.784 - if (prevChange && prevChange.end == view.frontier) prevChange.end++;
18.785 - else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
18.786 - }
18.787 - line.stateAfter = copyState(view.mode, state);
18.788 - } else {
18.789 - processLine(cm, line, state);
18.790 - line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
18.791 - }
18.792 - ++view.frontier;
18.793 - if (+new Date > end) {
18.794 - startWorker(cm, cm.options.workDelay);
18.795 - return true;
18.796 - }
18.797 - });
18.798 - if (changed.length)
18.799 - operation(cm, function() {
18.800 - for (var i = 0; i < changed.length; ++i)
18.801 - regChange(this, changed[i].start, changed[i].end);
18.802 - })();
18.803 - }
18.804 -
18.805 - // Finds the line to start with when starting a parse. Tries to
18.806 - // find a line with a stateAfter, so that it can start with a
18.807 - // valid state. If that fails, it returns the line with the
18.808 - // smallest indentation, which tends to need the least context to
18.809 - // parse correctly.
18.810 - function findStartLine(cm, n) {
18.811 - var minindent, minline, doc = cm.view.doc;
18.812 - for (var search = n, lim = n - 100; search > lim; --search) {
18.813 - if (search == 0) return 0;
18.814 - var line = getLine(doc, search-1);
18.815 - if (line.stateAfter) return search;
18.816 - var indented = countColumn(line.text, null, cm.options.tabSize);
18.817 - if (minline == null || minindent > indented) {
18.818 - minline = search - 1;
18.819 - minindent = indented;
18.820 - }
18.821 - }
18.822 - return minline;
18.823 - }
18.824 -
18.825 - function getStateBefore(cm, n) {
18.826 - var view = cm.view;
18.827 - var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
18.828 - if (!state) state = startState(view.mode);
18.829 - else state = copyState(view.mode, state);
18.830 - view.doc.iter(pos, n, function(line) {
18.831 - processLine(cm, line, state);
18.832 - var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
18.833 - line.stateAfter = save ? copyState(view.mode, state) : null;
18.834 - ++pos;
18.835 - });
18.836 - return state;
18.837 - }
18.838 -
18.839 - // POSITION MEASUREMENT
18.840 -
18.841 - function paddingTop(display) {return display.lineSpace.offsetTop;}
18.842 - function paddingLeft(display) {
18.843 - var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
18.844 - return e.offsetLeft;
18.845 - }
18.846 -
18.847 - function measureChar(cm, line, ch, data) {
18.848 - var data = data || measureLine(cm, line), dir = -1;
18.849 - for (var pos = ch;; pos += dir) {
18.850 - var r = data[pos];
18.851 - if (r) break;
18.852 - if (dir < 0 && pos == 0) dir = 1;
18.853 - }
18.854 - return {left: pos < ch ? r.right : r.left,
18.855 - right: pos > ch ? r.left : r.right,
18.856 - top: r.top, bottom: r.bottom};
18.857 - }
18.858 -
18.859 - function measureLine(cm, line) {
18.860 - // First look in the cache
18.861 - var display = cm.display, cache = cm.display.measureLineCache;
18.862 - for (var i = 0; i < cache.length; ++i) {
18.863 - var memo = cache[i];
18.864 - if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
18.865 - display.scroller.clientWidth == memo.width)
18.866 - return memo.measure;
18.867 - }
18.868 -
18.869 - var measure = measureLineInner(cm, line);
18.870 - // Store result in the cache
18.871 - var memo = {text: line.text, width: display.scroller.clientWidth,
18.872 - markedSpans: line.markedSpans, measure: measure};
18.873 - if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
18.874 - else cache.push(memo);
18.875 - return measure;
18.876 - }
18.877 -
18.878 - function measureLineInner(cm, line) {
18.879 - var display = cm.display, measure = emptyArray(line.text.length);
18.880 - var pre = lineContent(cm, line, measure);
18.881 -
18.882 - // IE does not cache element positions of inline elements between
18.883 - // calls to getBoundingClientRect. This makes the loop below,
18.884 - // which gathers the positions of all the characters on the line,
18.885 - // do an amount of layout work quadratic to the number of
18.886 - // characters. When line wrapping is off, we try to improve things
18.887 - // by first subdividing the line into a bunch of inline blocks, so
18.888 - // that IE can reuse most of the layout information from caches
18.889 - // for those blocks. This does interfere with line wrapping, so it
18.890 - // doesn't work when wrapping is on, but in that case the
18.891 - // situation is slightly better, since IE does cache line-wrapping
18.892 - // information and only recomputes per-line.
18.893 - if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
18.894 - var fragment = document.createDocumentFragment();
18.895 - var chunk = 10, n = pre.childNodes.length;
18.896 - for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
18.897 - var wrap = elt("div", null, null, "display: inline-block");
18.898 - for (var j = 0; j < chunk && n; ++j) {
18.899 - wrap.appendChild(pre.firstChild);
18.900 - --n;
18.901 - }
18.902 - fragment.appendChild(wrap);
18.903 - }
18.904 - pre.appendChild(fragment);
18.905 - }
18.906 -
18.907 - removeChildrenAndAdd(display.measure, pre);
18.908 -
18.909 - var outer = display.lineDiv.getBoundingClientRect();
18.910 - var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
18.911 - for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
18.912 - var size = cur.getBoundingClientRect();
18.913 - var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
18.914 - for (var j = 0; j < vranges.length; j += 2) {
18.915 - var rtop = vranges[j], rbot = vranges[j+1];
18.916 - if (rtop > bot || rbot < top) continue;
18.917 - if (rtop <= top && rbot >= bot ||
18.918 - top <= rtop && bot >= rbot ||
18.919 - Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
18.920 - vranges[j] = Math.min(top, rtop);
18.921 - vranges[j+1] = Math.max(bot, rbot);
18.922 - break;
18.923 - }
18.924 - }
18.925 - if (j == vranges.length) vranges.push(top, bot);
18.926 - data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
18.927 - }
18.928 - for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
18.929 - var vr = cur.top;
18.930 - cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
18.931 - }
18.932 - return data;
18.933 - }
18.934 -
18.935 - function clearCaches(cm) {
18.936 - cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
18.937 - cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
18.938 - cm.view.maxLineChanged = true;
18.939 - }
18.940 -
18.941 - // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
18.942 - function intoCoordSystem(cm, lineObj, rect, context) {
18.943 - if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
18.944 - var size = lineObj.widgets[i].node.offsetHeight;
18.945 - rect.top += size; rect.bottom += size;
18.946 - }
18.947 - if (context == "line") return rect;
18.948 - if (!context) context = "local";
18.949 - var yOff = heightAtLine(cm, lineObj);
18.950 - if (context != "local") yOff -= cm.display.viewOffset;
18.951 - if (context == "page") {
18.952 - var lOff = cm.display.lineSpace.getBoundingClientRect();
18.953 - yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
18.954 - var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
18.955 - rect.left += xOff; rect.right += xOff;
18.956 - }
18.957 - rect.top += yOff; rect.bottom += yOff;
18.958 - return rect;
18.959 - }
18.960 -
18.961 - function charCoords(cm, pos, context, lineObj) {
18.962 - if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
18.963 - return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
18.964 - }
18.965 -
18.966 - function cursorCoords(cm, pos, context, lineObj, measurement) {
18.967 - lineObj = lineObj || getLine(cm.view.doc, pos.line);
18.968 - if (!measurement) measurement = measureLine(cm, lineObj);
18.969 - function get(ch, right) {
18.970 - var m = measureChar(cm, lineObj, ch, measurement);
18.971 - if (right) m.left = m.right; else m.right = m.left;
18.972 - return intoCoordSystem(cm, lineObj, m, context);
18.973 - }
18.974 - var order = getOrder(lineObj), ch = pos.ch;
18.975 - if (!order) return get(ch);
18.976 - var main, other, linedir = order[0].level;
18.977 - for (var i = 0; i < order.length; ++i) {
18.978 - var part = order[i], rtl = part.level % 2, nb, here;
18.979 - if (part.from < ch && part.to > ch) return get(ch, rtl);
18.980 - var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
18.981 - if (left == ch) {
18.982 - // Opera and IE return bogus offsets and widths for edges
18.983 - // where the direction flips, but only for the side with the
18.984 - // lower level. So we try to use the side with the higher
18.985 - // level.
18.986 - if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
18.987 - else here = get(rtl && part.from != part.to ? ch - 1 : ch);
18.988 - if (rtl == linedir) main = here; else other = here;
18.989 - } else if (right == ch) {
18.990 - var nb = i < order.length - 1 && order[i+1];
18.991 - if (!rtl && nb && nb.from == nb.to) continue;
18.992 - if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
18.993 - else here = get(rtl ? ch : ch - 1, true);
18.994 - if (rtl == linedir) main = here; else other = here;
18.995 - }
18.996 - }
18.997 - if (linedir && !ch) other = get(order[0].to - 1);
18.998 - if (!main) return other;
18.999 - if (other) main.other = other;
18.1000 - return main;
18.1001 - }
18.1002 -
18.1003 - // Coords must be lineSpace-local
18.1004 - function coordsChar(cm, x, y) {
18.1005 - var doc = cm.view.doc;
18.1006 - y += cm.display.viewOffset;
18.1007 - if (y < 0) return {line: 0, ch: 0, outside: true};
18.1008 - var lineNo = lineAtHeight(doc, y);
18.1009 - if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
18.1010 - if (x < 0) x = 0;
18.1011 -
18.1012 - for (;;) {
18.1013 - var lineObj = getLine(doc, lineNo);
18.1014 - var found = coordsCharInner(cm, lineObj, lineNo, x, y);
18.1015 - var merged = collapsedSpanAtEnd(lineObj);
18.1016 - if (merged && found.ch == lineRight(lineObj))
18.1017 - lineNo = merged.find().to.line;
18.1018 - else
18.1019 - return found;
18.1020 - }
18.1021 - }
18.1022 -
18.1023 - function coordsCharInner(cm, lineObj, lineNo, x, y) {
18.1024 - var innerOff = y - heightAtLine(cm, lineObj);
18.1025 - var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
18.1026 - var measurement = measureLine(cm, lineObj);
18.1027 -
18.1028 - function getX(ch) {
18.1029 - var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
18.1030 - lineObj, measurement);
18.1031 - wrongLine = true;
18.1032 - if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
18.1033 - else if (innerOff < sp.top) return sp.left + cWidth;
18.1034 - else wrongLine = false;
18.1035 - return sp.left;
18.1036 - }
18.1037 -
18.1038 - var bidi = getOrder(lineObj), dist = lineObj.text.length;
18.1039 - var from = lineLeft(lineObj), to = lineRight(lineObj);
18.1040 - var fromX = paddingLeft(cm.display), toX = getX(to);
18.1041 -
18.1042 - if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
18.1043 - // Do a binary search between these bounds.
18.1044 - for (;;) {
18.1045 - if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
18.1046 - var after = x - fromX < toX - x, ch = after ? from : to;
18.1047 - while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
18.1048 - return {line: lineNo, ch: ch, after: after, outside: wrongLine};
18.1049 - }
18.1050 - var step = Math.ceil(dist / 2), middle = from + step;
18.1051 - if (bidi) {
18.1052 - middle = from;
18.1053 - for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
18.1054 - }
18.1055 - var middleX = getX(middle);
18.1056 - if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
18.1057 - else {from = middle; fromX = middleX; dist = step;}
18.1058 - }
18.1059 - }
18.1060 -
18.1061 - var measureText;
18.1062 - function textHeight(display) {
18.1063 - if (display.cachedTextHeight != null) return display.cachedTextHeight;
18.1064 - if (measureText == null) {
18.1065 - measureText = elt("pre");
18.1066 - // Measure a bunch of lines, for browsers that compute
18.1067 - // fractional heights.
18.1068 - for (var i = 0; i < 49; ++i) {
18.1069 - measureText.appendChild(document.createTextNode("x"));
18.1070 - measureText.appendChild(elt("br"));
18.1071 - }
18.1072 - measureText.appendChild(document.createTextNode("x"));
18.1073 - }
18.1074 - removeChildrenAndAdd(display.measure, measureText);
18.1075 - var height = measureText.offsetHeight / 50;
18.1076 - if (height > 3) display.cachedTextHeight = height;
18.1077 - removeChildren(display.measure);
18.1078 - return height || 1;
18.1079 - }
18.1080 -
18.1081 - function charWidth(display) {
18.1082 - if (display.cachedCharWidth != null) return display.cachedCharWidth;
18.1083 - var anchor = elt("span", "x");
18.1084 - var pre = elt("pre", [anchor]);
18.1085 - removeChildrenAndAdd(display.measure, pre);
18.1086 - var width = anchor.offsetWidth;
18.1087 - if (width > 2) display.cachedCharWidth = width;
18.1088 - return width || 10;
18.1089 - }
18.1090 -
18.1091 - // OPERATIONS
18.1092 -
18.1093 - // Operations are used to wrap changes in such a way that each
18.1094 - // change won't have to update the cursor and display (which would
18.1095 - // be awkward, slow, and error-prone), but instead updates are
18.1096 - // batched and then all combined and executed at once.
18.1097 -
18.1098 - function startOperation(cm) {
18.1099 - if (cm.curOp) ++cm.curOp.depth;
18.1100 - else cm.curOp = {
18.1101 - // Nested operations delay update until the outermost one
18.1102 - // finishes.
18.1103 - depth: 1,
18.1104 - // An array of ranges of lines that have to be updated. See
18.1105 - // updateDisplay.
18.1106 - changes: [],
18.1107 - delayedCallbacks: [],
18.1108 - updateInput: null,
18.1109 - userSelChange: null,
18.1110 - textChanged: null,
18.1111 - selectionChanged: false,
18.1112 - updateMaxLine: false,
18.1113 - id: ++cm.nextOpId
18.1114 - };
18.1115 - }
18.1116 -
18.1117 - function endOperation(cm) {
18.1118 - var op = cm.curOp;
18.1119 - if (--op.depth) return;
18.1120 - cm.curOp = null;
18.1121 - var view = cm.view, display = cm.display;
18.1122 - if (op.updateMaxLine) computeMaxLength(view);
18.1123 - if (view.maxLineChanged && !cm.options.lineWrapping) {
18.1124 - var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
18.1125 - display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
18.1126 - view.maxLineChanged = false;
18.1127 - }
18.1128 - var newScrollPos, updated;
18.1129 - if (op.selectionChanged) {
18.1130 - var coords = cursorCoords(cm, view.sel.head);
18.1131 - newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
18.1132 - }
18.1133 - if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
18.1134 - updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
18.1135 - if (!updated && op.selectionChanged) updateSelection(cm);
18.1136 - if (newScrollPos) scrollCursorIntoView(cm);
18.1137 - if (op.selectionChanged) restartBlink(cm);
18.1138 -
18.1139 - if (view.focused && op.updateInput)
18.1140 - resetInput(cm, op.userSelChange);
18.1141 -
18.1142 - if (op.textChanged)
18.1143 - signal(cm, "change", cm, op.textChanged);
18.1144 - if (op.selectionChanged) signal(cm, "cursorActivity", cm);
18.1145 - for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
18.1146 - }
18.1147 -
18.1148 - // Wraps a function in an operation. Returns the wrapped function.
18.1149 - function operation(cm1, f) {
18.1150 - return function() {
18.1151 - var cm = cm1 || this;
18.1152 - startOperation(cm);
18.1153 - try {var result = f.apply(cm, arguments);}
18.1154 - finally {endOperation(cm);}
18.1155 - return result;
18.1156 - };
18.1157 - }
18.1158 -
18.1159 - function regChange(cm, from, to, lendiff) {
18.1160 - cm.curOp.changes.push({from: from, to: to, diff: lendiff});
18.1161 - }
18.1162 -
18.1163 - // INPUT HANDLING
18.1164 -
18.1165 - function slowPoll(cm) {
18.1166 - if (cm.view.pollingFast) return;
18.1167 - cm.display.poll.set(cm.options.pollInterval, function() {
18.1168 - readInput(cm);
18.1169 - if (cm.view.focused) slowPoll(cm);
18.1170 - });
18.1171 - }
18.1172 -
18.1173 - function fastPoll(cm) {
18.1174 - var missed = false;
18.1175 - cm.display.pollingFast = true;
18.1176 - function p() {
18.1177 - var changed = readInput(cm);
18.1178 - if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
18.1179 - else {cm.display.pollingFast = false; slowPoll(cm);}
18.1180 - }
18.1181 - cm.display.poll.set(20, p);
18.1182 - }
18.1183 -
18.1184 - // prevInput is a hack to work with IME. If we reset the textarea
18.1185 - // on every change, that breaks IME. So we look for changes
18.1186 - // compared to the previous content instead. (Modern browsers have
18.1187 - // events that indicate IME taking place, but these are not widely
18.1188 - // supported or compatible enough yet to rely on.)
18.1189 - function readInput(cm) {
18.1190 - var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
18.1191 - if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
18.1192 - var text = input.value;
18.1193 - if (text == prevInput && posEq(sel.from, sel.to)) return false;
18.1194 - startOperation(cm);
18.1195 - view.sel.shift = false;
18.1196 - var same = 0, l = Math.min(prevInput.length, text.length);
18.1197 - while (same < l && prevInput[same] == text[same]) ++same;
18.1198 - var from = sel.from, to = sel.to;
18.1199 - if (same < prevInput.length)
18.1200 - from = {line: from.line, ch: from.ch - (prevInput.length - same)};
18.1201 - else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
18.1202 - to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
18.1203 - var updateInput = cm.curOp.updateInput;
18.1204 - updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
18.1205 - cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
18.1206 - cm.curOp.updateInput = updateInput;
18.1207 - if (text.length > 1000) input.value = cm.display.prevInput = "";
18.1208 - else cm.display.prevInput = text;
18.1209 - endOperation(cm);
18.1210 - cm.display.pasteIncoming = false;
18.1211 - return true;
18.1212 - }
18.1213 -
18.1214 - function resetInput(cm, user) {
18.1215 - var view = cm.view, minimal, selected;
18.1216 - if (!posEq(view.sel.from, view.sel.to)) {
18.1217 - cm.display.prevInput = "";
18.1218 - minimal = hasCopyEvent &&
18.1219 - (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
18.1220 - if (minimal) cm.display.input.value = "-";
18.1221 - else cm.display.input.value = selected || cm.getSelection();
18.1222 - if (view.focused) selectInput(cm.display.input);
18.1223 - } else if (user) cm.display.prevInput = cm.display.input.value = "";
18.1224 - cm.display.inaccurateSelection = minimal;
18.1225 - }
18.1226 -
18.1227 - function focusInput(cm) {
18.1228 - if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
18.1229 - cm.display.input.focus();
18.1230 - }
18.1231 -
18.1232 - function isReadOnly(cm) {
18.1233 - return cm.options.readOnly || cm.view.cantEdit;
18.1234 - }
18.1235 -
18.1236 - // EVENT HANDLERS
18.1237 -
18.1238 - function registerEventHandlers(cm) {
18.1239 - var d = cm.display;
18.1240 - on(d.scroller, "mousedown", operation(cm, onMouseDown));
18.1241 - on(d.scroller, "dblclick", operation(cm, e_preventDefault));
18.1242 - on(d.lineSpace, "selectstart", function(e) {
18.1243 - if (!mouseEventInWidget(d, e)) e_preventDefault(e);
18.1244 - });
18.1245 - // Gecko browsers fire contextmenu *after* opening the menu, at
18.1246 - // which point we can't mess with it anymore. Context menu is
18.1247 - // handled in onMouseDown for Gecko.
18.1248 - if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
18.1249 -
18.1250 - on(d.scroller, "scroll", function() {
18.1251 - setScrollTop(cm, d.scroller.scrollTop);
18.1252 - setScrollLeft(cm, d.scroller.scrollLeft, true);
18.1253 - signal(cm, "scroll", cm);
18.1254 - });
18.1255 - on(d.scrollbarV, "scroll", function() {
18.1256 - setScrollTop(cm, d.scrollbarV.scrollTop);
18.1257 - });
18.1258 - on(d.scrollbarH, "scroll", function() {
18.1259 - setScrollLeft(cm, d.scrollbarH.scrollLeft);
18.1260 - });
18.1261 -
18.1262 - on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
18.1263 - on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
18.1264 -
18.1265 - function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
18.1266 - on(d.scrollbarH, "mousedown", reFocus);
18.1267 - on(d.scrollbarV, "mousedown", reFocus);
18.1268 - // Prevent wrapper from ever scrolling
18.1269 - on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
18.1270 - on(window, "resize", function resizeHandler() {
18.1271 - // Might be a text scaling operation, clear size caches.
18.1272 - d.cachedCharWidth = d.cachedTextHeight = null;
18.1273 - clearCaches(cm);
18.1274 - if (d.wrapper.parentNode) updateDisplay(cm, true);
18.1275 - else off(window, "resize", resizeHandler);
18.1276 - });
18.1277 -
18.1278 - on(d.input, "keyup", operation(cm, function(e) {
18.1279 - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
18.1280 - if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
18.1281 - }));
18.1282 - on(d.input, "input", bind(fastPoll, cm));
18.1283 - on(d.input, "keydown", operation(cm, onKeyDown));
18.1284 - on(d.input, "keypress", operation(cm, onKeyPress));
18.1285 - on(d.input, "focus", bind(onFocus, cm));
18.1286 - on(d.input, "blur", bind(onBlur, cm));
18.1287 -
18.1288 - function drag_(e) {
18.1289 - if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
18.1290 - e_stop(e);
18.1291 - }
18.1292 - if (cm.options.dragDrop) {
18.1293 - on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
18.1294 - on(d.scroller, "dragenter", drag_);
18.1295 - on(d.scroller, "dragover", drag_);
18.1296 - on(d.scroller, "drop", operation(cm, onDrop));
18.1297 - }
18.1298 - on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);});
18.1299 - on(d.input, "paste", function() {
18.1300 - d.pasteIncoming = true;
18.1301 - fastPoll(cm);
18.1302 - });
18.1303 -
18.1304 - function prepareCopy() {
18.1305 - if (d.inaccurateSelection) {
18.1306 - d.prevInput = "";
18.1307 - d.inaccurateSelection = false;
18.1308 - d.input.value = cm.getSelection();
18.1309 - selectInput(d.input);
18.1310 - }
18.1311 - }
18.1312 - on(d.input, "cut", prepareCopy);
18.1313 - on(d.input, "copy", prepareCopy);
18.1314 -
18.1315 - // Needed to handle Tab key in KHTML
18.1316 - if (khtml) on(d.sizer, "mouseup", function() {
18.1317 - if (document.activeElement == d.input) d.input.blur();
18.1318 - focusInput(cm);
18.1319 - });
18.1320 - }
18.1321 -
18.1322 - function mouseEventInWidget(display, e) {
18.1323 - for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
18.1324 - if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
18.1325 - n.parentNode == display.sizer && n != display.mover) return true;
18.1326 - }
18.1327 -
18.1328 - function posFromMouse(cm, e, liberal) {
18.1329 - var display = cm.display;
18.1330 - if (!liberal) {
18.1331 - var target = e_target(e);
18.1332 - if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
18.1333 - target == display.scrollbarV || target == display.scrollbarV.firstChild ||
18.1334 - target == display.scrollbarFiller) return null;
18.1335 - }
18.1336 - var x, y, space = display.lineSpace.getBoundingClientRect();
18.1337 - // Fails unpredictably on IE[67] when mouse is dragged around quickly.
18.1338 - try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
18.1339 - return coordsChar(cm, x - space.left, y - space.top);
18.1340 - }
18.1341 -
18.1342 - var lastClick, lastDoubleClick;
18.1343 - function onMouseDown(e) {
18.1344 - var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
18.1345 - sel.shift = e_prop(e, "shiftKey");
18.1346 -
18.1347 - if (mouseEventInWidget(display, e)) {
18.1348 - if (!webkit) {
18.1349 - display.scroller.draggable = false;
18.1350 - setTimeout(function(){display.scroller.draggable = true;}, 100);
18.1351 - }
18.1352 - return;
18.1353 - }
18.1354 - if (clickInGutter(cm, e)) return;
18.1355 - var start = posFromMouse(cm, e);
18.1356 -
18.1357 - switch (e_button(e)) {
18.1358 - case 3:
18.1359 - if (gecko) onContextMenu.call(cm, cm, e);
18.1360 - return;
18.1361 - case 2:
18.1362 - if (start) extendSelection(cm, start);
18.1363 - setTimeout(bind(focusInput, cm), 20);
18.1364 - e_preventDefault(e);
18.1365 - return;
18.1366 - }
18.1367 - // For button 1, if it was clicked inside the editor
18.1368 - // (posFromMouse returning non-null), we have to adjust the
18.1369 - // selection.
18.1370 - if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
18.1371 -
18.1372 - if (!view.focused) onFocus(cm);
18.1373 -
18.1374 - var now = +new Date, type = "single";
18.1375 - if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
18.1376 - type = "triple";
18.1377 - e_preventDefault(e);
18.1378 - setTimeout(bind(focusInput, cm), 20);
18.1379 - selectLine(cm, start.line);
18.1380 - } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
18.1381 - type = "double";
18.1382 - lastDoubleClick = {time: now, pos: start};
18.1383 - e_preventDefault(e);
18.1384 - var word = findWordAt(getLine(doc, start.line).text, start);
18.1385 - extendSelection(cm, word.from, word.to);
18.1386 - } else { lastClick = {time: now, pos: start}; }
18.1387 -
18.1388 - var last = start;
18.1389 - if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
18.1390 - !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
18.1391 - var dragEnd = operation(cm, function(e2) {
18.1392 - if (webkit) display.scroller.draggable = false;
18.1393 - view.draggingText = false;
18.1394 - off(document, "mouseup", dragEnd);
18.1395 - off(display.scroller, "drop", dragEnd);
18.1396 - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
18.1397 - e_preventDefault(e2);
18.1398 - extendSelection(cm, start);
18.1399 - focusInput(cm);
18.1400 - }
18.1401 - });
18.1402 - // Let the drag handler handle this.
18.1403 - if (webkit) display.scroller.draggable = true;
18.1404 - view.draggingText = dragEnd;
18.1405 - // IE's approach to draggable
18.1406 - if (display.scroller.dragDrop) display.scroller.dragDrop();
18.1407 - on(document, "mouseup", dragEnd);
18.1408 - on(display.scroller, "drop", dragEnd);
18.1409 - return;
18.1410 - }
18.1411 - e_preventDefault(e);
18.1412 - if (type == "single") extendSelection(cm, clipPos(doc, start));
18.1413 -
18.1414 - var startstart = sel.from, startend = sel.to;
18.1415 -
18.1416 - function doSelect(cur) {
18.1417 - if (type == "single") {
18.1418 - extendSelection(cm, clipPos(doc, start), cur);
18.1419 - return;
18.1420 - }
18.1421 -
18.1422 - startstart = clipPos(doc, startstart);
18.1423 - startend = clipPos(doc, startend);
18.1424 - if (type == "double") {
18.1425 - var word = findWordAt(getLine(doc, cur.line).text, cur);
18.1426 - if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
18.1427 - else extendSelection(cm, startstart, word.to);
18.1428 - } else if (type == "triple") {
18.1429 - if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
18.1430 - else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
18.1431 - }
18.1432 - }
18.1433 -
18.1434 - var editorSize = display.wrapper.getBoundingClientRect();
18.1435 - // Used to ensure timeout re-tries don't fire when another extend
18.1436 - // happened in the meantime (clearTimeout isn't reliable -- at
18.1437 - // least on Chrome, the timeouts still happen even when cleared,
18.1438 - // if the clear happens after their scheduled firing time).
18.1439 - var counter = 0;
18.1440 -
18.1441 - function extend(e) {
18.1442 - var curCount = ++counter;
18.1443 - var cur = posFromMouse(cm, e, true);
18.1444 - if (!cur) return;
18.1445 - if (!posEq(cur, last)) {
18.1446 - if (!view.focused) onFocus(cm);
18.1447 - last = cur;
18.1448 - doSelect(cur);
18.1449 - var visible = visibleLines(display, doc);
18.1450 - if (cur.line >= visible.to || cur.line < visible.from)
18.1451 - setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
18.1452 - } else {
18.1453 - var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
18.1454 - if (outside) setTimeout(operation(cm, function() {
18.1455 - if (counter != curCount) return;
18.1456 - display.scroller.scrollTop += outside;
18.1457 - extend(e);
18.1458 - }), 50);
18.1459 - }
18.1460 - }
18.1461 -
18.1462 - function done(e) {
18.1463 - counter = Infinity;
18.1464 - var cur = posFromMouse(cm, e);
18.1465 - if (cur) doSelect(cur);
18.1466 - e_preventDefault(e);
18.1467 - focusInput(cm);
18.1468 - off(document, "mousemove", move);
18.1469 - off(document, "mouseup", up);
18.1470 - }
18.1471 -
18.1472 - var move = operation(cm, function(e) {
18.1473 - if (!ie && !e_button(e)) done(e);
18.1474 - else extend(e);
18.1475 - });
18.1476 - var up = operation(cm, done);
18.1477 - on(document, "mousemove", move);
18.1478 - on(document, "mouseup", up);
18.1479 - }
18.1480 -
18.1481 - function onDrop(e) {
18.1482 - var cm = this;
18.1483 - if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
18.1484 - e_preventDefault(e);
18.1485 - var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
18.1486 - if (!pos || isReadOnly(cm)) return;
18.1487 - if (files && files.length && window.FileReader && window.File) {
18.1488 - var n = files.length, text = Array(n), read = 0;
18.1489 - var loadFile = function(file, i) {
18.1490 - var reader = new FileReader;
18.1491 - reader.onload = function() {
18.1492 - text[i] = reader.result;
18.1493 - if (++read == n) {
18.1494 - pos = clipPos(cm.view.doc, pos);
18.1495 - operation(cm, function() {
18.1496 - var end = replaceRange(cm, text.join(""), pos, pos, "paste");
18.1497 - setSelection(cm, pos, end);
18.1498 - })();
18.1499 - }
18.1500 - };
18.1501 - reader.readAsText(file);
18.1502 - };
18.1503 - for (var i = 0; i < n; ++i) loadFile(files[i], i);
18.1504 - } else {
18.1505 - // Don't do a replace if the drop happened inside of the selected text.
18.1506 - if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
18.1507 - cm.view.draggingText(e);
18.1508 - if (ie) setTimeout(bind(focusInput, cm), 50);
18.1509 - return;
18.1510 - }
18.1511 - try {
18.1512 - var text = e.dataTransfer.getData("Text");
18.1513 - if (text) {
18.1514 - var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
18.1515 - setSelection(cm, pos, pos);
18.1516 - if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
18.1517 - cm.replaceSelection(text, null, "paste");
18.1518 - focusInput(cm);
18.1519 - onFocus(cm);
18.1520 - }
18.1521 - }
18.1522 - catch(e){}
18.1523 - }
18.1524 - }
18.1525 -
18.1526 - function clickInGutter(cm, e) {
18.1527 - var display = cm.display;
18.1528 - try { var mX = e.clientX, mY = e.clientY; }
18.1529 - catch(e) { return false; }
18.1530 -
18.1531 - if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
18.1532 - e_preventDefault(e);
18.1533 - if (!hasHandler(cm, "gutterClick")) return true;
18.1534 -
18.1535 - var lineBox = display.lineDiv.getBoundingClientRect();
18.1536 - if (mY > lineBox.bottom) return true;
18.1537 - mY -= lineBox.top - display.viewOffset;
18.1538 -
18.1539 - for (var i = 0; i < cm.options.gutters.length; ++i) {
18.1540 - var g = display.gutters.childNodes[i];
18.1541 - if (g && g.getBoundingClientRect().right >= mX) {
18.1542 - var line = lineAtHeight(cm.view.doc, mY);
18.1543 - var gutter = cm.options.gutters[i];
18.1544 - signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
18.1545 - break;
18.1546 - }
18.1547 - }
18.1548 - return true;
18.1549 - }
18.1550 -
18.1551 - function onDragStart(cm, e) {
18.1552 - var txt = cm.getSelection();
18.1553 - e.dataTransfer.setData("Text", txt);
18.1554 -
18.1555 - // Use dummy image instead of default browsers image.
18.1556 - // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
18.1557 - if (e.dataTransfer.setDragImage && !safari)
18.1558 - e.dataTransfer.setDragImage(elt('img'), 0, 0);
18.1559 - }
18.1560 -
18.1561 - function setScrollTop(cm, val) {
18.1562 - if (Math.abs(cm.view.scrollTop - val) < 2) return;
18.1563 - cm.view.scrollTop = val;
18.1564 - if (!gecko) updateDisplay(cm, [], val);
18.1565 - if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
18.1566 - if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
18.1567 - if (gecko) updateDisplay(cm, []);
18.1568 - }
18.1569 - function setScrollLeft(cm, val, isScroller) {
18.1570 - if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
18.1571 - cm.view.scrollLeft = val;
18.1572 - alignHorizontally(cm);
18.1573 - if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
18.1574 - if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
18.1575 - }
18.1576 -
18.1577 - // Since the delta values reported on mouse wheel events are
18.1578 - // unstandardized between browsers and even browser versions, and
18.1579 - // generally horribly unpredictable, this code starts by measuring
18.1580 - // the scroll effect that the first few mouse wheel events have,
18.1581 - // and, from that, detects the way it can convert deltas to pixel
18.1582 - // offsets afterwards.
18.1583 - //
18.1584 - // The reason we want to know the amount a wheel event will scroll
18.1585 - // is that it gives us a chance to update the display before the
18.1586 - // actual scrolling happens, reducing flickering.
18.1587 -
18.1588 - var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null;
18.1589 - // Fill in a browser-detected starting value on browsers where we
18.1590 - // know one. These don't have to be accurate -- the result of them
18.1591 - // being wrong would just be a slight flicker on the first wheel
18.1592 - // scroll (if it is large enough).
18.1593 - if (ie) wheelPixelsPerUnit = -.53;
18.1594 - else if (gecko) wheelPixelsPerUnit = 15;
18.1595 - else if (chrome) wheelPixelsPerUnit = -.7;
18.1596 - else if (safari) wheelPixelsPerUnit = -1/3;
18.1597 -
18.1598 - function onScrollWheel(cm, e) {
18.1599 - var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
18.1600 - if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
18.1601 - if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
18.1602 - else if (dy == null) dy = e.wheelDelta;
18.1603 -
18.1604 - // Webkit browsers on OS X abort momentum scrolls when the target
18.1605 - // of the scroll event is removed from the scrollable element.
18.1606 - // This hack (see related code in patchDisplay) makes sure the
18.1607 - // element is kept around.
18.1608 - if (dy && mac && webkit) {
18.1609 - for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
18.1610 - if (cur.lineObj) {
18.1611 - cm.display.currentWheelTarget = cur;
18.1612 - break;
18.1613 - }
18.1614 - }
18.1615 - }
18.1616 -
18.1617 - var scroll = cm.display.scroller;
18.1618 - // On some browsers, horizontal scrolling will cause redraws to
18.1619 - // happen before the gutter has been realigned, causing it to
18.1620 - // wriggle around in a most unseemly way. When we have an
18.1621 - // estimated pixels/delta value, we just handle horizontal
18.1622 - // scrolling entirely here. It'll be slightly off from native, but
18.1623 - // better than glitching out.
18.1624 - if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
18.1625 - if (dy)
18.1626 - setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
18.1627 - setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
18.1628 - e_preventDefault(e);
18.1629 - wheelStartX = null; // Abort measurement, if in progress
18.1630 - return;
18.1631 - }
18.1632 -
18.1633 - if (dy && wheelPixelsPerUnit != null) {
18.1634 - var pixels = dy * wheelPixelsPerUnit;
18.1635 - var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight;
18.1636 - if (pixels < 0) top = Math.max(0, top + pixels - 50);
18.1637 - else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
18.1638 - updateDisplay(cm, [], {top: top, bottom: bot});
18.1639 - }
18.1640 -
18.1641 - if (wheelSamples < 20) {
18.1642 - if (wheelStartX == null) {
18.1643 - wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
18.1644 - wheelDX = dx; wheelDY = dy;
18.1645 - setTimeout(function() {
18.1646 - if (wheelStartX == null) return;
18.1647 - var movedX = scroll.scrollLeft - wheelStartX;
18.1648 - var movedY = scroll.scrollTop - wheelStartY;
18.1649 - var sample = (movedY && wheelDY && movedY / wheelDY) ||
18.1650 - (movedX && wheelDX && movedX / wheelDX);
18.1651 - wheelStartX = wheelStartY = null;
18.1652 - if (!sample) return;
18.1653 - wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
18.1654 - ++wheelSamples;
18.1655 - }, 200);
18.1656 - } else {
18.1657 - wheelDX += dx; wheelDY += dy;
18.1658 - }
18.1659 - }
18.1660 - }
18.1661 -
18.1662 - function doHandleBinding(cm, bound, dropShift) {
18.1663 - if (typeof bound == "string") {
18.1664 - bound = commands[bound];
18.1665 - if (!bound) return false;
18.1666 - }
18.1667 - // Ensure previous input has been read, so that the handler sees a
18.1668 - // consistent view of the document
18.1669 - if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
18.1670 - var view = cm.view, prevShift = view.sel.shift;
18.1671 - try {
18.1672 - if (isReadOnly(cm)) view.suppressEdits = true;
18.1673 - if (dropShift) view.sel.shift = false;
18.1674 - bound(cm);
18.1675 - } catch(e) {
18.1676 - if (e != Pass) throw e;
18.1677 - return false;
18.1678 - } finally {
18.1679 - view.sel.shift = prevShift;
18.1680 - view.suppressEdits = false;
18.1681 - }
18.1682 - return true;
18.1683 - }
18.1684 -
18.1685 - function allKeyMaps(cm) {
18.1686 - var maps = cm.view.keyMaps.slice(0);
18.1687 - maps.push(cm.options.keyMap);
18.1688 - if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
18.1689 - return maps;
18.1690 - }
18.1691 -
18.1692 - var maybeTransition;
18.1693 - function handleKeyBinding(cm, e) {
18.1694 - // Handle auto keymap transitions
18.1695 - var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
18.1696 - clearTimeout(maybeTransition);
18.1697 - if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
18.1698 - if (getKeyMap(cm.options.keyMap) == startMap)
18.1699 - cm.options.keyMap = (next.call ? next.call(null, cm) : next);
18.1700 - }, 50);
18.1701 -
18.1702 - var name = keyNames[e_prop(e, "keyCode")], handled = false;
18.1703 - var flipCtrlCmd = mac && (opera || qtwebkit);
18.1704 - if (name == null || e.altGraphKey) return false;
18.1705 - if (e_prop(e, "altKey")) name = "Alt-" + name;
18.1706 - if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
18.1707 - if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
18.1708 -
18.1709 - var stopped = false;
18.1710 - function stop() { stopped = true; }
18.1711 - var keymaps = allKeyMaps(cm);
18.1712 -
18.1713 - if (e_prop(e, "shiftKey")) {
18.1714 - handled = lookupKey("Shift-" + name, keymaps,
18.1715 - function(b) {return doHandleBinding(cm, b, true);}, stop)
18.1716 - || lookupKey(name, keymaps, function(b) {
18.1717 - if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
18.1718 - }, stop);
18.1719 - } else {
18.1720 - handled = lookupKey(name, keymaps,
18.1721 - function(b) { return doHandleBinding(cm, b); }, stop);
18.1722 - }
18.1723 - if (stopped) handled = false;
18.1724 - if (handled) {
18.1725 - e_preventDefault(e);
18.1726 - restartBlink(cm);
18.1727 - if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
18.1728 - }
18.1729 - return handled;
18.1730 - }
18.1731 -
18.1732 - function handleCharBinding(cm, e, ch) {
18.1733 - var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
18.1734 - function(b) { return doHandleBinding(cm, b, true); });
18.1735 - if (handled) {
18.1736 - e_preventDefault(e);
18.1737 - restartBlink(cm);
18.1738 - }
18.1739 - return handled;
18.1740 - }
18.1741 -
18.1742 - var lastStoppedKey = null;
18.1743 - function onKeyDown(e) {
18.1744 - var cm = this;
18.1745 - if (!cm.view.focused) onFocus(cm);
18.1746 - if (ie && e.keyCode == 27) { e.returnValue = false; }
18.1747 - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
18.1748 - var code = e_prop(e, "keyCode");
18.1749 - // IE does strange things with escape.
18.1750 - cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
18.1751 - // First give onKeyEvent option a chance to handle this.
18.1752 - var handled = handleKeyBinding(cm, e);
18.1753 - if (opera) {
18.1754 - lastStoppedKey = handled ? code : null;
18.1755 - // Opera has no cut event... we try to at least catch the key combo
18.1756 - if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
18.1757 - cm.replaceSelection("");
18.1758 - }
18.1759 - }
18.1760 -
18.1761 - function onKeyPress(e) {
18.1762 - var cm = this;
18.1763 - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
18.1764 - var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
18.1765 - if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
18.1766 - if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
18.1767 - var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
18.1768 - if (this.options.electricChars && this.view.mode.electricChars &&
18.1769 - this.options.smartIndent && !isReadOnly(this) &&
18.1770 - this.view.mode.electricChars.indexOf(ch) > -1)
18.1771 - setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
18.1772 - if (handleCharBinding(cm, e, ch)) return;
18.1773 - fastPoll(cm);
18.1774 - }
18.1775 -
18.1776 - function onFocus(cm) {
18.1777 - if (cm.options.readOnly == "nocursor") return;
18.1778 - if (!cm.view.focused) {
18.1779 - signal(cm, "focus", cm);
18.1780 - cm.view.focused = true;
18.1781 - if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
18.1782 - cm.display.scroller.className += " CodeMirror-focused";
18.1783 - resetInput(cm, true);
18.1784 - }
18.1785 - slowPoll(cm);
18.1786 - restartBlink(cm);
18.1787 - }
18.1788 - function onBlur(cm) {
18.1789 - if (cm.view.focused) {
18.1790 - signal(cm, "blur", cm);
18.1791 - cm.view.focused = false;
18.1792 - cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
18.1793 - }
18.1794 - clearInterval(cm.display.blinker);
18.1795 - setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
18.1796 - }
18.1797 -
18.1798 - var detectingSelectAll;
18.1799 - function onContextMenu(cm, e) {
18.1800 - var display = cm.display, sel = cm.view.sel;
18.1801 - var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
18.1802 - if (!pos || opera) return; // Opera is difficult.
18.1803 - if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
18.1804 - operation(cm, setSelection)(cm, pos, pos);
18.1805 -
18.1806 - var oldCSS = display.input.style.cssText;
18.1807 - display.inputDiv.style.position = "absolute";
18.1808 - display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
18.1809 - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
18.1810 - "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
18.1811 - focusInput(cm);
18.1812 - resetInput(cm, true);
18.1813 - // Adds "Select all" to context menu in FF
18.1814 - if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
18.1815 -
18.1816 - function rehide() {
18.1817 - display.inputDiv.style.position = "relative";
18.1818 - display.input.style.cssText = oldCSS;
18.1819 - if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
18.1820 - slowPoll(cm);
18.1821 -
18.1822 - // Try to detect the user choosing select-all
18.1823 - if (display.input.selectionStart != null) {
18.1824 - clearTimeout(detectingSelectAll);
18.1825 - var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
18.1826 - display.prevInput = " ";
18.1827 - display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
18.1828 - detectingSelectAll = setTimeout(function poll(){
18.1829 - if (display.prevInput == " " && display.input.selectionStart == 0)
18.1830 - operation(cm, commands.selectAll)(cm);
18.1831 - else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
18.1832 - else resetInput(cm);
18.1833 - }, 200);
18.1834 - }
18.1835 - }
18.1836 -
18.1837 - if (gecko) {
18.1838 - e_stop(e);
18.1839 - on(window, "mouseup", function mouseup() {
18.1840 - off(window, "mouseup", mouseup);
18.1841 - setTimeout(rehide, 20);
18.1842 - });
18.1843 - } else {
18.1844 - setTimeout(rehide, 50);
18.1845 - }
18.1846 - }
18.1847 -
18.1848 - // UPDATING
18.1849 -
18.1850 - // Replace the range from from to to by the strings in newText.
18.1851 - // Afterwards, set the selection to selFrom, selTo.
18.1852 - function updateDoc(cm, from, to, newText, selUpdate, origin) {
18.1853 - // Possibly split or suppress the update based on the presence
18.1854 - // of read-only spans in its range.
18.1855 - var split = sawReadOnlySpans &&
18.1856 - removeReadOnlyRanges(cm.view.doc, from, to);
18.1857 - if (split) {
18.1858 - for (var i = split.length - 1; i >= 1; --i)
18.1859 - updateDocInner(cm, split[i].from, split[i].to, [""], origin);
18.1860 - if (split.length)
18.1861 - return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
18.1862 - } else {
18.1863 - return updateDocInner(cm, from, to, newText, selUpdate, origin);
18.1864 - }
18.1865 - }
18.1866 -
18.1867 - function updateDocInner(cm, from, to, newText, selUpdate, origin) {
18.1868 - if (cm.view.suppressEdits) return;
18.1869 -
18.1870 - var view = cm.view, doc = view.doc, old = [];
18.1871 - doc.iter(from.line, to.line + 1, function(line) {
18.1872 - old.push(newHL(line.text, line.markedSpans));
18.1873 - });
18.1874 - var startSelFrom = view.sel.from, startSelTo = view.sel.to;
18.1875 - var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
18.1876 - var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
18.1877 - if (view.history) addChange(cm, from.line, newText.length, old, origin,
18.1878 - startSelFrom, startSelTo, view.sel.from, view.sel.to);
18.1879 - return retval;
18.1880 - }
18.1881 -
18.1882 - function unredoHelper(cm, type) {
18.1883 - var doc = cm.view.doc, hist = cm.view.history;
18.1884 - var set = (type == "undo" ? hist.done : hist.undone).pop();
18.1885 - if (!set) return;
18.1886 - var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
18.1887 - fromAfter: set.fromBefore, toAfter: set.toBefore};
18.1888 - for (var i = set.events.length - 1; i >= 0; i -= 1) {
18.1889 - hist.dirtyCounter += type == "undo" ? -1 : 1;
18.1890 - var change = set.events[i];
18.1891 - var replaced = [], end = change.start + change.added;
18.1892 - doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
18.1893 - anti.events.push({start: change.start, added: change.old.length, old: replaced});
18.1894 - var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
18.1895 - updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
18.1896 - change.old, selPos, type);
18.1897 - }
18.1898 - (type == "undo" ? hist.undone : hist.done).push(anti);
18.1899 - }
18.1900 -
18.1901 - function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
18.1902 - var view = cm.view, doc = view.doc, display = cm.display;
18.1903 - if (view.suppressEdits) return;
18.1904 -
18.1905 - var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
18.1906 - var recomputeMaxLength = false, checkWidthStart = from.line;
18.1907 - if (!cm.options.lineWrapping) {
18.1908 - checkWidthStart = lineNo(visualLine(doc, firstLine));
18.1909 - doc.iter(checkWidthStart, to.line + 1, function(line) {
18.1910 - if (lineLength(doc, line) == view.maxLineLength) {
18.1911 - recomputeMaxLength = true;
18.1912 - return true;
18.1913 - }
18.1914 - });
18.1915 - }
18.1916 -
18.1917 - var lastHL = lst(lines), th = textHeight(display);
18.1918 -
18.1919 - // First adjust the line structure
18.1920 - if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
18.1921 - // This is a whole-line replace. Treated specially to make
18.1922 - // sure line objects move the way they are supposed to.
18.1923 - var added = [];
18.1924 - for (var i = 0, e = lines.length - 1; i < e; ++i)
18.1925 - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
18.1926 - updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
18.1927 - if (nlines) doc.remove(from.line, nlines, cm);
18.1928 - if (added.length) doc.insert(from.line, added);
18.1929 - } else if (firstLine == lastLine) {
18.1930 - if (lines.length == 1) {
18.1931 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
18.1932 - firstLine.text.slice(to.ch), hlSpans(lines[0]));
18.1933 - } else {
18.1934 - for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
18.1935 - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
18.1936 - added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
18.1937 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
18.1938 - doc.insert(from.line + 1, added);
18.1939 - }
18.1940 - } else if (lines.length == 1) {
18.1941 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
18.1942 - lastLine.text.slice(to.ch), hlSpans(lines[0]));
18.1943 - doc.remove(from.line + 1, nlines, cm);
18.1944 - } else {
18.1945 - var added = [];
18.1946 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
18.1947 - updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
18.1948 - for (var i = 1, e = lines.length - 1; i < e; ++i)
18.1949 - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
18.1950 - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
18.1951 - doc.insert(from.line + 1, added);
18.1952 - }
18.1953 -
18.1954 - if (cm.options.lineWrapping) {
18.1955 - var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
18.1956 - doc.iter(from.line, from.line + lines.length, function(line) {
18.1957 - if (line.height == 0) return;
18.1958 - var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
18.1959 - if (guess != line.height) updateLineHeight(line, guess);
18.1960 - });
18.1961 - } else {
18.1962 - doc.iter(checkWidthStart, from.line + lines.length, function(line) {
18.1963 - var len = lineLength(doc, line);
18.1964 - if (len > view.maxLineLength) {
18.1965 - view.maxLine = line;
18.1966 - view.maxLineLength = len;
18.1967 - view.maxLineChanged = true;
18.1968 - recomputeMaxLength = false;
18.1969 - }
18.1970 - });
18.1971 - if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
18.1972 - }
18.1973 -
18.1974 - // Adjust frontier, schedule worker
18.1975 - view.frontier = Math.min(view.frontier, from.line);
18.1976 - startWorker(cm, 400);
18.1977 -
18.1978 - var lendiff = lines.length - nlines - 1;
18.1979 - // Remember that these lines changed, for updating the display
18.1980 - regChange(cm, from.line, to.line + 1, lendiff);
18.1981 - if (hasHandler(cm, "change")) {
18.1982 - // Normalize lines to contain only strings, since that's what
18.1983 - // the change event handler expects
18.1984 - for (var i = 0; i < lines.length; ++i)
18.1985 - if (typeof lines[i] != "string") lines[i] = lines[i].text;
18.1986 - var changeObj = {from: from, to: to, text: lines, origin: origin};
18.1987 - if (cm.curOp.textChanged) {
18.1988 - for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
18.1989 - cur.next = changeObj;
18.1990 - } else cm.curOp.textChanged = changeObj;
18.1991 - }
18.1992 -
18.1993 - // Update the selection
18.1994 - var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
18.1995 - ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
18.1996 - if (selUpdate && typeof selUpdate != "string") {
18.1997 - if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
18.1998 - else newSelFrom = newSelTo = selUpdate;
18.1999 - } else if (selUpdate == "end") {
18.2000 - newSelFrom = newSelTo = end;
18.2001 - } else if (selUpdate == "start") {
18.2002 - newSelFrom = newSelTo = from;
18.2003 - } else if (selUpdate == "around") {
18.2004 - newSelFrom = from; newSelTo = end;
18.2005 - } else {
18.2006 - var adjustPos = function(pos) {
18.2007 - if (posLess(pos, from)) return pos;
18.2008 - if (!posLess(to, pos)) return end;
18.2009 - var line = pos.line + lendiff;
18.2010 - var ch = pos.ch;
18.2011 - if (pos.line == to.line)
18.2012 - ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
18.2013 - return {line: line, ch: ch};
18.2014 - };
18.2015 - newSelFrom = adjustPos(view.sel.from);
18.2016 - newSelTo = adjustPos(view.sel.to);
18.2017 - }
18.2018 - setSelection(cm, newSelFrom, newSelTo, null, true);
18.2019 - return end;
18.2020 - }
18.2021 -
18.2022 - function replaceRange(cm, code, from, to, origin) {
18.2023 - if (!to) to = from;
18.2024 - if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
18.2025 - return updateDoc(cm, from, to, splitLines(code), null, origin);
18.2026 - }
18.2027 -
18.2028 - // SELECTION
18.2029 -
18.2030 - function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
18.2031 - function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
18.2032 - function copyPos(x) {return {line: x.line, ch: x.ch};}
18.2033 -
18.2034 - function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
18.2035 - function clipPos(doc, pos) {
18.2036 - if (pos.line < 0) return {line: 0, ch: 0};
18.2037 - if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
18.2038 - var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
18.2039 - if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
18.2040 - else if (ch < 0) return {line: pos.line, ch: 0};
18.2041 - else return pos;
18.2042 - }
18.2043 - function isLine(doc, l) {return l >= 0 && l < doc.size;}
18.2044 -
18.2045 - // If shift is held, this will move the selection anchor. Otherwise,
18.2046 - // it'll set the whole selection.
18.2047 - function extendSelection(cm, pos, other, bias) {
18.2048 - var sel = cm.view.sel;
18.2049 - if (sel.shift || sel.extend) {
18.2050 - var anchor = sel.anchor;
18.2051 - if (other) {
18.2052 - var posBefore = posLess(pos, anchor);
18.2053 - if (posBefore != posLess(other, anchor)) {
18.2054 - anchor = pos;
18.2055 - pos = other;
18.2056 - } else if (posBefore != posLess(pos, other)) {
18.2057 - pos = other;
18.2058 - }
18.2059 - }
18.2060 - setSelection(cm, anchor, pos, bias);
18.2061 - } else {
18.2062 - setSelection(cm, pos, other || pos, bias);
18.2063 - }
18.2064 - cm.curOp.userSelChange = true;
18.2065 - }
18.2066 -
18.2067 - // Update the selection. Last two args are only used by
18.2068 - // updateDoc, since they have to be expressed in the line
18.2069 - // numbers before the update.
18.2070 - function setSelection(cm, anchor, head, bias, checkAtomic) {
18.2071 - cm.view.goalColumn = null;
18.2072 - var sel = cm.view.sel;
18.2073 - // Skip over atomic spans.
18.2074 - if (checkAtomic || !posEq(anchor, sel.anchor))
18.2075 - anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
18.2076 - if (checkAtomic || !posEq(head, sel.head))
18.2077 - head = skipAtomic(cm, head, bias, checkAtomic != "push");
18.2078 -
18.2079 - if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
18.2080 -
18.2081 - sel.anchor = anchor; sel.head = head;
18.2082 - var inv = posLess(head, anchor);
18.2083 - sel.from = inv ? head : anchor;
18.2084 - sel.to = inv ? anchor : head;
18.2085 -
18.2086 - cm.curOp.updateInput = true;
18.2087 - cm.curOp.selectionChanged = true;
18.2088 - }
18.2089 -
18.2090 - function reCheckSelection(cm) {
18.2091 - setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
18.2092 - }
18.2093 -
18.2094 - function skipAtomic(cm, pos, bias, mayClear) {
18.2095 - var doc = cm.view.doc, flipped = false, curPos = pos;
18.2096 - var dir = bias || 1;
18.2097 - cm.view.cantEdit = false;
18.2098 - search: for (;;) {
18.2099 - var line = getLine(doc, curPos.line), toClear;
18.2100 - if (line.markedSpans) {
18.2101 - for (var i = 0; i < line.markedSpans.length; ++i) {
18.2102 - var sp = line.markedSpans[i], m = sp.marker;
18.2103 - if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
18.2104 - (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
18.2105 - if (mayClear && m.clearOnEnter) {
18.2106 - (toClear || (toClear = [])).push(m);
18.2107 - continue;
18.2108 - } else if (!m.atomic) continue;
18.2109 - var newPos = m.find()[dir < 0 ? "from" : "to"];
18.2110 - if (posEq(newPos, curPos)) {
18.2111 - newPos.ch += dir;
18.2112 - if (newPos.ch < 0) {
18.2113 - if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
18.2114 - else newPos = null;
18.2115 - } else if (newPos.ch > line.text.length) {
18.2116 - if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
18.2117 - else newPos = null;
18.2118 - }
18.2119 - if (!newPos) {
18.2120 - if (flipped) {
18.2121 - // Driven in a corner -- no valid cursor position found at all
18.2122 - // -- try again *with* clearing, if we didn't already
18.2123 - if (!mayClear) return skipAtomic(cm, pos, bias, true);
18.2124 - // Otherwise, turn off editing until further notice, and return the start of the doc
18.2125 - cm.view.cantEdit = true;
18.2126 - return {line: 0, ch: 0};
18.2127 - }
18.2128 - flipped = true; newPos = pos; dir = -dir;
18.2129 - }
18.2130 - }
18.2131 - curPos = newPos;
18.2132 - continue search;
18.2133 - }
18.2134 - }
18.2135 - if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
18.2136 - }
18.2137 - return curPos;
18.2138 - }
18.2139 - }
18.2140 -
18.2141 - // SCROLLING
18.2142 -
18.2143 - function scrollCursorIntoView(cm) {
18.2144 - var view = cm.view;
18.2145 - var coords = scrollPosIntoView(cm, view.sel.head);
18.2146 - if (!view.focused) return;
18.2147 - var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
18.2148 - if (coords.top + box.top < 0) doScroll = true;
18.2149 - else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
18.2150 - if (doScroll != null && !phantom) {
18.2151 - var hidden = display.cursor.style.display == "none";
18.2152 - if (hidden) {
18.2153 - display.cursor.style.display = "";
18.2154 - display.cursor.style.left = coords.left + "px";
18.2155 - display.cursor.style.top = (coords.top - display.viewOffset) + "px";
18.2156 - }
18.2157 - display.cursor.scrollIntoView(doScroll);
18.2158 - if (hidden) display.cursor.style.display = "none";
18.2159 - }
18.2160 - }
18.2161 -
18.2162 - function scrollPosIntoView(cm, pos) {
18.2163 - for (;;) {
18.2164 - var changed = false, coords = cursorCoords(cm, pos);
18.2165 - var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
18.2166 - var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
18.2167 - if (scrollPos.scrollTop != null) {
18.2168 - setScrollTop(cm, scrollPos.scrollTop);
18.2169 - if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
18.2170 - }
18.2171 - if (scrollPos.scrollLeft != null) {
18.2172 - setScrollLeft(cm, scrollPos.scrollLeft);
18.2173 - if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
18.2174 - }
18.2175 - if (!changed) return coords;
18.2176 - }
18.2177 - }
18.2178 -
18.2179 - function scrollIntoView(cm, x1, y1, x2, y2) {
18.2180 - var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
18.2181 - if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
18.2182 - if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
18.2183 - }
18.2184 -
18.2185 - function calculateScrollPos(cm, x1, y1, x2, y2) {
18.2186 - var display = cm.display, pt = paddingTop(display);
18.2187 - y1 += pt; y2 += pt;
18.2188 - var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
18.2189 - var docBottom = cm.view.doc.height + 2 * pt;
18.2190 - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
18.2191 - if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
18.2192 - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
18.2193 -
18.2194 - var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
18.2195 - x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
18.2196 - var gutterw = display.gutters.offsetWidth;
18.2197 - var atLeft = x1 < gutterw + 10;
18.2198 - if (x1 < screenleft + gutterw || atLeft) {
18.2199 - if (atLeft) x1 = 0;
18.2200 - result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
18.2201 - } else if (x2 > screenw + screenleft - 3) {
18.2202 - result.scrollLeft = x2 + 10 - screenw;
18.2203 - }
18.2204 - return result;
18.2205 - }
18.2206 -
18.2207 - // API UTILITIES
18.2208 -
18.2209 - function indentLine(cm, n, how, aggressive) {
18.2210 - var doc = cm.view.doc;
18.2211 - if (!how) how = "add";
18.2212 - if (how == "smart") {
18.2213 - if (!cm.view.mode.indent) how = "prev";
18.2214 - else var state = getStateBefore(cm, n);
18.2215 - }
18.2216 -
18.2217 - var tabSize = cm.options.tabSize;
18.2218 - var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
18.2219 - var curSpaceString = line.text.match(/^\s*/)[0], indentation;
18.2220 - if (how == "smart") {
18.2221 - indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
18.2222 - if (indentation == Pass) {
18.2223 - if (!aggressive) return;
18.2224 - how = "prev";
18.2225 - }
18.2226 - }
18.2227 - if (how == "prev") {
18.2228 - if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
18.2229 - else indentation = 0;
18.2230 - }
18.2231 - else if (how == "add") indentation = curSpace + cm.options.indentUnit;
18.2232 - else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
18.2233 - indentation = Math.max(0, indentation);
18.2234 -
18.2235 - var indentString = "", pos = 0;
18.2236 - if (cm.options.indentWithTabs)
18.2237 - for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
18.2238 - if (pos < indentation) indentString += spaceStr(indentation - pos);
18.2239 -
18.2240 - if (indentString != curSpaceString)
18.2241 - replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
18.2242 - line.stateAfter = null;
18.2243 - }
18.2244 -
18.2245 - function changeLine(cm, handle, op) {
18.2246 - var no = handle, line = handle, doc = cm.view.doc;
18.2247 - if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
18.2248 - else no = lineNo(handle);
18.2249 - if (no == null) return null;
18.2250 - if (op(line, no)) regChange(cm, no, no + 1);
18.2251 - else return null;
18.2252 - return line;
18.2253 - }
18.2254 -
18.2255 - function findPosH(cm, dir, unit, visually) {
18.2256 - var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
18.2257 - var lineObj = getLine(doc, line);
18.2258 - function findNextLine() {
18.2259 - var l = line + dir;
18.2260 - if (l < 0 || l == doc.size) return false;
18.2261 - line = l;
18.2262 - return lineObj = getLine(doc, l);
18.2263 - }
18.2264 - function moveOnce(boundToLine) {
18.2265 - var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
18.2266 - if (next == null) {
18.2267 - if (!boundToLine && findNextLine()) {
18.2268 - if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
18.2269 - else ch = dir < 0 ? lineObj.text.length : 0;
18.2270 - } else return false;
18.2271 - } else ch = next;
18.2272 - return true;
18.2273 - }
18.2274 - if (unit == "char") moveOnce();
18.2275 - else if (unit == "column") moveOnce(true);
18.2276 - else if (unit == "word") {
18.2277 - var sawWord = false;
18.2278 - for (;;) {
18.2279 - if (dir < 0) if (!moveOnce()) break;
18.2280 - if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
18.2281 - else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
18.2282 - if (dir > 0) if (!moveOnce()) break;
18.2283 - }
18.2284 - }
18.2285 - return skipAtomic(cm, {line: line, ch: ch}, dir, true);
18.2286 - }
18.2287 -
18.2288 - function findWordAt(line, pos) {
18.2289 - var start = pos.ch, end = pos.ch;
18.2290 - if (line) {
18.2291 - if (pos.after === false || end == line.length) --start; else ++end;
18.2292 - var startChar = line.charAt(start);
18.2293 - var check = isWordChar(startChar) ? isWordChar :
18.2294 - /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
18.2295 - function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
18.2296 - while (start > 0 && check(line.charAt(start - 1))) --start;
18.2297 - while (end < line.length && check(line.charAt(end))) ++end;
18.2298 - }
18.2299 - return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
18.2300 - }
18.2301 -
18.2302 - function selectLine(cm, line) {
18.2303 - extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
18.2304 - }
18.2305 -
18.2306 - // PROTOTYPE
18.2307 -
18.2308 - // The publicly visible API. Note that operation(null, f) means
18.2309 - // 'wrap f in an operation, performed on its `this` parameter'
18.2310 -
18.2311 - CodeMirror.prototype = {
18.2312 - getValue: function(lineSep) {
18.2313 - var text = [], doc = this.view.doc;
18.2314 - doc.iter(0, doc.size, function(line) { text.push(line.text); });
18.2315 - return text.join(lineSep || "\n");
18.2316 - },
18.2317 -
18.2318 - setValue: operation(null, function(code) {
18.2319 - var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
18.2320 - updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
18.2321 - }),
18.2322 -
18.2323 - getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
18.2324 -
18.2325 - replaceSelection: operation(null, function(code, collapse, origin) {
18.2326 - var sel = this.view.sel;
18.2327 - updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
18.2328 - }),
18.2329 -
18.2330 - focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
18.2331 -
18.2332 - setOption: function(option, value) {
18.2333 - var options = this.options, old = options[option];
18.2334 - if (options[option] == value && option != "mode") return;
18.2335 - options[option] = value;
18.2336 - if (optionHandlers.hasOwnProperty(option))
18.2337 - operation(this, optionHandlers[option])(this, value, old);
18.2338 - },
18.2339 -
18.2340 - getOption: function(option) {return this.options[option];},
18.2341 -
18.2342 - getMode: function() {return this.view.mode;},
18.2343 -
18.2344 - addKeyMap: function(map) {
18.2345 - this.view.keyMaps.push(map);
18.2346 - },
18.2347 -
18.2348 - removeKeyMap: function(map) {
18.2349 - var maps = this.view.keyMaps;
18.2350 - for (var i = 0; i < maps.length; ++i)
18.2351 - if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
18.2352 - maps.splice(i, 1);
18.2353 - return true;
18.2354 - }
18.2355 - },
18.2356 -
18.2357 - undo: operation(null, function() {unredoHelper(this, "undo");}),
18.2358 - redo: operation(null, function() {unredoHelper(this, "redo");}),
18.2359 -
18.2360 - indentLine: operation(null, function(n, dir, aggressive) {
18.2361 - if (typeof dir != "string") {
18.2362 - if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
18.2363 - else dir = dir ? "add" : "subtract";
18.2364 - }
18.2365 - if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
18.2366 - }),
18.2367 -
18.2368 - indentSelection: operation(null, function(how) {
18.2369 - var sel = this.view.sel;
18.2370 - if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
18.2371 - var e = sel.to.line - (sel.to.ch ? 0 : 1);
18.2372 - for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
18.2373 - }),
18.2374 -
18.2375 - historySize: function() {
18.2376 - var hist = this.view.history;
18.2377 - return {undo: hist.done.length, redo: hist.undone.length};
18.2378 - },
18.2379 -
18.2380 - clearHistory: function() {this.view.history = makeHistory();},
18.2381 -
18.2382 - markClean: function() {
18.2383 - this.view.history.dirtyCounter = 0;
18.2384 - this.view.history.lastOp = this.view.history.lastOrigin = null;
18.2385 - },
18.2386 -
18.2387 - isClean: function () {return this.view.history.dirtyCounter == 0;},
18.2388 -
18.2389 - getHistory: function() {
18.2390 - var hist = this.view.history;
18.2391 - function cp(arr) {
18.2392 - for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
18.2393 - var set = arr[i];
18.2394 - nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
18.2395 - fromAfter: set.fromAfter, toAfter: set.toAfter});
18.2396 - for (var j = 0, elt = set.events; j < elt.length; ++j) {
18.2397 - var old = [], cur = elt[j];
18.2398 - nwelt.push({start: cur.start, added: cur.added, old: old});
18.2399 - for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
18.2400 - }
18.2401 - }
18.2402 - return nw;
18.2403 - }
18.2404 - return {done: cp(hist.done), undone: cp(hist.undone)};
18.2405 - },
18.2406 -
18.2407 - setHistory: function(histData) {
18.2408 - var hist = this.view.history = makeHistory();
18.2409 - hist.done = histData.done;
18.2410 - hist.undone = histData.undone;
18.2411 - },
18.2412 -
18.2413 - // Fetch the parser token for a given character. Useful for hacks
18.2414 - // that want to inspect the mode state (say, for completion).
18.2415 - getTokenAt: function(pos) {
18.2416 - var doc = this.view.doc;
18.2417 - pos = clipPos(doc, pos);
18.2418 - var state = getStateBefore(this, pos.line), mode = this.view.mode;
18.2419 - var line = getLine(doc, pos.line);
18.2420 - var stream = new StringStream(line.text, this.options.tabSize);
18.2421 - while (stream.pos < pos.ch && !stream.eol()) {
18.2422 - stream.start = stream.pos;
18.2423 - var style = mode.token(stream, state);
18.2424 - }
18.2425 - return {start: stream.start,
18.2426 - end: stream.pos,
18.2427 - string: stream.current(),
18.2428 - className: style || null, // Deprecated, use 'type' instead
18.2429 - type: style || null,
18.2430 - state: state};
18.2431 - },
18.2432 -
18.2433 - getStateAfter: function(line) {
18.2434 - var doc = this.view.doc;
18.2435 - line = clipLine(doc, line == null ? doc.size - 1: line);
18.2436 - return getStateBefore(this, line + 1);
18.2437 - },
18.2438 -
18.2439 - cursorCoords: function(start, mode) {
18.2440 - var pos, sel = this.view.sel;
18.2441 - if (start == null) pos = sel.head;
18.2442 - else if (typeof start == "object") pos = clipPos(this.view.doc, start);
18.2443 - else pos = start ? sel.from : sel.to;
18.2444 - return cursorCoords(this, pos, mode || "page");
18.2445 - },
18.2446 -
18.2447 - charCoords: function(pos, mode) {
18.2448 - return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
18.2449 - },
18.2450 -
18.2451 - coordsChar: function(coords) {
18.2452 - var off = this.display.lineSpace.getBoundingClientRect();
18.2453 - return coordsChar(this, coords.left - off.left, coords.top - off.top);
18.2454 - },
18.2455 -
18.2456 - defaultTextHeight: function() { return textHeight(this.display); },
18.2457 -
18.2458 - markText: operation(null, function(from, to, options) {
18.2459 - return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
18.2460 - options, "range");
18.2461 - }),
18.2462 -
18.2463 - setBookmark: operation(null, function(pos, widget) {
18.2464 - pos = clipPos(this.view.doc, pos);
18.2465 - return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
18.2466 - }),
18.2467 -
18.2468 - findMarksAt: function(pos) {
18.2469 - var doc = this.view.doc;
18.2470 - pos = clipPos(doc, pos);
18.2471 - var markers = [], spans = getLine(doc, pos.line).markedSpans;
18.2472 - if (spans) for (var i = 0; i < spans.length; ++i) {
18.2473 - var span = spans[i];
18.2474 - if ((span.from == null || span.from <= pos.ch) &&
18.2475 - (span.to == null || span.to >= pos.ch))
18.2476 - markers.push(span.marker);
18.2477 - }
18.2478 - return markers;
18.2479 - },
18.2480 -
18.2481 - setGutterMarker: operation(null, function(line, gutterID, value) {
18.2482 - return changeLine(this, line, function(line) {
18.2483 - var markers = line.gutterMarkers || (line.gutterMarkers = {});
18.2484 - markers[gutterID] = value;
18.2485 - if (!value && isEmpty(markers)) line.gutterMarkers = null;
18.2486 - return true;
18.2487 - });
18.2488 - }),
18.2489 -
18.2490 - clearGutter: operation(null, function(gutterID) {
18.2491 - var i = 0, cm = this, doc = cm.view.doc;
18.2492 - doc.iter(0, doc.size, function(line) {
18.2493 - if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
18.2494 - line.gutterMarkers[gutterID] = null;
18.2495 - regChange(cm, i, i + 1);
18.2496 - if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
18.2497 - }
18.2498 - ++i;
18.2499 - });
18.2500 - }),
18.2501 -
18.2502 - addLineClass: operation(null, function(handle, where, cls) {
18.2503 - return changeLine(this, handle, function(line) {
18.2504 - var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
18.2505 - if (!line[prop]) line[prop] = cls;
18.2506 - else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
18.2507 - else line[prop] += " " + cls;
18.2508 - return true;
18.2509 - });
18.2510 - }),
18.2511 -
18.2512 - removeLineClass: operation(null, function(handle, where, cls) {
18.2513 - return changeLine(this, handle, function(line) {
18.2514 - var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
18.2515 - var cur = line[prop];
18.2516 - if (!cur) return false;
18.2517 - else if (cls == null) line[prop] = null;
18.2518 - else {
18.2519 - var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
18.2520 - if (upd == cur) return false;
18.2521 - line[prop] = upd || null;
18.2522 - }
18.2523 - return true;
18.2524 - });
18.2525 - }),
18.2526 -
18.2527 - addLineWidget: operation(null, function(handle, node, options) {
18.2528 - var widget = options || {};
18.2529 - widget.node = node;
18.2530 - if (widget.noHScroll) this.display.alignWidgets = true;
18.2531 - changeLine(this, handle, function(line) {
18.2532 - (line.widgets || (line.widgets = [])).push(widget);
18.2533 - widget.line = line;
18.2534 - return true;
18.2535 - });
18.2536 - return widget;
18.2537 - }),
18.2538 -
18.2539 - removeLineWidget: operation(null, function(widget) {
18.2540 - var ws = widget.line.widgets, no = lineNo(widget.line);
18.2541 - if (no == null) return;
18.2542 - for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
18.2543 - regChange(this, no, no + 1);
18.2544 - }),
18.2545 -
18.2546 - lineInfo: function(line) {
18.2547 - if (typeof line == "number") {
18.2548 - if (!isLine(this.view.doc, line)) return null;
18.2549 - var n = line;
18.2550 - line = getLine(this.view.doc, line);
18.2551 - if (!line) return null;
18.2552 - } else {
18.2553 - var n = lineNo(line);
18.2554 - if (n == null) return null;
18.2555 - }
18.2556 - return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
18.2557 - textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
18.2558 - widgets: line.widgets};
18.2559 - },
18.2560 -
18.2561 - getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
18.2562 -
18.2563 - addWidget: function(pos, node, scroll, vert, horiz) {
18.2564 - var display = this.display;
18.2565 - pos = cursorCoords(this, clipPos(this.view.doc, pos));
18.2566 - var top = pos.top, left = pos.left;
18.2567 - node.style.position = "absolute";
18.2568 - display.sizer.appendChild(node);
18.2569 - if (vert == "over") top = pos.top;
18.2570 - else if (vert == "near") {
18.2571 - var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
18.2572 - hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
18.2573 - if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
18.2574 - top = pos.top - node.offsetHeight;
18.2575 - if (left + node.offsetWidth > hspace)
18.2576 - left = hspace - node.offsetWidth;
18.2577 - }
18.2578 - node.style.top = (top + paddingTop(display)) + "px";
18.2579 - node.style.left = node.style.right = "";
18.2580 - if (horiz == "right") {
18.2581 - left = display.sizer.clientWidth - node.offsetWidth;
18.2582 - node.style.right = "0px";
18.2583 - } else {
18.2584 - if (horiz == "left") left = 0;
18.2585 - else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
18.2586 - node.style.left = left + "px";
18.2587 - }
18.2588 - if (scroll)
18.2589 - scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
18.2590 - },
18.2591 -
18.2592 - lineCount: function() {return this.view.doc.size;},
18.2593 -
18.2594 - clipPos: function(pos) {return clipPos(this.view.doc, pos);},
18.2595 -
18.2596 - getCursor: function(start) {
18.2597 - var sel = this.view.sel, pos;
18.2598 - if (start == null || start == "head") pos = sel.head;
18.2599 - else if (start == "anchor") pos = sel.anchor;
18.2600 - else if (start == "end" || start === false) pos = sel.to;
18.2601 - else pos = sel.from;
18.2602 - return copyPos(pos);
18.2603 - },
18.2604 -
18.2605 - somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
18.2606 -
18.2607 - setCursor: operation(null, function(line, ch, extend) {
18.2608 - var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
18.2609 - if (extend) extendSelection(this, pos);
18.2610 - else setSelection(this, pos, pos);
18.2611 - }),
18.2612 -
18.2613 - setSelection: operation(null, function(anchor, head) {
18.2614 - var doc = this.view.doc;
18.2615 - setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
18.2616 - }),
18.2617 -
18.2618 - extendSelection: operation(null, function(from, to) {
18.2619 - var doc = this.view.doc;
18.2620 - extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
18.2621 - }),
18.2622 -
18.2623 - setExtending: function(val) {this.view.sel.extend = val;},
18.2624 -
18.2625 - getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
18.2626 -
18.2627 - getLineHandle: function(line) {
18.2628 - var doc = this.view.doc;
18.2629 - if (isLine(doc, line)) return getLine(doc, line);
18.2630 - },
18.2631 -
18.2632 - getLineNumber: function(line) {return lineNo(line);},
18.2633 -
18.2634 - setLine: operation(null, function(line, text) {
18.2635 - if (isLine(this.view.doc, line))
18.2636 - replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
18.2637 - }),
18.2638 -
18.2639 - removeLine: operation(null, function(line) {
18.2640 - if (isLine(this.view.doc, line))
18.2641 - replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
18.2642 - }),
18.2643 -
18.2644 - replaceRange: operation(null, function(code, from, to) {
18.2645 - var doc = this.view.doc;
18.2646 - from = clipPos(doc, from);
18.2647 - to = to ? clipPos(doc, to) : from;
18.2648 - return replaceRange(this, code, from, to);
18.2649 - }),
18.2650 -
18.2651 - getRange: function(from, to, lineSep) {
18.2652 - var doc = this.view.doc;
18.2653 - from = clipPos(doc, from); to = clipPos(doc, to);
18.2654 - var l1 = from.line, l2 = to.line;
18.2655 - if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
18.2656 - var code = [getLine(doc, l1).text.slice(from.ch)];
18.2657 - doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
18.2658 - code.push(getLine(doc, l2).text.slice(0, to.ch));
18.2659 - return code.join(lineSep || "\n");
18.2660 - },
18.2661 -
18.2662 - triggerOnKeyDown: operation(null, onKeyDown),
18.2663 -
18.2664 - execCommand: function(cmd) {return commands[cmd](this);},
18.2665 -
18.2666 - // Stuff used by commands, probably not much use to outside code.
18.2667 - moveH: operation(null, function(dir, unit) {
18.2668 - var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
18.2669 - if (sel.shift || sel.extend || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true);
18.2670 - extendSelection(this, pos, pos, dir);
18.2671 - }),
18.2672 -
18.2673 - deleteH: operation(null, function(dir, unit) {
18.2674 - var sel = this.view.sel;
18.2675 - if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
18.2676 - else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
18.2677 - this.curOp.userSelChange = true;
18.2678 - }),
18.2679 -
18.2680 - moveV: operation(null, function(dir, unit) {
18.2681 - var view = this.view, doc = view.doc, display = this.display;
18.2682 - var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
18.2683 - var x = pos.left, y;
18.2684 - if (view.goalColumn != null) x = view.goalColumn;
18.2685 - if (unit == "page") {
18.2686 - var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
18.2687 - y = pos.top + dir * pageSize;
18.2688 - } else if (unit == "line") {
18.2689 - y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
18.2690 - }
18.2691 - do {
18.2692 - var target = coordsChar(this, x, y);
18.2693 - y += dir * 5;
18.2694 - } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
18.2695 -
18.2696 - if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
18.2697 - extendSelection(this, target, target, dir);
18.2698 - view.goalColumn = x;
18.2699 - }),
18.2700 -
18.2701 - toggleOverwrite: function() {
18.2702 - if (this.view.overwrite = !this.view.overwrite)
18.2703 - this.display.cursor.className += " CodeMirror-overwrite";
18.2704 - else
18.2705 - this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
18.2706 - },
18.2707 -
18.2708 - posFromIndex: function(off) {
18.2709 - var lineNo = 0, ch, doc = this.view.doc;
18.2710 - doc.iter(0, doc.size, function(line) {
18.2711 - var sz = line.text.length + 1;
18.2712 - if (sz > off) { ch = off; return true; }
18.2713 - off -= sz;
18.2714 - ++lineNo;
18.2715 - });
18.2716 - return clipPos(doc, {line: lineNo, ch: ch});
18.2717 - },
18.2718 - indexFromPos: function (coords) {
18.2719 - if (coords.line < 0 || coords.ch < 0) return 0;
18.2720 - var index = coords.ch;
18.2721 - this.view.doc.iter(0, coords.line, function (line) {
18.2722 - index += line.text.length + 1;
18.2723 - });
18.2724 - return index;
18.2725 - },
18.2726 -
18.2727 - scrollTo: function(x, y) {
18.2728 - if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
18.2729 - if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
18.2730 - updateDisplay(this, []);
18.2731 - },
18.2732 - getScrollInfo: function() {
18.2733 - var scroller = this.display.scroller, co = scrollerCutOff;
18.2734 - return {left: scroller.scrollLeft, top: scroller.scrollTop,
18.2735 - height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
18.2736 - clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
18.2737 - },
18.2738 -
18.2739 - scrollIntoView: function(pos) {
18.2740 - if (typeof pos == "number") pos = {line: pos, ch: 0};
18.2741 - pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
18.2742 - scrollPosIntoView(this, pos);
18.2743 - },
18.2744 -
18.2745 - setSize: function(width, height) {
18.2746 - function interpret(val) {
18.2747 - return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
18.2748 - }
18.2749 - if (width != null) this.display.wrapper.style.width = interpret(width);
18.2750 - if (height != null) this.display.wrapper.style.height = interpret(height);
18.2751 - this.refresh();
18.2752 - },
18.2753 -
18.2754 - on: function(type, f) {on(this, type, f);},
18.2755 - off: function(type, f) {off(this, type, f);},
18.2756 -
18.2757 - operation: function(f){return operation(this, f)();},
18.2758 -
18.2759 - refresh: function() {
18.2760 - clearCaches(this);
18.2761 - if (this.display.scroller.scrollHeight > this.view.scrollTop)
18.2762 - this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop;
18.2763 - updateDisplay(this, true);
18.2764 - },
18.2765 -
18.2766 - getInputField: function(){return this.display.input;},
18.2767 - getWrapperElement: function(){return this.display.wrapper;},
18.2768 - getScrollerElement: function(){return this.display.scroller;},
18.2769 - getGutterElement: function(){return this.display.gutters;}
18.2770 - };
18.2771 -
18.2772 - // OPTION DEFAULTS
18.2773 -
18.2774 - var optionHandlers = CodeMirror.optionHandlers = {};
18.2775 -
18.2776 - // The default configuration options.
18.2777 - var defaults = CodeMirror.defaults = {};
18.2778 -
18.2779 - function option(name, deflt, handle, notOnInit) {
18.2780 - CodeMirror.defaults[name] = deflt;
18.2781 - if (handle) optionHandlers[name] =
18.2782 - notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
18.2783 - }
18.2784 -
18.2785 - var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
18.2786 -
18.2787 - // These two are, on init, called from the constructor because they
18.2788 - // have to be initialized before the editor can start at all.
18.2789 - option("value", "", function(cm, val) {cm.setValue(val);}, true);
18.2790 - option("mode", null, loadMode, true);
18.2791 -
18.2792 - option("indentUnit", 2, loadMode, true);
18.2793 - option("indentWithTabs", false);
18.2794 - option("smartIndent", true);
18.2795 - option("tabSize", 4, function(cm) {
18.2796 - loadMode(cm);
18.2797 - clearCaches(cm);
18.2798 - updateDisplay(cm, true);
18.2799 - }, true);
18.2800 - option("electricChars", true);
18.2801 -
18.2802 - option("theme", "default", function(cm) {
18.2803 - themeChanged(cm);
18.2804 - guttersChanged(cm);
18.2805 - }, true);
18.2806 - option("keyMap", "default", keyMapChanged);
18.2807 - option("extraKeys", null);
18.2808 -
18.2809 - option("onKeyEvent", null);
18.2810 - option("onDragEvent", null);
18.2811 -
18.2812 - option("lineWrapping", false, wrappingChanged, true);
18.2813 - option("gutters", [], function(cm) {
18.2814 - setGuttersForLineNumbers(cm.options);
18.2815 - guttersChanged(cm);
18.2816 - }, true);
18.2817 - option("lineNumbers", false, function(cm) {
18.2818 - setGuttersForLineNumbers(cm.options);
18.2819 - guttersChanged(cm);
18.2820 - }, true);
18.2821 - option("firstLineNumber", 1, guttersChanged, true);
18.2822 - option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
18.2823 - option("showCursorWhenSelecting", false, updateSelection, true);
18.2824 -
18.2825 - option("readOnly", false, function(cm, val) {
18.2826 - if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
18.2827 - else if (!val) resetInput(cm, true);
18.2828 - });
18.2829 - option("dragDrop", true);
18.2830 -
18.2831 - option("cursorBlinkRate", 530);
18.2832 - option("cursorHeight", 1);
18.2833 - option("workTime", 100);
18.2834 - option("workDelay", 100);
18.2835 - option("flattenSpans", true);
18.2836 - option("pollInterval", 100);
18.2837 - option("undoDepth", 40);
18.2838 - option("viewportMargin", 10, function(cm){cm.refresh();}, true);
18.2839 -
18.2840 - option("tabindex", null, function(cm, val) {
18.2841 - cm.display.input.tabIndex = val || "";
18.2842 - });
18.2843 - option("autofocus", null);
18.2844 -
18.2845 - // MODE DEFINITION AND QUERYING
18.2846 -
18.2847 - // Known modes, by name and by MIME
18.2848 - var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
18.2849 -
18.2850 - CodeMirror.defineMode = function(name, mode) {
18.2851 - if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
18.2852 - if (arguments.length > 2) {
18.2853 - mode.dependencies = [];
18.2854 - for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
18.2855 - }
18.2856 - modes[name] = mode;
18.2857 - };
18.2858 -
18.2859 - CodeMirror.defineMIME = function(mime, spec) {
18.2860 - mimeModes[mime] = spec;
18.2861 - };
18.2862 -
18.2863 - CodeMirror.resolveMode = function(spec) {
18.2864 - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
18.2865 - spec = mimeModes[spec];
18.2866 - else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
18.2867 - return CodeMirror.resolveMode("application/xml");
18.2868 - if (typeof spec == "string") return {name: spec};
18.2869 - else return spec || {name: "null"};
18.2870 - };
18.2871 -
18.2872 - CodeMirror.getMode = function(options, spec) {
18.2873 - var spec = CodeMirror.resolveMode(spec);
18.2874 - var mfactory = modes[spec.name];
18.2875 - if (!mfactory) return CodeMirror.getMode(options, "text/plain");
18.2876 - var modeObj = mfactory(options, spec);
18.2877 - if (modeExtensions.hasOwnProperty(spec.name)) {
18.2878 - var exts = modeExtensions[spec.name];
18.2879 - for (var prop in exts) {
18.2880 - if (!exts.hasOwnProperty(prop)) continue;
18.2881 - if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
18.2882 - modeObj[prop] = exts[prop];
18.2883 - }
18.2884 - }
18.2885 - modeObj.name = spec.name;
18.2886 - return modeObj;
18.2887 - };
18.2888 -
18.2889 - CodeMirror.defineMode("null", function() {
18.2890 - return {token: function(stream) {stream.skipToEnd();}};
18.2891 - });
18.2892 - CodeMirror.defineMIME("text/plain", "null");
18.2893 -
18.2894 - var modeExtensions = CodeMirror.modeExtensions = {};
18.2895 - CodeMirror.extendMode = function(mode, properties) {
18.2896 - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
18.2897 - for (var prop in properties) if (properties.hasOwnProperty(prop))
18.2898 - exts[prop] = properties[prop];
18.2899 - };
18.2900 -
18.2901 - // EXTENSIONS
18.2902 -
18.2903 - CodeMirror.defineExtension = function(name, func) {
18.2904 - CodeMirror.prototype[name] = func;
18.2905 - };
18.2906 -
18.2907 - CodeMirror.defineOption = option;
18.2908 -
18.2909 - var initHooks = [];
18.2910 - CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
18.2911 -
18.2912 - // MODE STATE HANDLING
18.2913 -
18.2914 - // Utility functions for working with state. Exported because modes
18.2915 - // sometimes need to do this.
18.2916 - function copyState(mode, state) {
18.2917 - if (state === true) return state;
18.2918 - if (mode.copyState) return mode.copyState(state);
18.2919 - var nstate = {};
18.2920 - for (var n in state) {
18.2921 - var val = state[n];
18.2922 - if (val instanceof Array) val = val.concat([]);
18.2923 - nstate[n] = val;
18.2924 - }
18.2925 - return nstate;
18.2926 - }
18.2927 - CodeMirror.copyState = copyState;
18.2928 -
18.2929 - function startState(mode, a1, a2) {
18.2930 - return mode.startState ? mode.startState(a1, a2) : true;
18.2931 - }
18.2932 - CodeMirror.startState = startState;
18.2933 -
18.2934 - CodeMirror.innerMode = function(mode, state) {
18.2935 - while (mode.innerMode) {
18.2936 - var info = mode.innerMode(state);
18.2937 - state = info.state;
18.2938 - mode = info.mode;
18.2939 - }
18.2940 - return info || {mode: mode, state: state};
18.2941 - };
18.2942 -
18.2943 - // STANDARD COMMANDS
18.2944 -
18.2945 - var commands = CodeMirror.commands = {
18.2946 - selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
18.2947 - killLine: function(cm) {
18.2948 - var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
18.2949 - if (!sel && cm.getLine(from.line).length == from.ch)
18.2950 - cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
18.2951 - else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
18.2952 - },
18.2953 - deleteLine: function(cm) {
18.2954 - var l = cm.getCursor().line;
18.2955 - cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
18.2956 - },
18.2957 - undo: function(cm) {cm.undo();},
18.2958 - redo: function(cm) {cm.redo();},
18.2959 - goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
18.2960 - goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
18.2961 - goLineStart: function(cm) {
18.2962 - cm.extendSelection(lineStart(cm, cm.getCursor().line));
18.2963 - },
18.2964 - goLineStartSmart: function(cm) {
18.2965 - var cur = cm.getCursor(), start = lineStart(cm, cur.line);
18.2966 - var line = cm.getLineHandle(start.line);
18.2967 - var order = getOrder(line);
18.2968 - if (!order || order[0].level == 0) {
18.2969 - var firstNonWS = Math.max(0, line.text.search(/\S/));
18.2970 - var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
18.2971 - cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
18.2972 - } else cm.extendSelection(start);
18.2973 - },
18.2974 - goLineEnd: function(cm) {
18.2975 - cm.extendSelection(lineEnd(cm, cm.getCursor().line));
18.2976 - },
18.2977 - goLineUp: function(cm) {cm.moveV(-1, "line");},
18.2978 - goLineDown: function(cm) {cm.moveV(1, "line");},
18.2979 - goPageUp: function(cm) {cm.moveV(-1, "page");},
18.2980 - goPageDown: function(cm) {cm.moveV(1, "page");},
18.2981 - goCharLeft: function(cm) {cm.moveH(-1, "char");},
18.2982 - goCharRight: function(cm) {cm.moveH(1, "char");},
18.2983 - goColumnLeft: function(cm) {cm.moveH(-1, "column");},
18.2984 - goColumnRight: function(cm) {cm.moveH(1, "column");},
18.2985 - goWordLeft: function(cm) {cm.moveH(-1, "word");},
18.2986 - goWordRight: function(cm) {cm.moveH(1, "word");},
18.2987 - delCharBefore: function(cm) {cm.deleteH(-1, "char");},
18.2988 - delCharAfter: function(cm) {cm.deleteH(1, "char");},
18.2989 - delWordBefore: function(cm) {cm.deleteH(-1, "word");},
18.2990 - delWordAfter: function(cm) {cm.deleteH(1, "word");},
18.2991 - indentAuto: function(cm) {cm.indentSelection("smart");},
18.2992 - indentMore: function(cm) {cm.indentSelection("add");},
18.2993 - indentLess: function(cm) {cm.indentSelection("subtract");},
18.2994 - insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
18.2995 - defaultTab: function(cm) {
18.2996 - if (cm.somethingSelected()) cm.indentSelection("add");
18.2997 - else cm.replaceSelection("\t", "end", "input");
18.2998 - },
18.2999 - transposeChars: function(cm) {
18.3000 - var cur = cm.getCursor(), line = cm.getLine(cur.line);
18.3001 - if (cur.ch > 0 && cur.ch < line.length - 1)
18.3002 - cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
18.3003 - {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
18.3004 - },
18.3005 - newlineAndIndent: function(cm) {
18.3006 - operation(cm, function() {
18.3007 - cm.replaceSelection("\n", "end", "input");
18.3008 - cm.indentLine(cm.getCursor().line, null, true);
18.3009 - })();
18.3010 - },
18.3011 - toggleOverwrite: function(cm) {cm.toggleOverwrite();}
18.3012 - };
18.3013 -
18.3014 - // STANDARD KEYMAPS
18.3015 -
18.3016 - var keyMap = CodeMirror.keyMap = {};
18.3017 - keyMap.basic = {
18.3018 - "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
18.3019 - "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
18.3020 - "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
18.3021 - "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
18.3022 - };
18.3023 - // Note that the save and find-related commands aren't defined by
18.3024 - // default. Unknown commands are simply ignored.
18.3025 - keyMap.pcDefault = {
18.3026 - "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
18.3027 - "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
18.3028 - "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
18.3029 - "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
18.3030 - "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
18.3031 - "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
18.3032 - fallthrough: "basic"
18.3033 - };
18.3034 - keyMap.macDefault = {
18.3035 - "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
18.3036 - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
18.3037 - "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
18.3038 - "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
18.3039 - "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
18.3040 - "Cmd-[": "indentLess", "Cmd-]": "indentMore",
18.3041 - fallthrough: ["basic", "emacsy"]
18.3042 - };
18.3043 - keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
18.3044 - keyMap.emacsy = {
18.3045 - "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
18.3046 - "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
18.3047 - "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
18.3048 - "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
18.3049 - };
18.3050 -
18.3051 - // KEYMAP DISPATCH
18.3052 -
18.3053 - function getKeyMap(val) {
18.3054 - if (typeof val == "string") return keyMap[val];
18.3055 - else return val;
18.3056 - }
18.3057 -
18.3058 - function lookupKey(name, maps, handle, stop) {
18.3059 - function lookup(map) {
18.3060 - map = getKeyMap(map);
18.3061 - var found = map[name];
18.3062 - if (found === false) {
18.3063 - if (stop) stop();
18.3064 - return true;
18.3065 - }
18.3066 - if (found != null && handle(found)) return true;
18.3067 - if (map.nofallthrough) {
18.3068 - if (stop) stop();
18.3069 - return true;
18.3070 - }
18.3071 - var fallthrough = map.fallthrough;
18.3072 - if (fallthrough == null) return false;
18.3073 - if (Object.prototype.toString.call(fallthrough) != "[object Array]")
18.3074 - return lookup(fallthrough);
18.3075 - for (var i = 0, e = fallthrough.length; i < e; ++i) {
18.3076 - if (lookup(fallthrough[i])) return true;
18.3077 - }
18.3078 - return false;
18.3079 - }
18.3080 -
18.3081 - for (var i = 0; i < maps.length; ++i)
18.3082 - if (lookup(maps[i])) return true;
18.3083 - }
18.3084 - function isModifierKey(event) {
18.3085 - var name = keyNames[e_prop(event, "keyCode")];
18.3086 - return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
18.3087 - }
18.3088 - CodeMirror.isModifierKey = isModifierKey;
18.3089 -
18.3090 - // FROMTEXTAREA
18.3091 -
18.3092 - CodeMirror.fromTextArea = function(textarea, options) {
18.3093 - if (!options) options = {};
18.3094 - options.value = textarea.value;
18.3095 - if (!options.tabindex && textarea.tabindex)
18.3096 - options.tabindex = textarea.tabindex;
18.3097 - // Set autofocus to true if this textarea is focused, or if it has
18.3098 - // autofocus and no other element is focused.
18.3099 - if (options.autofocus == null) {
18.3100 - var hasFocus = document.body;
18.3101 - // doc.activeElement occasionally throws on IE
18.3102 - try { hasFocus = document.activeElement; } catch(e) {}
18.3103 - options.autofocus = hasFocus == textarea ||
18.3104 - textarea.getAttribute("autofocus") != null && hasFocus == document.body;
18.3105 - }
18.3106 -
18.3107 - function save() {textarea.value = cm.getValue();}
18.3108 - if (textarea.form) {
18.3109 - // Deplorable hack to make the submit method do the right thing.
18.3110 - on(textarea.form, "submit", save);
18.3111 - var form = textarea.form, realSubmit = form.submit;
18.3112 - try {
18.3113 - form.submit = function wrappedSubmit() {
18.3114 - save();
18.3115 - form.submit = realSubmit;
18.3116 - form.submit();
18.3117 - form.submit = wrappedSubmit;
18.3118 - };
18.3119 - } catch(e) {}
18.3120 - }
18.3121 -
18.3122 - textarea.style.display = "none";
18.3123 - var cm = CodeMirror(function(node) {
18.3124 - textarea.parentNode.insertBefore(node, textarea.nextSibling);
18.3125 - }, options);
18.3126 - cm.save = save;
18.3127 - cm.getTextArea = function() { return textarea; };
18.3128 - cm.toTextArea = function() {
18.3129 - save();
18.3130 - textarea.parentNode.removeChild(cm.getWrapperElement());
18.3131 - textarea.style.display = "";
18.3132 - if (textarea.form) {
18.3133 - off(textarea.form, "submit", save);
18.3134 - if (typeof textarea.form.submit == "function")
18.3135 - textarea.form.submit = realSubmit;
18.3136 - }
18.3137 - };
18.3138 - return cm;
18.3139 - };
18.3140 -
18.3141 - // STRING STREAM
18.3142 -
18.3143 - // Fed to the mode parsers, provides helper functions to make
18.3144 - // parsers more succinct.
18.3145 -
18.3146 - // The character stream used by a mode's parser.
18.3147 - function StringStream(string, tabSize) {
18.3148 - this.pos = this.start = 0;
18.3149 - this.string = string;
18.3150 - this.tabSize = tabSize || 8;
18.3151 - }
18.3152 -
18.3153 - StringStream.prototype = {
18.3154 - eol: function() {return this.pos >= this.string.length;},
18.3155 - sol: function() {return this.pos == 0;},
18.3156 - peek: function() {return this.string.charAt(this.pos) || undefined;},
18.3157 - next: function() {
18.3158 - if (this.pos < this.string.length)
18.3159 - return this.string.charAt(this.pos++);
18.3160 - },
18.3161 - eat: function(match) {
18.3162 - var ch = this.string.charAt(this.pos);
18.3163 - if (typeof match == "string") var ok = ch == match;
18.3164 - else var ok = ch && (match.test ? match.test(ch) : match(ch));
18.3165 - if (ok) {++this.pos; return ch;}
18.3166 - },
18.3167 - eatWhile: function(match) {
18.3168 - var start = this.pos;
18.3169 - while (this.eat(match)){}
18.3170 - return this.pos > start;
18.3171 - },
18.3172 - eatSpace: function() {
18.3173 - var start = this.pos;
18.3174 - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
18.3175 - return this.pos > start;
18.3176 - },
18.3177 - skipToEnd: function() {this.pos = this.string.length;},
18.3178 - skipTo: function(ch) {
18.3179 - var found = this.string.indexOf(ch, this.pos);
18.3180 - if (found > -1) {this.pos = found; return true;}
18.3181 - },
18.3182 - backUp: function(n) {this.pos -= n;},
18.3183 - column: function() {return countColumn(this.string, this.start, this.tabSize);},
18.3184 - indentation: function() {return countColumn(this.string, null, this.tabSize);},
18.3185 - match: function(pattern, consume, caseInsensitive) {
18.3186 - if (typeof pattern == "string") {
18.3187 - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
18.3188 - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
18.3189 - if (consume !== false) this.pos += pattern.length;
18.3190 - return true;
18.3191 - }
18.3192 - } else {
18.3193 - var match = this.string.slice(this.pos).match(pattern);
18.3194 - if (match && match.index > 0) return null;
18.3195 - if (match && consume !== false) this.pos += match[0].length;
18.3196 - return match;
18.3197 - }
18.3198 - },
18.3199 - current: function(){return this.string.slice(this.start, this.pos);}
18.3200 - };
18.3201 - CodeMirror.StringStream = StringStream;
18.3202 -
18.3203 - // TEXTMARKERS
18.3204 -
18.3205 - function TextMarker(cm, type) {
18.3206 - this.lines = [];
18.3207 - this.type = type;
18.3208 - this.cm = cm;
18.3209 - }
18.3210 -
18.3211 - TextMarker.prototype.clear = function() {
18.3212 - if (this.explicitlyCleared) return;
18.3213 - startOperation(this.cm);
18.3214 - var min = null, max = null;
18.3215 - for (var i = 0; i < this.lines.length; ++i) {
18.3216 - var line = this.lines[i];
18.3217 - var span = getMarkedSpanFor(line.markedSpans, this);
18.3218 - if (span.to != null) max = lineNo(line);
18.3219 - line.markedSpans = removeMarkedSpan(line.markedSpans, span);
18.3220 - if (span.from != null)
18.3221 - min = lineNo(line);
18.3222 - else if (this.collapsed && !lineIsHidden(line))
18.3223 - updateLineHeight(line, textHeight(this.cm.display));
18.3224 - }
18.3225 - if (min != null) regChange(this.cm, min, max + 1);
18.3226 - this.lines.length = 0;
18.3227 - this.explicitlyCleared = true;
18.3228 - if (this.collapsed && this.cm.view.cantEdit) {
18.3229 - this.cm.view.cantEdit = false;
18.3230 - reCheckSelection(this.cm);
18.3231 - }
18.3232 - endOperation(this.cm);
18.3233 - signalLater(this.cm, this, "clear");
18.3234 - };
18.3235 -
18.3236 - TextMarker.prototype.find = function() {
18.3237 - var from, to;
18.3238 - for (var i = 0; i < this.lines.length; ++i) {
18.3239 - var line = this.lines[i];
18.3240 - var span = getMarkedSpanFor(line.markedSpans, this);
18.3241 - if (span.from != null || span.to != null) {
18.3242 - var found = lineNo(line);
18.3243 - if (span.from != null) from = {line: found, ch: span.from};
18.3244 - if (span.to != null) to = {line: found, ch: span.to};
18.3245 - }
18.3246 - }
18.3247 - if (this.type == "bookmark") return from;
18.3248 - return from && {from: from, to: to};
18.3249 - };
18.3250 -
18.3251 - function markText(cm, from, to, options, type) {
18.3252 - var doc = cm.view.doc;
18.3253 - var marker = new TextMarker(cm, type);
18.3254 - if (type == "range" && !posLess(from, to)) return marker;
18.3255 - if (options) for (var opt in options) if (options.hasOwnProperty(opt))
18.3256 - marker[opt] = options[opt];
18.3257 - if (marker.replacedWith) {
18.3258 - marker.collapsed = true;
18.3259 - marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
18.3260 - }
18.3261 - if (marker.collapsed) sawCollapsedSpans = true;
18.3262 -
18.3263 - var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
18.3264 - doc.iter(curLine, to.line + 1, function(line) {
18.3265 - var span = {from: null, to: null, marker: marker};
18.3266 - size += line.text.length;
18.3267 - if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
18.3268 - if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
18.3269 - if (marker.collapsed) {
18.3270 - if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
18.3271 - if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
18.3272 - else updateLineHeight(line, 0);
18.3273 - }
18.3274 - addMarkedSpan(line, span);
18.3275 - if (marker.collapsed && curLine == from.line && lineIsHidden(line))
18.3276 - updateLineHeight(line, 0);
18.3277 - ++curLine;
18.3278 - });
18.3279 -
18.3280 - if (marker.readOnly) {
18.3281 - sawReadOnlySpans = true;
18.3282 - if (cm.view.history.done.length || cm.view.history.undone.length)
18.3283 - cm.clearHistory();
18.3284 - }
18.3285 - if (marker.collapsed) {
18.3286 - if (collapsedAtStart != collapsedAtEnd)
18.3287 - throw new Error("Inserting collapsed marker overlapping an existing one");
18.3288 - marker.size = size;
18.3289 - marker.atomic = true;
18.3290 - }
18.3291 - if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
18.3292 - regChange(cm, from.line, to.line + 1);
18.3293 - if (marker.atomic) reCheckSelection(cm);
18.3294 - return marker;
18.3295 - }
18.3296 -
18.3297 - // TEXTMARKER SPANS
18.3298 -
18.3299 - function getMarkedSpanFor(spans, marker) {
18.3300 - if (spans) for (var i = 0; i < spans.length; ++i) {
18.3301 - var span = spans[i];
18.3302 - if (span.marker == marker) return span;
18.3303 - }
18.3304 - }
18.3305 - function removeMarkedSpan(spans, span) {
18.3306 - for (var r, i = 0; i < spans.length; ++i)
18.3307 - if (spans[i] != span) (r || (r = [])).push(spans[i]);
18.3308 - return r;
18.3309 - }
18.3310 - function addMarkedSpan(line, span) {
18.3311 - line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
18.3312 - span.marker.lines.push(line);
18.3313 - }
18.3314 -
18.3315 - function markedSpansBefore(old, startCh) {
18.3316 - if (old) for (var i = 0, nw; i < old.length; ++i) {
18.3317 - var span = old[i], marker = span.marker;
18.3318 - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
18.3319 - if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
18.3320 - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
18.3321 - (nw || (nw = [])).push({from: span.from,
18.3322 - to: endsAfter ? null : span.to,
18.3323 - marker: marker});
18.3324 - }
18.3325 - }
18.3326 - return nw;
18.3327 - }
18.3328 -
18.3329 - function markedSpansAfter(old, startCh, endCh) {
18.3330 - if (old) for (var i = 0, nw; i < old.length; ++i) {
18.3331 - var span = old[i], marker = span.marker;
18.3332 - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
18.3333 - if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
18.3334 - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
18.3335 - (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
18.3336 - to: span.to == null ? null : span.to - endCh,
18.3337 - marker: marker});
18.3338 - }
18.3339 - }
18.3340 - return nw;
18.3341 - }
18.3342 -
18.3343 - function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
18.3344 - if (!oldFirst && !oldLast) return newText;
18.3345 - // Get the spans that 'stick out' on both sides
18.3346 - var first = markedSpansBefore(oldFirst, startCh);
18.3347 - var last = markedSpansAfter(oldLast, startCh, endCh);
18.3348 -
18.3349 - // Next, merge those two ends
18.3350 - var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
18.3351 - if (first) {
18.3352 - // Fix up .to properties of first
18.3353 - for (var i = 0; i < first.length; ++i) {
18.3354 - var span = first[i];
18.3355 - if (span.to == null) {
18.3356 - var found = getMarkedSpanFor(last, span.marker);
18.3357 - if (!found) span.to = startCh;
18.3358 - else if (sameLine) span.to = found.to == null ? null : found.to + offset;
18.3359 - }
18.3360 - }
18.3361 - }
18.3362 - if (last) {
18.3363 - // Fix up .from in last (or move them into first in case of sameLine)
18.3364 - for (var i = 0; i < last.length; ++i) {
18.3365 - var span = last[i];
18.3366 - if (span.to != null) span.to += offset;
18.3367 - if (span.from == null) {
18.3368 - var found = getMarkedSpanFor(first, span.marker);
18.3369 - if (!found) {
18.3370 - span.from = offset;
18.3371 - if (sameLine) (first || (first = [])).push(span);
18.3372 - }
18.3373 - } else {
18.3374 - span.from += offset;
18.3375 - if (sameLine) (first || (first = [])).push(span);
18.3376 - }
18.3377 - }
18.3378 - }
18.3379 -
18.3380 - var newMarkers = [newHL(newText[0], first)];
18.3381 - if (!sameLine) {
18.3382 - // Fill gap with whole-line-spans
18.3383 - var gap = newText.length - 2, gapMarkers;
18.3384 - if (gap > 0 && first)
18.3385 - for (var i = 0; i < first.length; ++i)
18.3386 - if (first[i].to == null)
18.3387 - (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
18.3388 - for (var i = 0; i < gap; ++i)
18.3389 - newMarkers.push(newHL(newText[i+1], gapMarkers));
18.3390 - newMarkers.push(newHL(lst(newText), last));
18.3391 - }
18.3392 - return newMarkers;
18.3393 - }
18.3394 -
18.3395 - function removeReadOnlyRanges(doc, from, to) {
18.3396 - var markers = null;
18.3397 - doc.iter(from.line, to.line + 1, function(line) {
18.3398 - if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
18.3399 - var mark = line.markedSpans[i].marker;
18.3400 - if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
18.3401 - (markers || (markers = [])).push(mark);
18.3402 - }
18.3403 - });
18.3404 - if (!markers) return null;
18.3405 - var parts = [{from: from, to: to}];
18.3406 - for (var i = 0; i < markers.length; ++i) {
18.3407 - var m = markers[i].find();
18.3408 - for (var j = 0; j < parts.length; ++j) {
18.3409 - var p = parts[j];
18.3410 - if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
18.3411 - var newParts = [j, 1];
18.3412 - if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
18.3413 - if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
18.3414 - parts.splice.apply(parts, newParts);
18.3415 - j += newParts.length - 1;
18.3416 - }
18.3417 - }
18.3418 - return parts;
18.3419 - }
18.3420 -
18.3421 - function collapsedSpanAt(line, ch) {
18.3422 - var sps = sawCollapsedSpans && line.markedSpans, found;
18.3423 - if (sps) for (var sp, i = 0; i < sps.length; ++i) {
18.3424 - sp = sps[i];
18.3425 - if (!sp.marker.collapsed) continue;
18.3426 - if ((sp.from == null || sp.from < ch) &&
18.3427 - (sp.to == null || sp.to > ch) &&
18.3428 - (!found || found.width < sp.marker.width))
18.3429 - found = sp.marker;
18.3430 - }
18.3431 - return found;
18.3432 - }
18.3433 - function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
18.3434 - function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
18.3435 -
18.3436 - function visualLine(doc, line) {
18.3437 - var merged;
18.3438 - while (merged = collapsedSpanAtStart(line))
18.3439 - line = getLine(doc, merged.find().from.line);
18.3440 - return line;
18.3441 - }
18.3442 -
18.3443 - function lineIsHidden(line) {
18.3444 - var sps = sawCollapsedSpans && line.markedSpans;
18.3445 - if (sps) for (var sp, i = 0; i < sps.length; ++i) {
18.3446 - sp = sps[i];
18.3447 - if (!sp.marker.collapsed) continue;
18.3448 - if (sp.from == null) return true;
18.3449 - if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
18.3450 - return true;
18.3451 - }
18.3452 - }
18.3453 - window.lineIsHidden = lineIsHidden;
18.3454 - function lineIsHiddenInner(line, span) {
18.3455 - if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length)
18.3456 - return true;
18.3457 - for (var sp, i = 0; i < line.markedSpans.length; ++i) {
18.3458 - sp = line.markedSpans[i];
18.3459 - if (sp.marker.collapsed && sp.from == span.to &&
18.3460 - (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
18.3461 - lineIsHiddenInner(line, sp)) return true;
18.3462 - }
18.3463 - }
18.3464 -
18.3465 - // hl stands for history-line, a data structure that can be either a
18.3466 - // string (line without markers) or a {text, markedSpans} object.
18.3467 - function hlText(val) { return typeof val == "string" ? val : val.text; }
18.3468 - function hlSpans(val) {
18.3469 - if (typeof val == "string") return null;
18.3470 - var spans = val.markedSpans, out = null;
18.3471 - for (var i = 0; i < spans.length; ++i) {
18.3472 - if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
18.3473 - else if (out) out.push(spans[i]);
18.3474 - }
18.3475 - return !out ? spans : out.length ? out : null;
18.3476 - }
18.3477 - function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
18.3478 -
18.3479 - function detachMarkedSpans(line) {
18.3480 - var spans = line.markedSpans;
18.3481 - if (!spans) return;
18.3482 - for (var i = 0; i < spans.length; ++i) {
18.3483 - var lines = spans[i].marker.lines;
18.3484 - var ix = indexOf(lines, line);
18.3485 - lines.splice(ix, 1);
18.3486 - }
18.3487 - line.markedSpans = null;
18.3488 - }
18.3489 -
18.3490 - function attachMarkedSpans(line, spans) {
18.3491 - if (!spans) return;
18.3492 - for (var i = 0; i < spans.length; ++i)
18.3493 - spans[i].marker.lines.push(line);
18.3494 - line.markedSpans = spans;
18.3495 - }
18.3496 -
18.3497 - // LINE DATA STRUCTURE
18.3498 -
18.3499 - // Line objects. These hold state related to a line, including
18.3500 - // highlighting info (the styles array).
18.3501 - function makeLine(text, markedSpans, height) {
18.3502 - var line = {text: text, height: height};
18.3503 - attachMarkedSpans(line, markedSpans);
18.3504 - if (lineIsHidden(line)) line.height = 0;
18.3505 - return line;
18.3506 - }
18.3507 -
18.3508 - function updateLine(cm, line, text, markedSpans) {
18.3509 - line.text = text;
18.3510 - line.stateAfter = line.styles = null;
18.3511 - if (line.order != null) line.order = null;
18.3512 - detachMarkedSpans(line);
18.3513 - attachMarkedSpans(line, markedSpans);
18.3514 - if (lineIsHidden(line)) line.height = 0;
18.3515 - else if (!line.height) line.height = textHeight(cm.display);
18.3516 - signalLater(cm, line, "change");
18.3517 - }
18.3518 -
18.3519 - function cleanUpLine(line) {
18.3520 - line.parent = null;
18.3521 - detachMarkedSpans(line);
18.3522 - }
18.3523 -
18.3524 - // Run the given mode's parser over a line, update the styles
18.3525 - // array, which contains alternating fragments of text and CSS
18.3526 - // classes.
18.3527 - function highlightLine(cm, line, state) {
18.3528 - var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans;
18.3529 - var changed = !line.styles, pos = 0, curText = "", curStyle = null;
18.3530 - var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []);
18.3531 - if (line.text == "" && mode.blankLine) mode.blankLine(state);
18.3532 - while (!stream.eol()) {
18.3533 - var style = mode.token(stream, state), substr = stream.current();
18.3534 - stream.start = stream.pos;
18.3535 - if (!flattenSpans || curStyle != style) {
18.3536 - if (curText) {
18.3537 - changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
18.3538 - st[pos++] = curText; st[pos++] = curStyle;
18.3539 - }
18.3540 - curText = substr; curStyle = style;
18.3541 - } else curText = curText + substr;
18.3542 - // Give up when line is ridiculously long
18.3543 - if (stream.pos > 5000) break;
18.3544 - }
18.3545 - if (curText) {
18.3546 - changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
18.3547 - st[pos++] = curText; st[pos++] = curStyle;
18.3548 - }
18.3549 - if (stream.pos > 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; }
18.3550 - if (pos != st.length) { st.length = pos; changed = true; }
18.3551 - return changed;
18.3552 - }
18.3553 -
18.3554 - // Lightweight form of highlight -- proceed over this line and
18.3555 - // update state, but don't save a style array.
18.3556 - function processLine(cm, line, state) {
18.3557 - var mode = cm.view.mode;
18.3558 - var stream = new StringStream(line.text, cm.options.tabSize);
18.3559 - if (line.text == "" && mode.blankLine) mode.blankLine(state);
18.3560 - while (!stream.eol() && stream.pos <= 5000) {
18.3561 - mode.token(stream, state);
18.3562 - stream.start = stream.pos;
18.3563 - }
18.3564 - }
18.3565 -
18.3566 - var styleToClassCache = {};
18.3567 - function styleToClass(style) {
18.3568 - if (!style) return null;
18.3569 - return styleToClassCache[style] ||
18.3570 - (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
18.3571 - }
18.3572 -
18.3573 - function lineContent(cm, realLine, measure) {
18.3574 - var merged, line = realLine, lineBefore, sawBefore, simple = true;
18.3575 - while (merged = collapsedSpanAtStart(line)) {
18.3576 - simple = false;
18.3577 - line = getLine(cm.view.doc, merged.find().from.line);
18.3578 - if (!lineBefore) lineBefore = line;
18.3579 - }
18.3580 -
18.3581 - var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
18.3582 - measure: null, addedOne: false, cm: cm};
18.3583 - if (line.textClass) builder.pre.className = line.textClass;
18.3584 -
18.3585 - do {
18.3586 - if (!line.styles)
18.3587 - highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
18.3588 - builder.measure = line == realLine && measure;
18.3589 - builder.pos = 0;
18.3590 - builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
18.3591 - if (measure && sawBefore && line != realLine && !builder.addedOne) {
18.3592 - measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
18.3593 - builder.addedOne = true;
18.3594 - }
18.3595 - var next = insertLineContent(line, builder);
18.3596 - sawBefore = line == lineBefore;
18.3597 - if (next) {
18.3598 - line = getLine(cm.view.doc, next.to.line);
18.3599 - simple = false;
18.3600 - }
18.3601 - } while (next);
18.3602 -
18.3603 - if (measure && !builder.addedOne)
18.3604 - measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
18.3605 - if (!builder.pre.firstChild && !lineIsHidden(realLine))
18.3606 - builder.pre.appendChild(document.createTextNode("\u00a0"));
18.3607 -
18.3608 - return builder.pre;
18.3609 - }
18.3610 -
18.3611 - var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
18.3612 - function buildToken(builder, text, style, startStyle, endStyle) {
18.3613 - if (!text) return;
18.3614 - if (!tokenSpecialChars.test(text)) {
18.3615 - builder.col += text.length;
18.3616 - var content = document.createTextNode(text);
18.3617 - } else {
18.3618 - var content = document.createDocumentFragment(), pos = 0;
18.3619 - while (true) {
18.3620 - tokenSpecialChars.lastIndex = pos;
18.3621 - var m = tokenSpecialChars.exec(text);
18.3622 - var skipped = m ? m.index - pos : text.length - pos;
18.3623 - if (skipped) {
18.3624 - content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
18.3625 - builder.col += skipped;
18.3626 - }
18.3627 - if (!m) break;
18.3628 - pos += skipped + 1;
18.3629 - if (m[0] == "\t") {
18.3630 - var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
18.3631 - content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
18.3632 - builder.col += tabWidth;
18.3633 - } else {
18.3634 - var token = elt("span", "\u2022", "cm-invalidchar");
18.3635 - token.title = "\\u" + m[0].charCodeAt(0).toString(16);
18.3636 - content.appendChild(token);
18.3637 - builder.col += 1;
18.3638 - }
18.3639 - }
18.3640 - }
18.3641 - if (style || startStyle || endStyle || builder.measure) {
18.3642 - var fullStyle = style || "";
18.3643 - if (startStyle) fullStyle += startStyle;
18.3644 - if (endStyle) fullStyle += endStyle;
18.3645 - return builder.pre.appendChild(elt("span", [content], fullStyle));
18.3646 - }
18.3647 - builder.pre.appendChild(content);
18.3648 - }
18.3649 -
18.3650 - function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
18.3651 - for (var i = 0; i < text.length; ++i) {
18.3652 - if (i && i < text.length - 1 &&
18.3653 - builder.cm.options.lineWrapping &&
18.3654 - spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
18.3655 - builder.pre.appendChild(elt("wbr"));
18.3656 - builder.measure[builder.pos++] =
18.3657 - buildToken(builder, text.charAt(i), style,
18.3658 - i == 0 && startStyle, i == text.length - 1 && endStyle);
18.3659 - }
18.3660 - if (text.length) builder.addedOne = true;
18.3661 - }
18.3662 -
18.3663 - function buildCollapsedSpan(builder, size, widget) {
18.3664 - if (widget) {
18.3665 - if (!builder.display) widget = widget.cloneNode(true);
18.3666 - builder.pre.appendChild(widget);
18.3667 - if (builder.measure && size) {
18.3668 - builder.measure[builder.pos] = widget;
18.3669 - builder.addedOne = true;
18.3670 - }
18.3671 - }
18.3672 - builder.pos += size;
18.3673 - }
18.3674 -
18.3675 - // Outputs a number of spans to make up a line, taking highlighting
18.3676 - // and marked text into account.
18.3677 - function insertLineContent(line, builder) {
18.3678 - var st = line.styles, spans = line.markedSpans;
18.3679 - if (!spans) {
18.3680 - for (var i = 0; i < st.length; i+=2)
18.3681 - builder.addToken(builder, st[i], styleToClass(st[i+1]));
18.3682 - return;
18.3683 - }
18.3684 -
18.3685 - var allText = line.text, len = allText.length;
18.3686 - var pos = 0, i = 0, text = "", style;
18.3687 - var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
18.3688 - for (;;) {
18.3689 - if (nextChange == pos) { // Update current marker set
18.3690 - spanStyle = spanEndStyle = spanStartStyle = "";
18.3691 - collapsed = null; nextChange = Infinity;
18.3692 - var foundBookmark = null;
18.3693 - for (var j = 0; j < spans.length; ++j) {
18.3694 - var sp = spans[j], m = sp.marker;
18.3695 - if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
18.3696 - if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
18.3697 - if (m.className) spanStyle += " " + m.className;
18.3698 - if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
18.3699 - if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
18.3700 - if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
18.3701 - collapsed = sp;
18.3702 - } else if (sp.from > pos && nextChange > sp.from) {
18.3703 - nextChange = sp.from;
18.3704 - }
18.3705 - if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
18.3706 - foundBookmark = m.replacedWith;
18.3707 - }
18.3708 - if (collapsed && (collapsed.from || 0) == pos) {
18.3709 - buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
18.3710 - collapsed.from != null && collapsed.marker.replacedWith);
18.3711 - if (collapsed.to == null) return collapsed.marker.find();
18.3712 - }
18.3713 - if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
18.3714 - }
18.3715 - if (pos >= len) break;
18.3716 -
18.3717 - var upto = Math.min(len, nextChange);
18.3718 - while (true) {
18.3719 - if (text) {
18.3720 - var end = pos + text.length;
18.3721 - if (!collapsed) {
18.3722 - var tokenText = end > upto ? text.slice(0, upto - pos) : text;
18.3723 - builder.addToken(builder, tokenText, style + spanStyle,
18.3724 - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
18.3725 - }
18.3726 - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
18.3727 - pos = end;
18.3728 - spanStartStyle = "";
18.3729 - }
18.3730 - text = st[i++]; style = styleToClass(st[i++]);
18.3731 - }
18.3732 - }
18.3733 - }
18.3734 -
18.3735 - // DOCUMENT DATA STRUCTURE
18.3736 -
18.3737 - function LeafChunk(lines) {
18.3738 - this.lines = lines;
18.3739 - this.parent = null;
18.3740 - for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
18.3741 - lines[i].parent = this;
18.3742 - height += lines[i].height;
18.3743 - }
18.3744 - this.height = height;
18.3745 - }
18.3746 -
18.3747 - LeafChunk.prototype = {
18.3748 - chunkSize: function() { return this.lines.length; },
18.3749 - remove: function(at, n, cm) {
18.3750 - for (var i = at, e = at + n; i < e; ++i) {
18.3751 - var line = this.lines[i];
18.3752 - this.height -= line.height;
18.3753 - cleanUpLine(line);
18.3754 - signalLater(cm, line, "delete");
18.3755 - }
18.3756 - this.lines.splice(at, n);
18.3757 - },
18.3758 - collapse: function(lines) {
18.3759 - lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
18.3760 - },
18.3761 - insertHeight: function(at, lines, height) {
18.3762 - this.height += height;
18.3763 - this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
18.3764 - for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
18.3765 - },
18.3766 - iterN: function(at, n, op) {
18.3767 - for (var e = at + n; at < e; ++at)
18.3768 - if (op(this.lines[at])) return true;
18.3769 - }
18.3770 - };
18.3771 -
18.3772 - function BranchChunk(children) {
18.3773 - this.children = children;
18.3774 - var size = 0, height = 0;
18.3775 - for (var i = 0, e = children.length; i < e; ++i) {
18.3776 - var ch = children[i];
18.3777 - size += ch.chunkSize(); height += ch.height;
18.3778 - ch.parent = this;
18.3779 - }
18.3780 - this.size = size;
18.3781 - this.height = height;
18.3782 - this.parent = null;
18.3783 - }
18.3784 -
18.3785 - BranchChunk.prototype = {
18.3786 - chunkSize: function() { return this.size; },
18.3787 - remove: function(at, n, callbacks) {
18.3788 - this.size -= n;
18.3789 - for (var i = 0; i < this.children.length; ++i) {
18.3790 - var child = this.children[i], sz = child.chunkSize();
18.3791 - if (at < sz) {
18.3792 - var rm = Math.min(n, sz - at), oldHeight = child.height;
18.3793 - child.remove(at, rm, callbacks);
18.3794 - this.height -= oldHeight - child.height;
18.3795 - if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
18.3796 - if ((n -= rm) == 0) break;
18.3797 - at = 0;
18.3798 - } else at -= sz;
18.3799 - }
18.3800 - if (this.size - n < 25) {
18.3801 - var lines = [];
18.3802 - this.collapse(lines);
18.3803 - this.children = [new LeafChunk(lines)];
18.3804 - this.children[0].parent = this;
18.3805 - }
18.3806 - },
18.3807 - collapse: function(lines) {
18.3808 - for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
18.3809 - },
18.3810 - insert: function(at, lines) {
18.3811 - var height = 0;
18.3812 - for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
18.3813 - this.insertHeight(at, lines, height);
18.3814 - },
18.3815 - insertHeight: function(at, lines, height) {
18.3816 - this.size += lines.length;
18.3817 - this.height += height;
18.3818 - for (var i = 0, e = this.children.length; i < e; ++i) {
18.3819 - var child = this.children[i], sz = child.chunkSize();
18.3820 - if (at <= sz) {
18.3821 - child.insertHeight(at, lines, height);
18.3822 - if (child.lines && child.lines.length > 50) {
18.3823 - while (child.lines.length > 50) {
18.3824 - var spilled = child.lines.splice(child.lines.length - 25, 25);
18.3825 - var newleaf = new LeafChunk(spilled);
18.3826 - child.height -= newleaf.height;
18.3827 - this.children.splice(i + 1, 0, newleaf);
18.3828 - newleaf.parent = this;
18.3829 - }
18.3830 - this.maybeSpill();
18.3831 - }
18.3832 - break;
18.3833 - }
18.3834 - at -= sz;
18.3835 - }
18.3836 - },
18.3837 - maybeSpill: function() {
18.3838 - if (this.children.length <= 10) return;
18.3839 - var me = this;
18.3840 - do {
18.3841 - var spilled = me.children.splice(me.children.length - 5, 5);
18.3842 - var sibling = new BranchChunk(spilled);
18.3843 - if (!me.parent) { // Become the parent node
18.3844 - var copy = new BranchChunk(me.children);
18.3845 - copy.parent = me;
18.3846 - me.children = [copy, sibling];
18.3847 - me = copy;
18.3848 - } else {
18.3849 - me.size -= sibling.size;
18.3850 - me.height -= sibling.height;
18.3851 - var myIndex = indexOf(me.parent.children, me);
18.3852 - me.parent.children.splice(myIndex + 1, 0, sibling);
18.3853 - }
18.3854 - sibling.parent = me.parent;
18.3855 - } while (me.children.length > 10);
18.3856 - me.parent.maybeSpill();
18.3857 - },
18.3858 - iter: function(from, to, op) { this.iterN(from, to - from, op); },
18.3859 - iterN: function(at, n, op) {
18.3860 - for (var i = 0, e = this.children.length; i < e; ++i) {
18.3861 - var child = this.children[i], sz = child.chunkSize();
18.3862 - if (at < sz) {
18.3863 - var used = Math.min(n, sz - at);
18.3864 - if (child.iterN(at, used, op)) return true;
18.3865 - if ((n -= used) == 0) break;
18.3866 - at = 0;
18.3867 - } else at -= sz;
18.3868 - }
18.3869 - }
18.3870 - };
18.3871 -
18.3872 - // LINE UTILITIES
18.3873 -
18.3874 - function getLine(chunk, n) {
18.3875 - while (!chunk.lines) {
18.3876 - for (var i = 0;; ++i) {
18.3877 - var child = chunk.children[i], sz = child.chunkSize();
18.3878 - if (n < sz) { chunk = child; break; }
18.3879 - n -= sz;
18.3880 - }
18.3881 - }
18.3882 - return chunk.lines[n];
18.3883 - }
18.3884 -
18.3885 - function updateLineHeight(line, height) {
18.3886 - var diff = height - line.height;
18.3887 - for (var n = line; n; n = n.parent) n.height += diff;
18.3888 - }
18.3889 -
18.3890 - function lineNo(line) {
18.3891 - if (line.parent == null) return null;
18.3892 - var cur = line.parent, no = indexOf(cur.lines, line);
18.3893 - for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
18.3894 - for (var i = 0;; ++i) {
18.3895 - if (chunk.children[i] == cur) break;
18.3896 - no += chunk.children[i].chunkSize();
18.3897 - }
18.3898 - }
18.3899 - return no;
18.3900 - }
18.3901 -
18.3902 - function lineAtHeight(chunk, h) {
18.3903 - var n = 0;
18.3904 - outer: do {
18.3905 - for (var i = 0, e = chunk.children.length; i < e; ++i) {
18.3906 - var child = chunk.children[i], ch = child.height;
18.3907 - if (h < ch) { chunk = child; continue outer; }
18.3908 - h -= ch;
18.3909 - n += child.chunkSize();
18.3910 - }
18.3911 - return n;
18.3912 - } while (!chunk.lines);
18.3913 - for (var i = 0, e = chunk.lines.length; i < e; ++i) {
18.3914 - var line = chunk.lines[i], lh = line.height;
18.3915 - if (h < lh) break;
18.3916 - h -= lh;
18.3917 - }
18.3918 - return n + i;
18.3919 - }
18.3920 -
18.3921 - function heightAtLine(cm, lineObj) {
18.3922 - lineObj = visualLine(cm.view.doc, lineObj);
18.3923 -
18.3924 - var h = 0, chunk = lineObj.parent;
18.3925 - for (var i = 0; i < chunk.lines.length; ++i) {
18.3926 - var line = chunk.lines[i];
18.3927 - if (line == lineObj) break;
18.3928 - else h += line.height;
18.3929 - }
18.3930 - for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
18.3931 - for (var i = 0; i < p.children.length; ++i) {
18.3932 - var cur = p.children[i];
18.3933 - if (cur == chunk) break;
18.3934 - else h += cur.height;
18.3935 - }
18.3936 - }
18.3937 - return h;
18.3938 - }
18.3939 -
18.3940 - function getOrder(line) {
18.3941 - var order = line.order;
18.3942 - if (order == null) order = line.order = bidiOrdering(line.text);
18.3943 - return order;
18.3944 - }
18.3945 -
18.3946 - // HISTORY
18.3947 -
18.3948 - function makeHistory() {
18.3949 - return {
18.3950 - // Arrays of history events. Doing something adds an event to
18.3951 - // done and clears undo. Undoing moves events from done to
18.3952 - // undone, redoing moves them in the other direction.
18.3953 - done: [], undone: [],
18.3954 - // Used to track when changes can be merged into a single undo
18.3955 - // event
18.3956 - lastTime: 0, lastOp: null, lastOrigin: null,
18.3957 - // Used by the isClean() method
18.3958 - dirtyCounter: 0
18.3959 - };
18.3960 - }
18.3961 -
18.3962 - function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
18.3963 - var history = cm.view.history;
18.3964 - history.undone.length = 0;
18.3965 - var time = +new Date, cur = lst(history.done);
18.3966 -
18.3967 - if (cur &&
18.3968 - (history.lastOp == cm.curOp.id ||
18.3969 - history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
18.3970 - history.lastTime > time - 600)) {
18.3971 - // Merge this change into the last event
18.3972 - var last = lst(cur.events);
18.3973 - if (last.start > start + old.length || last.start + last.added < start) {
18.3974 - // Doesn't intersect with last sub-event, add new sub-event
18.3975 - cur.events.push({start: start, added: added, old: old});
18.3976 - } else {
18.3977 - // Patch up the last sub-event
18.3978 - var startBefore = Math.max(0, last.start - start),
18.3979 - endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
18.3980 - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
18.3981 - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
18.3982 - if (startBefore) last.start = start;
18.3983 - last.added += added - (old.length - startBefore - endAfter);
18.3984 - }
18.3985 - cur.fromAfter = fromAfter; cur.toAfter = toAfter;
18.3986 - } else {
18.3987 - // Can not be merged, start a new event.
18.3988 - cur = {events: [{start: start, added: added, old: old}],
18.3989 - fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
18.3990 - history.done.push(cur);
18.3991 - while (history.done.length > cm.options.undoDepth)
18.3992 - history.done.shift();
18.3993 - if (history.dirtyCounter < 0)
18.3994 - // The user has made a change after undoing past the last clean state.
18.3995 - // We can never get back to a clean state now until markClean() is called.
18.3996 - history.dirtyCounter = NaN;
18.3997 - else
18.3998 - history.dirtyCounter++;
18.3999 - }
18.4000 - history.lastTime = time;
18.4001 - history.lastOp = cm.curOp.id;
18.4002 - history.lastOrigin = origin;
18.4003 - }
18.4004 -
18.4005 - // EVENT OPERATORS
18.4006 -
18.4007 - function stopMethod() {e_stop(this);}
18.4008 - // Ensure an event has a stop method.
18.4009 - function addStop(event) {
18.4010 - if (!event.stop) event.stop = stopMethod;
18.4011 - return event;
18.4012 - }
18.4013 -
18.4014 - function e_preventDefault(e) {
18.4015 - if (e.preventDefault) e.preventDefault();
18.4016 - else e.returnValue = false;
18.4017 - }
18.4018 - function e_stopPropagation(e) {
18.4019 - if (e.stopPropagation) e.stopPropagation();
18.4020 - else e.cancelBubble = true;
18.4021 - }
18.4022 - function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
18.4023 - CodeMirror.e_stop = e_stop;
18.4024 - CodeMirror.e_preventDefault = e_preventDefault;
18.4025 - CodeMirror.e_stopPropagation = e_stopPropagation;
18.4026 -
18.4027 - function e_target(e) {return e.target || e.srcElement;}
18.4028 - function e_button(e) {
18.4029 - var b = e.which;
18.4030 - if (b == null) {
18.4031 - if (e.button & 1) b = 1;
18.4032 - else if (e.button & 2) b = 3;
18.4033 - else if (e.button & 4) b = 2;
18.4034 - }
18.4035 - if (mac && e.ctrlKey && b == 1) b = 3;
18.4036 - return b;
18.4037 - }
18.4038 -
18.4039 - // Allow 3rd-party code to override event properties by adding an override
18.4040 - // object to an event object.
18.4041 - function e_prop(e, prop) {
18.4042 - var overridden = e.override && e.override.hasOwnProperty(prop);
18.4043 - return overridden ? e.override[prop] : e[prop];
18.4044 - }
18.4045 -
18.4046 - // EVENT HANDLING
18.4047 -
18.4048 - function on(emitter, type, f) {
18.4049 - if (emitter.addEventListener)
18.4050 - emitter.addEventListener(type, f, false);
18.4051 - else if (emitter.attachEvent)
18.4052 - emitter.attachEvent("on" + type, f);
18.4053 - else {
18.4054 - var map = emitter._handlers || (emitter._handlers = {});
18.4055 - var arr = map[type] || (map[type] = []);
18.4056 - arr.push(f);
18.4057 - }
18.4058 - }
18.4059 -
18.4060 - function off(emitter, type, f) {
18.4061 - if (emitter.removeEventListener)
18.4062 - emitter.removeEventListener(type, f, false);
18.4063 - else if (emitter.detachEvent)
18.4064 - emitter.detachEvent("on" + type, f);
18.4065 - else {
18.4066 - var arr = emitter._handlers && emitter._handlers[type];
18.4067 - if (!arr) return;
18.4068 - for (var i = 0; i < arr.length; ++i)
18.4069 - if (arr[i] == f) { arr.splice(i, 1); break; }
18.4070 - }
18.4071 - }
18.4072 -
18.4073 - function signal(emitter, type /*, values...*/) {
18.4074 - var arr = emitter._handlers && emitter._handlers[type];
18.4075 - if (!arr) return;
18.4076 - var args = Array.prototype.slice.call(arguments, 2);
18.4077 - for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
18.4078 - }
18.4079 -
18.4080 - function signalLater(cm, emitter, type /*, values...*/) {
18.4081 - var arr = emitter._handlers && emitter._handlers[type];
18.4082 - if (!arr) return;
18.4083 - var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
18.4084 - function bnd(f) {return function(){f.apply(null, args);};};
18.4085 - for (var i = 0; i < arr.length; ++i)
18.4086 - if (flist) flist.push(bnd(arr[i]));
18.4087 - else arr[i].apply(null, args);
18.4088 - }
18.4089 -
18.4090 - function hasHandler(emitter, type) {
18.4091 - var arr = emitter._handlers && emitter._handlers[type];
18.4092 - return arr && arr.length > 0;
18.4093 - }
18.4094 -
18.4095 - CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
18.4096 -
18.4097 - // MISC UTILITIES
18.4098 -
18.4099 - // Number of pixels added to scroller and sizer to hide scrollbar
18.4100 - var scrollerCutOff = 30;
18.4101 -
18.4102 - // Returned or thrown by various protocols to signal 'I'm not
18.4103 - // handling this'.
18.4104 - var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
18.4105 -
18.4106 - function Delayed() {this.id = null;}
18.4107 - Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
18.4108 -
18.4109 - // Counts the column offset in a string, taking tabs into account.
18.4110 - // Used mostly to find indentation.
18.4111 - function countColumn(string, end, tabSize) {
18.4112 - if (end == null) {
18.4113 - end = string.search(/[^\s\u00a0]/);
18.4114 - if (end == -1) end = string.length;
18.4115 - }
18.4116 - for (var i = 0, n = 0; i < end; ++i) {
18.4117 - if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
18.4118 - else ++n;
18.4119 - }
18.4120 - return n;
18.4121 - }
18.4122 - CodeMirror.countColumn = countColumn;
18.4123 -
18.4124 - var spaceStrs = [""];
18.4125 - function spaceStr(n) {
18.4126 - while (spaceStrs.length <= n)
18.4127 - spaceStrs.push(lst(spaceStrs) + " ");
18.4128 - return spaceStrs[n];
18.4129 - }
18.4130 -
18.4131 - function lst(arr) { return arr[arr.length-1]; }
18.4132 -
18.4133 - function selectInput(node) {
18.4134 - if (ios) { // Mobile Safari apparently has a bug where select() is broken.
18.4135 - node.selectionStart = 0;
18.4136 - node.selectionEnd = node.value.length;
18.4137 - } else node.select();
18.4138 - }
18.4139 -
18.4140 - function indexOf(collection, elt) {
18.4141 - if (collection.indexOf) return collection.indexOf(elt);
18.4142 - for (var i = 0, e = collection.length; i < e; ++i)
18.4143 - if (collection[i] == elt) return i;
18.4144 - return -1;
18.4145 - }
18.4146 -
18.4147 - function emptyArray(size) {
18.4148 - for (var a = [], i = 0; i < size; ++i) a.push(undefined);
18.4149 - return a;
18.4150 - }
18.4151 -
18.4152 - function bind(f) {
18.4153 - var args = Array.prototype.slice.call(arguments, 1);
18.4154 - return function(){return f.apply(null, args);};
18.4155 - }
18.4156 -
18.4157 - var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
18.4158 - function isWordChar(ch) {
18.4159 - return /\w/.test(ch) || ch > "\x80" &&
18.4160 - (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
18.4161 - }
18.4162 -
18.4163 - function isEmpty(obj) {
18.4164 - var c = 0;
18.4165 - for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
18.4166 - return !c;
18.4167 - }
18.4168 -
18.4169 - var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
18.4170 -
18.4171 - // DOM UTILITIES
18.4172 -
18.4173 - function elt(tag, content, className, style) {
18.4174 - var e = document.createElement(tag);
18.4175 - if (className) e.className = className;
18.4176 - if (style) e.style.cssText = style;
18.4177 - if (typeof content == "string") setTextContent(e, content);
18.4178 - else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
18.4179 - return e;
18.4180 - }
18.4181 -
18.4182 - function removeChildren(e) {
18.4183 - e.innerHTML = "";
18.4184 - return e;
18.4185 - }
18.4186 -
18.4187 - function removeChildrenAndAdd(parent, e) {
18.4188 - return removeChildren(parent).appendChild(e);
18.4189 - }
18.4190 -
18.4191 - function setTextContent(e, str) {
18.4192 - if (ie_lt9) {
18.4193 - e.innerHTML = "";
18.4194 - e.appendChild(document.createTextNode(str));
18.4195 - } else e.textContent = str;
18.4196 - }
18.4197 -
18.4198 - // FEATURE DETECTION
18.4199 -
18.4200 - // Detect drag-and-drop
18.4201 - var dragAndDrop = function() {
18.4202 - // There is *some* kind of drag-and-drop support in IE6-8, but I
18.4203 - // couldn't get it to work yet.
18.4204 - if (ie_lt9) return false;
18.4205 - var div = elt('div');
18.4206 - return "draggable" in div || "dragDrop" in div;
18.4207 - }();
18.4208 -
18.4209 - // For a reason I have yet to figure out, some browsers disallow
18.4210 - // word wrapping between certain characters *only* if a new inline
18.4211 - // element is started between them. This makes it hard to reliably
18.4212 - // measure the position of things, since that requires inserting an
18.4213 - // extra span. This terribly fragile set of regexps matches the
18.4214 - // character combinations that suffer from this phenomenon on the
18.4215 - // various browsers.
18.4216 - var spanAffectsWrapping = /^$/; // Won't match any two-character string
18.4217 - if (gecko) spanAffectsWrapping = /$'/;
18.4218 - else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
18.4219 - else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
18.4220 -
18.4221 - var knownScrollbarWidth;
18.4222 - function scrollbarWidth(measure) {
18.4223 - if (knownScrollbarWidth != null) return knownScrollbarWidth;
18.4224 - var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
18.4225 - removeChildrenAndAdd(measure, test);
18.4226 - if (test.offsetWidth)
18.4227 - knownScrollbarWidth = test.offsetHeight - test.clientHeight;
18.4228 - return knownScrollbarWidth || 0;
18.4229 - }
18.4230 -
18.4231 - var zwspSupported;
18.4232 - function zeroWidthElement(measure) {
18.4233 - if (zwspSupported == null) {
18.4234 - var test = elt("span", "\u200b");
18.4235 - removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
18.4236 - if (measure.firstChild.offsetHeight != 0)
18.4237 - zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
18.4238 - }
18.4239 - if (zwspSupported) return elt("span", "\u200b");
18.4240 - else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
18.4241 - }
18.4242 -
18.4243 - // See if "".split is the broken IE version, if so, provide an
18.4244 - // alternative way to split lines.
18.4245 - var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
18.4246 - var pos = 0, result = [], l = string.length;
18.4247 - while (pos <= l) {
18.4248 - var nl = string.indexOf("\n", pos);
18.4249 - if (nl == -1) nl = string.length;
18.4250 - var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
18.4251 - var rt = line.indexOf("\r");
18.4252 - if (rt != -1) {
18.4253 - result.push(line.slice(0, rt));
18.4254 - pos += rt + 1;
18.4255 - } else {
18.4256 - result.push(line);
18.4257 - pos = nl + 1;
18.4258 - }
18.4259 - }
18.4260 - return result;
18.4261 - } : function(string){return string.split(/\r\n?|\n/);};
18.4262 - CodeMirror.splitLines = splitLines;
18.4263 -
18.4264 - var hasSelection = window.getSelection ? function(te) {
18.4265 - try { return te.selectionStart != te.selectionEnd; }
18.4266 - catch(e) { return false; }
18.4267 - } : function(te) {
18.4268 - try {var range = te.ownerDocument.selection.createRange();}
18.4269 - catch(e) {}
18.4270 - if (!range || range.parentElement() != te) return false;
18.4271 - return range.compareEndPoints("StartToEnd", range) != 0;
18.4272 - };
18.4273 -
18.4274 - var hasCopyEvent = (function() {
18.4275 - var e = elt("div");
18.4276 - if ("oncopy" in e) return true;
18.4277 - e.setAttribute("oncopy", "return;");
18.4278 - return typeof e.oncopy == 'function';
18.4279 - })();
18.4280 -
18.4281 - // KEY NAMING
18.4282 -
18.4283 - var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
18.4284 - 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
18.4285 - 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
18.4286 - 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
18.4287 - 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
18.4288 - 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
18.4289 - 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
18.4290 - CodeMirror.keyNames = keyNames;
18.4291 - (function() {
18.4292 - // Number keys
18.4293 - for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
18.4294 - // Alphabetic keys
18.4295 - for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
18.4296 - // Function keys
18.4297 - for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
18.4298 - })();
18.4299 -
18.4300 - // BIDI HELPERS
18.4301 -
18.4302 - function iterateBidiSections(order, from, to, f) {
18.4303 - if (!order) return f(from, to, "ltr");
18.4304 - for (var i = 0; i < order.length; ++i) {
18.4305 - var part = order[i];
18.4306 - if (part.from < to && part.to > from)
18.4307 - f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
18.4308 - }
18.4309 - }
18.4310 -
18.4311 - function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
18.4312 - function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
18.4313 -
18.4314 - function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
18.4315 - function lineRight(line) {
18.4316 - var order = getOrder(line);
18.4317 - if (!order) return line.text.length;
18.4318 - return bidiRight(lst(order));
18.4319 - }
18.4320 -
18.4321 - function lineStart(cm, lineN) {
18.4322 - var line = getLine(cm.view.doc, lineN);
18.4323 - var visual = visualLine(cm.view.doc, line);
18.4324 - if (visual != line) lineN = lineNo(visual);
18.4325 - var order = getOrder(visual);
18.4326 - var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
18.4327 - return {line: lineN, ch: ch};
18.4328 - }
18.4329 - function lineEnd(cm, lineNo) {
18.4330 - var merged, line;
18.4331 - while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
18.4332 - lineNo = merged.find().to.line;
18.4333 - var order = getOrder(line);
18.4334 - var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
18.4335 - return {line: lineNo, ch: ch};
18.4336 - }
18.4337 -
18.4338 - // This is somewhat involved. It is needed in order to move
18.4339 - // 'visually' through bi-directional text -- i.e., pressing left
18.4340 - // should make the cursor go left, even when in RTL text. The
18.4341 - // tricky part is the 'jumps', where RTL and LTR text touch each
18.4342 - // other. This often requires the cursor offset to move more than
18.4343 - // one unit, in order to visually move one unit.
18.4344 - function moveVisually(line, start, dir, byUnit) {
18.4345 - var bidi = getOrder(line);
18.4346 - if (!bidi) return moveLogically(line, start, dir, byUnit);
18.4347 - var moveOneUnit = byUnit ? function(pos, dir) {
18.4348 - do pos += dir;
18.4349 - while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
18.4350 - return pos;
18.4351 - } : function(pos, dir) { return pos + dir; };
18.4352 - var linedir = bidi[0].level;
18.4353 - for (var i = 0; i < bidi.length; ++i) {
18.4354 - var part = bidi[i], sticky = part.level % 2 == linedir;
18.4355 - if ((part.from < start && part.to > start) ||
18.4356 - (sticky && (part.from == start || part.to == start))) break;
18.4357 - }
18.4358 - var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
18.4359 -
18.4360 - while (target != null) {
18.4361 - if (part.level % 2 == linedir) {
18.4362 - if (target < part.from || target > part.to) {
18.4363 - part = bidi[i += dir];
18.4364 - target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
18.4365 - } else break;
18.4366 - } else {
18.4367 - if (target == bidiLeft(part)) {
18.4368 - part = bidi[--i];
18.4369 - target = part && bidiRight(part);
18.4370 - } else if (target == bidiRight(part)) {
18.4371 - part = bidi[++i];
18.4372 - target = part && bidiLeft(part);
18.4373 - } else break;
18.4374 - }
18.4375 - }
18.4376 -
18.4377 - return target < 0 || target > line.text.length ? null : target;
18.4378 - }
18.4379 -
18.4380 - function moveLogically(line, start, dir, byUnit) {
18.4381 - var target = start + dir;
18.4382 - if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
18.4383 - return target < 0 || target > line.text.length ? null : target;
18.4384 - }
18.4385 -
18.4386 - // Bidirectional ordering algorithm
18.4387 - // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
18.4388 - // that this (partially) implements.
18.4389 -
18.4390 - // One-char codes used for character types:
18.4391 - // L (L): Left-to-Right
18.4392 - // R (R): Right-to-Left
18.4393 - // r (AL): Right-to-Left Arabic
18.4394 - // 1 (EN): European Number
18.4395 - // + (ES): European Number Separator
18.4396 - // % (ET): European Number Terminator
18.4397 - // n (AN): Arabic Number
18.4398 - // , (CS): Common Number Separator
18.4399 - // m (NSM): Non-Spacing Mark
18.4400 - // b (BN): Boundary Neutral
18.4401 - // s (B): Paragraph Separator
18.4402 - // t (S): Segment Separator
18.4403 - // w (WS): Whitespace
18.4404 - // N (ON): Other Neutrals
18.4405 -
18.4406 - // Returns null if characters are ordered as they appear
18.4407 - // (left-to-right), or an array of sections ({from, to, level}
18.4408 - // objects) in the order in which they occur visually.
18.4409 - var bidiOrdering = (function() {
18.4410 - // Character types for codepoints 0 to 0xff
18.4411 - var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
18.4412 - // Character types for codepoints 0x600 to 0x6ff
18.4413 - var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
18.4414 - function charType(code) {
18.4415 - if (code <= 0xff) return lowTypes.charAt(code);
18.4416 - else if (0x590 <= code && code <= 0x5f4) return "R";
18.4417 - else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
18.4418 - else if (0x700 <= code && code <= 0x8ac) return "r";
18.4419 - else return "L";
18.4420 - }
18.4421 -
18.4422 - var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
18.4423 - var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
18.4424 -
18.4425 - return function charOrdering(str) {
18.4426 - if (!bidiRE.test(str)) return false;
18.4427 - var len = str.length, types = [], startType = null;
18.4428 - for (var i = 0, type; i < len; ++i) {
18.4429 - types.push(type = charType(str.charCodeAt(i)));
18.4430 - if (startType == null) {
18.4431 - if (type == "L") startType = "L";
18.4432 - else if (type == "R" || type == "r") startType = "R";
18.4433 - }
18.4434 - }
18.4435 - if (startType == null) startType = "L";
18.4436 -
18.4437 - // W1. Examine each non-spacing mark (NSM) in the level run, and
18.4438 - // change the type of the NSM to the type of the previous
18.4439 - // character. If the NSM is at the start of the level run, it will
18.4440 - // get the type of sor.
18.4441 - for (var i = 0, prev = startType; i < len; ++i) {
18.4442 - var type = types[i];
18.4443 - if (type == "m") types[i] = prev;
18.4444 - else prev = type;
18.4445 - }
18.4446 -
18.4447 - // W2. Search backwards from each instance of a European number
18.4448 - // until the first strong type (R, L, AL, or sor) is found. If an
18.4449 - // AL is found, change the type of the European number to Arabic
18.4450 - // number.
18.4451 - // W3. Change all ALs to R.
18.4452 - for (var i = 0, cur = startType; i < len; ++i) {
18.4453 - var type = types[i];
18.4454 - if (type == "1" && cur == "r") types[i] = "n";
18.4455 - else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
18.4456 - }
18.4457 -
18.4458 - // W4. A single European separator between two European numbers
18.4459 - // changes to a European number. A single common separator between
18.4460 - // two numbers of the same type changes to that type.
18.4461 - for (var i = 1, prev = types[0]; i < len - 1; ++i) {
18.4462 - var type = types[i];
18.4463 - if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
18.4464 - else if (type == "," && prev == types[i+1] &&
18.4465 - (prev == "1" || prev == "n")) types[i] = prev;
18.4466 - prev = type;
18.4467 - }
18.4468 -
18.4469 - // W5. A sequence of European terminators adjacent to European
18.4470 - // numbers changes to all European numbers.
18.4471 - // W6. Otherwise, separators and terminators change to Other
18.4472 - // Neutral.
18.4473 - for (var i = 0; i < len; ++i) {
18.4474 - var type = types[i];
18.4475 - if (type == ",") types[i] = "N";
18.4476 - else if (type == "%") {
18.4477 - for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
18.4478 - var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
18.4479 - for (var j = i; j < end; ++j) types[j] = replace;
18.4480 - i = end - 1;
18.4481 - }
18.4482 - }
18.4483 -
18.4484 - // W7. Search backwards from each instance of a European number
18.4485 - // until the first strong type (R, L, or sor) is found. If an L is
18.4486 - // found, then change the type of the European number to L.
18.4487 - for (var i = 0, cur = startType; i < len; ++i) {
18.4488 - var type = types[i];
18.4489 - if (cur == "L" && type == "1") types[i] = "L";
18.4490 - else if (isStrong.test(type)) cur = type;
18.4491 - }
18.4492 -
18.4493 - // N1. A sequence of neutrals takes the direction of the
18.4494 - // surrounding strong text if the text on both sides has the same
18.4495 - // direction. European and Arabic numbers act as if they were R in
18.4496 - // terms of their influence on neutrals. Start-of-level-run (sor)
18.4497 - // and end-of-level-run (eor) are used at level run boundaries.
18.4498 - // N2. Any remaining neutrals take the embedding direction.
18.4499 - for (var i = 0; i < len; ++i) {
18.4500 - if (isNeutral.test(types[i])) {
18.4501 - for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
18.4502 - var before = (i ? types[i-1] : startType) == "L";
18.4503 - var after = (end < len - 1 ? types[end] : startType) == "L";
18.4504 - var replace = before || after ? "L" : "R";
18.4505 - for (var j = i; j < end; ++j) types[j] = replace;
18.4506 - i = end - 1;
18.4507 - }
18.4508 - }
18.4509 -
18.4510 - // Here we depart from the documented algorithm, in order to avoid
18.4511 - // building up an actual levels array. Since there are only three
18.4512 - // levels (0, 1, 2) in an implementation that doesn't take
18.4513 - // explicit embedding into account, we can build up the order on
18.4514 - // the fly, without following the level-based algorithm.
18.4515 - var order = [], m;
18.4516 - for (var i = 0; i < len;) {
18.4517 - if (countsAsLeft.test(types[i])) {
18.4518 - var start = i;
18.4519 - for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
18.4520 - order.push({from: start, to: i, level: 0});
18.4521 - } else {
18.4522 - var pos = i, at = order.length;
18.4523 - for (++i; i < len && types[i] != "L"; ++i) {}
18.4524 - for (var j = pos; j < i;) {
18.4525 - if (countsAsNum.test(types[j])) {
18.4526 - if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
18.4527 - var nstart = j;
18.4528 - for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
18.4529 - order.splice(at, 0, {from: nstart, to: j, level: 2});
18.4530 - pos = j;
18.4531 - } else ++j;
18.4532 - }
18.4533 - if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
18.4534 - }
18.4535 - }
18.4536 - if (order[0].level == 1 && (m = str.match(/^\s+/))) {
18.4537 - order[0].from = m[0].length;
18.4538 - order.unshift({from: 0, to: m[0].length, level: 0});
18.4539 - }
18.4540 - if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
18.4541 - lst(order).to -= m[0].length;
18.4542 - order.push({from: len - m[0].length, to: len, level: 0});
18.4543 - }
18.4544 - if (order[0].level != lst(order).level)
18.4545 - order.push({from: len, to: len, level: order[0].level});
18.4546 -
18.4547 - return order;
18.4548 - };
18.4549 - })();
18.4550 -
18.4551 - // THE END
18.4552 -
18.4553 - CodeMirror.version = "3.0";
18.4554 -
18.4555 - return CodeMirror;
18.4556 -})();
19.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/mode/clike.js Tue Feb 11 10:48:24 2014 +0100
19.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
19.3 @@ -1,300 +0,0 @@
19.4 -CodeMirror.defineMode("clike", function(config, parserConfig) {
19.5 - var indentUnit = config.indentUnit,
19.6 - statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
19.7 - keywords = parserConfig.keywords || {},
19.8 - builtin = parserConfig.builtin || {},
19.9 - blockKeywords = parserConfig.blockKeywords || {},
19.10 - atoms = parserConfig.atoms || {},
19.11 - hooks = parserConfig.hooks || {},
19.12 - multiLineStrings = parserConfig.multiLineStrings;
19.13 - var isOperatorChar = /[+\-*&%=<>!?|\/]/;
19.14 -
19.15 - var curPunc;
19.16 -
19.17 - function tokenBase(stream, state) {
19.18 - var ch = stream.next();
19.19 - if (hooks[ch]) {
19.20 - var result = hooks[ch](stream, state);
19.21 - if (result !== false) return result;
19.22 - }
19.23 - if (ch == '"' || ch == "'") {
19.24 - state.tokenize = tokenString(ch);
19.25 - return state.tokenize(stream, state);
19.26 - }
19.27 - if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
19.28 - curPunc = ch;
19.29 - return null;
19.30 - }
19.31 - if (/\d/.test(ch)) {
19.32 - stream.eatWhile(/[\w\.]/);
19.33 - return "number";
19.34 - }
19.35 - if (ch == "/") {
19.36 - if (stream.eat("*")) {
19.37 - state.tokenize = tokenComment;
19.38 - return tokenComment(stream, state);
19.39 - }
19.40 - if (stream.eat("/")) {
19.41 - stream.skipToEnd();
19.42 - return "comment";
19.43 - }
19.44 - }
19.45 - if (isOperatorChar.test(ch)) {
19.46 - stream.eatWhile(isOperatorChar);
19.47 - return "operator";
19.48 - }
19.49 - stream.eatWhile(/[\w\$_]/);
19.50 - var cur = stream.current();
19.51 - if (keywords.propertyIsEnumerable(cur)) {
19.52 - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
19.53 - return "keyword";
19.54 - }
19.55 - if (builtin.propertyIsEnumerable(cur)) {
19.56 - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
19.57 - return "builtin";
19.58 - }
19.59 - if (atoms.propertyIsEnumerable(cur)) return "atom";
19.60 - return "variable";
19.61 - }
19.62 -
19.63 - function tokenString(quote) {
19.64 - return function(stream, state) {
19.65 - var escaped = false, next, end = false;
19.66 - while ((next = stream.next()) != null) {
19.67 - if (next == quote && !escaped) {end = true; break;}
19.68 - escaped = !escaped && next == "\\";
19.69 - }
19.70 - if (end || !(escaped || multiLineStrings))
19.71 - state.tokenize = null;
19.72 - return "string";
19.73 - };
19.74 - }
19.75 -
19.76 - function tokenComment(stream, state) {
19.77 - var maybeEnd = false, ch;
19.78 - while (ch = stream.next()) {
19.79 - if (ch == "/" && maybeEnd) {
19.80 - state.tokenize = null;
19.81 - break;
19.82 - }
19.83 - maybeEnd = (ch == "*");
19.84 - }
19.85 - return "comment";
19.86 - }
19.87 -
19.88 - function Context(indented, column, type, align, prev) {
19.89 - this.indented = indented;
19.90 - this.column = column;
19.91 - this.type = type;
19.92 - this.align = align;
19.93 - this.prev = prev;
19.94 - }
19.95 - function pushContext(state, col, type) {
19.96 - var indent = state.indented;
19.97 - if (state.context && state.context.type == "statement")
19.98 - indent = state.context.indented;
19.99 - return state.context = new Context(indent, col, type, null, state.context);
19.100 - }
19.101 - function popContext(state) {
19.102 - var t = state.context.type;
19.103 - if (t == ")" || t == "]" || t == "}")
19.104 - state.indented = state.context.indented;
19.105 - return state.context = state.context.prev;
19.106 - }
19.107 -
19.108 - // Interface
19.109 -
19.110 - return {
19.111 - startState: function(basecolumn) {
19.112 - return {
19.113 - tokenize: null,
19.114 - context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
19.115 - indented: 0,
19.116 - startOfLine: true
19.117 - };
19.118 - },
19.119 -
19.120 - token: function(stream, state) {
19.121 - var ctx = state.context;
19.122 - if (stream.sol()) {
19.123 - if (ctx.align == null) ctx.align = false;
19.124 - state.indented = stream.indentation();
19.125 - state.startOfLine = true;
19.126 - }
19.127 - if (stream.eatSpace()) return null;
19.128 - curPunc = null;
19.129 - var style = (state.tokenize || tokenBase)(stream, state);
19.130 - if (style == "comment" || style == "meta") return style;
19.131 - if (ctx.align == null) ctx.align = true;
19.132 -
19.133 - if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
19.134 - else if (curPunc == "{") pushContext(state, stream.column(), "}");
19.135 - else if (curPunc == "[") pushContext(state, stream.column(), "]");
19.136 - else if (curPunc == "(") pushContext(state, stream.column(), ")");
19.137 - else if (curPunc == "}") {
19.138 - while (ctx.type == "statement") ctx = popContext(state);
19.139 - if (ctx.type == "}") ctx = popContext(state);
19.140 - while (ctx.type == "statement") ctx = popContext(state);
19.141 - }
19.142 - else if (curPunc == ctx.type) popContext(state);
19.143 - else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
19.144 - pushContext(state, stream.column(), "statement");
19.145 - state.startOfLine = false;
19.146 - return style;
19.147 - },
19.148 -
19.149 - indent: function(state, textAfter) {
19.150 - if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
19.151 - var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
19.152 - if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
19.153 - var closing = firstChar == ctx.type;
19.154 - if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
19.155 - else if (ctx.align) return ctx.column + (closing ? 0 : 1);
19.156 - else return ctx.indented + (closing ? 0 : indentUnit);
19.157 - },
19.158 -
19.159 - electricChars: "{}"
19.160 - };
19.161 -});
19.162 -
19.163 -(function() {
19.164 - function words(str) {
19.165 - var obj = {}, words = str.split(" ");
19.166 - for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
19.167 - return obj;
19.168 - }
19.169 - var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
19.170 - "double static else struct entry switch extern typedef float union for unsigned " +
19.171 - "goto while enum void const signed volatile";
19.172 -
19.173 - function cppHook(stream, state) {
19.174 - if (!state.startOfLine) return false;
19.175 - for (;;) {
19.176 - if (stream.skipTo("\\")) {
19.177 - stream.next();
19.178 - if (stream.eol()) {
19.179 - state.tokenize = cppHook;
19.180 - break;
19.181 - }
19.182 - } else {
19.183 - stream.skipToEnd();
19.184 - state.tokenize = null;
19.185 - break;
19.186 - }
19.187 - }
19.188 - return "meta";
19.189 - }
19.190 -
19.191 - // C#-style strings where "" escapes a quote.
19.192 - function tokenAtString(stream, state) {
19.193 - var next;
19.194 - while ((next = stream.next()) != null) {
19.195 - if (next == '"' && !stream.eat('"')) {
19.196 - state.tokenize = null;
19.197 - break;
19.198 - }
19.199 - }
19.200 - return "string";
19.201 - }
19.202 -
19.203 - function mimes(ms, mode) {
19.204 - for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
19.205 - }
19.206 -
19.207 - mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
19.208 - name: "clike",
19.209 - keywords: words(cKeywords),
19.210 - blockKeywords: words("case do else for if switch while struct"),
19.211 - atoms: words("null"),
19.212 - hooks: {"#": cppHook}
19.213 - });
19.214 - mimes(["text/x-c++src", "text/x-c++hdr"], {
19.215 - name: "clike",
19.216 - keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
19.217 - "static_cast typeid catch operator template typename class friend private " +
19.218 - "this using const_cast inline public throw virtual delete mutable protected " +
19.219 - "wchar_t"),
19.220 - blockKeywords: words("catch class do else finally for if struct switch try while"),
19.221 - atoms: words("true false null"),
19.222 - hooks: {"#": cppHook}
19.223 - });
19.224 - CodeMirror.defineMIME("text/x-java", {
19.225 - name: "clike",
19.226 - keywords: words("abstract assert boolean break byte case catch char class const continue default " +
19.227 - "do double else enum extends final finally float for goto if implements import " +
19.228 - "instanceof int interface long native new package private protected public " +
19.229 - "return short static strictfp super switch synchronized this throw throws transient " +
19.230 - "try void volatile while"),
19.231 - blockKeywords: words("catch class do else finally for if switch try while"),
19.232 - atoms: words("true false null"),
19.233 - hooks: {
19.234 - "@": function(stream) {
19.235 - stream.eatWhile(/[\w\$_]/);
19.236 - return "meta";
19.237 - }
19.238 - }
19.239 - });
19.240 - CodeMirror.defineMIME("text/x-csharp", {
19.241 - name: "clike",
19.242 - keywords: words("abstract as base break case catch checked class const continue" +
19.243 - " default delegate do else enum event explicit extern finally fixed for" +
19.244 - " foreach goto if implicit in interface internal is lock namespace new" +
19.245 - " operator out override params private protected public readonly ref return sealed" +
19.246 - " sizeof stackalloc static struct switch this throw try typeof unchecked" +
19.247 - " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
19.248 - " global group into join let orderby partial remove select set value var yield"),
19.249 - blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
19.250 - builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
19.251 - " Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
19.252 - " UInt64 bool byte char decimal double short int long object" +
19.253 - " sbyte float string ushort uint ulong"),
19.254 - atoms: words("true false null"),
19.255 - hooks: {
19.256 - "@": function(stream, state) {
19.257 - if (stream.eat('"')) {
19.258 - state.tokenize = tokenAtString;
19.259 - return tokenAtString(stream, state);
19.260 - }
19.261 - stream.eatWhile(/[\w\$_]/);
19.262 - return "meta";
19.263 - }
19.264 - }
19.265 - });
19.266 - CodeMirror.defineMIME("text/x-scala", {
19.267 - name: "clike",
19.268 - keywords: words(
19.269 -
19.270 - /* scala */
19.271 - "abstract case catch class def do else extends false final finally for forSome if " +
19.272 - "implicit import lazy match new null object override package private protected return " +
19.273 - "sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
19.274 - "<% >: # @ " +
19.275 -
19.276 - /* package scala */
19.277 - "assert assume require print println printf readLine readBoolean readByte readShort " +
19.278 - "readChar readInt readLong readFloat readDouble " +
19.279 -
19.280 - "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
19.281 - "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
19.282 - "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
19.283 - "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
19.284 - "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
19.285 -
19.286 - /* package java.lang */
19.287 - "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
19.288 - "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
19.289 - "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
19.290 - "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
19.291 -
19.292 -
19.293 - ),
19.294 - blockKeywords: words("catch class do else finally for forSome if match switch try while"),
19.295 - atoms: words("true false null"),
19.296 - hooks: {
19.297 - "@": function(stream) {
19.298 - stream.eatWhile(/[\w\$_]/);
19.299 - return "meta";
19.300 - }
19.301 - }
19.302 - });
19.303 -}());
20.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/mode/xml.js Tue Feb 11 10:48:24 2014 +0100
20.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
20.3 @@ -1,324 +0,0 @@
20.4 -CodeMirror.defineMode("xml", function(config, parserConfig) {
20.5 - var indentUnit = config.indentUnit;
20.6 - var Kludges = parserConfig.htmlMode ? {
20.7 - autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
20.8 - 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
20.9 - 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
20.10 - 'track': true, 'wbr': true},
20.11 - implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
20.12 - 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
20.13 - 'th': true, 'tr': true},
20.14 - contextGrabbers: {
20.15 - 'dd': {'dd': true, 'dt': true},
20.16 - 'dt': {'dd': true, 'dt': true},
20.17 - 'li': {'li': true},
20.18 - 'option': {'option': true, 'optgroup': true},
20.19 - 'optgroup': {'optgroup': true},
20.20 - 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
20.21 - 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
20.22 - 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
20.23 - 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
20.24 - 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
20.25 - 'rp': {'rp': true, 'rt': true},
20.26 - 'rt': {'rp': true, 'rt': true},
20.27 - 'tbody': {'tbody': true, 'tfoot': true},
20.28 - 'td': {'td': true, 'th': true},
20.29 - 'tfoot': {'tbody': true},
20.30 - 'th': {'td': true, 'th': true},
20.31 - 'thead': {'tbody': true, 'tfoot': true},
20.32 - 'tr': {'tr': true}
20.33 - },
20.34 - doNotIndent: {"pre": true},
20.35 - allowUnquoted: true,
20.36 - allowMissing: true
20.37 - } : {
20.38 - autoSelfClosers: {},
20.39 - implicitlyClosed: {},
20.40 - contextGrabbers: {},
20.41 - doNotIndent: {},
20.42 - allowUnquoted: false,
20.43 - allowMissing: false
20.44 - };
20.45 - var alignCDATA = parserConfig.alignCDATA;
20.46 -
20.47 - // Return variables for tokenizers
20.48 - var tagName, type;
20.49 -
20.50 - function inText(stream, state) {
20.51 - function chain(parser) {
20.52 - state.tokenize = parser;
20.53 - return parser(stream, state);
20.54 - }
20.55 -
20.56 - var ch = stream.next();
20.57 - if (ch == "<") {
20.58 - if (stream.eat("!")) {
20.59 - if (stream.eat("[")) {
20.60 - if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
20.61 - else return null;
20.62 - }
20.63 - else if (stream.match("--")) return chain(inBlock("comment", "-->"));
20.64 - else if (stream.match("DOCTYPE", true, true)) {
20.65 - stream.eatWhile(/[\w\._\-]/);
20.66 - return chain(doctype(1));
20.67 - }
20.68 - else return null;
20.69 - }
20.70 - else if (stream.eat("?")) {
20.71 - stream.eatWhile(/[\w\._\-]/);
20.72 - state.tokenize = inBlock("meta", "?>");
20.73 - return "meta";
20.74 - }
20.75 - else {
20.76 - var isClose = stream.eat("/");
20.77 - tagName = "";
20.78 - var c;
20.79 - while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
20.80 - if (!tagName) return "error";
20.81 - type = isClose ? "closeTag" : "openTag";
20.82 - state.tokenize = inTag;
20.83 - return "tag";
20.84 - }
20.85 - }
20.86 - else if (ch == "&") {
20.87 - var ok;
20.88 - if (stream.eat("#")) {
20.89 - if (stream.eat("x")) {
20.90 - ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
20.91 - } else {
20.92 - ok = stream.eatWhile(/[\d]/) && stream.eat(";");
20.93 - }
20.94 - } else {
20.95 - ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
20.96 - }
20.97 - return ok ? "atom" : "error";
20.98 - }
20.99 - else {
20.100 - stream.eatWhile(/[^&<]/);
20.101 - return null;
20.102 - }
20.103 - }
20.104 -
20.105 - function inTag(stream, state) {
20.106 - var ch = stream.next();
20.107 - if (ch == ">" || (ch == "/" && stream.eat(">"))) {
20.108 - state.tokenize = inText;
20.109 - type = ch == ">" ? "endTag" : "selfcloseTag";
20.110 - return "tag";
20.111 - }
20.112 - else if (ch == "=") {
20.113 - type = "equals";
20.114 - return null;
20.115 - }
20.116 - else if (/[\'\"]/.test(ch)) {
20.117 - state.tokenize = inAttribute(ch);
20.118 - return state.tokenize(stream, state);
20.119 - }
20.120 - else {
20.121 - stream.eatWhile(/[^\s\u00a0=<>\"\']/);
20.122 - return "word";
20.123 - }
20.124 - }
20.125 -
20.126 - function inAttribute(quote) {
20.127 - return function(stream, state) {
20.128 - while (!stream.eol()) {
20.129 - if (stream.next() == quote) {
20.130 - state.tokenize = inTag;
20.131 - break;
20.132 - }
20.133 - }
20.134 - return "string";
20.135 - };
20.136 - }
20.137 -
20.138 - function inBlock(style, terminator) {
20.139 - return function(stream, state) {
20.140 - while (!stream.eol()) {
20.141 - if (stream.match(terminator)) {
20.142 - state.tokenize = inText;
20.143 - break;
20.144 - }
20.145 - stream.next();
20.146 - }
20.147 - return style;
20.148 - };
20.149 - }
20.150 - function doctype(depth) {
20.151 - return function(stream, state) {
20.152 - var ch;
20.153 - while ((ch = stream.next()) != null) {
20.154 - if (ch == "<") {
20.155 - state.tokenize = doctype(depth + 1);
20.156 - return state.tokenize(stream, state);
20.157 - } else if (ch == ">") {
20.158 - if (depth == 1) {
20.159 - state.tokenize = inText;
20.160 - break;
20.161 - } else {
20.162 - state.tokenize = doctype(depth - 1);
20.163 - return state.tokenize(stream, state);
20.164 - }
20.165 - }
20.166 - }
20.167 - return "meta";
20.168 - };
20.169 - }
20.170 -
20.171 - var curState, setStyle;
20.172 - function pass() {
20.173 - for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
20.174 - }
20.175 - function cont() {
20.176 - pass.apply(null, arguments);
20.177 - return true;
20.178 - }
20.179 -
20.180 - function pushContext(tagName, startOfLine) {
20.181 - var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
20.182 - curState.context = {
20.183 - prev: curState.context,
20.184 - tagName: tagName,
20.185 - indent: curState.indented,
20.186 - startOfLine: startOfLine,
20.187 - noIndent: noIndent
20.188 - };
20.189 - }
20.190 - function popContext() {
20.191 - if (curState.context) curState.context = curState.context.prev;
20.192 - }
20.193 -
20.194 - function element(type) {
20.195 - if (type == "openTag") {
20.196 - curState.tagName = tagName;
20.197 - return cont(attributes, endtag(curState.startOfLine));
20.198 - } else if (type == "closeTag") {
20.199 - var err = false;
20.200 - if (curState.context) {
20.201 - if (curState.context.tagName != tagName) {
20.202 - if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
20.203 - popContext();
20.204 - }
20.205 - err = !curState.context || curState.context.tagName != tagName;
20.206 - }
20.207 - } else {
20.208 - err = true;
20.209 - }
20.210 - if (err) setStyle = "error";
20.211 - return cont(endclosetag(err));
20.212 - }
20.213 - return cont();
20.214 - }
20.215 - function endtag(startOfLine) {
20.216 - return function(type) {
20.217 - var tagName = curState.tagName;
20.218 - curState.tagName = null;
20.219 - if (type == "selfcloseTag" ||
20.220 - (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
20.221 - maybePopContext(tagName.toLowerCase());
20.222 - return cont();
20.223 - }
20.224 - if (type == "endTag") {
20.225 - maybePopContext(tagName.toLowerCase());
20.226 - pushContext(tagName, startOfLine);
20.227 - return cont();
20.228 - }
20.229 - return cont();
20.230 - };
20.231 - }
20.232 - function endclosetag(err) {
20.233 - return function(type) {
20.234 - if (err) setStyle = "error";
20.235 - if (type == "endTag") { popContext(); return cont(); }
20.236 - setStyle = "error";
20.237 - return cont(arguments.callee);
20.238 - };
20.239 - }
20.240 - function maybePopContext(nextTagName) {
20.241 - var parentTagName;
20.242 - while (true) {
20.243 - if (!curState.context) {
20.244 - return;
20.245 - }
20.246 - parentTagName = curState.context.tagName.toLowerCase();
20.247 - if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
20.248 - !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
20.249 - return;
20.250 - }
20.251 - popContext();
20.252 - }
20.253 - }
20.254 -
20.255 - function attributes(type) {
20.256 - if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
20.257 - if (type == "endTag" || type == "selfcloseTag") return pass();
20.258 - setStyle = "error";
20.259 - return cont(attributes);
20.260 - }
20.261 - function attribute(type) {
20.262 - if (type == "equals") return cont(attvalue, attributes);
20.263 - if (!Kludges.allowMissing) setStyle = "error";
20.264 - else if (type == "word") setStyle = "attribute";
20.265 - return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
20.266 - }
20.267 - function attvalue(type) {
20.268 - if (type == "string") return cont(attvaluemaybe);
20.269 - if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
20.270 - setStyle = "error";
20.271 - return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
20.272 - }
20.273 - function attvaluemaybe(type) {
20.274 - if (type == "string") return cont(attvaluemaybe);
20.275 - else return pass();
20.276 - }
20.277 -
20.278 - return {
20.279 - startState: function() {
20.280 - return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
20.281 - },
20.282 -
20.283 - token: function(stream, state) {
20.284 - if (stream.sol()) {
20.285 - state.startOfLine = true;
20.286 - state.indented = stream.indentation();
20.287 - }
20.288 - if (stream.eatSpace()) return null;
20.289 -
20.290 - setStyle = type = tagName = null;
20.291 - var style = state.tokenize(stream, state);
20.292 - state.type = type;
20.293 - if ((style || type) && style != "comment") {
20.294 - curState = state;
20.295 - while (true) {
20.296 - var comb = state.cc.pop() || element;
20.297 - if (comb(type || style)) break;
20.298 - }
20.299 - }
20.300 - state.startOfLine = false;
20.301 - return setStyle || style;
20.302 - },
20.303 -
20.304 - indent: function(state, textAfter, fullLine) {
20.305 - var context = state.context;
20.306 - if ((state.tokenize != inTag && state.tokenize != inText) ||
20.307 - context && context.noIndent)
20.308 - return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
20.309 - if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
20.310 - if (context && /^<\//.test(textAfter))
20.311 - context = context.prev;
20.312 - while (context && !context.startOfLine)
20.313 - context = context.prev;
20.314 - if (context) return context.indent + indentUnit;
20.315 - else return 0;
20.316 - },
20.317 -
20.318 - electricChars: "/",
20.319 -
20.320 - configuration: parserConfig.htmlMode ? "html" : "xml"
20.321 - };
20.322 -});
20.323 -
20.324 -CodeMirror.defineMIME("text/xml", "xml");
20.325 -CodeMirror.defineMIME("application/xml", "xml");
20.326 -if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
20.327 - CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
21.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/theme/elegant.css Tue Feb 11 10:48:24 2014 +0100
21.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
21.3 @@ -1,10 +0,0 @@
21.4 -.cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
21.5 -.cm-s-elegant span.cm-comment {color: #262; font-style: italic; line-height: 1em;}
21.6 -.cm-s-elegant span.cm-meta {color: #555; font-style: italic; line-height: 1em;}
21.7 -.cm-s-elegant span.cm-variable {color: black;}
21.8 -.cm-s-elegant span.cm-variable-2 {color: #b11;}
21.9 -.cm-s-elegant span.cm-qualifier {color: #555;}
21.10 -.cm-s-elegant span.cm-keyword {color: #730;}
21.11 -.cm-s-elegant span.cm-builtin {color: #30a;}
21.12 -.cm-s-elegant span.cm-error {background-color: #fdd;}
21.13 -.cm-s-elegant span.cm-link {color: #762;}
22.1 --- a/dew/src/test/java/org/apidesign/bck2brwsr/dew/CompileTest.java Tue Feb 11 10:48:24 2014 +0100
22.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
22.3 @@ -1,45 +0,0 @@
22.4 -/**
22.5 - * Back 2 Browser Bytecode Translator
22.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
22.7 - *
22.8 - * This program is free software: you can redistribute it and/or modify
22.9 - * it under the terms of the GNU General Public License as published by
22.10 - * the Free Software Foundation, version 2 of the License.
22.11 - *
22.12 - * This program is distributed in the hope that it will be useful,
22.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
22.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22.15 - * GNU General Public License for more details.
22.16 - *
22.17 - * You should have received a copy of the GNU General Public License
22.18 - * along with this program. Look for COPYING file in the top folder.
22.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
22.20 - */
22.21 -package org.apidesign.bck2brwsr.dew;
22.22 -
22.23 -import java.io.IOException;
22.24 -import static org.testng.Assert.*;
22.25 -import org.testng.annotations.Test;
22.26 -
22.27 -/**
22.28 - *
22.29 - * @author Jaroslav Tulach <jtulach@netbeans.org>
22.30 - */
22.31 -public class CompileTest {
22.32 - @Test public void testCompile() throws IOException {
22.33 - String html = "<html><body>"
22.34 - + " <button id='btn'>Hello!</button>"
22.35 - + "</body></html>";
22.36 - String java = "package x.y.z;"
22.37 - + "import org.apidesign.bck2brwsr.htmlpage.api.*;"
22.38 - + "import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;"
22.39 - + "@Page(xhtml=\"index.html\", className=\"Index\")"
22.40 - + "class X { "
22.41 - + " @On(event=CLICK, id=\"btn\") static void clcs() {}"
22.42 - + "}";
22.43 - Compile result = Compile.create(html, java);
22.44 -
22.45 - assertNotNull(result.get("x/y/z/X.class"), "Class X is compiled: " + result);
22.46 - assertNotNull(result.get("x/y/z/Index.class"), "Class Index is compiled: " + result);
22.47 - }
22.48 -}
23.1 --- a/ide/editor/pom.xml Tue Feb 11 10:48:24 2014 +0100
23.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
23.3 @@ -1,185 +0,0 @@
23.4 -<?xml version="1.0" encoding="UTF-8"?>
23.5 -<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">
23.6 - <modelVersion>4.0.0</modelVersion>
23.7 - <parent>
23.8 - <artifactId>ide</artifactId>
23.9 - <groupId>org.apidesign.bck2brwsr</groupId>
23.10 - <version>0.9-SNAPSHOT</version>
23.11 - </parent>
23.12 -
23.13 - <groupId>org.apidesign.bck2brwsr.ide</groupId>
23.14 - <artifactId>editor</artifactId>
23.15 - <version>0.9-SNAPSHOT</version>
23.16 - <packaging>nbm</packaging>
23.17 -
23.18 - <name>Editor Support for Bck2Brwsr</name>
23.19 -
23.20 - <properties>
23.21 - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23.22 - <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
23.23 - </properties>
23.24 -
23.25 - <repositories>
23.26 - <!--
23.27 - Repository hosting NetBeans modules, especially APIs.
23.28 - Versions are based on IDE releases, e.g.: RELEASE691
23.29 - To create your own repository, use: nbm:populate-repository
23.30 - -->
23.31 - <repository>
23.32 - <id>netbeans</id>
23.33 - <name>NetBeans</name>
23.34 - <url>http://bits.netbeans.org/maven2/</url>
23.35 - <snapshots>
23.36 - <enabled>false</enabled>
23.37 - </snapshots>
23.38 - </repository>
23.39 - </repositories>
23.40 -
23.41 - <dependencies>
23.42 - <dependency>
23.43 - <groupId>org.netbeans.api</groupId>
23.44 - <artifactId>org-netbeans-api-annotations-common</artifactId>
23.45 - </dependency>
23.46 - <dependency>
23.47 - <groupId>org.netbeans.api</groupId>
23.48 - <artifactId>org-netbeans-modules-java-source</artifactId>
23.49 - </dependency>
23.50 - <dependency>
23.51 - <groupId>org.netbeans.api</groupId>
23.52 - <artifactId>org-netbeans-libs-javacapi</artifactId>
23.53 - </dependency>
23.54 - <dependency>
23.55 - <groupId>org.netbeans.api</groupId>
23.56 - <artifactId>org-netbeans-spi-java-hints</artifactId>
23.57 - </dependency>
23.58 - <dependency>
23.59 - <groupId>org.netbeans.api</groupId>
23.60 - <artifactId>org-netbeans-modules-parsing-api</artifactId>
23.61 - </dependency>
23.62 - <dependency>
23.63 - <groupId>org.netbeans.api</groupId>
23.64 - <artifactId>org-netbeans-spi-editor-hints</artifactId>
23.65 - </dependency>
23.66 - <dependency>
23.67 - <groupId>org.netbeans.api</groupId>
23.68 - <artifactId>org-openide-util</artifactId>
23.69 - </dependency>
23.70 - <dependency>
23.71 - <groupId>org.netbeans.api</groupId>
23.72 - <artifactId>org-netbeans-modules-java-lexer</artifactId>
23.73 - </dependency>
23.74 - <dependency>
23.75 - <groupId>org.netbeans.api</groupId>
23.76 - <artifactId>org-netbeans-modules-lexer</artifactId>
23.77 - </dependency>
23.78 - <dependency>
23.79 - <groupId>org.apidesign.bck2brwsr</groupId>
23.80 - <artifactId>core</artifactId>
23.81 - <version>0.9-SNAPSHOT</version>
23.82 - <type>jar</type>
23.83 - <scope>test</scope>
23.84 - </dependency>
23.85 - <dependency>
23.86 - <groupId>org.netbeans.api</groupId>
23.87 - <artifactId>org-netbeans-modules-java-hints-test</artifactId>
23.88 - <scope>test</scope>
23.89 - </dependency>
23.90 - <dependency>
23.91 - <groupId>org.netbeans.api</groupId>
23.92 - <artifactId>org-netbeans-libs-junit4</artifactId>
23.93 - <scope>test</scope>
23.94 - </dependency>
23.95 - <dependency>
23.96 - <groupId>org.netbeans.modules</groupId>
23.97 - <artifactId>org-netbeans-lib-nbjavac</artifactId>
23.98 - <scope>test</scope>
23.99 - </dependency>
23.100 - <dependency>
23.101 - <groupId>org.testng</groupId>
23.102 - <artifactId>testng</artifactId>
23.103 - <scope>test</scope>
23.104 - </dependency>
23.105 - </dependencies>
23.106 -
23.107 - <build>
23.108 - <plugins>
23.109 - <plugin>
23.110 - <groupId>org.codehaus.mojo</groupId>
23.111 - <artifactId>nbm-maven-plugin</artifactId>
23.112 - <version>3.8</version>
23.113 - <extensions>true</extensions>
23.114 - </plugin>
23.115 -
23.116 - <plugin>
23.117 - <!-- NetBeans 6.9+ requires JDK 6 -->
23.118 - <groupId>org.apache.maven.plugins</groupId>
23.119 - <artifactId>maven-compiler-plugin</artifactId>
23.120 - <version>2.5.1</version>
23.121 - <configuration>
23.122 - <source>1.6</source>
23.123 - <target>1.6</target>
23.124 - <compilerArguments>
23.125 - <endorseddirs>${endorsed.dir}</endorseddirs>
23.126 - </compilerArguments>
23.127 - </configuration>
23.128 - </plugin>
23.129 -
23.130 - <plugin>
23.131 - <groupId>org.apache.maven.plugins</groupId>
23.132 - <artifactId>maven-jar-plugin</artifactId>
23.133 - <version>2.4</version>
23.134 - <configuration>
23.135 - <!-- to have the jar plugin pickup the nbm generated manifest -->
23.136 - <useDefaultManifestFile>true</useDefaultManifestFile>
23.137 - </configuration>
23.138 - </plugin>
23.139 -
23.140 - <plugin>
23.141 - <groupId>org.apache.maven.plugins</groupId>
23.142 - <artifactId>maven-dependency-plugin</artifactId>
23.143 - <executions>
23.144 - <execution>
23.145 - <id>endorsed</id>
23.146 - <phase>validate</phase>
23.147 - <goals>
23.148 - <goal>copy</goal>
23.149 - </goals>
23.150 - </execution>
23.151 - </executions>
23.152 - <configuration>
23.153 - <outputDirectory>${endorsed.dir}</outputDirectory>
23.154 - <silent>true</silent>
23.155 - <artifactItems>
23.156 - <artifactItem>
23.157 - <groupId>org.netbeans.api</groupId>
23.158 - <artifactId>org-netbeans-libs-javacapi</artifactId>
23.159 - <version>${netbeans.version}</version>
23.160 - </artifactItem>
23.161 - <artifactItem>
23.162 - <groupId>org.netbeans.external</groupId>
23.163 - <artifactId>nb-javac-api</artifactId>
23.164 - <version>${netbeans.version}</version>
23.165 - </artifactItem>
23.166 - <artifactItem>
23.167 - <groupId>org.netbeans.modules</groupId>
23.168 - <artifactId>org-netbeans-libs-javacimpl</artifactId>
23.169 - <version>${netbeans.version}</version>
23.170 - </artifactItem>
23.171 - <artifactItem>
23.172 - <groupId>org.netbeans.external</groupId>
23.173 - <artifactId>nb-javac-impl</artifactId>
23.174 - <version>${netbeans.version}</version>
23.175 - </artifactItem>
23.176 - </artifactItems>
23.177 - </configuration>
23.178 - </plugin>
23.179 - <plugin>
23.180 - <groupId>org.apache.maven.plugins</groupId>
23.181 - <artifactId>maven-surefire-plugin</artifactId>
23.182 - <configuration>
23.183 - <argLine>-Djava.endorsed.dirs=${endorsed.dir}</argLine>
23.184 - </configuration>
23.185 - </plugin>
23.186 - </plugins>
23.187 - </build>
23.188 -</project>
24.1 --- a/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JNIHelper.java Tue Feb 11 10:48:24 2014 +0100
24.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
24.3 @@ -1,80 +0,0 @@
24.4 -/**
24.5 - * Back 2 Browser Bytecode Translator
24.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
24.7 - *
24.8 - * This program is free software: you can redistribute it and/or modify
24.9 - * it under the terms of the GNU General Public License as published by
24.10 - * the Free Software Foundation, version 2 of the License.
24.11 - *
24.12 - * This program is distributed in the hope that it will be useful,
24.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
24.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24.15 - * GNU General Public License for more details.
24.16 - *
24.17 - * You should have received a copy of the GNU General Public License
24.18 - * along with this program. Look for COPYING file in the top folder.
24.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
24.20 - */
24.21 -package org.apidesign.bck2brwsr.ide.editor;
24.22 -
24.23 -import java.lang.reflect.Method;
24.24 -import java.util.HashMap;
24.25 -import java.util.Map;
24.26 -
24.27 -/**
24.28 - * JNI Helper.
24.29 - * To facilitate lookup of methods by name and signature, instead of manually parsing signatures,
24.30 - * constructs the map of all methods and uses Class.getName() to generate almost-correct signatures.
24.31 - */
24.32 -class JNIHelper {
24.33 -
24.34 - static Method method(String clazz, String method, String signature) {
24.35 - final Map<String, Method> methods = methodMap(JNIHelper.clazz(clazz));
24.36 - return methods.get(methodKey(method, signature));
24.37 - }
24.38 -
24.39 - static Class<?> clazz(String clazz) {
24.40 - try {
24.41 - return Class.forName(clazz);
24.42 - } catch (ClassNotFoundException e) {
24.43 - throw new IllegalArgumentException(e);
24.44 - }
24.45 - }
24.46 -
24.47 - static Map<String, Method> methodMap(final Class<?> clazz) {
24.48 - final Map<String, Method> map = new HashMap<String, Method>();
24.49 - final Method[] methods = clazz.getDeclaredMethods();
24.50 - for (int i = 0; i < methods.length; i++) {
24.51 - final Method method = methods[i];
24.52 - map.put(methodKey(method.getName(), signature(method)), method);
24.53 - }
24.54 - return map;
24.55 - }
24.56 -
24.57 - static String methodKey(String method, String signature) {
24.58 - return method + '@' + signature;
24.59 - }
24.60 -
24.61 - static String signature(final Method method) {
24.62 - final Class<?>[] parameterTypes = method.getParameterTypes();
24.63 - final StringBuilder b = new StringBuilder();
24.64 - for (int j = 0; j < parameterTypes.length; j++) {
24.65 - b.append(signature(parameterTypes[j]));
24.66 - }
24.67 - return b.toString();
24.68 - }
24.69 -
24.70 - static String signature(final Class<?> clazz) {
24.71 - if (clazz == boolean.class) return "Z";
24.72 - else if (clazz == byte.class) return "B";
24.73 - else if (clazz == char.class) return "C";
24.74 - else if (clazz == double.class) return "D";
24.75 - else if (clazz == float.class) return "F";
24.76 - else if (clazz == int.class) return "I";
24.77 - else if (clazz == long.class) return "J";
24.78 - else if (clazz == short.class) return "S";
24.79 - else if (clazz == void.class) return "V";
24.80 - else if (clazz.isArray()) return clazz.getName().replace('.','/');
24.81 - else return "L" + clazz.getName().replace('.','/') + ";";
24.82 - }
24.83 -}
25.1 --- a/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JSEmbeddingProvider.java Tue Feb 11 10:48:24 2014 +0100
25.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
25.3 @@ -1,188 +0,0 @@
25.4 -/**
25.5 - * Back 2 Browser Bytecode Translator
25.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
25.7 - *
25.8 - * This program is free software: you can redistribute it and/or modify
25.9 - * it under the terms of the GNU General Public License as published by
25.10 - * the Free Software Foundation, version 2 of the License.
25.11 - *
25.12 - * This program is distributed in the hope that it will be useful,
25.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
25.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25.15 - * GNU General Public License for more details.
25.16 - *
25.17 - * You should have received a copy of the GNU General Public License
25.18 - * along with this program. Look for COPYING file in the top folder.
25.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
25.20 - */
25.21 -package org.apidesign.bck2brwsr.ide.editor;
25.22 -
25.23 -import com.sun.source.tree.AnnotationTree;
25.24 -import com.sun.source.tree.AssignmentTree;
25.25 -import com.sun.source.tree.CompilationUnitTree;
25.26 -import com.sun.source.tree.ExpressionTree;
25.27 -import com.sun.source.tree.LiteralTree;
25.28 -import com.sun.source.tree.MethodTree;
25.29 -import com.sun.source.util.SourcePositions;
25.30 -import com.sun.source.util.TreePath;
25.31 -import com.sun.source.util.TreePathScanner;
25.32 -import com.sun.source.util.Trees;
25.33 -import java.io.IOException;
25.34 -import java.util.ArrayList;
25.35 -import java.util.Collection;
25.36 -import java.util.Collections;
25.37 -import java.util.List;
25.38 -import java.util.concurrent.atomic.AtomicBoolean;
25.39 -import javax.lang.model.element.TypeElement;
25.40 -import javax.swing.text.Document;
25.41 -import org.netbeans.api.editor.mimelookup.MimeRegistration;
25.42 -import org.netbeans.api.java.source.CompilationInfo;
25.43 -import org.netbeans.api.java.source.JavaParserResultTask;
25.44 -import org.netbeans.api.java.source.JavaSource;
25.45 -import org.netbeans.api.lexer.Language;
25.46 -import org.netbeans.api.lexer.TokenHierarchy;
25.47 -import org.netbeans.api.lexer.TokenSequence;
25.48 -import org.netbeans.modules.parsing.api.Snapshot;
25.49 -import org.netbeans.modules.parsing.spi.Parser;
25.50 -import org.netbeans.modules.parsing.spi.Scheduler;
25.51 -import org.netbeans.modules.parsing.spi.SchedulerEvent;
25.52 -import org.netbeans.modules.parsing.spi.SchedulerTask;
25.53 -import org.netbeans.modules.parsing.spi.TaskFactory;
25.54 -import org.openide.util.Exceptions;
25.55 -
25.56 -/**
25.57 - *
25.58 - * @author Tomas Zezula
25.59 - */
25.60 -public final class JSEmbeddingProvider extends JavaParserResultTask<Parser.Result> {
25.61 -
25.62 - private static final int PRIORITY = 1000;
25.63 - private static final String JS_ANNOTATION = "org.apidesign.bck2brwsr.core.JavaScriptBody"; //NOI18N
25.64 - private static final String BODY = "body"; //NOI18N
25.65 - private static final String JAVA_MIME_TYPE = "text/x-java"; //NOI18N
25.66 - private static final String JAVASCRIPT_MIME_TYPE = "text/javascript"; //NOI18N
25.67 - private final AtomicBoolean canceled = new AtomicBoolean();
25.68 -
25.69 - private JSEmbeddingProvider() {
25.70 - super(JavaSource.Phase.ELEMENTS_RESOLVED);
25.71 - }
25.72 -
25.73 - @Override
25.74 - public int getPriority() {
25.75 - return PRIORITY;
25.76 - }
25.77 -
25.78 - @Override
25.79 - public void cancel() {
25.80 - canceled.set(true);
25.81 - }
25.82 -
25.83 - @Override
25.84 - public Class<? extends Scheduler> getSchedulerClass() {
25.85 - return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER;
25.86 - }
25.87 -
25.88 - @Override
25.89 - public void run(Parser.Result t, SchedulerEvent se) {
25.90 - canceled.set(false);
25.91 - final CompilationInfo ci = CompilationInfo.get(t);
25.92 - final CompilationUnitTree cu = ci.getCompilationUnit();
25.93 - final Trees trees = ci.getTrees();
25.94 - final SourcePositions sp = trees.getSourcePositions();
25.95 - final Finder f = new Finder(trees);
25.96 - final List<LiteralTree> result = new ArrayList<LiteralTree>();
25.97 - f.scan(cu, result);
25.98 - if (!result.isEmpty()) {
25.99 - try {
25.100 - final TokenHierarchy<Document> tk = TokenHierarchy.get(ci.getDocument());
25.101 - final Language<?> java = Language.find(JAVA_MIME_TYPE);
25.102 - final Language<?> javaScript = Language.find(JAVASCRIPT_MIME_TYPE);
25.103 - if (java != null && javaScript != null) {
25.104 - final TokenSequence<?> seq = tk.tokenSequence(java);
25.105 - if (seq != null) {
25.106 - for (LiteralTree lt : result) {
25.107 - final int start = (int) sp.getStartPosition(cu, lt);
25.108 - final int end = (int) sp.getEndPosition(cu, lt);
25.109 - seq.move(start);
25.110 - while (seq.moveNext() && seq.offset() < end) {
25.111 - seq.createEmbedding(javaScript, 1, 1, true);
25.112 - }
25.113 - }
25.114 - }
25.115 - }
25.116 - } catch (IOException ioe) {
25.117 - Exceptions.printStackTrace(ioe);
25.118 - }
25.119 - }
25.120 - }
25.121 -
25.122 -
25.123 -
25.124 -
25.125 - private static final class Finder extends TreePathScanner<Void, List<? super LiteralTree>> {
25.126 -
25.127 - private final Trees trees;
25.128 - private CompilationUnitTree cu;
25.129 - private boolean inEmbedding;
25.130 -
25.131 - Finder(final Trees trees) {
25.132 - this.trees = trees;
25.133 - }
25.134 -
25.135 - @Override
25.136 - public Void visitCompilationUnit(
25.137 - final CompilationUnitTree unit,
25.138 - final List p) {
25.139 - this.cu = unit;
25.140 - return super.visitCompilationUnit(unit, p);
25.141 - }
25.142 -
25.143 -
25.144 -
25.145 - @Override
25.146 - public Void visitMethod(
25.147 - final MethodTree m,
25.148 - final List<? super LiteralTree> p) {
25.149 - for (AnnotationTree a : m.getModifiers().getAnnotations()) {
25.150 - final TypeElement ae = (TypeElement) trees.getElement(TreePath.getPath(cu, a.getAnnotationType()));
25.151 - if (ae != null && JS_ANNOTATION.contentEquals(ae.getQualifiedName())) {
25.152 - final List<? extends ExpressionTree> args = a.getArguments();
25.153 - for (ExpressionTree kvp : args) {
25.154 - if (kvp instanceof AssignmentTree) {
25.155 - final AssignmentTree assignemt = (AssignmentTree) kvp;
25.156 - if (BODY.equals(assignemt.getVariable().toString())) {
25.157 - inEmbedding = true;
25.158 - try {
25.159 - scan(assignemt.getExpression(), p);
25.160 - } finally {
25.161 - inEmbedding = false;
25.162 - }
25.163 - }
25.164 - }
25.165 - }
25.166 - }
25.167 - }
25.168 - return null;
25.169 - }
25.170 -
25.171 - @Override
25.172 - public Void visitLiteral(LiteralTree node, List<? super LiteralTree> p) {
25.173 - if (inEmbedding) {
25.174 - p.add(node);
25.175 - }
25.176 - return super.visitLiteral(node, p);
25.177 - }
25.178 -
25.179 - }
25.180 -
25.181 - @MimeRegistration(
25.182 - service = TaskFactory.class,
25.183 - mimeType = JAVA_MIME_TYPE)
25.184 - public static final class Factory extends TaskFactory {
25.185 - @Override
25.186 - public Collection<? extends SchedulerTask> create(Snapshot snpsht) {
25.187 - return Collections.singleton(new JSEmbeddingProvider());
25.188 - }
25.189 - }
25.190 -
25.191 -}
26.1 --- a/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JSNI2JavaScriptBody.java Tue Feb 11 10:48:24 2014 +0100
26.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
26.3 @@ -1,149 +0,0 @@
26.4 -/**
26.5 - * Back 2 Browser Bytecode Translator
26.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
26.7 - *
26.8 - * This program is free software: you can redistribute it and/or modify
26.9 - * it under the terms of the GNU General Public License as published by
26.10 - * the Free Software Foundation, version 2 of the License.
26.11 - *
26.12 - * This program is distributed in the hope that it will be useful,
26.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
26.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26.15 - * GNU General Public License for more details.
26.16 - *
26.17 - * You should have received a copy of the GNU General Public License
26.18 - * along with this program. Look for COPYING file in the top folder.
26.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
26.20 - */
26.21 -package org.apidesign.bck2brwsr.ide.editor;
26.22 -
26.23 -import com.sun.source.tree.AnnotationTree;
26.24 -import com.sun.source.tree.ExpressionTree;
26.25 -import com.sun.source.tree.LiteralTree;
26.26 -import com.sun.source.tree.MethodTree;
26.27 -import com.sun.source.tree.Tree.Kind;
26.28 -import com.sun.source.tree.VariableTree;
26.29 -import com.sun.source.util.TreePath;
26.30 -import java.util.ArrayList;
26.31 -import java.util.Arrays;
26.32 -import java.util.Collections;
26.33 -import java.util.List;
26.34 -import org.netbeans.api.java.lexer.JavaTokenId;
26.35 -import static org.netbeans.api.java.lexer.JavaTokenId.BLOCK_COMMENT;
26.36 -import static org.netbeans.api.java.lexer.JavaTokenId.JAVADOC_COMMENT;
26.37 -import static org.netbeans.api.java.lexer.JavaTokenId.LINE_COMMENT;
26.38 -import static org.netbeans.api.java.lexer.JavaTokenId.WHITESPACE;
26.39 -import org.netbeans.api.java.source.CompilationInfo;
26.40 -import org.netbeans.api.java.source.TreeMaker;
26.41 -import org.netbeans.api.lexer.Token;
26.42 -import org.netbeans.api.lexer.TokenSequence;
26.43 -import org.netbeans.spi.editor.hints.ErrorDescription;
26.44 -import org.netbeans.spi.editor.hints.Fix;
26.45 -import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
26.46 -import org.netbeans.spi.java.hints.Hint;
26.47 -import org.netbeans.spi.java.hints.HintContext;
26.48 -import org.netbeans.spi.java.hints.JavaFix;
26.49 -import org.netbeans.spi.java.hints.TriggerTreeKind;
26.50 -import org.openide.util.NbBundle.Messages;
26.51 -
26.52 -@Hint(displayName = "#DN_JSNI2JavaScriptBody", description = "#DESC_JSNI2JavaScriptBody", category = "general")
26.53 -@Messages({
26.54 - "DN_JSNI2JavaScriptBody=JSNI to @JavaScriptBody",
26.55 - "DESC_JSNI2JavaScriptBody=JSNI to @JavaScriptBody"
26.56 -})
26.57 -public class JSNI2JavaScriptBody {
26.58 -
26.59 - @TriggerTreeKind(Kind.METHOD)
26.60 - @Messages("ERR_JSNI2JavaScriptBody=Can convert JSNI to @JavaScriptBody")
26.61 - public static ErrorDescription computeWarning(final HintContext ctx) {
26.62 - Token<JavaTokenId> token = findBlockToken(ctx.getInfo(), ctx.getPath(), ctx);
26.63 -
26.64 - if (token == null) {
26.65 - return null;
26.66 - }
26.67 -
26.68 - Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath()).toEditorFix();
26.69 - return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_JSNI2JavaScriptBody(), fix);
26.70 - }
26.71 -
26.72 - private static Token<JavaTokenId> findBlockToken(CompilationInfo info, TreePath path, HintContext ctx) {
26.73 - int end = (int) info.getTrees().getSourcePositions().getEndPosition(path.getCompilationUnit(), path.getLeaf());
26.74 - TokenSequence<JavaTokenId> ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language());
26.75 -
26.76 - if (ts == null) return null;
26.77 -
26.78 - ts.move(end);
26.79 -
26.80 - if ((ctx != null && ctx.isCanceled()) || !ts.movePrevious() || ts.token().id() != JavaTokenId.SEMICOLON) return null;
26.81 -
26.82 - OUTER: while (ts.movePrevious()) {
26.83 - if (ctx != null && ctx.isCanceled()) return null;
26.84 -
26.85 - switch (ts.token().id()) {
26.86 - case WHITESPACE: break;
26.87 - case LINE_COMMENT: break;
26.88 - case JAVADOC_COMMENT: break;
26.89 - case BLOCK_COMMENT:
26.90 - final CharSequence tok = ts.token().text();
26.91 - final int l = tok.length();
26.92 - if (l > 4
26.93 - && tok.subSequence(0, 4).toString().equals("/*-{") // NOI18N
26.94 - && tok.subSequence(l - 4, l).toString().equals("}-*/") // NOI18N
26.95 - ) {
26.96 - return ts.offsetToken();
26.97 - }
26.98 - break;
26.99 - default:
26.100 - break OUTER;
26.101 - }
26.102 - }
26.103 -
26.104 - return null;
26.105 - }
26.106 -
26.107 - private static final class FixImpl extends JavaFix {
26.108 -
26.109 - public FixImpl(CompilationInfo info, TreePath tp) {
26.110 - super(info, tp);
26.111 - }
26.112 -
26.113 - @Override
26.114 - @Messages("FIX_JSNI2JavaScriptBody=Convert JSNI to @JavaScriptBody")
26.115 - protected String getText() {
26.116 - return Bundle.FIX_JSNI2JavaScriptBody();
26.117 - }
26.118 -
26.119 - @Override
26.120 - protected void performRewrite(TransformationContext ctx) {
26.121 - Token<JavaTokenId> jsniComment = findBlockToken(ctx.getWorkingCopy(), ctx.getPath(), null);
26.122 -
26.123 - if (jsniComment == null) {
26.124 - //XXX: warn?
26.125 - return ;
26.126 - }
26.127 -
26.128 - JsniCommentTokenizer tok = new JsniCommentTokenizer();
26.129 - ManglingSink ms = new ManglingSink();
26.130 - final CharSequence cmnt = jsniComment.text();
26.131 - tok.process(cmnt.subSequence(4, cmnt.length() - 4), ms);
26.132 -
26.133 - TreeMaker make = ctx.getWorkingCopy().getTreeMaker();
26.134 - MethodTree mt = (MethodTree) ctx.getPath().getLeaf();
26.135 - List<LiteralTree> params = new ArrayList<LiteralTree>();
26.136 -
26.137 - for (VariableTree p : mt.getParameters()) {
26.138 - params.add(make.Literal(p.getName().toString()));
26.139 - }
26.140 -
26.141 - AnnotationTree jsBody = make.Annotation(make.QualIdent("org.apidesign.bck2brwsr.core.JavaScriptBody"),
26.142 - Arrays.<ExpressionTree>asList(
26.143 - make.Assignment(make.Identifier("args"), make.NewArray(null, Collections.<ExpressionTree>emptyList(), params)),
26.144 - make.Assignment(make.Identifier("body"), make.Literal(ms.out.toString()))
26.145 - )
26.146 - );
26.147 -
26.148 -
26.149 - ctx.getWorkingCopy().rewrite(mt.getModifiers(), make.addModifiersAnnotation(mt.getModifiers(), jsBody));
26.150 - }
26.151 - }
26.152 -}
27.1 --- a/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JsniCommentTokenizer.java Tue Feb 11 10:48:24 2014 +0100
27.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
27.3 @@ -1,70 +0,0 @@
27.4 -/**
27.5 - * Back 2 Browser Bytecode Translator
27.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
27.7 - *
27.8 - * This program is free software: you can redistribute it and/or modify
27.9 - * it under the terms of the GNU General Public License as published by
27.10 - * the Free Software Foundation, version 2 of the License.
27.11 - *
27.12 - * This program is distributed in the hope that it will be useful,
27.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
27.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27.15 - * GNU General Public License for more details.
27.16 - *
27.17 - * You should have received a copy of the GNU General Public License
27.18 - * along with this program. Look for COPYING file in the top folder.
27.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
27.20 - */
27.21 -package org.apidesign.bck2brwsr.ide.editor;
27.22 -
27.23 -import java.util.regex.Matcher;
27.24 -import java.util.regex.Pattern;
27.25 -
27.26 -final class JsniCommentTokenizer {
27.27 -
27.28 - /**
27.29 - * Tokenize the contents of JSNI comment into the provided {@linkplain Sink}.
27.30 - * @param in the contents of JSNI comment
27.31 - * @param out the sink that consumes parsed tokens
27.32 - */
27.33 - public void process(final CharSequence in, final Sink out) {
27.34 - final Matcher member = Pattern.compile("@([^:]+)::([a-zA-Z_$][a-zA-Z\\d_$]*)").matcher(in);
27.35 - final Matcher signature = Pattern.compile("\\(([^\\)]*)\\)").matcher(in);
27.36 -
27.37 - int i = 0;
27.38 - while (true) {
27.39 - if (member.find(i)) {
27.40 - final int memberStart = member.start();
27.41 - final int memberEnd = member.end();
27.42 - if (memberStart > i) out.javascript(in.subSequence(i, memberStart).toString());
27.43 -
27.44 - final String clazz = member.group(1);
27.45 - final String name = member.group(2);
27.46 -
27.47 - if (in.charAt(memberEnd) == '(') {
27.48 - if (!signature.find(memberEnd)) {
27.49 - throw new IllegalStateException("Expected method signature");
27.50 - }
27.51 - assert signature.start() == memberEnd;
27.52 - out.method(clazz, name, signature.group(1));
27.53 - i = signature.end();
27.54 - } else {
27.55 - out.field(clazz, name);
27.56 - i = memberEnd;
27.57 - }
27.58 - } else {
27.59 - out.javascript(in.subSequence(i, in.length()).toString());
27.60 - break;
27.61 - }
27.62 - }
27.63 - }
27.64 -
27.65 -
27.66 - static interface Sink {
27.67 - void javascript(String s);
27.68 -
27.69 - void method(String clazz, String method, String signature);
27.70 -
27.71 - void field(String clazz, String field);
27.72 - }
27.73 -}
28.1 --- a/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/ManglingSink.java Tue Feb 11 10:48:24 2014 +0100
28.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
28.3 @@ -1,71 +0,0 @@
28.4 -/**
28.5 - * Back 2 Browser Bytecode Translator
28.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
28.7 - *
28.8 - * This program is free software: you can redistribute it and/or modify
28.9 - * it under the terms of the GNU General Public License as published by
28.10 - * the Free Software Foundation, version 2 of the License.
28.11 - *
28.12 - * This program is distributed in the hope that it will be useful,
28.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
28.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28.15 - * GNU General Public License for more details.
28.16 - *
28.17 - * You should have received a copy of the GNU General Public License
28.18 - * along with this program. Look for COPYING file in the top folder.
28.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
28.20 - */
28.21 -package org.apidesign.bck2brwsr.ide.editor;
28.22 -
28.23 -/**
28.24 - * An implementation of {@linkplain JsniCommentTokenizer.Sink} that generates B2B
28.25 - */
28.26 -class ManglingSink implements JsniCommentTokenizer.Sink {
28.27 -
28.28 - final StringBuilder out = new StringBuilder();
28.29 -
28.30 - public void javascript(String s) {
28.31 - out.append(s);
28.32 - }
28.33 -
28.34 - public void method(String clazz, String method, String signature) {
28.35 - out.append(mangle(clazz, method, signature));
28.36 - }
28.37 -
28.38 - public void field(String clazz, String field) {
28.39 -// out.append(field);
28.40 - out.append('_').append(field).append('(').append(')');
28.41 - }
28.42 -
28.43 -
28.44 - @Override
28.45 - public String toString() {
28.46 - return out.toString();
28.47 - }
28.48 -
28.49 -
28.50 - static String mangle(String clazz, String method, String signature) {
28.51 - final StringBuilder builder = new StringBuilder();
28.52 - builder.append(method);
28.53 - builder.append("__");
28.54 - builder.append(mangle(JNIHelper.signature(JNIHelper.method(clazz, method, signature).getReturnType())));
28.55 - builder.append(mangle(signature));
28.56 - return builder.toString();
28.57 - }
28.58 -
28.59 -
28.60 - static String mangle(String txt) {
28.61 - final StringBuilder sb = new StringBuilder();
28.62 - for (int i = 0; i < txt.length(); i++) {
28.63 - final char ch = txt.charAt(i);
28.64 - switch (ch) {
28.65 - case '/': sb.append('_'); break;
28.66 - case '_': sb.append("_1"); break;
28.67 - case ';': sb.append("_2"); break;
28.68 - case '[': sb.append("_3"); break;
28.69 - default: sb.append(ch); break;
28.70 - }
28.71 - }
28.72 - return sb.toString();
28.73 - }
28.74 -}
29.1 --- a/ide/editor/src/main/nbm/manifest.mf Tue Feb 11 10:48:24 2014 +0100
29.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
29.3 @@ -1,2 +0,0 @@
29.4 -Manifest-Version: 1.0
29.5 -OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/jackpot30/test/hints/Bundle.properties
30.1 --- a/ide/editor/src/main/resources/org/netbeans/modules/jackpot30/test/hints/Bundle.properties Tue Feb 11 10:48:24 2014 +0100
30.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
30.3 @@ -1,21 +0,0 @@
30.4 -#
30.5 -# Back 2 Browser Bytecode Translator
30.6 -# Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
30.7 -#
30.8 -# This program is free software: you can redistribute it and/or modify
30.9 -# it under the terms of the GNU General Public License as published by
30.10 -# the Free Software Foundation, version 2 of the License.
30.11 -#
30.12 -# This program is distributed in the hope that it will be useful,
30.13 -# but WITHOUT ANY WARRANTY; without even the implied warranty of
30.14 -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30.15 -# GNU General Public License for more details.
30.16 -#
30.17 -# You should have received a copy of the GNU General Public License
30.18 -# along with this program. Look for COPYING file in the top folder.
30.19 -# If not, see http://opensource.org/licenses/GPL-2.0.
30.20 -#
30.21 -
30.22 -OpenIDE-Module-Name=Bck2Brwsr Editor Support
30.23 -OpenIDE-Module-Short-Description=Provides hints, coloring, etc. for the bck2brwsr.apidesign.org project
30.24 -OpenIDE-Module-Display-Category=Java
31.1 --- a/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/DejsniReaderTest.java Tue Feb 11 10:48:24 2014 +0100
31.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
31.3 @@ -1,84 +0,0 @@
31.4 -/**
31.5 - * Back 2 Browser Bytecode Translator
31.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
31.7 - *
31.8 - * This program is free software: you can redistribute it and/or modify
31.9 - * it under the terms of the GNU General Public License as published by
31.10 - * the Free Software Foundation, version 2 of the License.
31.11 - *
31.12 - * This program is distributed in the hope that it will be useful,
31.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
31.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31.15 - * GNU General Public License for more details.
31.16 - *
31.17 - * You should have received a copy of the GNU General Public License
31.18 - * along with this program. Look for COPYING file in the top folder.
31.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
31.20 - */
31.21 -package org.apidesign.bck2brwsr.ide.editor;
31.22 -
31.23 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
31.24 -import org.netbeans.modules.java.hints.test.api.HintTest;
31.25 -import org.openide.filesystems.FileUtil;
31.26 -import org.testng.annotations.Test;
31.27 -
31.28 -public class DejsniReaderTest {
31.29 -
31.30 - @Test
31.31 - public void test1() throws Exception {
31.32 - String s = "class Test {\n" +
31.33 - " /** javadoc */\n" +
31.34 - " public native void test() /*-{\n" +
31.35 - " // body\n" +
31.36 - " }-*/;\n" +
31.37 - "}\n";
31.38 -
31.39 - String expected = " import org.apidesign.bck2brwsr.core.JavaScriptBody;\n"
31.40 - + "class Test {\n" +
31.41 - "\n" +
31.42 - " /** javadoc */\n" +
31.43 - " @JavaScriptBody(args = {}, body = \"\\n // body\\n \")\n" +
31.44 - " public native void test();\n" +
31.45 - "}\n";
31.46 -
31.47 - HintTest.create()
31.48 - .input(s)
31.49 - .classpath(FileUtil.getArchiveRoot(JavaScriptBody.class.getProtectionDomain().getCodeSource().getLocation()))
31.50 - .run(JSNI2JavaScriptBody.class)
31.51 - .findWarning("2:23-2:27:verifier:" + Bundle.ERR_JSNI2JavaScriptBody())
31.52 - .applyFix()
31.53 - .assertCompilable()
31.54 - .assertOutput(expected);
31.55 - }
31.56 -
31.57 -
31.58 - @Test
31.59 - public void test2() throws Exception {
31.60 - String s = "class Test {\n" +
31.61 - " /** javadoc */\n" +
31.62 - " @SuppressWarnings(\"unused\")\n" +
31.63 - " // comment\n" +
31.64 - " public native void test() /*-{\n" +
31.65 - " // body\n" +
31.66 - " }-*/;\n" +
31.67 - "}\n";
31.68 -
31.69 - String expected = " import org.apidesign.bck2brwsr.core.JavaScriptBody;\n"
31.70 - + "class Test {\n" +
31.71 - "\n" +
31.72 - " /** javadoc */\n" +
31.73 - " @SuppressWarnings(\"unused\")\n" +
31.74 - " // comment\n" +
31.75 - " @JavaScriptBody(args = {}, body = \"\\n // body\\n \")\n" +
31.76 - " public native void test();\n" +
31.77 - "}\n";
31.78 - HintTest.create()
31.79 - .input(s)
31.80 - .classpath(FileUtil.getArchiveRoot(JavaScriptBody.class.getProtectionDomain().getCodeSource().getLocation()))
31.81 - .run(JSNI2JavaScriptBody.class)
31.82 - .findWarning("4:23-4:27:verifier:" + Bundle.ERR_JSNI2JavaScriptBody())
31.83 - .applyFix()
31.84 - .assertCompilable()
31.85 - .assertOutput(expected);
31.86 - }
31.87 -}
31.88 \ No newline at end of file
32.1 --- a/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/JSNI2JavaScriptBodyTest.java Tue Feb 11 10:48:24 2014 +0100
32.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
32.3 @@ -1,46 +0,0 @@
32.4 -/**
32.5 - * Back 2 Browser Bytecode Translator
32.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
32.7 - *
32.8 - * This program is free software: you can redistribute it and/or modify
32.9 - * it under the terms of the GNU General Public License as published by
32.10 - * the Free Software Foundation, version 2 of the License.
32.11 - *
32.12 - * This program is distributed in the hope that it will be useful,
32.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
32.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32.15 - * GNU General Public License for more details.
32.16 - *
32.17 - * You should have received a copy of the GNU General Public License
32.18 - * along with this program. Look for COPYING file in the top folder.
32.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
32.20 - */
32.21 -package org.apidesign.bck2brwsr.ide.editor;
32.22 -
32.23 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
32.24 -import org.netbeans.modules.java.hints.test.api.HintTest;
32.25 -import org.openide.filesystems.FileUtil;
32.26 -import org.testng.annotations.Test;
32.27 -
32.28 -public class JSNI2JavaScriptBodyTest {
32.29 -
32.30 - @Test
32.31 - public void testFixWorking() throws Exception {
32.32 - HintTest.create()
32.33 - .input("package test;\n" +
32.34 - "public class Test {\n" +
32.35 - " public native void run(int a) /*-{ this.a = a; }-*/;\n" +
32.36 - "}\n")
32.37 - .classpath(FileUtil.getArchiveRoot(JavaScriptBody.class.getProtectionDomain().getCodeSource().getLocation()))
32.38 - .run(JSNI2JavaScriptBody.class)
32.39 - .findWarning("2:23-2:26:verifier:" + Bundle.ERR_JSNI2JavaScriptBody())
32.40 - .applyFix()
32.41 - .assertCompilable()
32.42 - .assertOutput("package test;\n" +
32.43 - "import org.apidesign.bck2brwsr.core.JavaScriptBody;\n" +
32.44 - "public class Test {\n" +
32.45 - " @JavaScriptBody(args = {\"a\"}, body = \" this.a = a; \")\n" +
32.46 - " public native void run(int a);\n" +
32.47 - "}\n");
32.48 - }
32.49 -}
33.1 --- a/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/JsniCommentTokenizerTest.java Tue Feb 11 10:48:24 2014 +0100
33.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
33.3 @@ -1,154 +0,0 @@
33.4 -/**
33.5 - * Back 2 Browser Bytecode Translator
33.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
33.7 - *
33.8 - * This program is free software: you can redistribute it and/or modify
33.9 - * it under the terms of the GNU General Public License as published by
33.10 - * the Free Software Foundation, version 2 of the License.
33.11 - *
33.12 - * This program is distributed in the hope that it will be useful,
33.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
33.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33.15 - * GNU General Public License for more details.
33.16 - *
33.17 - * You should have received a copy of the GNU General Public License
33.18 - * along with this program. Look for COPYING file in the top folder.
33.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
33.20 - */
33.21 -package org.apidesign.bck2brwsr.ide.editor;
33.22 -
33.23 -import java.io.IOException;
33.24 -import java.util.ArrayList;
33.25 -import java.util.List;
33.26 -import org.testng.Assert;
33.27 -import org.testng.annotations.Test;
33.28 -
33.29 -public class JsniCommentTokenizerTest {
33.30 -
33.31 - private static class MockSink implements JsniCommentTokenizer.Sink {
33.32 - final List<String> out = new ArrayList<String>();
33.33 -
33.34 - public void javascript(String s) {
33.35 - out.add("J " + s);
33.36 - }
33.37 -
33.38 - public void method(String clazz, String method, String signature) {
33.39 - out.add("M " + clazz + "|" + method + "|" + signature);
33.40 - }
33.41 -
33.42 - public void field(String clazz, String field) {
33.43 - out.add("F " + clazz + "|" + field);
33.44 - }
33.45 - }
33.46 -
33.47 -
33.48 - @Test
33.49 - public void testProcess_nop() throws IOException {
33.50 - final String in = "foo bar";
33.51 - final List<String> expected = new ArrayList<String>();
33.52 - expected.add("J foo bar");
33.53 -
33.54 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.55 - final MockSink out = new MockSink();
33.56 - jsniCommentTokenizer.process(in, out);
33.57 -
33.58 - Assert.assertEquals(expected, out.out);
33.59 - }
33.60 -
33.61 - @Test
33.62 - public void testProcess_read_static_field() throws IOException {
33.63 - final String in = " @com.google.gwt.examples.JSNIExample::myStaticField = val + \" and stuff\";";
33.64 - final List<String> expected = new ArrayList<String>();
33.65 - expected.add("J ");
33.66 - expected.add("F com.google.gwt.examples.JSNIExample|myStaticField");
33.67 - expected.add("J = val + \" and stuff\";");
33.68 -
33.69 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.70 - final MockSink out = new MockSink();
33.71 - jsniCommentTokenizer.process(in, out);
33.72 -
33.73 - Assert.assertEquals(expected, out.out);
33.74 - }
33.75 -
33.76 - @Test
33.77 - public void testProcess_write_instance_field() throws IOException {
33.78 - final String in = " x.@com.google.gwt.examples.JSNIExample::myInstanceField = val + \" and stuff\";";
33.79 - final List<String> expected = new ArrayList<String>();
33.80 - expected.add("J x.");
33.81 - expected.add("F com.google.gwt.examples.JSNIExample|myInstanceField");
33.82 - expected.add("J = val + \" and stuff\";");
33.83 -
33.84 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.85 - final MockSink out = new MockSink();
33.86 - jsniCommentTokenizer.process(in, out);
33.87 -
33.88 - Assert.assertEquals(expected, out.out);
33.89 - }
33.90 -
33.91 - @Test
33.92 - public void testProcess_read_instance_field() throws IOException {
33.93 - final String in = " var val = this.@com.google.gwt.examples.JSNIExample::myInstanceField;";
33.94 - final List<String> expected = new ArrayList<String>();
33.95 - expected.add("J var val = this.");
33.96 - expected.add("F com.google.gwt.examples.JSNIExample|myInstanceField");
33.97 - expected.add("J ;");
33.98 -
33.99 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.100 - final MockSink out = new MockSink();
33.101 - jsniCommentTokenizer.process(in, out);
33.102 -
33.103 - Assert.assertEquals(expected, out.out);
33.104 - }
33.105 -
33.106 -
33.107 - @Test
33.108 - public void testProcess_static_method() throws IOException {
33.109 - final String in = " @com.google.gwt.examples.JSNIExample::staticFoo(Ljava/lang/String;)(s);";
33.110 - final List<String> expected = new ArrayList<String>();
33.111 - expected.add("J ");
33.112 - expected.add("M com.google.gwt.examples.JSNIExample|staticFoo|Ljava/lang/String;");
33.113 - expected.add("J (s);");
33.114 -
33.115 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.116 - final MockSink out = new MockSink();
33.117 - jsniCommentTokenizer.process(in, out);
33.118 -
33.119 - Assert.assertEquals(expected, out.out);
33.120 - }
33.121 -
33.122 -
33.123 - @Test
33.124 - public void testProcess_instance_method() throws IOException {
33.125 - final String in = " x.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);";
33.126 - final List<String> expected = new ArrayList<String>();
33.127 - expected.add("J x.");
33.128 - expected.add("M com.google.gwt.examples.JSNIExample|instanceFoo|Ljava/lang/String;");
33.129 - expected.add("J (s);");
33.130 -
33.131 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.132 - final MockSink out = new MockSink();
33.133 - jsniCommentTokenizer.process(in, out);
33.134 -
33.135 - Assert.assertEquals(expected, out.out);
33.136 - }
33.137 -
33.138 -
33.139 - @Test
33.140 - public void testProcess_multiline() throws IOException {
33.141 - final String in =
33.142 - " x.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);" +
33.143 - " @com.google.gwt.examples.JSNIExample::myStaticField = val + \" and stuff\";";
33.144 - final List<String> expected = new ArrayList<String>();
33.145 - expected.add("J x.");
33.146 - expected.add("M com.google.gwt.examples.JSNIExample|instanceFoo|Ljava/lang/String;");
33.147 - expected.add("J (s); ");
33.148 - expected.add("F com.google.gwt.examples.JSNIExample|myStaticField");
33.149 - expected.add("J = val + \" and stuff\";");
33.150 -
33.151 - final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer();
33.152 - final MockSink out = new MockSink();
33.153 - jsniCommentTokenizer.process(in, out);
33.154 -
33.155 - Assert.assertEquals(expected, out.out);
33.156 - }
33.157 -}
34.1 --- a/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/ManglingSinkTest.java Tue Feb 11 10:48:24 2014 +0100
34.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
34.3 @@ -1,58 +0,0 @@
34.4 -/**
34.5 - * Back 2 Browser Bytecode Translator
34.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
34.7 - *
34.8 - * This program is free software: you can redistribute it and/or modify
34.9 - * it under the terms of the GNU General Public License as published by
34.10 - * the Free Software Foundation, version 2 of the License.
34.11 - *
34.12 - * This program is distributed in the hope that it will be useful,
34.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
34.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34.15 - * GNU General Public License for more details.
34.16 - *
34.17 - * You should have received a copy of the GNU General Public License
34.18 - * along with this program. Look for COPYING file in the top folder.
34.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
34.20 - */
34.21 -package org.apidesign.bck2brwsr.ide.editor;
34.22 -
34.23 -import org.testng.Assert;
34.24 -import org.testng.annotations.Test;
34.25 -
34.26 -
34.27 -public class ManglingSinkTest {
34.28 -
34.29 - @Test
34.30 - public void testMangle_1() {
34.31 - Assert.assertEquals(
34.32 - "binarySearch__I_3BIIB",
34.33 - ManglingSink.mangle("java.util.Arrays", "binarySearch", "[BIIB")
34.34 - );
34.35 - }
34.36 -
34.37 - @Test
34.38 - public void testMangle_2() {
34.39 - Assert.assertEquals(
34.40 - "sort__V_3I",
34.41 - ManglingSink.mangle("java.util.Arrays", "sort", "[I")
34.42 - );
34.43 - }
34.44 -
34.45 - @Test
34.46 - public void testMangle_3() {
34.47 - Assert.assertEquals(
34.48 - "binarySearch__I_3Ljava_lang_Object_2IILjava_lang_Object_2",
34.49 - ManglingSink.mangle("java.util.Arrays", "binarySearch", "[Ljava/lang/Object;IILjava/lang/Object;")
34.50 - );
34.51 - }
34.52 -
34.53 -
34.54 - @Test
34.55 - public void testField() {
34.56 - final ManglingSink manglingSink = new ManglingSink();
34.57 - manglingSink.field(null, "value");
34.58 -
34.59 - Assert.assertEquals("_value()", manglingSink.toString());
34.60 - }
34.61 -}
35.1 --- a/ide/pom.xml Tue Feb 11 10:48:24 2014 +0100
35.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
35.3 @@ -1,29 +0,0 @@
35.4 -<?xml version="1.0" encoding="UTF-8"?>
35.5 -<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">
35.6 - <modelVersion>4.0.0</modelVersion>
35.7 - <parent>
35.8 - <artifactId>bck2brwsr</artifactId>
35.9 - <groupId>org.apidesign</groupId>
35.10 - <version>0.9-SNAPSHOT</version>
35.11 - </parent>
35.12 - <groupId>org.apidesign.bck2brwsr</groupId>
35.13 - <artifactId>ide</artifactId>
35.14 - <version>0.9-SNAPSHOT</version>
35.15 - <packaging>pom</packaging>
35.16 - <name>IDE Support</name>
35.17 - <modules>
35.18 - <module>editor</module>
35.19 - </modules>
35.20 - <build>
35.21 - <plugins>
35.22 - <plugin>
35.23 - <groupId>org.apache.maven.plugins</groupId>
35.24 - <artifactId>maven-deploy-plugin</artifactId>
35.25 - <version>2.7</version>
35.26 - <configuration>
35.27 - <skip>true</skip>
35.28 - </configuration>
35.29 - </plugin>
35.30 - </plugins>
35.31 - </build>
35.32 -</project>
36.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Feb 11 10:48:24 2014 +0100
36.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Feb 11 13:31:42 2014 +0100
36.3 @@ -1226,6 +1226,12 @@
36.4 isModel[0] = true;
36.5 } else {
36.6 ret = tm.toString();
36.7 + int idx = ret.indexOf("<any?>.");
36.8 + if (idx >= 0) {
36.9 + ret = ret.substring(idx + 7);
36.10 + isEnum[0] = false;
36.11 + return ret;
36.12 + }
36.13 }
36.14 TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
36.15 enm = processingEnv.getTypeUtils().erasure(enm);
37.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java Tue Feb 11 10:48:24 2014 +0100
37.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java Tue Feb 11 13:31:42 2014 +0100
37.3 @@ -281,31 +281,31 @@
37.4 assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
37.5 }
37.6
37.7 - @Http(@Http.Resource(
37.8 - content = "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
37.9 - path="/people.json",
37.10 - mimeType = "application/json"
37.11 - ))
37.12 - @BrwsrTest public void loadAndParseArrayInPeople() throws InterruptedException {
37.13 - if (js == null) {
37.14 - js = new JSONik();
37.15 - js.applyBindings();
37.16 -
37.17 - js.fetchPeople("people.json");
37.18 - }
37.19 -
37.20 - if (0 == js.getFetchedCount()) {
37.21 - throw new InterruptedException();
37.22 - }
37.23 -
37.24 - assert js.getFetchedCount() == 1 : "One person loaded: " + js.getFetchedCount();
37.25 -
37.26 - Person p = js.getFetched();
37.27 -
37.28 - assert p != null : "We should get our person back: " + p;
37.29 - assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
37.30 - assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
37.31 - }
37.32 +// @Http(@Http.Resource(
37.33 +// content = "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
37.34 +// path="/people.json",
37.35 +// mimeType = "application/json"
37.36 +// ))
37.37 +// @BrwsrTest public void loadAndParseArrayInPeople() throws InterruptedException {
37.38 +// if (js == null) {
37.39 +// js = new JSONik();
37.40 +// js.applyBindings();
37.41 +//
37.42 +// js.fetchPeople("people.json");
37.43 +// }
37.44 +//
37.45 +// if (0 == js.getFetchedCount()) {
37.46 +// throw new InterruptedException();
37.47 +// }
37.48 +//
37.49 +// assert js.getFetchedCount() == 1 : "One person loaded: " + js.getFetchedCount();
37.50 +//
37.51 +// Person p = js.getFetched();
37.52 +//
37.53 +// assert p != null : "We should get our person back: " + p;
37.54 +// assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
37.55 +// assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
37.56 +// }
37.57
37.58 @Http(@Http.Resource(
37.59 content = "{'age':[1, 2, 3]}",
38.1 --- a/javaquery/demo-twitter/bck2brwsr-assembly.xml Tue Feb 11 10:48:24 2014 +0100
38.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
38.3 @@ -1,62 +0,0 @@
38.4 -<?xml version="1.0"?>
38.5 -<!--
38.6 -
38.7 - Back 2 Browser Bytecode Translator
38.8 - Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
38.9 -
38.10 - This program is free software: you can redistribute it and/or modify
38.11 - it under the terms of the GNU General Public License as published by
38.12 - the Free Software Foundation, version 2 of the License.
38.13 -
38.14 - This program is distributed in the hope that it will be useful,
38.15 - but WITHOUT ANY WARRANTY; without even the implied warranty of
38.16 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38.17 - GNU General Public License for more details.
38.18 -
38.19 - You should have received a copy of the GNU General Public License
38.20 - along with this program. Look for COPYING file in the top folder.
38.21 - If not, see http://opensource.org/licenses/GPL-2.0.
38.22 -
38.23 --->
38.24 -<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
38.25 - xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
38.26 -
38.27 - <id>bck2brwsr</id>
38.28 - <formats>
38.29 - <format>zip</format>
38.30 - </formats>
38.31 - <baseDirectory>public_html</baseDirectory>
38.32 - <dependencySets>
38.33 - <dependencySet>
38.34 - <useProjectArtifact>false</useProjectArtifact>
38.35 - <scope>runtime</scope>
38.36 - <outputDirectory>lib</outputDirectory>
38.37 - <includes>
38.38 - <include>*:jar</include>
38.39 - <include>*:rt</include>
38.40 - </includes>
38.41 - </dependencySet>
38.42 - </dependencySets>
38.43 - <fileSets>
38.44 - <fileSet>
38.45 - <directory>${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/twitter/</directory>
38.46 - <includes>
38.47 - <include>**/*</include>
38.48 - </includes>
38.49 - <excludes>
38.50 - <exclude>**/*.class</exclude>
38.51 - </excludes>
38.52 - <outputDirectory>/</outputDirectory>
38.53 - </fileSet>
38.54 - </fileSets>
38.55 - <files>
38.56 - <file>
38.57 - <source>${project.build.directory}/${project.build.finalName}.jar</source>
38.58 - <outputDirectory>/</outputDirectory>
38.59 - </file>
38.60 - <file>
38.61 - <source>${project.build.directory}/bck2brwsr.js</source>
38.62 - <outputDirectory>/</outputDirectory>
38.63 - </file>
38.64 - </files>
38.65 -</assembly>
38.66 \ No newline at end of file
39.1 --- a/javaquery/demo-twitter/nb-configuration.xml Tue Feb 11 10:48:24 2014 +0100
39.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
39.3 @@ -1,37 +0,0 @@
39.4 -<?xml version="1.0" encoding="UTF-8"?>
39.5 -<!--
39.6 -
39.7 - Back 2 Browser Bytecode Translator
39.8 - Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
39.9 -
39.10 - This program is free software: you can redistribute it and/or modify
39.11 - it under the terms of the GNU General Public License as published by
39.12 - the Free Software Foundation, version 2 of the License.
39.13 -
39.14 - This program is distributed in the hope that it will be useful,
39.15 - but WITHOUT ANY WARRANTY; without even the implied warranty of
39.16 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39.17 - GNU General Public License for more details.
39.18 -
39.19 - You should have received a copy of the GNU General Public License
39.20 - along with this program. Look for COPYING file in the top folder.
39.21 - If not, see http://opensource.org/licenses/GPL-2.0.
39.22 -
39.23 --->
39.24 -<project-shared-configuration>
39.25 - <!--
39.26 -This file contains additional configuration written by modules in the NetBeans IDE.
39.27 -The configuration is intended to be shared among all the users of project and
39.28 -therefore it is assumed to be part of version control checkout.
39.29 -Without this configuration present, some functionality in the IDE may be limited or fail altogether.
39.30 --->
39.31 - <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
39.32 - <!--
39.33 -Properties that influence various parts of the IDE, especially code formatting and the like.
39.34 -You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
39.35 -That way multiple projects can share the same settings (useful for formatting rules for example).
39.36 -Any value defined here will override the pom.xml file value but is only applicable to the current project.
39.37 --->
39.38 - <netbeans.compile.on.save>none</netbeans.compile.on.save>
39.39 - </properties>
39.40 -</project-shared-configuration>
40.1 --- a/javaquery/demo-twitter/nbactions.xml Tue Feb 11 10:48:24 2014 +0100
40.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
40.3 @@ -1,29 +0,0 @@
40.4 -<?xml version="1.0" encoding="UTF-8"?>
40.5 -<!--
40.6 -
40.7 - Back 2 Browser Bytecode Translator
40.8 - Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
40.9 -
40.10 - This program is free software: you can redistribute it and/or modify
40.11 - it under the terms of the GNU General Public License as published by
40.12 - the Free Software Foundation, version 2 of the License.
40.13 -
40.14 - This program is distributed in the hope that it will be useful,
40.15 - but WITHOUT ANY WARRANTY; without even the implied warranty of
40.16 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40.17 - GNU General Public License for more details.
40.18 -
40.19 - You should have received a copy of the GNU General Public License
40.20 - along with this program. Look for COPYING file in the top folder.
40.21 - If not, see http://opensource.org/licenses/GPL-2.0.
40.22 -
40.23 --->
40.24 -<actions>
40.25 - <action>
40.26 - <actionName>run</actionName>
40.27 - <goals>
40.28 - <goal>process-classes</goal>
40.29 - <goal>bck2brwsr:brwsr</goal>
40.30 - </goals>
40.31 - </action>
40.32 -</actions>
41.1 --- a/javaquery/demo-twitter/pom.xml Tue Feb 11 10:48:24 2014 +0100
41.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
41.3 @@ -1,151 +0,0 @@
41.4 -<?xml version="1.0" encoding="UTF-8"?>
41.5 -<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">
41.6 - <modelVersion>4.0.0</modelVersion>
41.7 - <parent>
41.8 - <artifactId>javaquery</artifactId>
41.9 - <groupId>org.apidesign.bck2brwsr</groupId>
41.10 - <version>0.9-SNAPSHOT</version>
41.11 - </parent>
41.12 -
41.13 - <groupId>org.apidesign.bck2brwsr</groupId>
41.14 - <artifactId>demo-twitter</artifactId>
41.15 - <version>0.9-SNAPSHOT</version>
41.16 - <packaging>jar</packaging>
41.17 -
41.18 - <name>Bck2Brwsr's Twttr</name>
41.19 - <description>
41.20 - Rewrite of knockoutjs example to use model written in Java and
41.21 - execute using Bck2Brwsr virtual machine.
41.22 - </description>
41.23 -
41.24 - <repositories>
41.25 - <repository>
41.26 - <id>java.net</id>
41.27 - <name>Java.net</name>
41.28 - <url>https://maven.java.net/content/repositories/releases/</url>
41.29 - <snapshots>
41.30 - </snapshots>
41.31 - </repository>
41.32 - <repository>
41.33 - <id>netbeans</id>
41.34 - <name>NetBeans</name>
41.35 - <url>http://bits.netbeans.org/maven2/</url>
41.36 - </repository>
41.37 - </repositories>
41.38 - <pluginRepositories>
41.39 - <pluginRepository>
41.40 - <id>java.net</id>
41.41 - <name>Java.net</name>
41.42 - <url>https://maven.java.net/content/repositories/releases/</url>
41.43 - <snapshots>
41.44 - </snapshots>
41.45 - </pluginRepository>
41.46 - </pluginRepositories>
41.47 -
41.48 - <properties>
41.49 - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
41.50 - <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
41.51 - </properties>
41.52 - <build>
41.53 - <plugins>
41.54 - <plugin>
41.55 - <groupId>org.apidesign.bck2brwsr</groupId>
41.56 - <artifactId>bck2brwsr-maven-plugin</artifactId>
41.57 - <version>${project.version}</version>
41.58 - <executions>
41.59 - <execution>
41.60 - <goals>
41.61 - <goal>brwsr</goal>
41.62 - <goal>j2js</goal>
41.63 - </goals>
41.64 - </execution>
41.65 - </executions>
41.66 - <configuration>
41.67 - <startpage>org/apidesign/bck2brwsr/demo/twitter/index.html</startpage>
41.68 - <javascript>${project.build.directory}/bck2brwsr.js</javascript>
41.69 - <obfuscation>${bck2brwsr.obfuscationlevel}</obfuscation>
41.70 - </configuration>
41.71 - </plugin>
41.72 - <plugin>
41.73 - <groupId>org.apache.maven.plugins</groupId>
41.74 - <artifactId>maven-compiler-plugin</artifactId>
41.75 - <version>2.3.2</version>
41.76 - <configuration>
41.77 - <source>1.7</source>
41.78 - <target>1.7</target>
41.79 - </configuration>
41.80 - </plugin>
41.81 - <plugin>
41.82 - <groupId>org.apache.maven.plugins</groupId>
41.83 - <artifactId>maven-jar-plugin</artifactId>
41.84 - <version>2.4</version>
41.85 - <configuration>
41.86 - <archive>
41.87 - <manifest>
41.88 - <addClasspath>true</addClasspath>
41.89 - <classpathPrefix>lib/</classpathPrefix>
41.90 - </manifest>
41.91 - </archive>
41.92 - </configuration>
41.93 - </plugin>
41.94 - <plugin>
41.95 - <groupId>org.apache.maven.plugins</groupId>
41.96 - <artifactId>maven-deploy-plugin</artifactId>
41.97 - <version>2.7</version>
41.98 - <configuration>
41.99 - <skip>true</skip>
41.100 - </configuration>
41.101 - </plugin>
41.102 - <plugin>
41.103 - <artifactId>maven-assembly-plugin</artifactId>
41.104 - <version>2.4</version>
41.105 - <executions>
41.106 - <execution>
41.107 - <id>distro-assembly</id>
41.108 - <phase>package</phase>
41.109 - <goals>
41.110 - <goal>single</goal>
41.111 - </goals>
41.112 - <configuration>
41.113 - <descriptors>
41.114 - <descriptor>bck2brwsr-assembly.xml</descriptor>
41.115 - </descriptors>
41.116 - </configuration>
41.117 - </execution>
41.118 - </executions>
41.119 - </plugin>
41.120 - </plugins>
41.121 - </build>
41.122 -
41.123 - <dependencies>
41.124 - <dependency>
41.125 - <groupId>org.apidesign.bck2brwsr</groupId>
41.126 - <artifactId>emul</artifactId>
41.127 - <version>${project.version}</version>
41.128 - <classifier>rt</classifier>
41.129 - </dependency>
41.130 - <dependency>
41.131 - <groupId>org.apidesign.bck2brwsr</groupId>
41.132 - <artifactId>javaquery.api</artifactId>
41.133 - <version>${project.version}</version>
41.134 - </dependency>
41.135 - <dependency>
41.136 - <groupId>org.testng</groupId>
41.137 - <artifactId>testng</artifactId>
41.138 - <version>6.5.2</version>
41.139 - <scope>test</scope>
41.140 - </dependency>
41.141 - <dependency>
41.142 - <groupId>org.apidesign.bck2brwsr</groupId>
41.143 - <artifactId>vmtest</artifactId>
41.144 - <version>${project.version}</version>
41.145 - <scope>test</scope>
41.146 - </dependency>
41.147 - <dependency>
41.148 - <groupId>org.apidesign.bck2brwsr</groupId>
41.149 - <artifactId>launcher.http</artifactId>
41.150 - <version>${project.version}</version>
41.151 - <scope>runtime</scope>
41.152 - </dependency>
41.153 - </dependencies>
41.154 -</project>
42.1 --- a/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java Tue Feb 11 10:48:24 2014 +0100
42.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
42.3 @@ -1,194 +0,0 @@
42.4 -/**
42.5 - * Back 2 Browser Bytecode Translator
42.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
42.7 - *
42.8 - * This program is free software: you can redistribute it and/or modify
42.9 - * it under the terms of the GNU General Public License as published by
42.10 - * the Free Software Foundation, version 2 of the License.
42.11 - *
42.12 - * This program is distributed in the hope that it will be useful,
42.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
42.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42.15 - * GNU General Public License for more details.
42.16 - *
42.17 - * You should have received a copy of the GNU General Public License
42.18 - * along with this program. Look for COPYING file in the top folder.
42.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
42.20 - */
42.21 -package org.apidesign.bck2brwsr.demo.twitter;
42.22 -
42.23 -import java.util.Arrays;
42.24 -import java.util.List;
42.25 -import org.apidesign.bck2brwsr.htmlpage.api.*;
42.26 -import org.apidesign.bck2brwsr.htmlpage.api.Page;
42.27 -import org.apidesign.bck2brwsr.htmlpage.api.Property;
42.28 -import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
42.29 -
42.30 -/** Controller class for access to Twitter.
42.31 - *
42.32 - * @author Jaroslav Tulach
42.33 - */
42.34 -@Page(xhtml="index.html", className="TwitterModel", properties={
42.35 - @Property(name="savedLists", type=Tweeters.class, array = true),
42.36 - @Property(name="activeTweetersName", type=String.class),
42.37 - @Property(name="activeTweeters", type=String.class, array = true),
42.38 - @Property(name="userNameToAdd", type=String.class),
42.39 - @Property(name="currentTweets", type=Tweet.class, array = true)
42.40 -})
42.41 -public class TwitterClient {
42.42 - @Model(className = "Tweeters", properties = {
42.43 - @Property(name="name", type = String.class),
42.44 - @Property(name="userNames", type = String.class, array = true)
42.45 - })
42.46 - static class Twttrs {
42.47 - }
42.48 - @Model(className = "Tweet", properties = {
42.49 - @Property(name = "from_user", type = String.class),
42.50 - @Property(name = "from_user_id", type = int.class),
42.51 - @Property(name = "profile_image_url", type = String.class),
42.52 - @Property(name = "text", type = String.class),
42.53 - @Property(name = "created_at", type = String.class),
42.54 - })
42.55 - static final class Twt {
42.56 - @ComputedProperty static String html(String text) {
42.57 - StringBuilder sb = new StringBuilder(320);
42.58 - for (int pos = 0;;) {
42.59 - int http = text.indexOf("http", pos);
42.60 - if (http == -1) {
42.61 - sb.append(text.substring(pos));
42.62 - return sb.toString();
42.63 - }
42.64 - int spc = text.indexOf(' ', http);
42.65 - if (spc == -1) {
42.66 - spc = text.length();
42.67 - }
42.68 - sb.append(text.substring(pos, http));
42.69 - String url = text.substring(http, spc);
42.70 - sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
42.71 - pos = spc;
42.72 - }
42.73 - }
42.74 -
42.75 - @ComputedProperty static String userUrl(String from_user) {
42.76 - return "http://twitter.com/" + from_user;
42.77 - }
42.78 - }
42.79 - @Model(className = "TwitterQuery", properties = {
42.80 - @Property(array = true, name = "results", type = Twt.class)
42.81 - })
42.82 - public static final class TwttrQr {
42.83 - }
42.84 -
42.85 - @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
42.86 - static void queryTweets(TwitterModel page, TwitterQuery q) {
42.87 - page.getCurrentTweets().clear();
42.88 - page.getCurrentTweets().addAll(q.getResults());
42.89 - }
42.90 -
42.91 - @OnPropertyChange("activeTweetersName")
42.92 - static void changeTweetersList(TwitterModel model) {
42.93 - Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());
42.94 - model.getActiveTweeters().clear();
42.95 - model.getActiveTweeters().addAll(people.getUserNames());
42.96 - }
42.97 -
42.98 - @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
42.99 - static void refreshTweets(TwitterModel model) {
42.100 - StringBuilder sb = new StringBuilder();
42.101 - sb.append("rpp=25&q=");
42.102 - String sep = "";
42.103 - for (String p : model.getActiveTweeters()) {
42.104 - sb.append(sep);
42.105 - sb.append("from:");
42.106 - sb.append(p);
42.107 - sep = " OR ";
42.108 - }
42.109 - model.queryTweets("http://search.twitter.com", sb.toString());
42.110 - }
42.111 -
42.112 - static {
42.113 - final TwitterModel model = new TwitterModel();
42.114 - final List<Tweeters> svdLst = model.getSavedLists();
42.115 - svdLst.add(newTweeters("API Design", "JaroslavTulach"));
42.116 - svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
42.117 - svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
42.118 - svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
42.119 - svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
42.120 -
42.121 - model.setActiveTweetersName("NetBeans");
42.122 -
42.123 - model.applyBindings();
42.124 - }
42.125 -
42.126 - @ComputedProperty
42.127 - static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
42.128 - Tweeters tw = findByName(savedLists, activeTweetersName);
42.129 - if (activeTweeters == null) {
42.130 - return false;
42.131 - }
42.132 - return !tw.getUserNames().equals(activeTweeters);
42.133 - }
42.134 -
42.135 - @ComputedProperty
42.136 - static int activeTweetersCount(List<String> activeTweeters) {
42.137 - return activeTweeters.size();
42.138 - }
42.139 -
42.140 - @ComputedProperty
42.141 - static boolean userNameToAddIsValid(
42.142 - String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
42.143 - ) {
42.144 - return userNameToAdd != null &&
42.145 - userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
42.146 - !activeTweeters.contains(userNameToAdd);
42.147 - }
42.148 -
42.149 - @OnFunction
42.150 - static void deleteList(TwitterModel model) {
42.151 - final List<Tweeters> sl = model.getSavedLists();
42.152 - sl.remove(findByName(sl, model.getActiveTweetersName()));
42.153 - if (sl.isEmpty()) {
42.154 - final Tweeters t = new Tweeters();
42.155 - t.setName("New");
42.156 - sl.add(t);
42.157 - }
42.158 - model.setActiveTweetersName(sl.get(0).getName());
42.159 - }
42.160 -
42.161 - @OnFunction
42.162 - static void saveChanges(TwitterModel model) {
42.163 - Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
42.164 - int indx = model.getSavedLists().indexOf(t);
42.165 - if (indx != -1) {
42.166 - t.setName(model.getActiveTweetersName());
42.167 - t.getUserNames().clear();
42.168 - t.getUserNames().addAll(model.getActiveTweeters());
42.169 - }
42.170 - }
42.171 -
42.172 - @OnFunction
42.173 - static void addUser(TwitterModel model) {
42.174 - String n = model.getUserNameToAdd();
42.175 - model.getActiveTweeters().add(n);
42.176 - }
42.177 - @OnFunction
42.178 - static void removeUser(String data, TwitterModel model) {
42.179 - model.getActiveTweeters().remove(data);
42.180 - }
42.181 -
42.182 - private static Tweeters findByName(List<Tweeters> list, String name) {
42.183 - for (Tweeters l : list) {
42.184 - if (l.getName() != null && l.getName().equals(name)) {
42.185 - return l;
42.186 - }
42.187 - }
42.188 - return list.isEmpty() ? new Tweeters() : list.get(0);
42.189 - }
42.190 -
42.191 - private static Tweeters newTweeters(String listName, String... userNames) {
42.192 - Tweeters t = new Tweeters();
42.193 - t.setName(listName);
42.194 - t.getUserNames().addAll(Arrays.asList(userNames));
42.195 - return t;
42.196 - }
42.197 -}
43.1 --- a/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/index.html Tue Feb 11 10:48:24 2014 +0100
43.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
43.3 @@ -1,99 +0,0 @@
43.4 -<?xml version="1.0" encoding="UTF-8"?>
43.5 -<!--
43.6 -
43.7 - Back 2 Browser Bytecode Translator
43.8 - Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
43.9 -
43.10 - This program is free software: you can redistribute it and/or modify
43.11 - it under the terms of the GNU General Public License as published by
43.12 - the Free Software Foundation, version 2 of the License.
43.13 -
43.14 - This program is distributed in the hope that it will be useful,
43.15 - but WITHOUT ANY WARRANTY; without even the implied warranty of
43.16 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43.17 - GNU General Public License for more details.
43.18 -
43.19 - You should have received a copy of the GNU General Public License
43.20 - along with this program. Look for COPYING file in the top folder.
43.21 - If not, see http://opensource.org/licenses/GPL-2.0.
43.22 -
43.23 --->
43.24 -
43.25 -<!--
43.26 - Copied from knockout.js Twitter example:
43.27 - http://knockoutjs.com/examples/twitter.html
43.28 --->
43.29 -
43.30 -<!DOCTYPE html>
43.31 -<html xmlns="http://www.w3.org/1999/xhtml">
43.32 - <head>
43.33 - <title>Bck2Brwsr's Twitter</title>
43.34 - </head>
43.35 - <body>
43.36 - <link href='twitterExample.css' rel='Stylesheet' ></link>
43.37 -
43.38 - <style type='text/css'>
43.39 - .liveExample select { height: 1.7em; }
43.40 - .liveExample button { height: 2em; }
43.41 - </style>
43.42 -
43.43 -
43.44 - <h2>Bck2Brwsr's Twitter</h2>
43.45 -
43.46 - <p>
43.47 - This code based on original <a href="http://knockoutjs.com/examples/twitter.html">knockout.js Twitter example</a> and
43.48 - uses almost unmodified HTML code. It just changes the model. It
43.49 - is written in Java language and it is executed using <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a>
43.50 - virtual machine. The Java source code has about 190 lines and is available
43.51 - <a href="http://source.apidesign.org/hg/bck2brwsr/file/7fc6b7e9c982/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java">here</a>
43.52 - - in fact it may even be more dense than the original JavaScript model.
43.53 - </p>
43.54 -
43.55 - <div class='liveExample'>
43.56 - <div class='configuration'>
43.57 - <div class='listChooser'>
43.58 - <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
43.59 - <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button>
43.60 - <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
43.61 - </div>
43.62 -
43.63 - <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
43.64 - <div class='currentUsers' >
43.65 - <ul data-bind='foreach: activeTweeters'>
43.66 - <li>
43.67 - <button data-bind='click: $root.removeUser'>Remove</button>
43.68 - <div data-bind='text: $data'> </div>
43.69 - </li>
43.70 - </ul>
43.71 - </div>
43.72 -
43.73 - <form data-bind='submit: addUser'>
43.74 - <label>Add user:</label>
43.75 - <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
43.76 - <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
43.77 - </form>
43.78 - </div>
43.79 - <div class='tweets'>
43.80 - <div class='loadingIndicator'>Loading...</div>
43.81 - <table data-bind='foreach: currentTweets' width='100%'>
43.82 - <tr>
43.83 - <td><img data-bind='attr: { src: profile_image_url }' /></td>
43.84 - <td>
43.85 - <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
43.86 - <span data-bind='html: html'> </span>
43.87 - <div class='tweetInfo' data-bind='text: created_at'> </div>
43.88 - </td>
43.89 - </tr>
43.90 - </table>
43.91 - </div>
43.92 - </div>
43.93 -
43.94 - <script src="bck2brwsr.js"></script>
43.95 - <script type="text/javascript">
43.96 - var vm = bck2brwsr('demo-twitter-0.8-SNAPSHOT.jar');
43.97 - vm.loadClass('org.apidesign.bck2brwsr.demo.twitter.TwitterClient');
43.98 - </script>
43.99 -
43.100 -
43.101 - </body>
43.102 -</html>
44.1 --- a/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/twitterExample.css Tue Feb 11 10:48:24 2014 +0100
44.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
44.3 @@ -1,50 +0,0 @@
44.4 -/**
44.5 - * Back 2 Browser Bytecode Translator
44.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
44.7 - *
44.8 - * This program is free software: you can redistribute it and/or modify
44.9 - * it under the terms of the GNU General Public License as published by
44.10 - * the Free Software Foundation, version 2 of the License.
44.11 - *
44.12 - * This program is distributed in the hope that it will be useful,
44.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
44.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44.15 - * GNU General Public License for more details.
44.16 - *
44.17 - * You should have received a copy of the GNU General Public License
44.18 - * along with this program. Look for COPYING file in the top folder.
44.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
44.20 - */
44.21 -
44.22 -/*
44.23 - Copied from knockout.js Twitter example:
44.24 - http://knockoutjs.com/examples/twitter.html
44.25 -*/
44.26 -
44.27 -.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
44.28 -.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
44.29 -.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; }
44.30 -.tweets table { border-width: 0;}
44.31 -.tweets tr { vertical-align: top; }
44.32 -.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
44.33 -.tweets img { width: 4em; }
44.34 -.tweetInfo { color: Gray; font-size: 0.9em; }
44.35 -.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
44.36 -input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
44.37 -
44.38 -.listChooser select, .listChooser button { vertical-align:top; }
44.39 -.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
44.40 -.listChooser button { width: 19%; height:1.68em; float:right; }
44.41 -
44.42 -.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
44.43 -.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
44.44 -.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
44.45 -.currentUsers li { height: 2.4em; font-size: 1.2em; background-color: #A7D0E3; border: 1px solid gray; margin-bottom: 0.3em; -webkit-border-radius: 5px; -moz-border-radius: 5px; -webkit-box-shadow: 0 0.2em 0.5em gray; -moz-box-shadow: 0 0.2em 0.5em gray; }
44.46 -.currentUsers li div { padding: 0.6em; }
44.47 -.currentUsers li:hover { background-color: #EEC; }
44.48 -
44.49 -.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
44.50 -.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
44.51 -.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
44.52 -
44.53 -.loadingIndicator { position: absolute; top: 0.1em; left: 0.1em; font: 0.8em Arial; background-color: #229; color: White; padding: 0.2em 0.5em 0.2em 0.5em; display: none; }
45.1 --- a/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClientTest.java Tue Feb 11 10:48:24 2014 +0100
45.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
45.3 @@ -1,67 +0,0 @@
45.4 -/**
45.5 - * Back 2 Browser Bytecode Translator
45.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
45.7 - *
45.8 - * This program is free software: you can redistribute it and/or modify
45.9 - * it under the terms of the GNU General Public License as published by
45.10 - * the Free Software Foundation, version 2 of the License.
45.11 - *
45.12 - * This program is distributed in the hope that it will be useful,
45.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
45.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45.15 - * GNU General Public License for more details.
45.16 - *
45.17 - * You should have received a copy of the GNU General Public License
45.18 - * along with this program. Look for COPYING file in the top folder.
45.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
45.20 - */
45.21 -package org.apidesign.bck2brwsr.demo.twitter;
45.22 -
45.23 -import java.util.List;
45.24 -import static org.testng.Assert.*;
45.25 -import org.testng.annotations.BeforeMethod;
45.26 -import org.testng.annotations.Test;
45.27 -
45.28 -/** We can unit test the TwitterModel smoothly.
45.29 - *
45.30 - * @author Jaroslav Tulach <jtulach@netbeans.org>
45.31 - */
45.32 -public class TwitterClientTest {
45.33 - private TwitterModel model;
45.34 -
45.35 -
45.36 - @BeforeMethod
45.37 - public void initModel() {
45.38 - model = new TwitterModel().applyBindings();
45.39 - }
45.40 -
45.41 - @Test public void testIsValidToAdd() {
45.42 - model.setUserNameToAdd("Joe");
45.43 - Tweeters t = new Tweeters();
45.44 - t.setName("test");
45.45 - model.getSavedLists().add(t);
45.46 - model.setActiveTweetersName("test");
45.47 -
45.48 - assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
45.49 - TwitterClient.addUser(model);
45.50 - assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
45.51 - assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
45.52 -
45.53 - List<String> mod = model.getActiveTweeters();
45.54 - assertTrue(model.isHasUnsavedChanges(), "We have modifications");
45.55 - assertEquals(mod.size(), 1, "One element in the list");
45.56 - assertEquals(mod.get(0), "Joe", "Its name is Joe");
45.57 -
45.58 - assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
45.59 -
45.60 - TwitterClient.saveChanges(model);
45.61 - assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
45.62 -
45.63 - assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
45.64 - }
45.65 -
45.66 - @Test public void httpAtTheEnd() {
45.67 - String res = TwitterClient.Twt.html("Ahoj http://kuk");
45.68 - assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
45.69 - }
45.70 -}
46.1 --- a/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterProtocolTest.java Tue Feb 11 10:48:24 2014 +0100
46.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
46.3 @@ -1,94 +0,0 @@
46.4 -/**
46.5 - * Back 2 Browser Bytecode Translator
46.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
46.7 - *
46.8 - * This program is free software: you can redistribute it and/or modify
46.9 - * it under the terms of the GNU General Public License as published by
46.10 - * the Free Software Foundation, version 2 of the License.
46.11 - *
46.12 - * This program is distributed in the hope that it will be useful,
46.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
46.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46.15 - * GNU General Public License for more details.
46.16 - *
46.17 - * You should have received a copy of the GNU General Public License
46.18 - * along with this program. Look for COPYING file in the top folder.
46.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
46.20 - */
46.21 -package org.apidesign.bck2brwsr.demo.twitter;
46.22 -
46.23 -import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
46.24 -import org.apidesign.bck2brwsr.vmtest.Http;
46.25 -import org.apidesign.bck2brwsr.vmtest.VMTest;
46.26 -import org.testng.annotations.Factory;
46.27 -
46.28 -/**
46.29 - *
46.30 - * @author Jaroslav Tulach <jtulach@netbeans.org>
46.31 - */
46.32 -public class TwitterProtocolTest {
46.33 - private TwitterModel page;
46.34 - @Http(@Http.Resource(
46.35 - path = "/search.json",
46.36 - mimeType = "application/json",
46.37 - parameters = {"callback"},
46.38 - content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
46.39 - + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
46.40 - + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
46.41 - + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
46.42 - + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
46.43 - + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
46.44 - + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
46.45 - + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.46 - + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.47 - + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
46.48 - + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
46.49 - + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
46.50 - + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
46.51 - + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
46.52 - + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
46.53 - + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
46.54 - + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
46.55 - + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.56 - + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.57 - + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
46.58 - + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
46.59 - + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
46.60 - + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
46.61 - + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
46.62 - + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
46.63 - + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.64 - + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.65 - + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
46.66 - + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
46.67 - + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
46.68 - + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
46.69 - + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
46.70 - + "{\"result_type\":\"recent\"},\"profile_image_url\":"
46.71 - + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.72 - + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
46.73 - + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
46.74 - + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
46.75 - + "\"since_id\":0,\"since_id_str\":\"0\"})"
46.76 - ))
46.77 - @BrwsrTest public void readFromTwttr() throws InterruptedException {
46.78 - if (page == null) {
46.79 - page = new TwitterModel();
46.80 - page.applyBindings();
46.81 - page.queryTweets("", "q=xyz");
46.82 - }
46.83 -
46.84 - if (page.getCurrentTweets().isEmpty()) {
46.85 - throw new InterruptedException();
46.86 - }
46.87 -
46.88 - assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
46.89 -
46.90 - String firstDate = page.getCurrentTweets().get(0).getCreated_at();
46.91 - assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
46.92 - }
46.93 -
46.94 - @Factory public static Object[] create() {
46.95 - return VMTest.create(TwitterProtocolTest.class);
46.96 - }
46.97 -}
47.1 --- a/javaquery/pom.xml Tue Feb 11 10:48:24 2014 +0100
47.2 +++ b/javaquery/pom.xml Tue Feb 11 13:31:42 2014 +0100
47.3 @@ -15,7 +15,6 @@
47.4 <module>api</module>
47.5 <module>demo-calculator</module>
47.6 <module>demo-calculator-dynamic</module>
47.7 - <module>demo-twitter</module>
47.8 - <module>canvas</module>
47.9 + <module>canvas</module>
47.10 </modules>
47.11 -</project>
47.12 \ No newline at end of file
47.13 +</project>
48.1 --- a/ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/VerifyArchetypeTest.java Tue Feb 11 10:48:24 2014 +0100
48.2 +++ b/ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/VerifyArchetypeTest.java Tue Feb 11 13:31:42 2014 +0100
48.3 @@ -74,8 +74,8 @@
48.4
48.5 v.verifyErrorFreeLog();
48.6
48.7 - // does pre-compilation to JavaScript
48.8 - v.verifyTextInLog("j2js");
48.9 + // no longer does pre-compilation to JavaScript
48.10 + // v.verifyTextInLog("j2js");
48.11 // uses Bck2BrwsrLauncher
48.12 v.verifyTextInLog("BaseHTTPLauncher showBrwsr");
48.13 // building zip:
49.1 --- a/ko/archetype/src/main/resources/archetype-resources/pom.xml Tue Feb 11 10:48:24 2014 +0100
49.2 +++ b/ko/archetype/src/main/resources/archetype-resources/pom.xml Tue Feb 11 13:31:42 2014 +0100
49.3 @@ -43,6 +43,7 @@
49.4 <bck2brwsr.launcher.version>${project.version}</bck2brwsr.launcher.version>
49.5 <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
49.6 <brwsr.startpage>pages/index.html</brwsr.startpage>
49.7 + <netbeans.compile.on.save>none</netbeans.compile.on.save>
49.8 </properties>
49.9 <build>
49.10 <plugins>
49.11 @@ -64,6 +65,19 @@
49.12 </configuration>
49.13 </plugin>
49.14 <plugin>
49.15 + <groupId>org.netbeans.html</groupId>
49.16 + <artifactId>html4j-maven-plugin</artifactId>
49.17 + <version>${net.java.html.version}</version>
49.18 + <executions>
49.19 + <execution>
49.20 + <id>js-classes</id>
49.21 + <goals>
49.22 + <goal>process-js-annotations</goal>
49.23 + </goals>
49.24 + </execution>
49.25 + </executions>
49.26 + </plugin>
49.27 + <plugin>
49.28 <groupId>org.apache.maven.plugins</groupId>
49.29 <artifactId>maven-compiler-plugin</artifactId>
49.30 <version>2.3.2</version>
49.31 @@ -110,7 +124,7 @@
49.32 <dependency>
49.33 <groupId>org.testng</groupId>
49.34 <artifactId>testng</artifactId>
49.35 - <version>6.5.2</version>
49.36 + <version>6.7</version>
49.37 <scope>test</scope>
49.38 </dependency>
49.39 <dependency>
49.40 @@ -126,11 +140,17 @@
49.41 <scope>test</scope>
49.42 </dependency>
49.43 <dependency>
49.44 - <groupId>org.apidesign.html</groupId>
49.45 + <groupId>org.netbeans.html</groupId>
49.46 <artifactId>net.java.html.json</artifactId>
49.47 <version>\${net.java.html.version}</version>
49.48 <type>jar</type>
49.49 </dependency>
49.50 + <dependency>
49.51 + <groupId>org.netbeans.html</groupId>
49.52 + <artifactId>net.java.html.boot</artifactId>
49.53 + <version>\${net.java.html.version}</version>
49.54 + <type>jar</type>
49.55 + </dependency>
49.56 </dependencies>
49.57 <profiles>
49.58 <profile>
49.59 @@ -182,8 +202,8 @@
49.60 </build>
49.61 <dependencies>
49.62 <dependency>
49.63 - <groupId>org.apidesign.html</groupId>
49.64 - <artifactId>ko-fx</artifactId>
49.65 + <groupId>org.netbeans.html</groupId>
49.66 + <artifactId>ko4j</artifactId>
49.67 <version>\${net.java.html.version}</version>
49.68 </dependency>
49.69 <dependency>
49.70 @@ -205,27 +225,15 @@
49.71 <build>
49.72 <plugins>
49.73 <plugin>
49.74 - <groupId>org.apidesign.bck2brwsr</groupId>
49.75 - <artifactId>bck2brwsr-maven-plugin</artifactId>
49.76 - <executions>
49.77 - <execution>
49.78 - <goals>
49.79 - <goal>j2js</goal>
49.80 - </goals>
49.81 - </execution>
49.82 - </executions>
49.83 - <configuration>
49.84 - <javascript>\${project.build.directory}/bck2brwsr.js</javascript>
49.85 - <obfuscation>\${bck2brwsr.obfuscationlevel}</obfuscation>
49.86 - </configuration>
49.87 - </plugin>
49.88 - <plugin>
49.89 <groupId>org.apache.maven.plugins</groupId>
49.90 <artifactId>maven-compiler-plugin</artifactId>
49.91 <configuration>
49.92 <compilerArguments>
49.93 <bootclasspath>netbeans.ignore.jdk.bootclasspath</bootclasspath>
49.94 </compilerArguments>
49.95 + <testExcludes>
49.96 + <exclude>**/JsInteractionTest*</exclude>
49.97 + </testExcludes>
49.98 </configuration>
49.99 </plugin>
49.100 <plugin>
49.101 @@ -261,6 +269,14 @@
49.102 <version>\${bck2brwsr.version}</version>
49.103 <scope>runtime</scope>
49.104 </dependency>
49.105 + <dependency>
49.106 + <groupId>org.apidesign.bck2brwsr</groupId>
49.107 + <artifactId>vm4brwsr</artifactId>
49.108 + <classifier>js</classifier>
49.109 + <type>zip</type>
49.110 + <version>\${bck2brwsr.version}</version>
49.111 + <scope>provided</scope>
49.112 + </dependency>
49.113 </dependencies>
49.114 </profile>
49.115 </profiles>
50.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml Tue Feb 11 10:48:24 2014 +0100
50.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml Tue Feb 11 13:31:42 2014 +0100
50.3 @@ -17,19 +17,18 @@
50.4 <include>*:rt</include>
50.5 </includes>
50.6 </dependencySet>
50.7 + <dependencySet>
50.8 + <useProjectArtifact>false</useProjectArtifact>
50.9 + <scope>provided</scope>
50.10 + <includes>
50.11 + <include>*:js</include>
50.12 + </includes>
50.13 + <unpack>true</unpack>
50.14 + <outputDirectory>/</outputDirectory>
50.15 + </dependencySet>
50.16 </dependencySets>
50.17 <fileSets>
50.18 <fileSet>
50.19 - <directory>${project.build.directory}/classes/${package.replace('.','/')}/</directory>
50.20 - <includes>
50.21 - <include>**/*</include>
50.22 - </includes>
50.23 - <excludes>
50.24 - <exclude>**/*.class</exclude>
50.25 - </excludes>
50.26 - <outputDirectory>/</outputDirectory>
50.27 - </fileSet>
50.28 - <fileSet>
50.29 <directory>src/main/webapp/pages</directory>
50.30 <outputDirectory>/</outputDirectory>
50.31 <filtered>true</filtered>
50.32 @@ -40,9 +39,5 @@
50.33 <source>${project.build.directory}/${project.build.finalName}.jar</source>
50.34 <outputDirectory>/</outputDirectory>
50.35 </file>
50.36 - <file>
50.37 - <source>${project.build.directory}/bck2brwsr.js</source>
50.38 - <outputDirectory>/</outputDirectory>
50.39 - </file>
50.40 </files>
50.41 </assembly>
50.42 \ No newline at end of file
51.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/java/DataModel.java Tue Feb 11 10:48:24 2014 +0100
51.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/java/DataModel.java Tue Feb 11 13:31:42 2014 +0100
51.3 @@ -25,7 +25,21 @@
51.4 @Function static void turnOn(Data model) {
51.5 model.setOn(true);
51.6 }
51.7 - @Function static void turnOff(Data model) {
51.8 - model.setOn(false);
51.9 +
51.10 + @Function static void turnOff(final Data model) {
51.11 + confirmByUser("Really turn off?", new Runnable() {
51.12 + @Override
51.13 + public void run() {
51.14 + model.setOn(false);
51.15 + }
51.16 + });
51.17 }
51.18 +
51.19 + /** Shows direct interaction with JavaScript */
51.20 + @net.java.html.js.JavaScriptBody(
51.21 + args = { "msg", "callback" },
51.22 + javacall = true,
51.23 + body = "alert(msg); callback.@java.lang.Runnable::run()();"
51.24 + )
51.25 + static native void confirmByUser(String msg, Runnable callback);
51.26 }
52.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html Tue Feb 11 10:48:24 2014 +0100
52.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html Tue Feb 11 13:31:42 2014 +0100
52.3 @@ -9,12 +9,21 @@
52.4 0% { -webkit-transform: rotate(0deg); }
52.5 100% { -webkit-transform: rotate(360deg); }
52.6 }
52.7 + @keyframes spin {
52.8 + 0% { transform: rotate(0deg); }
52.9 + 100% { transform: rotate(360deg); }
52.10 + }
52.11
52.12 .rotate {
52.13 -webkit-animation-name: spin;
52.14 -webkit-animation-duration: 3s;
52.15 -webkit-animation-iteration-count: infinite;
52.16 -webkit-animation-direction: alternate;
52.17 +
52.18 + animation-name: spin;
52.19 + animation-duration: 3s;
52.20 + animation-iteration-count: infinite;
52.21 + animation-direction: alternate;
52.22 }
52.23
52.24 #scene {
53.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
53.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/JsInteractionTest.java Tue Feb 11 13:31:42 2014 +0100
53.3 @@ -0,0 +1,103 @@
53.4 +package ${package};
53.5 +
53.6 +import java.io.Closeable;
53.7 +import java.io.Reader;
53.8 +import java.net.URL;
53.9 +import java.util.ArrayList;
53.10 +import java.util.List;
53.11 +import javax.script.Invocable;
53.12 +import javax.script.ScriptEngine;
53.13 +import javax.script.ScriptEngineManager;
53.14 +import javax.script.ScriptException;
53.15 +import org.apidesign.html.boot.spi.Fn;
53.16 +import static org.testng.Assert.assertEquals;
53.17 +import org.testng.annotations.AfterMethod;
53.18 +import org.testng.annotations.BeforeMethod;
53.19 +import org.testng.annotations.Test;
53.20 +
53.21 +/** Tests for behavior of @JavaScriptBody methods. Set your JavaScript
53.22 + * environment up (for example define <code>alert</code> or use some
53.23 + * emulation library like <em>env.js</em>), register script presenter
53.24 + * and then you can call methods that deal with JavaScript in your tests.
53.25 + */
53.26 +public class JsInteractionTest {
53.27 + private Closeable jsEngine;
53.28 + @BeforeMethod public void initializeJSEngine() throws Exception {
53.29 + jsEngine = Fn.activate(new ScriptPresenter());
53.30 + }
53.31 +
53.32 + @AfterMethod public void shutdownJSEngine() throws Exception {
53.33 + jsEngine.close();
53.34 + }
53.35 +
53.36 + @Test public void testCallbackFromJavaScript() throws Exception {
53.37 + class R implements Runnable {
53.38 + int called;
53.39 +
53.40 + @Override
53.41 + public void run() {
53.42 + called++;
53.43 + }
53.44 + }
53.45 + R callback = new R();
53.46 +
53.47 + DataModel.confirmByUser("Hello", callback);
53.48 +
53.49 + assertEquals(callback.called, 1, "One immediate callback");
53.50 + }
53.51 +
53.52 + private static class ScriptPresenter implements Fn.Presenter {
53.53 + private final ScriptEngine eng;
53.54 +
53.55 + public ScriptPresenter() throws ScriptException {
53.56 + eng = new ScriptEngineManager().getEngineByName("javascript");
53.57 + eng.eval("function alert(msg) { Packages.java.lang.System.out.println(msg); };");
53.58 + }
53.59 +
53.60 + @Override
53.61 + public Fn defineFn(String code, String... names) {
53.62 + StringBuilder sb = new StringBuilder();
53.63 + sb.append("(function() {");
53.64 + sb.append(" return function(");
53.65 + String sep = "";
53.66 + for (String n : names) {
53.67 + sb.append(sep).append(n);
53.68 + sep = ",";
53.69 + }
53.70 + sb.append(") {\n");
53.71 + sb.append(code);
53.72 + sb.append("};");
53.73 + sb.append("})()");
53.74 +
53.75 + final Object fn;
53.76 + try {
53.77 + fn = eng.eval(sb.toString());
53.78 + } catch (ScriptException ex) {
53.79 + throw new IllegalStateException(ex);
53.80 + }
53.81 + return new Fn(this) {
53.82 + @Override
53.83 + public Object invoke(Object thiz, Object... args) throws Exception {
53.84 + List<Object> all = new ArrayList<Object>(args.length + 1);
53.85 + all.add(thiz == null ? fn : thiz);
53.86 + for (int i = 0; i < args.length; i++) {
53.87 + all.add(args[i]);
53.88 + }
53.89 + Object ret = ((Invocable)eng).invokeMethod(fn, "call", all.toArray()); // NOI18N
53.90 + return fn.equals(ret) ? null : thiz;
53.91 + }
53.92 + };
53.93 + }
53.94 +
53.95 + @Override
53.96 + public void displayPage(URL page, Runnable onPageLoad) {
53.97 + // not really displaying anything
53.98 + onPageLoad.run();
53.99 + }
53.100 +
53.101 + @Override
53.102 + public void loadScript(Reader code) throws Exception {
53.103 + eng.eval(code);
53.104 + }
53.105 + }
53.106 +}
54.1 --- a/ko/bck2brwsr/pom.xml Tue Feb 11 10:48:24 2014 +0100
54.2 +++ b/ko/bck2brwsr/pom.xml Tue Feb 11 13:31:42 2014 +0100
54.3 @@ -62,6 +62,12 @@
54.4 <version>${project.version}</version>
54.5 <type>jar</type>
54.6 <scope>test</scope>
54.7 + <exclusions>
54.8 + <exclusion>
54.9 + <artifactId>json</artifactId>
54.10 + <groupId>org.json</groupId>
54.11 + </exclusion>
54.12 + </exclusions>
54.13 </dependency>
54.14 <dependency>
54.15 <groupId>org.apidesign.bck2brwsr</groupId>
54.16 @@ -74,14 +80,20 @@
54.17 <artifactId>launcher.http</artifactId>
54.18 <version>${project.version}</version>
54.19 <scope>test</scope>
54.20 + <exclusions>
54.21 + <exclusion>
54.22 + <artifactId>asm</artifactId>
54.23 + <groupId>org.ow2.asm</groupId>
54.24 + </exclusion>
54.25 + </exclusions>
54.26 </dependency>
54.27 <dependency>
54.28 - <groupId>org.apidesign.html</groupId>
54.29 + <groupId>org.netbeans.html</groupId>
54.30 <artifactId>net.java.html.json</artifactId>
54.31 <version>${net.java.html.version}</version>
54.32 </dependency>
54.33 <dependency>
54.34 - <groupId>org.apidesign.html</groupId>
54.35 + <groupId>org.netbeans.html</groupId>
54.36 <artifactId>net.java.html.json.tck</artifactId>
54.37 <version>${net.java.html.version}</version>
54.38 <scope>test</scope>
54.39 @@ -93,10 +105,31 @@
54.40 <type>jar</type>
54.41 </dependency>
54.42 <dependency>
54.43 - <groupId>org.apidesign.html</groupId>
54.44 + <groupId>org.netbeans.html</groupId>
54.45 <artifactId>net.java.html.boot</artifactId>
54.46 - <version>0.5</version>
54.47 + <version>${net.java.html.version}</version>
54.48 <type>jar</type>
54.49 + <exclusions>
54.50 + <exclusion>
54.51 + <artifactId>asm</artifactId>
54.52 + <groupId>org.ow2.asm</groupId>
54.53 + </exclusion>
54.54 + </exclusions>
54.55 + </dependency>
54.56 + <dependency>
54.57 + <groupId>org.netbeans.html</groupId>
54.58 + <artifactId>ko4j</artifactId>
54.59 + <version>${net.java.html.version}</version>
54.60 + <exclusions>
54.61 + <exclusion>
54.62 + <artifactId>json</artifactId>
54.63 + <groupId>org.json</groupId>
54.64 + </exclusion>
54.65 + <exclusion>
54.66 + <artifactId>org.json-osgi</artifactId>
54.67 + <groupId>de.twentyeleven.skysail</groupId>
54.68 + </exclusion>
54.69 + </exclusions>
54.70 </dependency>
54.71 </dependencies>
54.72 </project>
55.1 --- a/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/BrwsrCtxImpl.java Tue Feb 11 10:48:24 2014 +0100
55.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
55.3 @@ -1,166 +0,0 @@
55.4 -/**
55.5 - * Back 2 Browser Bytecode Translator
55.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
55.7 - *
55.8 - * This program is free software: you can redistribute it and/or modify
55.9 - * it under the terms of the GNU General Public License as published by
55.10 - * the Free Software Foundation, version 2 of the License.
55.11 - *
55.12 - * This program is distributed in the hope that it will be useful,
55.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
55.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
55.15 - * GNU General Public License for more details.
55.16 - *
55.17 - * You should have received a copy of the GNU General Public License
55.18 - * along with this program. Look for COPYING file in the top folder.
55.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
55.20 - */
55.21 -package org.apidesign.bck2brwsr.ko2brwsr;
55.22 -
55.23 -import java.io.ByteArrayOutputStream;
55.24 -import java.io.IOException;
55.25 -import java.io.InputStream;
55.26 -import java.io.InputStreamReader;
55.27 -import org.apidesign.html.json.spi.FunctionBinding;
55.28 -import org.apidesign.html.json.spi.JSONCall;
55.29 -import org.apidesign.html.json.spi.PropertyBinding;
55.30 -import org.apidesign.html.json.spi.Technology;
55.31 -import org.apidesign.html.json.spi.Transfer;
55.32 -import org.apidesign.html.json.spi.WSTransfer;
55.33 -
55.34 -/**
55.35 - *
55.36 - * @author Jaroslav Tulach <jtulach@netbeans.org>
55.37 - */
55.38 -final class BrwsrCtxImpl implements Technology<Object>, Transfer, WSTransfer<LoadWS> {
55.39 - private BrwsrCtxImpl() {}
55.40 -
55.41 - public static final BrwsrCtxImpl DEFAULT = new BrwsrCtxImpl();
55.42 -
55.43 - @Override
55.44 - public void extract(Object obj, String[] props, Object[] values) {
55.45 - ConvertTypes.extractJSON(obj, props, values);
55.46 - }
55.47 -
55.48 - @Override
55.49 - public void loadJSON(final JSONCall call) {
55.50 - class R implements Runnable {
55.51 - final boolean success;
55.52 -
55.53 - public R(boolean success) {
55.54 - this.success = success;
55.55 - }
55.56 -
55.57 - Object[] arr = { null };
55.58 - @Override
55.59 - public void run() {
55.60 - if (success) {
55.61 - call.notifySuccess(arr[0]);
55.62 - } else {
55.63 - Throwable t;
55.64 - if (arr[0] instanceof Throwable) {
55.65 - t = (Throwable) arr[0];
55.66 - } else {
55.67 - if (arr[0] == null) {
55.68 - t = new IOException();
55.69 - } else {
55.70 - t = new IOException(arr[0].toString());
55.71 - }
55.72 - }
55.73 - call.notifyError(t);
55.74 - }
55.75 - }
55.76 - }
55.77 - R success = new R(true);
55.78 - R failure = new R(false);
55.79 - if (call.isJSONP()) {
55.80 - String me = ConvertTypes.createJSONP(success.arr, success);
55.81 - ConvertTypes.loadJSONP(call.composeURL(me), me);
55.82 - } else {
55.83 - String data = null;
55.84 - if (call.isDoOutput()) {
55.85 - try {
55.86 - ByteArrayOutputStream bos = new ByteArrayOutputStream();
55.87 - call.writeData(bos);
55.88 - data = new String(bos.toByteArray(), "UTF-8");
55.89 - } catch (IOException ex) {
55.90 - call.notifyError(ex);
55.91 - }
55.92 - }
55.93 - ConvertTypes.loadJSON(call.composeURL(null), success.arr, success, failure, call.getMethod(), data);
55.94 - }
55.95 - }
55.96 -
55.97 - @Override
55.98 - public Object wrapModel(Object model) {
55.99 - return model;
55.100 - }
55.101 -
55.102 - @Override
55.103 - public void bind(PropertyBinding b, Object model, Object data) {
55.104 - Knockout.bind(data, b, b.getPropertyName(),
55.105 - "getValue__Ljava_lang_Object_2",
55.106 - b.isReadOnly() ? null : "setValue__VLjava_lang_Object_2",
55.107 - false, false
55.108 - );
55.109 - }
55.110 -
55.111 - @Override
55.112 - public void valueHasMutated(Object data, String propertyName) {
55.113 - Knockout.valueHasMutated(data, propertyName);
55.114 - }
55.115 -
55.116 - @Override
55.117 - public void expose(FunctionBinding fb, Object model, Object d) {
55.118 - Knockout.expose(d, fb, fb.getFunctionName(), "call__VLjava_lang_Object_2Ljava_lang_Object_2");
55.119 - }
55.120 -
55.121 - @Override
55.122 - public void applyBindings(Object data) {
55.123 - Knockout.applyBindings(data);
55.124 - }
55.125 -
55.126 - @Override
55.127 - public Object wrapArray(Object[] arr) {
55.128 - return arr;
55.129 - }
55.130 -
55.131 - @Override
55.132 - public <M> M toModel(Class<M> modelClass, Object data) {
55.133 - return modelClass.cast(data);
55.134 - }
55.135 -
55.136 - @Override
55.137 - public Object toJSON(InputStream is) throws IOException {
55.138 - StringBuilder sb = new StringBuilder();
55.139 - InputStreamReader r = new InputStreamReader(is);
55.140 - for (;;) {
55.141 - int ch = r.read();
55.142 - if (ch == -1) {
55.143 - break;
55.144 - }
55.145 - sb.append((char)ch);
55.146 - }
55.147 - return ConvertTypes.parse(sb.toString());
55.148 - }
55.149 -
55.150 - @Override
55.151 - public void runSafe(Runnable r) {
55.152 - r.run();
55.153 - }
55.154 -
55.155 - @Override
55.156 - public LoadWS open(String url, JSONCall callback) {
55.157 - return new LoadWS(callback, url);
55.158 - }
55.159 -
55.160 - @Override
55.161 - public void send(LoadWS socket, JSONCall data) {
55.162 - socket.send(data);
55.163 - }
55.164 -
55.165 - @Override
55.166 - public void close(LoadWS socket) {
55.167 - socket.close();
55.168 - }
55.169 -}
56.1 --- a/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/BrwsrCtxPrvdr.java Tue Feb 11 10:48:24 2014 +0100
56.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
56.3 @@ -1,53 +0,0 @@
56.4 -/**
56.5 - * Back 2 Browser Bytecode Translator
56.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
56.7 - *
56.8 - * This program is free software: you can redistribute it and/or modify
56.9 - * it under the terms of the GNU General Public License as published by
56.10 - * the Free Software Foundation, version 2 of the License.
56.11 - *
56.12 - * This program is distributed in the hope that it will be useful,
56.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
56.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56.15 - * GNU General Public License for more details.
56.16 - *
56.17 - * You should have received a copy of the GNU General Public License
56.18 - * along with this program. Look for COPYING file in the top folder.
56.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
56.20 - */
56.21 -package org.apidesign.bck2brwsr.ko2brwsr;
56.22 -
56.23 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
56.24 -import org.apidesign.html.context.spi.Contexts;
56.25 -import org.apidesign.html.json.spi.Technology;
56.26 -import org.apidesign.html.json.spi.Transfer;
56.27 -import org.apidesign.html.json.spi.WSTransfer;
56.28 -import org.openide.util.lookup.ServiceProvider;
56.29 -
56.30 -/** This is an implementation package - just
56.31 - * include its JAR on classpath and use official {@link Context} API
56.32 - * to access the functionality.
56.33 - * <p>
56.34 - * Provides binding between models and <a href="http://bck2brwsr.apidesign.org">
56.35 - * Bck2Brwsr</a> VM.
56.36 - * Registers {@link ContextProvider}, so {@link ServiceLoader} can find it.
56.37 - *
56.38 - * @author Jaroslav Tulach <jtulach@netbeans.org>
56.39 - */
56.40 -@ServiceProvider(service = Contexts.Provider.class)
56.41 -public final class BrwsrCtxPrvdr implements Contexts.Provider {
56.42 -
56.43 - @Override
56.44 - public void fillContext(Contexts.Builder context, Class<?> requestor) {
56.45 - if (bck2BrwsrVM()) {
56.46 - context.register(Technology.class, BrwsrCtxImpl.DEFAULT, 50).
56.47 - register(Transfer.class, BrwsrCtxImpl.DEFAULT, 50).
56.48 - register(WSTransfer.class, BrwsrCtxImpl.DEFAULT, 50);
56.49 - }
56.50 - }
56.51 -
56.52 - @JavaScriptBody(args = { }, body = "return true;")
56.53 - private static boolean bck2BrwsrVM() {
56.54 - return false;
56.55 - }
56.56 -}
57.1 --- a/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/ConvertTypes.java Tue Feb 11 10:48:24 2014 +0100
57.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
57.3 @@ -1,157 +0,0 @@
57.4 -/**
57.5 - * Back 2 Browser Bytecode Translator
57.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
57.7 - *
57.8 - * This program is free software: you can redistribute it and/or modify
57.9 - * it under the terms of the GNU General Public License as published by
57.10 - * the Free Software Foundation, version 2 of the License.
57.11 - *
57.12 - * This program is distributed in the hope that it will be useful,
57.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
57.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
57.15 - * GNU General Public License for more details.
57.16 - *
57.17 - * You should have received a copy of the GNU General Public License
57.18 - * along with this program. Look for COPYING file in the top folder.
57.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
57.20 - */
57.21 -package org.apidesign.bck2brwsr.ko2brwsr;
57.22 -
57.23 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
57.24 -
57.25 -/**
57.26 - *
57.27 - * @author Jaroslav Tulach <jtulach@netbeans.org>
57.28 - */
57.29 -final class ConvertTypes {
57.30 - ConvertTypes() {
57.31 - }
57.32 -
57.33 - public static String toString(Object object, String property) {
57.34 - Object ret = getProperty(object, property);
57.35 - return ret == null ? null : ret.toString();
57.36 - }
57.37 -
57.38 - public static double toDouble(Object object, String property) {
57.39 - Object ret = getProperty(object, property);
57.40 - return ret instanceof Number ? ((Number)ret).doubleValue() : Double.NaN;
57.41 - }
57.42 -
57.43 - public static int toInt(Object object, String property) {
57.44 - Object ret = getProperty(object, property);
57.45 - return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE;
57.46 - }
57.47 -
57.48 - public static <T> T toModel(Class<T> modelClass, Object object, String property) {
57.49 - Object ret = getProperty(object, property);
57.50 - if (ret == null || modelClass.isInstance(ret)) {
57.51 - return modelClass.cast(ret);
57.52 - }
57.53 - throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
57.54 - }
57.55 -
57.56 - public static String toJSON(Object value) {
57.57 - if (value == null) {
57.58 - return "null";
57.59 - }
57.60 - if (value instanceof Enum) {
57.61 - value = value.toString();
57.62 - }
57.63 - if (value instanceof String) {
57.64 - return '"' +
57.65 - ((String)value).
57.66 - replace("\"", "\\\"").
57.67 - replace("\n", "\\n").
57.68 - replace("\r", "\\r").
57.69 - replace("\t", "\\t")
57.70 - + '"';
57.71 - }
57.72 - return value.toString();
57.73 - }
57.74 -
57.75 - @JavaScriptBody(args = { "object", "property" },
57.76 - body =
57.77 - "if (property === null) return object;\n"
57.78 - + "if (object === null) return null;\n"
57.79 - + "var p = object[property]; return p ? p : null;"
57.80 - )
57.81 - private static Object getProperty(Object object, String property) {
57.82 - return null;
57.83 - }
57.84 -
57.85 - public static String createJSONP(Object[] jsonResult, Runnable whenDone) {
57.86 - int h = whenDone.hashCode();
57.87 - String name;
57.88 - for (;;) {
57.89 - name = "jsonp" + Integer.toHexString(h);
57.90 - if (defineIfUnused(name, jsonResult, whenDone)) {
57.91 - return name;
57.92 - }
57.93 - h++;
57.94 - }
57.95 - }
57.96 -
57.97 - @JavaScriptBody(args = { "name", "arr", "run" }, body =
57.98 - "if (window[name]) return false;\n "
57.99 - + "window[name] = function(data) {\n "
57.100 - + " delete window[name];\n"
57.101 - + " var el = window.document.getElementById(name);\n"
57.102 - + " el.parentNode.removeChild(el);\n"
57.103 - + " arr[0] = data;\n"
57.104 - + " run.run__V();\n"
57.105 - + "};\n"
57.106 - + "return true;\n"
57.107 - )
57.108 - private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
57.109 - return true;
57.110 - }
57.111 -
57.112 - @JavaScriptBody(args = { "s" }, body = "return eval('(' + s + ')');")
57.113 - static Object parse(String s) {
57.114 - return s;
57.115 - }
57.116 -
57.117 - @JavaScriptBody(args = { "url", "arr", "callback", "onError", "method", "data" }, body = ""
57.118 - + "var request = new XMLHttpRequest();\n"
57.119 - + "if (!method) method = 'GET';\n"
57.120 - + "request.open(method, url, true);\n"
57.121 - + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
57.122 - + "request.onreadystatechange = function() {\n"
57.123 - + " if (this.readyState!==4) return;\n"
57.124 - + " try {\n"
57.125 - + " arr[0] = eval('(' + this.response + ')');\n"
57.126 - + " } catch (error) {;\n"
57.127 - + " arr[0] = this.response;\n"
57.128 - + " }\n"
57.129 - + " callback.run__V();\n"
57.130 - + "};\n"
57.131 - + "request.onerror = function (e) {\n"
57.132 - + " arr[0] = e; onError.run__V();\n"
57.133 - + "}\n"
57.134 - + "if (data) request.send(data);"
57.135 - + "else request.send();"
57.136 - )
57.137 - static void loadJSON(
57.138 - String url, Object[] jsonResult, Runnable whenDone, Runnable whenErr, String method, String data
57.139 - ) {
57.140 - }
57.141 -
57.142 - @JavaScriptBody(args = { "url", "jsonp" }, body =
57.143 - "var scrpt = window.document.createElement('script');\n "
57.144 - + "scrpt.setAttribute('src', url);\n "
57.145 - + "scrpt.setAttribute('id', jsonp);\n "
57.146 - + "scrpt.setAttribute('type', 'text/javascript');\n "
57.147 - + "var body = document.getElementsByTagName('body')[0];\n "
57.148 - + "body.appendChild(scrpt);\n"
57.149 - )
57.150 - static void loadJSONP(String url, String jsonp) {
57.151 -
57.152 - }
57.153 -
57.154 - public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
57.155 - for (int i = 0; i < props.length; i++) {
57.156 - values[i] = getProperty(jsonObject, props[i]);
57.157 - }
57.158 - }
57.159 -
57.160 -}
58.1 --- a/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/Knockout.java Tue Feb 11 10:48:24 2014 +0100
58.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
58.3 @@ -1,131 +0,0 @@
58.4 -/**
58.5 - * Back 2 Browser Bytecode Translator
58.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
58.7 - *
58.8 - * This program is free software: you can redistribute it and/or modify
58.9 - * it under the terms of the GNU General Public License as published by
58.10 - * the Free Software Foundation, version 2 of the License.
58.11 - *
58.12 - * This program is distributed in the hope that it will be useful,
58.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
58.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
58.15 - * GNU General Public License for more details.
58.16 - *
58.17 - * You should have received a copy of the GNU General Public License
58.18 - * along with this program. Look for COPYING file in the top folder.
58.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
58.20 - */
58.21 -package org.apidesign.bck2brwsr.ko2brwsr;
58.22 -
58.23 -import java.lang.reflect.Method;
58.24 -import java.util.List;
58.25 -import org.apidesign.bck2brwsr.core.ExtraJavaScript;
58.26 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
58.27 -
58.28 -/** Provides binding between models and bck2brwsr VM.
58.29 - *
58.30 - * @author Jaroslav Tulach <jtulach@netbeans.org>
58.31 - */
58.32 -@ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
58.33 -final class Knockout {
58.34 - /** used by tests */
58.35 - static Knockout next;
58.36 - private final Object model;
58.37 -
58.38 - Knockout(Object model) {
58.39 - this.model = model == null ? this : model;
58.40 - }
58.41 -
58.42 - public static <M> Knockout applyBindings(
58.43 - Object model, String[] propsGettersAndSetters,
58.44 - String[] methodsAndSignatures
58.45 - ) {
58.46 - applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
58.47 - return new Knockout(model);
58.48 - }
58.49 - public static <M> Knockout applyBindings(
58.50 - Class<M> modelClass, M model, String[] propsGettersAndSetters,
58.51 - String[] methodsAndSignatures
58.52 - ) {
58.53 - Knockout bindings = next;
58.54 - next = null;
58.55 - if (bindings == null) {
58.56 - bindings = new Knockout(null);
58.57 - }
58.58 - applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
58.59 - applyBindings(bindings);
58.60 - return bindings;
58.61 - }
58.62 -
58.63 - public void valueHasMutated(String prop) {
58.64 - valueHasMutated(model, prop);
58.65 - }
58.66 - @JavaScriptBody(args = { "self", "prop" }, body =
58.67 - "var p = self[prop]; if (p) p.valueHasMutated();"
58.68 - )
58.69 - public static void valueHasMutated(Object self, String prop) {
58.70 - }
58.71 -
58.72 -
58.73 - @JavaScriptBody(args = { "id", "ev" }, body = "ko.utils.triggerEvent(window.document.getElementById(id), ev.substring(2));")
58.74 - public static void triggerEvent(String id, String ev) {
58.75 - }
58.76 -
58.77 - @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
58.78 - "var bnd = {\n"
58.79 - + " 'read': function() {\n"
58.80 - + " var v = model[getter]();\n"
58.81 - + " if (array) v = v.koArray(); else if (v !== null) v = v.valueOf();\n"
58.82 - + " return v;\n"
58.83 - + " },\n"
58.84 - + " 'owner': bindings\n"
58.85 - + "};\n"
58.86 - + "if (setter != null) {\n"
58.87 - + " bnd['write'] = function(val) {\n"
58.88 - + " var v = val === null ? null : val.valueOf();"
58.89 - + " model[setter](v);\n"
58.90 - + " };\n"
58.91 - + "}\n"
58.92 - + "bindings[prop] = ko['computed'](bnd);"
58.93 - )
58.94 - static void bind(
58.95 - Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
58.96 - ) {
58.97 - }
58.98 -
58.99 - @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
58.100 - "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
58.101 - )
58.102 - static void expose(
58.103 - Object bindings, Object model, String prop, String sig
58.104 - ) {
58.105 - }
58.106 -
58.107 - @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
58.108 - static void applyBindings(Object bindings) {}
58.109 -
58.110 - private static void applyImpl(
58.111 - String[] propsGettersAndSetters,
58.112 - Class<?> modelClass,
58.113 - Object bindings,
58.114 - Object model,
58.115 - String[] methodsAndSignatures
58.116 - ) throws IllegalStateException, SecurityException {
58.117 - for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
58.118 - try {
58.119 - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
58.120 - bind(bindings, model, propsGettersAndSetters[i],
58.121 - propsGettersAndSetters[i + 1],
58.122 - propsGettersAndSetters[i + 2],
58.123 - getter.getReturnType().isPrimitive(),
58.124 - List.class.isAssignableFrom(getter.getReturnType()));
58.125 - } catch (NoSuchMethodException ex) {
58.126 - throw new IllegalStateException(ex.getMessage());
58.127 - }
58.128 - }
58.129 - for (int i = 0; i < methodsAndSignatures.length; i += 2) {
58.130 - expose(
58.131 - bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
58.132 - }
58.133 - }
58.134 -}
59.1 --- a/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/LoadWS.java Tue Feb 11 10:48:24 2014 +0100
59.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
59.3 @@ -1,126 +0,0 @@
59.4 -/**
59.5 - * Back 2 Browser Bytecode Translator
59.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
59.7 - *
59.8 - * This program is free software: you can redistribute it and/or modify
59.9 - * it under the terms of the GNU General Public License as published by
59.10 - * the Free Software Foundation, version 2 of the License.
59.11 - *
59.12 - * This program is distributed in the hope that it will be useful,
59.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
59.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59.15 - * GNU General Public License for more details.
59.16 - *
59.17 - * You should have received a copy of the GNU General Public License
59.18 - * along with this program. Look for COPYING file in the top folder.
59.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
59.20 - */
59.21 -package org.apidesign.bck2brwsr.ko2brwsr;
59.22 -
59.23 -import net.java.html.js.JavaScriptBody;
59.24 -import org.apidesign.html.json.spi.JSONCall;
59.25 -
59.26 -/** Communication with WebSockets for WebView 1.8.
59.27 - *
59.28 - * @author Jaroslav Tulach <jtulach@netbeans.org>
59.29 - */
59.30 -final class LoadWS {
59.31 - private static final boolean SUPPORTED = isWebSocket();
59.32 - private final Object ws;
59.33 - private final JSONCall call;
59.34 - LoadWS(JSONCall first, String url) {
59.35 - call = first;
59.36 - ws = initWebSocket(this, url);
59.37 - if (ws == null) {
59.38 - first.notifyError(new IllegalArgumentException("Wrong URL: " + url));
59.39 - }
59.40 - }
59.41 -
59.42 - static boolean isSupported() {
59.43 - return SUPPORTED;
59.44 - }
59.45 -
59.46 - void send(JSONCall call) {
59.47 - push(call);
59.48 - }
59.49 -
59.50 - private synchronized void push(JSONCall call) {
59.51 - send(ws, call.getMessage());
59.52 - }
59.53 -
59.54 - void onOpen(Object ev) {
59.55 - if (!call.isDoOutput()) {
59.56 - call.notifySuccess(null);
59.57 - }
59.58 - }
59.59 -
59.60 -
59.61 - @JavaScriptBody(args = { "data" }, body = "try {\n"
59.62 - + " return eval('(' + data + ')');\n"
59.63 - + " } catch (error) {;\n"
59.64 - + " return data;\n"
59.65 - + " }\n"
59.66 - )
59.67 - private static native Object toJSON(String data);
59.68 -
59.69 - void onMessage(Object ev, String data) {
59.70 - Object json = toJSON(data);
59.71 - call.notifySuccess(json);
59.72 - }
59.73 -
59.74 - void onError(Object ev) {
59.75 - call.notifyError(new Exception(ev.toString()));
59.76 - }
59.77 -
59.78 - void onClose(boolean wasClean, int code, String reason) {
59.79 - call.notifyError(null);
59.80 - }
59.81 -
59.82 - @JavaScriptBody(args = {}, body = "if (window.WebSocket) return true; else return false;")
59.83 - private static boolean isWebSocket() {
59.84 - return false;
59.85 - }
59.86 -
59.87 - @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
59.88 - + "if (window.WebSocket) {\n"
59.89 - + " try {\n"
59.90 - + " var ws = new window.WebSocket(url);\n"
59.91 - + " ws.onopen = function(ev) {\n"
59.92 - + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onOpen(Ljava/lang/Object;)(ev);\n"
59.93 - + " };\n"
59.94 - + " ws.onmessage = function(ev) {\n"
59.95 - + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data);\n"
59.96 - + " };\n"
59.97 - + " ws.onerror = function(ev) {\n"
59.98 - + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onError(Ljava/lang/Object;)(ev);\n"
59.99 - + " };\n"
59.100 - + " ws.onclose = function(ev) {\n"
59.101 - + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason);\n"
59.102 - + " };\n"
59.103 - + " return ws;\n"
59.104 - + " } catch (ex) {\n"
59.105 - + " return null;\n"
59.106 - + " }\n"
59.107 - + "} else {\n"
59.108 - + " return null;\n"
59.109 - + "}\n"
59.110 - )
59.111 - private static Object initWebSocket(Object back, String url) {
59.112 - return null;
59.113 - }
59.114 -
59.115 -
59.116 - @JavaScriptBody(args = { "ws", "msg" }, body = ""
59.117 - + "ws.send(msg);"
59.118 - )
59.119 - private void send(Object ws, String msg) {
59.120 - }
59.121 -
59.122 - @JavaScriptBody(args = { "ws" }, body = "ws.close();")
59.123 - private static void close(Object ws) {
59.124 - }
59.125 -
59.126 - void close() {
59.127 - close(ws);
59.128 - }
59.129 -}
60.1 --- a/ko/bck2brwsr/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Tue Feb 11 10:48:24 2014 +0100
60.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
60.3 @@ -1,3614 +0,0 @@
60.4 -/*
60.5 - * HTML via Java(tm) Language Bindings
60.6 - * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
60.7 - *
60.8 - * This program is free software: you can redistribute it and/or modify
60.9 - * it under the terms of the GNU General Public License as published by
60.10 - * the Free Software Foundation, version 2 of the License.
60.11 - *
60.12 - * This program is distributed in the hope that it will be useful,
60.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
60.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60.15 - * GNU General Public License for more details. apidesign.org
60.16 - * designates this particular file as subject to the
60.17 - * "Classpath" exception as provided by apidesign.org
60.18 - * in the License file that accompanied this code.
60.19 - *
60.20 - * You should have received a copy of the GNU General Public License
60.21 - * along with this program. Look for COPYING file in the top folder.
60.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
60.23 - */
60.24 -// Knockout JavaScript library v2.2.1
60.25 -// (c) Steven Sanderson - http://knockoutjs.com/
60.26 -// License: MIT (http://www.opensource.org/licenses/mit-license.php)
60.27 -
60.28 -(function(){
60.29 -var DEBUG=true;
60.30 -(function(window,document,navigator,jQuery,undefined){
60.31 -!function(factory) {
60.32 - // Support three module loading scenarios
60.33 - if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
60.34 - // [1] CommonJS/Node.js
60.35 - var target = module['exports'] || exports; // module.exports is for Node.js
60.36 - factory(target);
60.37 - } else if (typeof define === 'function' && define['amd']) {
60.38 - // [2] AMD anonymous module
60.39 - define(['exports'], factory);
60.40 - } else {
60.41 - // [3] No module loader (plain <script> tag) - put directly in global namespace
60.42 - factory(window['ko'] = {});
60.43 - }
60.44 -}(function(koExports){
60.45 -// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
60.46 -// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
60.47 -var ko = typeof koExports !== 'undefined' ? koExports : {};
60.48 -// Google Closure Compiler helpers (used only to make the minified file smaller)
60.49 -ko.exportSymbol = function(koPath, object) {
60.50 - var tokens = koPath.split(".");
60.51 -
60.52 - // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
60.53 - // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
60.54 - var target = ko;
60.55 -
60.56 - for (var i = 0; i < tokens.length - 1; i++)
60.57 - target = target[tokens[i]];
60.58 - target[tokens[tokens.length - 1]] = object;
60.59 -};
60.60 -ko.exportProperty = function(owner, publicName, object) {
60.61 - owner[publicName] = object;
60.62 -};
60.63 -ko.version = "2.2.1";
60.64 -
60.65 -ko.exportSymbol('version', ko.version);
60.66 -ko.utils = new (function () {
60.67 - var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
60.68 -
60.69 - // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
60.70 - var knownEvents = {}, knownEventTypesByEventName = {};
60.71 - var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
60.72 - knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
60.73 - knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
60.74 - for (var eventType in knownEvents) {
60.75 - var knownEventsForType = knownEvents[eventType];
60.76 - if (knownEventsForType.length) {
60.77 - for (var i = 0, j = knownEventsForType.length; i < j; i++)
60.78 - knownEventTypesByEventName[knownEventsForType[i]] = eventType;
60.79 - }
60.80 - }
60.81 - var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
60.82 -
60.83 - // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
60.84 - // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
60.85 - // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
60.86 - // If there is a future need to detect specific versions of IE10+, we will amend this.
60.87 - var ieVersion = (function() {
60.88 - var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
60.89 -
60.90 - // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
60.91 - while (
60.92 - div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
60.93 - iElems[0]
60.94 - );
60.95 - return version > 4 ? version : undefined;
60.96 - }());
60.97 - var isIe6 = ieVersion === 6,
60.98 - isIe7 = ieVersion === 7;
60.99 -
60.100 - function isClickOnCheckableElement(element, eventType) {
60.101 - if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
60.102 - if (eventType.toLowerCase() != "click") return false;
60.103 - var inputType = element.type;
60.104 - return (inputType == "checkbox") || (inputType == "radio");
60.105 - }
60.106 -
60.107 - return {
60.108 - fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
60.109 -
60.110 - arrayForEach: function (array, action) {
60.111 - for (var i = 0, j = array.length; i < j; i++)
60.112 - action(array[i]);
60.113 - },
60.114 -
60.115 - arrayIndexOf: function (array, item) {
60.116 - if (typeof Array.prototype.indexOf == "function")
60.117 - return Array.prototype.indexOf.call(array, item);
60.118 - for (var i = 0, j = array.length; i < j; i++)
60.119 - if (array[i] === item)
60.120 - return i;
60.121 - return -1;
60.122 - },
60.123 -
60.124 - arrayFirst: function (array, predicate, predicateOwner) {
60.125 - for (var i = 0, j = array.length; i < j; i++)
60.126 - if (predicate.call(predicateOwner, array[i]))
60.127 - return array[i];
60.128 - return null;
60.129 - },
60.130 -
60.131 - arrayRemoveItem: function (array, itemToRemove) {
60.132 - var index = ko.utils.arrayIndexOf(array, itemToRemove);
60.133 - if (index >= 0)
60.134 - array.splice(index, 1);
60.135 - },
60.136 -
60.137 - arrayGetDistinctValues: function (array) {
60.138 - array = array || [];
60.139 - var result = [];
60.140 - for (var i = 0, j = array.length; i < j; i++) {
60.141 - if (ko.utils.arrayIndexOf(result, array[i]) < 0)
60.142 - result.push(array[i]);
60.143 - }
60.144 - return result;
60.145 - },
60.146 -
60.147 - arrayMap: function (array, mapping) {
60.148 - array = array || [];
60.149 - var result = [];
60.150 - for (var i = 0, j = array.length; i < j; i++)
60.151 - result.push(mapping(array[i]));
60.152 - return result;
60.153 - },
60.154 -
60.155 - arrayFilter: function (array, predicate) {
60.156 - array = array || [];
60.157 - var result = [];
60.158 - for (var i = 0, j = array.length; i < j; i++)
60.159 - if (predicate(array[i]))
60.160 - result.push(array[i]);
60.161 - return result;
60.162 - },
60.163 -
60.164 - arrayPushAll: function (array, valuesToPush) {
60.165 - if (valuesToPush instanceof Array)
60.166 - array.push.apply(array, valuesToPush);
60.167 - else
60.168 - for (var i = 0, j = valuesToPush.length; i < j; i++)
60.169 - array.push(valuesToPush[i]);
60.170 - return array;
60.171 - },
60.172 -
60.173 - extend: function (target, source) {
60.174 - if (source) {
60.175 - for(var prop in source) {
60.176 - if(source.hasOwnProperty(prop)) {
60.177 - target[prop] = source[prop];
60.178 - }
60.179 - }
60.180 - }
60.181 - return target;
60.182 - },
60.183 -
60.184 - emptyDomNode: function (domNode) {
60.185 - while (domNode.firstChild) {
60.186 - ko.removeNode(domNode.firstChild);
60.187 - }
60.188 - },
60.189 -
60.190 - moveCleanedNodesToContainerElement: function(nodes) {
60.191 - // Ensure it's a real array, as we're about to reparent the nodes and
60.192 - // we don't want the underlying collection to change while we're doing that.
60.193 - var nodesArray = ko.utils.makeArray(nodes);
60.194 -
60.195 - var container = document.createElement('div');
60.196 - for (var i = 0, j = nodesArray.length; i < j; i++) {
60.197 - container.appendChild(ko.cleanNode(nodesArray[i]));
60.198 - }
60.199 - return container;
60.200 - },
60.201 -
60.202 - cloneNodes: function (nodesArray, shouldCleanNodes) {
60.203 - for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
60.204 - var clonedNode = nodesArray[i].cloneNode(true);
60.205 - newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
60.206 - }
60.207 - return newNodesArray;
60.208 - },
60.209 -
60.210 - setDomNodeChildren: function (domNode, childNodes) {
60.211 - ko.utils.emptyDomNode(domNode);
60.212 - if (childNodes) {
60.213 - for (var i = 0, j = childNodes.length; i < j; i++)
60.214 - domNode.appendChild(childNodes[i]);
60.215 - }
60.216 - },
60.217 -
60.218 - replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
60.219 - var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
60.220 - if (nodesToReplaceArray.length > 0) {
60.221 - var insertionPoint = nodesToReplaceArray[0];
60.222 - var parent = insertionPoint.parentNode;
60.223 - for (var i = 0, j = newNodesArray.length; i < j; i++)
60.224 - parent.insertBefore(newNodesArray[i], insertionPoint);
60.225 - for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
60.226 - ko.removeNode(nodesToReplaceArray[i]);
60.227 - }
60.228 - }
60.229 - },
60.230 -
60.231 - setOptionNodeSelectionState: function (optionNode, isSelected) {
60.232 - // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
60.233 - if (ieVersion < 7)
60.234 - optionNode.setAttribute("selected", isSelected);
60.235 - else
60.236 - optionNode.selected = isSelected;
60.237 - },
60.238 -
60.239 - stringTrim: function (string) {
60.240 - return (string || "").replace(stringTrimRegex, "");
60.241 - },
60.242 -
60.243 - stringTokenize: function (string, delimiter) {
60.244 - var result = [];
60.245 - var tokens = (string || "").split(delimiter);
60.246 - for (var i = 0, j = tokens.length; i < j; i++) {
60.247 - var trimmed = ko.utils.stringTrim(tokens[i]);
60.248 - if (trimmed !== "")
60.249 - result.push(trimmed);
60.250 - }
60.251 - return result;
60.252 - },
60.253 -
60.254 - stringStartsWith: function (string, startsWith) {
60.255 - string = string || "";
60.256 - if (startsWith.length > string.length)
60.257 - return false;
60.258 - return string.substring(0, startsWith.length) === startsWith;
60.259 - },
60.260 -
60.261 - domNodeIsContainedBy: function (node, containedByNode) {
60.262 - if (containedByNode.compareDocumentPosition)
60.263 - return (containedByNode.compareDocumentPosition(node) & 16) == 16;
60.264 - while (node != null) {
60.265 - if (node == containedByNode)
60.266 - return true;
60.267 - node = node.parentNode;
60.268 - }
60.269 - return false;
60.270 - },
60.271 -
60.272 - domNodeIsAttachedToDocument: function (node) {
60.273 - return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
60.274 - },
60.275 -
60.276 - tagNameLower: function(element) {
60.277 - // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
60.278 - // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
60.279 - // we don't need to do the .toLowerCase() as it will always be lower case anyway.
60.280 - return element && element.tagName && element.tagName.toLowerCase();
60.281 - },
60.282 -
60.283 - registerEventHandler: function (element, eventType, handler) {
60.284 - var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
60.285 - if (!mustUseAttachEvent && typeof jQuery != "undefined") {
60.286 - if (isClickOnCheckableElement(element, eventType)) {
60.287 - // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
60.288 - // it toggles the element checked state *after* the click event handlers run, whereas native
60.289 - // click events toggle the checked state *before* the event handler.
60.290 - // Fix this by intecepting the handler and applying the correct checkedness before it runs.
60.291 - var originalHandler = handler;
60.292 - handler = function(event, eventData) {
60.293 - var jQuerySuppliedCheckedState = this.checked;
60.294 - if (eventData)
60.295 - this.checked = eventData.checkedStateBeforeEvent !== true;
60.296 - originalHandler.call(this, event);
60.297 - this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
60.298 - };
60.299 - }
60.300 - jQuery(element)['bind'](eventType, handler);
60.301 - } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
60.302 - element.addEventListener(eventType, handler, false);
60.303 - else if (typeof element.attachEvent != "undefined")
60.304 - element.attachEvent("on" + eventType, function (event) {
60.305 - handler.call(element, event);
60.306 - });
60.307 - else
60.308 - throw new Error("Browser doesn't support addEventListener or attachEvent");
60.309 - },
60.310 -
60.311 - triggerEvent: function (element, eventType) {
60.312 - if (!(element && element.nodeType))
60.313 - throw new Error("element must be a DOM node when calling triggerEvent");
60.314 -
60.315 - if (typeof jQuery != "undefined") {
60.316 - var eventData = [];
60.317 - if (isClickOnCheckableElement(element, eventType)) {
60.318 - // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
60.319 - eventData.push({ checkedStateBeforeEvent: element.checked });
60.320 - }
60.321 - jQuery(element)['trigger'](eventType, eventData);
60.322 - } else if (typeof document.createEvent == "function") {
60.323 - if (typeof element.dispatchEvent == "function") {
60.324 - var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
60.325 - var event = document.createEvent(eventCategory);
60.326 - event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
60.327 - element.dispatchEvent(event);
60.328 - }
60.329 - else
60.330 - throw new Error("The supplied element doesn't support dispatchEvent");
60.331 - } else if (typeof element.fireEvent != "undefined") {
60.332 - // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
60.333 - // so to make it consistent, we'll do it manually here
60.334 - if (isClickOnCheckableElement(element, eventType))
60.335 - element.checked = element.checked !== true;
60.336 - element.fireEvent("on" + eventType);
60.337 - }
60.338 - else
60.339 - throw new Error("Browser doesn't support triggering events");
60.340 - },
60.341 -
60.342 - unwrapObservable: function (value) {
60.343 - return ko.isObservable(value) ? value() : value;
60.344 - },
60.345 -
60.346 - peekObservable: function (value) {
60.347 - return ko.isObservable(value) ? value.peek() : value;
60.348 - },
60.349 -
60.350 - toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
60.351 - if (classNames) {
60.352 - var cssClassNameRegex = /[\w-]+/g,
60.353 - currentClassNames = node.className.match(cssClassNameRegex) || [];
60.354 - ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
60.355 - var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
60.356 - if (indexOfClass >= 0) {
60.357 - if (!shouldHaveClass)
60.358 - currentClassNames.splice(indexOfClass, 1);
60.359 - } else {
60.360 - if (shouldHaveClass)
60.361 - currentClassNames.push(className);
60.362 - }
60.363 - });
60.364 - node.className = currentClassNames.join(" ");
60.365 - }
60.366 - },
60.367 -
60.368 - setTextContent: function(element, textContent) {
60.369 - var value = ko.utils.unwrapObservable(textContent);
60.370 - if ((value === null) || (value === undefined))
60.371 - value = "";
60.372 -
60.373 - if (element.nodeType === 3) {
60.374 - element.data = value;
60.375 - } else {
60.376 - // We need there to be exactly one child: a text node.
60.377 - // If there are no children, more than one, or if it's not a text node,
60.378 - // we'll clear everything and create a single text node.
60.379 - var innerTextNode = ko.virtualElements.firstChild(element);
60.380 - if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
60.381 - ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
60.382 - } else {
60.383 - innerTextNode.data = value;
60.384 - }
60.385 -
60.386 - ko.utils.forceRefresh(element);
60.387 - }
60.388 - },
60.389 -
60.390 - setElementName: function(element, name) {
60.391 - element.name = name;
60.392 -
60.393 - // Workaround IE 6/7 issue
60.394 - // - https://github.com/SteveSanderson/knockout/issues/197
60.395 - // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
60.396 - if (ieVersion <= 7) {
60.397 - try {
60.398 - element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
60.399 - }
60.400 - catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
60.401 - }
60.402 - },
60.403 -
60.404 - forceRefresh: function(node) {
60.405 - // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
60.406 - if (ieVersion >= 9) {
60.407 - // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
60.408 - var elem = node.nodeType == 1 ? node : node.parentNode;
60.409 - if (elem.style)
60.410 - elem.style.zoom = elem.style.zoom;
60.411 - }
60.412 - },
60.413 -
60.414 - ensureSelectElementIsRenderedCorrectly: function(selectElement) {
60.415 - // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
60.416 - // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
60.417 - if (ieVersion >= 9) {
60.418 - var originalWidth = selectElement.style.width;
60.419 - selectElement.style.width = 0;
60.420 - selectElement.style.width = originalWidth;
60.421 - }
60.422 - },
60.423 -
60.424 - range: function (min, max) {
60.425 - min = ko.utils.unwrapObservable(min);
60.426 - max = ko.utils.unwrapObservable(max);
60.427 - var result = [];
60.428 - for (var i = min; i <= max; i++)
60.429 - result.push(i);
60.430 - return result;
60.431 - },
60.432 -
60.433 - makeArray: function(arrayLikeObject) {
60.434 - var result = [];
60.435 - for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
60.436 - result.push(arrayLikeObject[i]);
60.437 - };
60.438 - return result;
60.439 - },
60.440 -
60.441 - isIe6 : isIe6,
60.442 - isIe7 : isIe7,
60.443 - ieVersion : ieVersion,
60.444 -
60.445 - getFormFields: function(form, fieldName) {
60.446 - var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
60.447 - var isMatchingField = (typeof fieldName == 'string')
60.448 - ? function(field) { return field.name === fieldName }
60.449 - : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
60.450 - var matches = [];
60.451 - for (var i = fields.length - 1; i >= 0; i--) {
60.452 - if (isMatchingField(fields[i]))
60.453 - matches.push(fields[i]);
60.454 - };
60.455 - return matches;
60.456 - },
60.457 -
60.458 - parseJson: function (jsonString) {
60.459 - if (typeof jsonString == "string") {
60.460 - jsonString = ko.utils.stringTrim(jsonString);
60.461 - if (jsonString) {
60.462 - if (window.JSON && window.JSON.parse) // Use native parsing where available
60.463 - return window.JSON.parse(jsonString);
60.464 - return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
60.465 - }
60.466 - }
60.467 - return null;
60.468 - },
60.469 -
60.470 - stringifyJson: function (data, replacer, space) { // replacer and space are optional
60.471 - if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
60.472 - throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
60.473 - return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
60.474 - },
60.475 -
60.476 - postJson: function (urlOrForm, data, options) {
60.477 - options = options || {};
60.478 - var params = options['params'] || {};
60.479 - var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
60.480 - var url = urlOrForm;
60.481 -
60.482 - // If we were given a form, use its 'action' URL and pick out any requested field values
60.483 - if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
60.484 - var originalForm = urlOrForm;
60.485 - url = originalForm.action;
60.486 - for (var i = includeFields.length - 1; i >= 0; i--) {
60.487 - var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
60.488 - for (var j = fields.length - 1; j >= 0; j--)
60.489 - params[fields[j].name] = fields[j].value;
60.490 - }
60.491 - }
60.492 -
60.493 - data = ko.utils.unwrapObservable(data);
60.494 - var form = document.createElement("form");
60.495 - form.style.display = "none";
60.496 - form.action = url;
60.497 - form.method = "post";
60.498 - for (var key in data) {
60.499 - var input = document.createElement("input");
60.500 - input.name = key;
60.501 - input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
60.502 - form.appendChild(input);
60.503 - }
60.504 - for (var key in params) {
60.505 - var input = document.createElement("input");
60.506 - input.name = key;
60.507 - input.value = params[key];
60.508 - form.appendChild(input);
60.509 - }
60.510 - document.body.appendChild(form);
60.511 - options['submitter'] ? options['submitter'](form) : form.submit();
60.512 - setTimeout(function () { form.parentNode.removeChild(form); }, 0);
60.513 - }
60.514 - }
60.515 -})();
60.516 -
60.517 -ko.exportSymbol('utils', ko.utils);
60.518 -ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
60.519 -ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
60.520 -ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
60.521 -ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
60.522 -ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
60.523 -ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
60.524 -ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
60.525 -ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
60.526 -ko.exportSymbol('utils.extend', ko.utils.extend);
60.527 -ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
60.528 -ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
60.529 -ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
60.530 -ko.exportSymbol('utils.postJson', ko.utils.postJson);
60.531 -ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
60.532 -ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
60.533 -ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
60.534 -ko.exportSymbol('utils.range', ko.utils.range);
60.535 -ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
60.536 -ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
60.537 -ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
60.538 -
60.539 -if (!Function.prototype['bind']) {
60.540 - // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
60.541 - // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
60.542 - Function.prototype['bind'] = function (object) {
60.543 - var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
60.544 - return function () {
60.545 - return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
60.546 - };
60.547 - };
60.548 -}
60.549 -
60.550 -ko.utils.domData = new (function () {
60.551 - var uniqueId = 0;
60.552 - var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
60.553 - var dataStore = {};
60.554 - return {
60.555 - get: function (node, key) {
60.556 - var allDataForNode = ko.utils.domData.getAll(node, false);
60.557 - return allDataForNode === undefined ? undefined : allDataForNode[key];
60.558 - },
60.559 - set: function (node, key, value) {
60.560 - if (value === undefined) {
60.561 - // Make sure we don't actually create a new domData key if we are actually deleting a value
60.562 - if (ko.utils.domData.getAll(node, false) === undefined)
60.563 - return;
60.564 - }
60.565 - var allDataForNode = ko.utils.domData.getAll(node, true);
60.566 - allDataForNode[key] = value;
60.567 - },
60.568 - getAll: function (node, createIfNotFound) {
60.569 - var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
60.570 - var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
60.571 - if (!hasExistingDataStore) {
60.572 - if (!createIfNotFound)
60.573 - return undefined;
60.574 - dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
60.575 - dataStore[dataStoreKey] = {};
60.576 - }
60.577 - return dataStore[dataStoreKey];
60.578 - },
60.579 - clear: function (node) {
60.580 - var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
60.581 - if (dataStoreKey) {
60.582 - delete dataStore[dataStoreKey];
60.583 - node[dataStoreKeyExpandoPropertyName] = null;
60.584 - return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
60.585 - }
60.586 - return false;
60.587 - }
60.588 - }
60.589 -})();
60.590 -
60.591 -ko.exportSymbol('utils.domData', ko.utils.domData);
60.592 -ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
60.593 -
60.594 -ko.utils.domNodeDisposal = new (function () {
60.595 - var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
60.596 - var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
60.597 - var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
60.598 -
60.599 - function getDisposeCallbacksCollection(node, createIfNotFound) {
60.600 - var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
60.601 - if ((allDisposeCallbacks === undefined) && createIfNotFound) {
60.602 - allDisposeCallbacks = [];
60.603 - ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
60.604 - }
60.605 - return allDisposeCallbacks;
60.606 - }
60.607 - function destroyCallbacksCollection(node) {
60.608 - ko.utils.domData.set(node, domDataKey, undefined);
60.609 - }
60.610 -
60.611 - function cleanSingleNode(node) {
60.612 - // Run all the dispose callbacks
60.613 - var callbacks = getDisposeCallbacksCollection(node, false);
60.614 - if (callbacks) {
60.615 - callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
60.616 - for (var i = 0; i < callbacks.length; i++)
60.617 - callbacks[i](node);
60.618 - }
60.619 -
60.620 - // Also erase the DOM data
60.621 - ko.utils.domData.clear(node);
60.622 -
60.623 - // Special support for jQuery here because it's so commonly used.
60.624 - // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
60.625 - // so notify it to tear down any resources associated with the node & descendants here.
60.626 - if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
60.627 - jQuery['cleanData']([node]);
60.628 -
60.629 - // Also clear any immediate-child comment nodes, as these wouldn't have been found by
60.630 - // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
60.631 - if (cleanableNodeTypesWithDescendants[node.nodeType])
60.632 - cleanImmediateCommentTypeChildren(node);
60.633 - }
60.634 -
60.635 - function cleanImmediateCommentTypeChildren(nodeWithChildren) {
60.636 - var child, nextChild = nodeWithChildren.firstChild;
60.637 - while (child = nextChild) {
60.638 - nextChild = child.nextSibling;
60.639 - if (child.nodeType === 8)
60.640 - cleanSingleNode(child);
60.641 - }
60.642 - }
60.643 -
60.644 - return {
60.645 - addDisposeCallback : function(node, callback) {
60.646 - if (typeof callback != "function")
60.647 - throw new Error("Callback must be a function");
60.648 - getDisposeCallbacksCollection(node, true).push(callback);
60.649 - },
60.650 -
60.651 - removeDisposeCallback : function(node, callback) {
60.652 - var callbacksCollection = getDisposeCallbacksCollection(node, false);
60.653 - if (callbacksCollection) {
60.654 - ko.utils.arrayRemoveItem(callbacksCollection, callback);
60.655 - if (callbacksCollection.length == 0)
60.656 - destroyCallbacksCollection(node);
60.657 - }
60.658 - },
60.659 -
60.660 - cleanNode : function(node) {
60.661 - // First clean this node, where applicable
60.662 - if (cleanableNodeTypes[node.nodeType]) {
60.663 - cleanSingleNode(node);
60.664 -
60.665 - // ... then its descendants, where applicable
60.666 - if (cleanableNodeTypesWithDescendants[node.nodeType]) {
60.667 - // Clone the descendants list in case it changes during iteration
60.668 - var descendants = [];
60.669 - ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
60.670 - for (var i = 0, j = descendants.length; i < j; i++)
60.671 - cleanSingleNode(descendants[i]);
60.672 - }
60.673 - }
60.674 - return node;
60.675 - },
60.676 -
60.677 - removeNode : function(node) {
60.678 - ko.cleanNode(node);
60.679 - if (node.parentNode)
60.680 - node.parentNode.removeChild(node);
60.681 - }
60.682 - }
60.683 -})();
60.684 -ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
60.685 -ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
60.686 -ko.exportSymbol('cleanNode', ko.cleanNode);
60.687 -ko.exportSymbol('removeNode', ko.removeNode);
60.688 -ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
60.689 -ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
60.690 -ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
60.691 -(function () {
60.692 - var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
60.693 -
60.694 - function simpleHtmlParse(html) {
60.695 - // Based on jQuery's "clean" function, but only accounting for table-related elements.
60.696 - // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
60.697 -
60.698 - // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
60.699 - // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
60.700 - // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
60.701 - // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
60.702 -
60.703 - // Trim whitespace, otherwise indexOf won't work as expected
60.704 - var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
60.705 -
60.706 - // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
60.707 - var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
60.708 - !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
60.709 - (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
60.710 - /* anything else */ [0, "", ""];
60.711 -
60.712 - // Go to html and back, then peel off extra wrappers
60.713 - // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
60.714 - var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
60.715 - if (typeof window['innerShiv'] == "function") {
60.716 - div.appendChild(window['innerShiv'](markup));
60.717 - } else {
60.718 - div.innerHTML = markup;
60.719 - }
60.720 -
60.721 - // Move to the right depth
60.722 - while (wrap[0]--)
60.723 - div = div.lastChild;
60.724 -
60.725 - return ko.utils.makeArray(div.lastChild.childNodes);
60.726 - }
60.727 -
60.728 - function jQueryHtmlParse(html) {
60.729 - // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
60.730 - if (jQuery['parseHTML']) {
60.731 - return jQuery['parseHTML'](html);
60.732 - } else {
60.733 - // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
60.734 - var elems = jQuery['clean']([html]);
60.735 -
60.736 - // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
60.737 - // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
60.738 - // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
60.739 - if (elems && elems[0]) {
60.740 - // Find the top-most parent element that's a direct child of a document fragment
60.741 - var elem = elems[0];
60.742 - while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
60.743 - elem = elem.parentNode;
60.744 - // ... then detach it
60.745 - if (elem.parentNode)
60.746 - elem.parentNode.removeChild(elem);
60.747 - }
60.748 -
60.749 - return elems;
60.750 - }
60.751 - }
60.752 -
60.753 - ko.utils.parseHtmlFragment = function(html) {
60.754 - return typeof jQuery != 'undefined' ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
60.755 - : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
60.756 - };
60.757 -
60.758 - ko.utils.setHtml = function(node, html) {
60.759 - ko.utils.emptyDomNode(node);
60.760 -
60.761 - // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
60.762 - html = ko.utils.unwrapObservable(html);
60.763 -
60.764 - if ((html !== null) && (html !== undefined)) {
60.765 - if (typeof html != 'string')
60.766 - html = html.toString();
60.767 -
60.768 - // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
60.769 - // for example <tr> elements which are not normally allowed to exist on their own.
60.770 - // If you've referenced jQuery we'll use that rather than duplicating its code.
60.771 - if (typeof jQuery != 'undefined') {
60.772 - jQuery(node)['html'](html);
60.773 - } else {
60.774 - // ... otherwise, use KO's own parsing logic.
60.775 - var parsedNodes = ko.utils.parseHtmlFragment(html);
60.776 - for (var i = 0; i < parsedNodes.length; i++)
60.777 - node.appendChild(parsedNodes[i]);
60.778 - }
60.779 - }
60.780 - };
60.781 -})();
60.782 -
60.783 -ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
60.784 -ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
60.785 -
60.786 -ko.memoization = (function () {
60.787 - var memos = {};
60.788 -
60.789 - function randomMax8HexChars() {
60.790 - return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
60.791 - }
60.792 - function generateRandomId() {
60.793 - return randomMax8HexChars() + randomMax8HexChars();
60.794 - }
60.795 - function findMemoNodes(rootNode, appendToArray) {
60.796 - if (!rootNode)
60.797 - return;
60.798 - if (rootNode.nodeType == 8) {
60.799 - var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
60.800 - if (memoId != null)
60.801 - appendToArray.push({ domNode: rootNode, memoId: memoId });
60.802 - } else if (rootNode.nodeType == 1) {
60.803 - for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
60.804 - findMemoNodes(childNodes[i], appendToArray);
60.805 - }
60.806 - }
60.807 -
60.808 - return {
60.809 - memoize: function (callback) {
60.810 - if (typeof callback != "function")
60.811 - throw new Error("You can only pass a function to ko.memoization.memoize()");
60.812 - var memoId = generateRandomId();
60.813 - memos[memoId] = callback;
60.814 - return "<!--[ko_memo:" + memoId + "]-->";
60.815 - },
60.816 -
60.817 - unmemoize: function (memoId, callbackParams) {
60.818 - var callback = memos[memoId];
60.819 - if (callback === undefined)
60.820 - throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
60.821 - try {
60.822 - callback.apply(null, callbackParams || []);
60.823 - return true;
60.824 - }
60.825 - finally { delete memos[memoId]; }
60.826 - },
60.827 -
60.828 - unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
60.829 - var memos = [];
60.830 - findMemoNodes(domNode, memos);
60.831 - for (var i = 0, j = memos.length; i < j; i++) {
60.832 - var node = memos[i].domNode;
60.833 - var combinedParams = [node];
60.834 - if (extraCallbackParamsArray)
60.835 - ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
60.836 - ko.memoization.unmemoize(memos[i].memoId, combinedParams);
60.837 - node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
60.838 - if (node.parentNode)
60.839 - node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
60.840 - }
60.841 - },
60.842 -
60.843 - parseMemoText: function (memoText) {
60.844 - var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
60.845 - return match ? match[1] : null;
60.846 - }
60.847 - };
60.848 -})();
60.849 -
60.850 -ko.exportSymbol('memoization', ko.memoization);
60.851 -ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
60.852 -ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
60.853 -ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
60.854 -ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
60.855 -ko.extenders = {
60.856 - 'throttle': function(target, timeout) {
60.857 - // Throttling means two things:
60.858 -
60.859 - // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
60.860 - // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
60.861 - target['throttleEvaluation'] = timeout;
60.862 -
60.863 - // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
60.864 - // so the target cannot change value synchronously or faster than a certain rate
60.865 - var writeTimeoutInstance = null;
60.866 - return ko.dependentObservable({
60.867 - 'read': target,
60.868 - 'write': function(value) {
60.869 - clearTimeout(writeTimeoutInstance);
60.870 - writeTimeoutInstance = setTimeout(function() {
60.871 - target(value);
60.872 - }, timeout);
60.873 - }
60.874 - });
60.875 - },
60.876 -
60.877 - 'notify': function(target, notifyWhen) {
60.878 - target["equalityComparer"] = notifyWhen == "always"
60.879 - ? function() { return false } // Treat all values as not equal
60.880 - : ko.observable["fn"]["equalityComparer"];
60.881 - return target;
60.882 - }
60.883 -};
60.884 -
60.885 -function applyExtenders(requestedExtenders) {
60.886 - var target = this;
60.887 - if (requestedExtenders) {
60.888 - for (var key in requestedExtenders) {
60.889 - var extenderHandler = ko.extenders[key];
60.890 - if (typeof extenderHandler == 'function') {
60.891 - target = extenderHandler(target, requestedExtenders[key]);
60.892 - }
60.893 - }
60.894 - }
60.895 - return target;
60.896 -}
60.897 -
60.898 -ko.exportSymbol('extenders', ko.extenders);
60.899 -
60.900 -ko.subscription = function (target, callback, disposeCallback) {
60.901 - this.target = target;
60.902 - this.callback = callback;
60.903 - this.disposeCallback = disposeCallback;
60.904 - ko.exportProperty(this, 'dispose', this.dispose);
60.905 -};
60.906 -ko.subscription.prototype.dispose = function () {
60.907 - this.isDisposed = true;
60.908 - this.disposeCallback();
60.909 -};
60.910 -
60.911 -ko.subscribable = function () {
60.912 - this._subscriptions = {};
60.913 -
60.914 - ko.utils.extend(this, ko.subscribable['fn']);
60.915 - ko.exportProperty(this, 'subscribe', this.subscribe);
60.916 - ko.exportProperty(this, 'extend', this.extend);
60.917 - ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
60.918 -}
60.919 -
60.920 -var defaultEvent = "change";
60.921 -
60.922 -ko.subscribable['fn'] = {
60.923 - subscribe: function (callback, callbackTarget, event) {
60.924 - event = event || defaultEvent;
60.925 - var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
60.926 -
60.927 - var subscription = new ko.subscription(this, boundCallback, function () {
60.928 - ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
60.929 - }.bind(this));
60.930 -
60.931 - if (!this._subscriptions[event])
60.932 - this._subscriptions[event] = [];
60.933 - this._subscriptions[event].push(subscription);
60.934 - return subscription;
60.935 - },
60.936 -
60.937 - "notifySubscribers": function (valueToNotify, event) {
60.938 - event = event || defaultEvent;
60.939 - if (this._subscriptions[event]) {
60.940 - ko.dependencyDetection.ignore(function() {
60.941 - ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
60.942 - // In case a subscription was disposed during the arrayForEach cycle, check
60.943 - // for isDisposed on each subscription before invoking its callback
60.944 - if (subscription && (subscription.isDisposed !== true))
60.945 - subscription.callback(valueToNotify);
60.946 - });
60.947 - }, this);
60.948 - }
60.949 - },
60.950 -
60.951 - getSubscriptionsCount: function () {
60.952 - var total = 0;
60.953 - for (var eventName in this._subscriptions) {
60.954 - if (this._subscriptions.hasOwnProperty(eventName))
60.955 - total += this._subscriptions[eventName].length;
60.956 - }
60.957 - return total;
60.958 - },
60.959 -
60.960 - extend: applyExtenders
60.961 -};
60.962 -
60.963 -
60.964 -ko.isSubscribable = function (instance) {
60.965 - return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
60.966 -};
60.967 -
60.968 -ko.exportSymbol('subscribable', ko.subscribable);
60.969 -ko.exportSymbol('isSubscribable', ko.isSubscribable);
60.970 -
60.971 -ko.dependencyDetection = (function () {
60.972 - var _frames = [];
60.973 -
60.974 - return {
60.975 - begin: function (callback) {
60.976 - _frames.push({ callback: callback, distinctDependencies:[] });
60.977 - },
60.978 -
60.979 - end: function () {
60.980 - _frames.pop();
60.981 - },
60.982 -
60.983 - registerDependency: function (subscribable) {
60.984 - if (!ko.isSubscribable(subscribable))
60.985 - throw new Error("Only subscribable things can act as dependencies");
60.986 - if (_frames.length > 0) {
60.987 - var topFrame = _frames[_frames.length - 1];
60.988 - if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
60.989 - return;
60.990 - topFrame.distinctDependencies.push(subscribable);
60.991 - topFrame.callback(subscribable);
60.992 - }
60.993 - },
60.994 -
60.995 - ignore: function(callback, callbackTarget, callbackArgs) {
60.996 - try {
60.997 - _frames.push(null);
60.998 - return callback.apply(callbackTarget, callbackArgs || []);
60.999 - } finally {
60.1000 - _frames.pop();
60.1001 - }
60.1002 - }
60.1003 - };
60.1004 -})();
60.1005 -var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
60.1006 -
60.1007 -ko.observable = function (initialValue) {
60.1008 - var _latestValue = initialValue;
60.1009 -
60.1010 - function observable() {
60.1011 - if (arguments.length > 0) {
60.1012 - // Write
60.1013 -
60.1014 - // Ignore writes if the value hasn't changed
60.1015 - if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
60.1016 - observable.valueWillMutate();
60.1017 - _latestValue = arguments[0];
60.1018 - if (DEBUG) observable._latestValue = _latestValue;
60.1019 - observable.valueHasMutated();
60.1020 - }
60.1021 - return this; // Permits chained assignments
60.1022 - }
60.1023 - else {
60.1024 - // Read
60.1025 - ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
60.1026 - return _latestValue;
60.1027 - }
60.1028 - }
60.1029 - if (DEBUG) observable._latestValue = _latestValue;
60.1030 - ko.subscribable.call(observable);
60.1031 - observable.peek = function() { return _latestValue };
60.1032 - observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
60.1033 - observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
60.1034 - ko.utils.extend(observable, ko.observable['fn']);
60.1035 -
60.1036 - ko.exportProperty(observable, 'peek', observable.peek);
60.1037 - ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
60.1038 - ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
60.1039 -
60.1040 - return observable;
60.1041 -}
60.1042 -
60.1043 -ko.observable['fn'] = {
60.1044 - "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
60.1045 - var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
60.1046 - return oldValueIsPrimitive ? (a === b) : false;
60.1047 - }
60.1048 -};
60.1049 -
60.1050 -var protoProperty = ko.observable.protoProperty = "__ko_proto__";
60.1051 -ko.observable['fn'][protoProperty] = ko.observable;
60.1052 -
60.1053 -ko.hasPrototype = function(instance, prototype) {
60.1054 - if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
60.1055 - if (instance[protoProperty] === prototype) return true;
60.1056 - return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
60.1057 -};
60.1058 -
60.1059 -ko.isObservable = function (instance) {
60.1060 - return ko.hasPrototype(instance, ko.observable);
60.1061 -}
60.1062 -ko.isWriteableObservable = function (instance) {
60.1063 - // Observable
60.1064 - if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
60.1065 - return true;
60.1066 - // Writeable dependent observable
60.1067 - if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
60.1068 - return true;
60.1069 - // Anything else
60.1070 - return false;
60.1071 -}
60.1072 -
60.1073 -
60.1074 -ko.exportSymbol('observable', ko.observable);
60.1075 -ko.exportSymbol('isObservable', ko.isObservable);
60.1076 -ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
60.1077 -ko.observableArray = function (initialValues) {
60.1078 - if (arguments.length == 0) {
60.1079 - // Zero-parameter constructor initializes to empty array
60.1080 - initialValues = [];
60.1081 - }
60.1082 - if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
60.1083 - throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
60.1084 -
60.1085 - var result = ko.observable(initialValues);
60.1086 - ko.utils.extend(result, ko.observableArray['fn']);
60.1087 - return result;
60.1088 -}
60.1089 -
60.1090 -ko.observableArray['fn'] = {
60.1091 - 'remove': function (valueOrPredicate) {
60.1092 - var underlyingArray = this.peek();
60.1093 - var removedValues = [];
60.1094 - var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
60.1095 - for (var i = 0; i < underlyingArray.length; i++) {
60.1096 - var value = underlyingArray[i];
60.1097 - if (predicate(value)) {
60.1098 - if (removedValues.length === 0) {
60.1099 - this.valueWillMutate();
60.1100 - }
60.1101 - removedValues.push(value);
60.1102 - underlyingArray.splice(i, 1);
60.1103 - i--;
60.1104 - }
60.1105 - }
60.1106 - if (removedValues.length) {
60.1107 - this.valueHasMutated();
60.1108 - }
60.1109 - return removedValues;
60.1110 - },
60.1111 -
60.1112 - 'removeAll': function (arrayOfValues) {
60.1113 - // If you passed zero args, we remove everything
60.1114 - if (arrayOfValues === undefined) {
60.1115 - var underlyingArray = this.peek();
60.1116 - var allValues = underlyingArray.slice(0);
60.1117 - this.valueWillMutate();
60.1118 - underlyingArray.splice(0, underlyingArray.length);
60.1119 - this.valueHasMutated();
60.1120 - return allValues;
60.1121 - }
60.1122 - // If you passed an arg, we interpret it as an array of entries to remove
60.1123 - if (!arrayOfValues)
60.1124 - return [];
60.1125 - return this['remove'](function (value) {
60.1126 - return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
60.1127 - });
60.1128 - },
60.1129 -
60.1130 - 'destroy': function (valueOrPredicate) {
60.1131 - var underlyingArray = this.peek();
60.1132 - var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
60.1133 - this.valueWillMutate();
60.1134 - for (var i = underlyingArray.length - 1; i >= 0; i--) {
60.1135 - var value = underlyingArray[i];
60.1136 - if (predicate(value))
60.1137 - underlyingArray[i]["_destroy"] = true;
60.1138 - }
60.1139 - this.valueHasMutated();
60.1140 - },
60.1141 -
60.1142 - 'destroyAll': function (arrayOfValues) {
60.1143 - // If you passed zero args, we destroy everything
60.1144 - if (arrayOfValues === undefined)
60.1145 - return this['destroy'](function() { return true });
60.1146 -
60.1147 - // If you passed an arg, we interpret it as an array of entries to destroy
60.1148 - if (!arrayOfValues)
60.1149 - return [];
60.1150 - return this['destroy'](function (value) {
60.1151 - return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
60.1152 - });
60.1153 - },
60.1154 -
60.1155 - 'indexOf': function (item) {
60.1156 - var underlyingArray = this();
60.1157 - return ko.utils.arrayIndexOf(underlyingArray, item);
60.1158 - },
60.1159 -
60.1160 - 'replace': function(oldItem, newItem) {
60.1161 - var index = this['indexOf'](oldItem);
60.1162 - if (index >= 0) {
60.1163 - this.valueWillMutate();
60.1164 - this.peek()[index] = newItem;
60.1165 - this.valueHasMutated();
60.1166 - }
60.1167 - }
60.1168 -}
60.1169 -
60.1170 -// Populate ko.observableArray.fn with read/write functions from native arrays
60.1171 -// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
60.1172 -// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
60.1173 -ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
60.1174 - ko.observableArray['fn'][methodName] = function () {
60.1175 - // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
60.1176 - // (for consistency with mutating regular observables)
60.1177 - var underlyingArray = this.peek();
60.1178 - this.valueWillMutate();
60.1179 - var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
60.1180 - this.valueHasMutated();
60.1181 - return methodCallResult;
60.1182 - };
60.1183 -});
60.1184 -
60.1185 -// Populate ko.observableArray.fn with read-only functions from native arrays
60.1186 -ko.utils.arrayForEach(["slice"], function (methodName) {
60.1187 - ko.observableArray['fn'][methodName] = function () {
60.1188 - var underlyingArray = this();
60.1189 - return underlyingArray[methodName].apply(underlyingArray, arguments);
60.1190 - };
60.1191 -});
60.1192 -
60.1193 -ko.exportSymbol('observableArray', ko.observableArray);
60.1194 -ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
60.1195 - var _latestValue,
60.1196 - _hasBeenEvaluated = false,
60.1197 - _isBeingEvaluated = false,
60.1198 - readFunction = evaluatorFunctionOrOptions;
60.1199 -
60.1200 - if (readFunction && typeof readFunction == "object") {
60.1201 - // Single-parameter syntax - everything is on this "options" param
60.1202 - options = readFunction;
60.1203 - readFunction = options["read"];
60.1204 - } else {
60.1205 - // Multi-parameter syntax - construct the options according to the params passed
60.1206 - options = options || {};
60.1207 - if (!readFunction)
60.1208 - readFunction = options["read"];
60.1209 - }
60.1210 - if (typeof readFunction != "function")
60.1211 - throw new Error("Pass a function that returns the value of the ko.computed");
60.1212 -
60.1213 - function addSubscriptionToDependency(subscribable) {
60.1214 - _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
60.1215 - }
60.1216 -
60.1217 - function disposeAllSubscriptionsToDependencies() {
60.1218 - ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
60.1219 - subscription.dispose();
60.1220 - });
60.1221 - _subscriptionsToDependencies = [];
60.1222 - }
60.1223 -
60.1224 - function evaluatePossiblyAsync() {
60.1225 - var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
60.1226 - if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
60.1227 - clearTimeout(evaluationTimeoutInstance);
60.1228 - evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
60.1229 - } else
60.1230 - evaluateImmediate();
60.1231 - }
60.1232 -
60.1233 - function evaluateImmediate() {
60.1234 - if (_isBeingEvaluated) {
60.1235 - // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
60.1236 - // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
60.1237 - // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
60.1238 - // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
60.1239 - return;
60.1240 - }
60.1241 -
60.1242 - // Don't dispose on first evaluation, because the "disposeWhen" callback might
60.1243 - // e.g., dispose when the associated DOM element isn't in the doc, and it's not
60.1244 - // going to be in the doc until *after* the first evaluation
60.1245 - if (_hasBeenEvaluated && disposeWhen()) {
60.1246 - dispose();
60.1247 - return;
60.1248 - }
60.1249 -
60.1250 - _isBeingEvaluated = true;
60.1251 - try {
60.1252 - // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
60.1253 - // Then, during evaluation, we cross off any that are in fact still being used.
60.1254 - var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
60.1255 -
60.1256 - ko.dependencyDetection.begin(function(subscribable) {
60.1257 - var inOld;
60.1258 - if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
60.1259 - disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
60.1260 - else
60.1261 - addSubscriptionToDependency(subscribable); // Brand new subscription - add it
60.1262 - });
60.1263 -
60.1264 - var newValue = readFunction.call(evaluatorFunctionTarget);
60.1265 -
60.1266 - // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
60.1267 - for (var i = disposalCandidates.length - 1; i >= 0; i--) {
60.1268 - if (disposalCandidates[i])
60.1269 - _subscriptionsToDependencies.splice(i, 1)[0].dispose();
60.1270 - }
60.1271 - _hasBeenEvaluated = true;
60.1272 -
60.1273 - dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
60.1274 - _latestValue = newValue;
60.1275 - if (DEBUG) dependentObservable._latestValue = _latestValue;
60.1276 - } finally {
60.1277 - ko.dependencyDetection.end();
60.1278 - }
60.1279 -
60.1280 - dependentObservable["notifySubscribers"](_latestValue);
60.1281 - _isBeingEvaluated = false;
60.1282 - if (!_subscriptionsToDependencies.length)
60.1283 - dispose();
60.1284 - }
60.1285 -
60.1286 - function dependentObservable() {
60.1287 - if (arguments.length > 0) {
60.1288 - if (typeof writeFunction === "function") {
60.1289 - // Writing a value
60.1290 - writeFunction.apply(evaluatorFunctionTarget, arguments);
60.1291 - } else {
60.1292 - throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
60.1293 - }
60.1294 - return this; // Permits chained assignments
60.1295 - } else {
60.1296 - // Reading the value
60.1297 - if (!_hasBeenEvaluated)
60.1298 - evaluateImmediate();
60.1299 - ko.dependencyDetection.registerDependency(dependentObservable);
60.1300 - return _latestValue;
60.1301 - }
60.1302 - }
60.1303 -
60.1304 - function peek() {
60.1305 - if (!_hasBeenEvaluated)
60.1306 - evaluateImmediate();
60.1307 - return _latestValue;
60.1308 - }
60.1309 -
60.1310 - function isActive() {
60.1311 - return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
60.1312 - }
60.1313 -
60.1314 - // By here, "options" is always non-null
60.1315 - var writeFunction = options["write"],
60.1316 - disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
60.1317 - disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
60.1318 - dispose = disposeAllSubscriptionsToDependencies,
60.1319 - _subscriptionsToDependencies = [],
60.1320 - evaluationTimeoutInstance = null;
60.1321 -
60.1322 - if (!evaluatorFunctionTarget)
60.1323 - evaluatorFunctionTarget = options["owner"];
60.1324 -
60.1325 - dependentObservable.peek = peek;
60.1326 - dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
60.1327 - dependentObservable.hasWriteFunction = typeof options["write"] === "function";
60.1328 - dependentObservable.dispose = function () { dispose(); };
60.1329 - dependentObservable.isActive = isActive;
60.1330 - dependentObservable.valueHasMutated = function() {
60.1331 - _hasBeenEvaluated = false;
60.1332 - evaluateImmediate();
60.1333 - };
60.1334 -
60.1335 - ko.subscribable.call(dependentObservable);
60.1336 - ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
60.1337 -
60.1338 - ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
60.1339 - ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
60.1340 - ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
60.1341 - ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
60.1342 -
60.1343 - // Evaluate, unless deferEvaluation is true
60.1344 - if (options['deferEvaluation'] !== true)
60.1345 - evaluateImmediate();
60.1346 -
60.1347 - // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
60.1348 - // But skip if isActive is false (there will never be any dependencies to dispose).
60.1349 - // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
60.1350 - // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
60.1351 - if (disposeWhenNodeIsRemoved && isActive()) {
60.1352 - dispose = function() {
60.1353 - ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
60.1354 - disposeAllSubscriptionsToDependencies();
60.1355 - };
60.1356 - ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
60.1357 - var existingDisposeWhenFunction = disposeWhen;
60.1358 - disposeWhen = function () {
60.1359 - return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
60.1360 - }
60.1361 - }
60.1362 -
60.1363 - return dependentObservable;
60.1364 -};
60.1365 -
60.1366 -ko.isComputed = function(instance) {
60.1367 - return ko.hasPrototype(instance, ko.dependentObservable);
60.1368 -};
60.1369 -
60.1370 -var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
60.1371 -ko.dependentObservable[protoProp] = ko.observable;
60.1372 -
60.1373 -ko.dependentObservable['fn'] = {};
60.1374 -ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
60.1375 -
60.1376 -ko.exportSymbol('dependentObservable', ko.dependentObservable);
60.1377 -ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
60.1378 -ko.exportSymbol('isComputed', ko.isComputed);
60.1379 -
60.1380 -(function() {
60.1381 - var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
60.1382 -
60.1383 - ko.toJS = function(rootObject) {
60.1384 - if (arguments.length == 0)
60.1385 - throw new Error("When calling ko.toJS, pass the object you want to convert.");
60.1386 -
60.1387 - // We just unwrap everything at every level in the object graph
60.1388 - return mapJsObjectGraph(rootObject, function(valueToMap) {
60.1389 - // Loop because an observable's value might in turn be another observable wrapper
60.1390 - for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
60.1391 - valueToMap = valueToMap();
60.1392 - return valueToMap;
60.1393 - });
60.1394 - };
60.1395 -
60.1396 - ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
60.1397 - var plainJavaScriptObject = ko.toJS(rootObject);
60.1398 - return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
60.1399 - };
60.1400 -
60.1401 - function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
60.1402 - visitedObjects = visitedObjects || new objectLookup();
60.1403 -
60.1404 - rootObject = mapInputCallback(rootObject);
60.1405 - var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
60.1406 - if (!canHaveProperties)
60.1407 - return rootObject;
60.1408 -
60.1409 - var outputProperties = rootObject instanceof Array ? [] : {};
60.1410 - visitedObjects.save(rootObject, outputProperties);
60.1411 -
60.1412 - visitPropertiesOrArrayEntries(rootObject, function(indexer) {
60.1413 - var propertyValue = mapInputCallback(rootObject[indexer]);
60.1414 -
60.1415 - switch (typeof propertyValue) {
60.1416 - case "boolean":
60.1417 - case "number":
60.1418 - case "string":
60.1419 - case "function":
60.1420 - outputProperties[indexer] = propertyValue;
60.1421 - break;
60.1422 - case "object":
60.1423 - case "undefined":
60.1424 - var previouslyMappedValue = visitedObjects.get(propertyValue);
60.1425 - outputProperties[indexer] = (previouslyMappedValue !== undefined)
60.1426 - ? previouslyMappedValue
60.1427 - : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
60.1428 - break;
60.1429 - }
60.1430 - });
60.1431 -
60.1432 - return outputProperties;
60.1433 - }
60.1434 -
60.1435 - function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
60.1436 - if (rootObject instanceof Array) {
60.1437 - for (var i = 0; i < rootObject.length; i++)
60.1438 - visitorCallback(i);
60.1439 -
60.1440 - // For arrays, also respect toJSON property for custom mappings (fixes #278)
60.1441 - if (typeof rootObject['toJSON'] == 'function')
60.1442 - visitorCallback('toJSON');
60.1443 - } else {
60.1444 - for (var propertyName in rootObject)
60.1445 - visitorCallback(propertyName);
60.1446 - }
60.1447 - };
60.1448 -
60.1449 - function objectLookup() {
60.1450 - var keys = [];
60.1451 - var values = [];
60.1452 - this.save = function(key, value) {
60.1453 - var existingIndex = ko.utils.arrayIndexOf(keys, key);
60.1454 - if (existingIndex >= 0)
60.1455 - values[existingIndex] = value;
60.1456 - else {
60.1457 - keys.push(key);
60.1458 - values.push(value);
60.1459 - }
60.1460 - };
60.1461 - this.get = function(key) {
60.1462 - var existingIndex = ko.utils.arrayIndexOf(keys, key);
60.1463 - return (existingIndex >= 0) ? values[existingIndex] : undefined;
60.1464 - };
60.1465 - };
60.1466 -})();
60.1467 -
60.1468 -ko.exportSymbol('toJS', ko.toJS);
60.1469 -ko.exportSymbol('toJSON', ko.toJSON);
60.1470 -(function () {
60.1471 - var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
60.1472 -
60.1473 - // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
60.1474 - // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
60.1475 - // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
60.1476 - ko.selectExtensions = {
60.1477 - readValue : function(element) {
60.1478 - switch (ko.utils.tagNameLower(element)) {
60.1479 - case 'option':
60.1480 - if (element[hasDomDataExpandoProperty] === true)
60.1481 - return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
60.1482 - return ko.utils.ieVersion <= 7
60.1483 - ? (element.getAttributeNode('value').specified ? element.value : element.text)
60.1484 - : element.value;
60.1485 - case 'select':
60.1486 - return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
60.1487 - default:
60.1488 - return element.value;
60.1489 - }
60.1490 - },
60.1491 -
60.1492 - writeValue: function(element, value) {
60.1493 - switch (ko.utils.tagNameLower(element)) {
60.1494 - case 'option':
60.1495 - switch(typeof value) {
60.1496 - case "string":
60.1497 - ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
60.1498 - if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
60.1499 - delete element[hasDomDataExpandoProperty];
60.1500 - }
60.1501 - element.value = value;
60.1502 - break;
60.1503 - default:
60.1504 - // Store arbitrary object using DomData
60.1505 - ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
60.1506 - element[hasDomDataExpandoProperty] = true;
60.1507 -
60.1508 - // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
60.1509 - element.value = typeof value === "number" ? value : "";
60.1510 - break;
60.1511 - }
60.1512 - break;
60.1513 - case 'select':
60.1514 - for (var i = element.options.length - 1; i >= 0; i--) {
60.1515 - if (ko.selectExtensions.readValue(element.options[i]) == value) {
60.1516 - element.selectedIndex = i;
60.1517 - break;
60.1518 - }
60.1519 - }
60.1520 - break;
60.1521 - default:
60.1522 - if ((value === null) || (value === undefined))
60.1523 - value = "";
60.1524 - element.value = value;
60.1525 - break;
60.1526 - }
60.1527 - }
60.1528 - };
60.1529 -})();
60.1530 -
60.1531 -ko.exportSymbol('selectExtensions', ko.selectExtensions);
60.1532 -ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
60.1533 -ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
60.1534 -ko.expressionRewriting = (function () {
60.1535 - var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
60.1536 - var javaScriptReservedWords = ["true", "false"];
60.1537 -
60.1538 - // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
60.1539 - // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
60.1540 - var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
60.1541 -
60.1542 - function restoreTokens(string, tokens) {
60.1543 - var prevValue = null;
60.1544 - while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
60.1545 - prevValue = string;
60.1546 - string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
60.1547 - return tokens[tokenIndex];
60.1548 - });
60.1549 - }
60.1550 - return string;
60.1551 - }
60.1552 -
60.1553 - function getWriteableValue(expression) {
60.1554 - if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
60.1555 - return false;
60.1556 - var match = expression.match(javaScriptAssignmentTarget);
60.1557 - return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
60.1558 - }
60.1559 -
60.1560 - function ensureQuoted(key) {
60.1561 - var trimmedKey = ko.utils.stringTrim(key);
60.1562 - switch (trimmedKey.length && trimmedKey.charAt(0)) {
60.1563 - case "'":
60.1564 - case '"':
60.1565 - return key;
60.1566 - default:
60.1567 - return "'" + trimmedKey + "'";
60.1568 - }
60.1569 - }
60.1570 -
60.1571 - return {
60.1572 - bindingRewriteValidators: [],
60.1573 -
60.1574 - parseObjectLiteral: function(objectLiteralString) {
60.1575 - // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
60.1576 - // that is sufficient just to split an object literal string into a set of top-level key-value pairs
60.1577 -
60.1578 - var str = ko.utils.stringTrim(objectLiteralString);
60.1579 - if (str.length < 3)
60.1580 - return [];
60.1581 - if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
60.1582 - str = str.substring(1, str.length - 1);
60.1583 -
60.1584 - // Pull out any string literals and regex literals
60.1585 - var tokens = [];
60.1586 - var tokenStart = null, tokenEndChar;
60.1587 - for (var position = 0; position < str.length; position++) {
60.1588 - var c = str.charAt(position);
60.1589 - if (tokenStart === null) {
60.1590 - switch (c) {
60.1591 - case '"':
60.1592 - case "'":
60.1593 - case "/":
60.1594 - tokenStart = position;
60.1595 - tokenEndChar = c;
60.1596 - break;
60.1597 - }
60.1598 - } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
60.1599 - var token = str.substring(tokenStart, position + 1);
60.1600 - tokens.push(token);
60.1601 - var replacement = "@ko_token_" + (tokens.length - 1) + "@";
60.1602 - str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
60.1603 - position -= (token.length - replacement.length);
60.1604 - tokenStart = null;
60.1605 - }
60.1606 - }
60.1607 -
60.1608 - // Next pull out balanced paren, brace, and bracket blocks
60.1609 - tokenStart = null;
60.1610 - tokenEndChar = null;
60.1611 - var tokenDepth = 0, tokenStartChar = null;
60.1612 - for (var position = 0; position < str.length; position++) {
60.1613 - var c = str.charAt(position);
60.1614 - if (tokenStart === null) {
60.1615 - switch (c) {
60.1616 - case "{": tokenStart = position; tokenStartChar = c;
60.1617 - tokenEndChar = "}";
60.1618 - break;
60.1619 - case "(": tokenStart = position; tokenStartChar = c;
60.1620 - tokenEndChar = ")";
60.1621 - break;
60.1622 - case "[": tokenStart = position; tokenStartChar = c;
60.1623 - tokenEndChar = "]";
60.1624 - break;
60.1625 - }
60.1626 - }
60.1627 -
60.1628 - if (c === tokenStartChar)
60.1629 - tokenDepth++;
60.1630 - else if (c === tokenEndChar) {
60.1631 - tokenDepth--;
60.1632 - if (tokenDepth === 0) {
60.1633 - var token = str.substring(tokenStart, position + 1);
60.1634 - tokens.push(token);
60.1635 - var replacement = "@ko_token_" + (tokens.length - 1) + "@";
60.1636 - str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
60.1637 - position -= (token.length - replacement.length);
60.1638 - tokenStart = null;
60.1639 - }
60.1640 - }
60.1641 - }
60.1642 -
60.1643 - // Now we can safely split on commas to get the key/value pairs
60.1644 - var result = [];
60.1645 - var keyValuePairs = str.split(",");
60.1646 - for (var i = 0, j = keyValuePairs.length; i < j; i++) {
60.1647 - var pair = keyValuePairs[i];
60.1648 - var colonPos = pair.indexOf(":");
60.1649 - if ((colonPos > 0) && (colonPos < pair.length - 1)) {
60.1650 - var key = pair.substring(0, colonPos);
60.1651 - var value = pair.substring(colonPos + 1);
60.1652 - result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
60.1653 - } else {
60.1654 - result.push({ 'unknown': restoreTokens(pair, tokens) });
60.1655 - }
60.1656 - }
60.1657 - return result;
60.1658 - },
60.1659 -
60.1660 - preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
60.1661 - var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
60.1662 - ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
60.1663 - : objectLiteralStringOrKeyValueArray;
60.1664 - var resultStrings = [], propertyAccessorResultStrings = [];
60.1665 -
60.1666 - var keyValueEntry;
60.1667 - for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
60.1668 - if (resultStrings.length > 0)
60.1669 - resultStrings.push(",");
60.1670 -
60.1671 - if (keyValueEntry['key']) {
60.1672 - var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
60.1673 - resultStrings.push(quotedKey);
60.1674 - resultStrings.push(":");
60.1675 - resultStrings.push(val);
60.1676 -
60.1677 - if (val = getWriteableValue(ko.utils.stringTrim(val))) {
60.1678 - if (propertyAccessorResultStrings.length > 0)
60.1679 - propertyAccessorResultStrings.push(", ");
60.1680 - propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
60.1681 - }
60.1682 - } else if (keyValueEntry['unknown']) {
60.1683 - resultStrings.push(keyValueEntry['unknown']);
60.1684 - }
60.1685 - }
60.1686 -
60.1687 - var combinedResult = resultStrings.join("");
60.1688 - if (propertyAccessorResultStrings.length > 0) {
60.1689 - var allPropertyAccessors = propertyAccessorResultStrings.join("");
60.1690 - combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
60.1691 - }
60.1692 -
60.1693 - return combinedResult;
60.1694 - },
60.1695 -
60.1696 - keyValueArrayContainsKey: function(keyValueArray, key) {
60.1697 - for (var i = 0; i < keyValueArray.length; i++)
60.1698 - if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
60.1699 - return true;
60.1700 - return false;
60.1701 - },
60.1702 -
60.1703 - // Internal, private KO utility for updating model properties from within bindings
60.1704 - // property: If the property being updated is (or might be) an observable, pass it here
60.1705 - // If it turns out to be a writable observable, it will be written to directly
60.1706 - // allBindingsAccessor: All bindings in the current execution context.
60.1707 - // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
60.1708 - // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
60.1709 - // value: The value to be written
60.1710 - // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
60.1711 - // it is !== existing value on that writable observable
60.1712 - writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
60.1713 - if (!property || !ko.isWriteableObservable(property)) {
60.1714 - var propWriters = allBindingsAccessor()['_ko_property_writers'];
60.1715 - if (propWriters && propWriters[key])
60.1716 - propWriters[key](value);
60.1717 - } else if (!checkIfDifferent || property.peek() !== value) {
60.1718 - property(value);
60.1719 - }
60.1720 - }
60.1721 - };
60.1722 -})();
60.1723 -
60.1724 -ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
60.1725 -ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
60.1726 -ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
60.1727 -ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
60.1728 -
60.1729 -// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
60.1730 -// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
60.1731 -ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
60.1732 -ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
60.1733 - // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
60.1734 - // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
60.1735 - // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
60.1736 - // of that virtual hierarchy
60.1737 - //
60.1738 - // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
60.1739 - // without having to scatter special cases all over the binding and templating code.
60.1740 -
60.1741 - // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
60.1742 - // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
60.1743 - // So, use node.text where available, and node.nodeValue elsewhere
60.1744 - var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
60.1745 -
60.1746 - var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
60.1747 - var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
60.1748 - var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
60.1749 -
60.1750 - function isStartComment(node) {
60.1751 - return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
60.1752 - }
60.1753 -
60.1754 - function isEndComment(node) {
60.1755 - return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
60.1756 - }
60.1757 -
60.1758 - function getVirtualChildren(startComment, allowUnbalanced) {
60.1759 - var currentNode = startComment;
60.1760 - var depth = 1;
60.1761 - var children = [];
60.1762 - while (currentNode = currentNode.nextSibling) {
60.1763 - if (isEndComment(currentNode)) {
60.1764 - depth--;
60.1765 - if (depth === 0)
60.1766 - return children;
60.1767 - }
60.1768 -
60.1769 - children.push(currentNode);
60.1770 -
60.1771 - if (isStartComment(currentNode))
60.1772 - depth++;
60.1773 - }
60.1774 - if (!allowUnbalanced)
60.1775 - throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
60.1776 - return null;
60.1777 - }
60.1778 -
60.1779 - function getMatchingEndComment(startComment, allowUnbalanced) {
60.1780 - var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
60.1781 - if (allVirtualChildren) {
60.1782 - if (allVirtualChildren.length > 0)
60.1783 - return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
60.1784 - return startComment.nextSibling;
60.1785 - } else
60.1786 - return null; // Must have no matching end comment, and allowUnbalanced is true
60.1787 - }
60.1788 -
60.1789 - function getUnbalancedChildTags(node) {
60.1790 - // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
60.1791 - // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
60.1792 - var childNode = node.firstChild, captureRemaining = null;
60.1793 - if (childNode) {
60.1794 - do {
60.1795 - if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
60.1796 - captureRemaining.push(childNode);
60.1797 - else if (isStartComment(childNode)) {
60.1798 - var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
60.1799 - if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
60.1800 - childNode = matchingEndComment;
60.1801 - else
60.1802 - captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
60.1803 - } else if (isEndComment(childNode)) {
60.1804 - captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
60.1805 - }
60.1806 - } while (childNode = childNode.nextSibling);
60.1807 - }
60.1808 - return captureRemaining;
60.1809 - }
60.1810 -
60.1811 - ko.virtualElements = {
60.1812 - allowedBindings: {},
60.1813 -
60.1814 - childNodes: function(node) {
60.1815 - return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
60.1816 - },
60.1817 -
60.1818 - emptyNode: function(node) {
60.1819 - if (!isStartComment(node))
60.1820 - ko.utils.emptyDomNode(node);
60.1821 - else {
60.1822 - var virtualChildren = ko.virtualElements.childNodes(node);
60.1823 - for (var i = 0, j = virtualChildren.length; i < j; i++)
60.1824 - ko.removeNode(virtualChildren[i]);
60.1825 - }
60.1826 - },
60.1827 -
60.1828 - setDomNodeChildren: function(node, childNodes) {
60.1829 - if (!isStartComment(node))
60.1830 - ko.utils.setDomNodeChildren(node, childNodes);
60.1831 - else {
60.1832 - ko.virtualElements.emptyNode(node);
60.1833 - var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
60.1834 - for (var i = 0, j = childNodes.length; i < j; i++)
60.1835 - endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
60.1836 - }
60.1837 - },
60.1838 -
60.1839 - prepend: function(containerNode, nodeToPrepend) {
60.1840 - if (!isStartComment(containerNode)) {
60.1841 - if (containerNode.firstChild)
60.1842 - containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
60.1843 - else
60.1844 - containerNode.appendChild(nodeToPrepend);
60.1845 - } else {
60.1846 - // Start comments must always have a parent and at least one following sibling (the end comment)
60.1847 - containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
60.1848 - }
60.1849 - },
60.1850 -
60.1851 - insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
60.1852 - if (!insertAfterNode) {
60.1853 - ko.virtualElements.prepend(containerNode, nodeToInsert);
60.1854 - } else if (!isStartComment(containerNode)) {
60.1855 - // Insert after insertion point
60.1856 - if (insertAfterNode.nextSibling)
60.1857 - containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
60.1858 - else
60.1859 - containerNode.appendChild(nodeToInsert);
60.1860 - } else {
60.1861 - // Children of start comments must always have a parent and at least one following sibling (the end comment)
60.1862 - containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
60.1863 - }
60.1864 - },
60.1865 -
60.1866 - firstChild: function(node) {
60.1867 - if (!isStartComment(node))
60.1868 - return node.firstChild;
60.1869 - if (!node.nextSibling || isEndComment(node.nextSibling))
60.1870 - return null;
60.1871 - return node.nextSibling;
60.1872 - },
60.1873 -
60.1874 - nextSibling: function(node) {
60.1875 - if (isStartComment(node))
60.1876 - node = getMatchingEndComment(node);
60.1877 - if (node.nextSibling && isEndComment(node.nextSibling))
60.1878 - return null;
60.1879 - return node.nextSibling;
60.1880 - },
60.1881 -
60.1882 - virtualNodeBindingValue: function(node) {
60.1883 - var regexMatch = isStartComment(node);
60.1884 - return regexMatch ? regexMatch[1] : null;
60.1885 - },
60.1886 -
60.1887 - normaliseVirtualElementDomStructure: function(elementVerified) {
60.1888 - // Workaround for https://github.com/SteveSanderson/knockout/issues/155
60.1889 - // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
60.1890 - // that are direct descendants of <ul> into the preceding <li>)
60.1891 - if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
60.1892 - return;
60.1893 -
60.1894 - // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
60.1895 - // must be intended to appear *after* that child, so move them there.
60.1896 - var childNode = elementVerified.firstChild;
60.1897 - if (childNode) {
60.1898 - do {
60.1899 - if (childNode.nodeType === 1) {
60.1900 - var unbalancedTags = getUnbalancedChildTags(childNode);
60.1901 - if (unbalancedTags) {
60.1902 - // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
60.1903 - var nodeToInsertBefore = childNode.nextSibling;
60.1904 - for (var i = 0; i < unbalancedTags.length; i++) {
60.1905 - if (nodeToInsertBefore)
60.1906 - elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
60.1907 - else
60.1908 - elementVerified.appendChild(unbalancedTags[i]);
60.1909 - }
60.1910 - }
60.1911 - }
60.1912 - } while (childNode = childNode.nextSibling);
60.1913 - }
60.1914 - }
60.1915 - };
60.1916 -})();
60.1917 -ko.exportSymbol('virtualElements', ko.virtualElements);
60.1918 -ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
60.1919 -ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
60.1920 -//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild); // firstChild is not minified
60.1921 -ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
60.1922 -//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
60.1923 -ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
60.1924 -ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
60.1925 -(function() {
60.1926 - var defaultBindingAttributeName = "data-bind";
60.1927 -
60.1928 - ko.bindingProvider = function() {
60.1929 - this.bindingCache = {};
60.1930 - };
60.1931 -
60.1932 - ko.utils.extend(ko.bindingProvider.prototype, {
60.1933 - 'nodeHasBindings': function(node) {
60.1934 - switch (node.nodeType) {
60.1935 - case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
60.1936 - case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
60.1937 - default: return false;
60.1938 - }
60.1939 - },
60.1940 -
60.1941 - 'getBindings': function(node, bindingContext) {
60.1942 - var bindingsString = this['getBindingsString'](node, bindingContext);
60.1943 - return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
60.1944 - },
60.1945 -
60.1946 - // The following function is only used internally by this default provider.
60.1947 - // It's not part of the interface definition for a general binding provider.
60.1948 - 'getBindingsString': function(node, bindingContext) {
60.1949 - switch (node.nodeType) {
60.1950 - case 1: return node.getAttribute(defaultBindingAttributeName); // Element
60.1951 - case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
60.1952 - default: return null;
60.1953 - }
60.1954 - },
60.1955 -
60.1956 - // The following function is only used internally by this default provider.
60.1957 - // It's not part of the interface definition for a general binding provider.
60.1958 - 'parseBindingsString': function(bindingsString, bindingContext, node) {
60.1959 - try {
60.1960 - var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
60.1961 - return bindingFunction(bindingContext, node);
60.1962 - } catch (ex) {
60.1963 - throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
60.1964 - }
60.1965 - }
60.1966 - });
60.1967 -
60.1968 - ko.bindingProvider['instance'] = new ko.bindingProvider();
60.1969 -
60.1970 - function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
60.1971 - var cacheKey = bindingsString;
60.1972 - return cache[cacheKey]
60.1973 - || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
60.1974 - }
60.1975 -
60.1976 - function createBindingsStringEvaluator(bindingsString) {
60.1977 - // Build the source for a function that evaluates "expression"
60.1978 - // For each scope variable, add an extra level of "with" nesting
60.1979 - // Example result: with(sc1) { with(sc0) { return (expression) } }
60.1980 - var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
60.1981 - functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
60.1982 - return new Function("$context", "$element", functionBody);
60.1983 - }
60.1984 -})();
60.1985 -
60.1986 -ko.exportSymbol('bindingProvider', ko.bindingProvider);
60.1987 -(function () {
60.1988 - ko.bindingHandlers = {};
60.1989 -
60.1990 - ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
60.1991 - if (parentBindingContext) {
60.1992 - ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
60.1993 - this['$parentContext'] = parentBindingContext;
60.1994 - this['$parent'] = parentBindingContext['$data'];
60.1995 - this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
60.1996 - this['$parents'].unshift(this['$parent']);
60.1997 - } else {
60.1998 - this['$parents'] = [];
60.1999 - this['$root'] = dataItem;
60.2000 - // Export 'ko' in the binding context so it will be available in bindings and templates
60.2001 - // even if 'ko' isn't exported as a global, such as when using an AMD loader.
60.2002 - // See https://github.com/SteveSanderson/knockout/issues/490
60.2003 - this['ko'] = ko;
60.2004 - }
60.2005 - this['$data'] = dataItem;
60.2006 - if (dataItemAlias)
60.2007 - this[dataItemAlias] = dataItem;
60.2008 - }
60.2009 - ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
60.2010 - return new ko.bindingContext(dataItem, this, dataItemAlias);
60.2011 - };
60.2012 - ko.bindingContext.prototype['extend'] = function(properties) {
60.2013 - var clone = ko.utils.extend(new ko.bindingContext(), this);
60.2014 - return ko.utils.extend(clone, properties);
60.2015 - };
60.2016 -
60.2017 - function validateThatBindingIsAllowedForVirtualElements(bindingName) {
60.2018 - var validator = ko.virtualElements.allowedBindings[bindingName];
60.2019 - if (!validator)
60.2020 - throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
60.2021 - }
60.2022 -
60.2023 - function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
60.2024 - var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
60.2025 - while (currentChild = nextInQueue) {
60.2026 - // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
60.2027 - nextInQueue = ko.virtualElements.nextSibling(currentChild);
60.2028 - applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
60.2029 - }
60.2030 - }
60.2031 -
60.2032 - function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
60.2033 - var shouldBindDescendants = true;
60.2034 -
60.2035 - // Perf optimisation: Apply bindings only if...
60.2036 - // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
60.2037 - // Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
60.2038 - // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
60.2039 - var isElement = (nodeVerified.nodeType === 1);
60.2040 - if (isElement) // Workaround IE <= 8 HTML parsing weirdness
60.2041 - ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
60.2042 -
60.2043 - var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
60.2044 - || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
60.2045 - if (shouldApplyBindings)
60.2046 - shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
60.2047 -
60.2048 - if (shouldBindDescendants) {
60.2049 - // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
60.2050 - // * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
60.2051 - // hence bindingContextsMayDifferFromDomParentElement is false
60.2052 - // * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
60.2053 - // skip over any number of intermediate virtual elements, any of which might define a custom binding context,
60.2054 - // hence bindingContextsMayDifferFromDomParentElement is true
60.2055 - applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
60.2056 - }
60.2057 - }
60.2058 -
60.2059 - function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
60.2060 - // Need to be sure that inits are only run once, and updates never run until all the inits have been run
60.2061 - var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
60.2062 -
60.2063 - // Each time the dependentObservable is evaluated (after data changes),
60.2064 - // the binding attribute is reparsed so that it can pick out the correct
60.2065 - // model properties in the context of the changed data.
60.2066 - // DOM event callbacks need to be able to access this changed data,
60.2067 - // so we need a single parsedBindings variable (shared by all callbacks
60.2068 - // associated with this node's bindings) that all the closures can access.
60.2069 - var parsedBindings;
60.2070 - function makeValueAccessor(bindingKey) {
60.2071 - return function () { return parsedBindings[bindingKey] }
60.2072 - }
60.2073 - function parsedBindingsAccessor() {
60.2074 - return parsedBindings;
60.2075 - }
60.2076 -
60.2077 - var bindingHandlerThatControlsDescendantBindings;
60.2078 - ko.dependentObservable(
60.2079 - function () {
60.2080 - // Ensure we have a nonnull binding context to work with
60.2081 - var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
60.2082 - ? viewModelOrBindingContext
60.2083 - : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
60.2084 - var viewModel = bindingContextInstance['$data'];
60.2085 -
60.2086 - // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
60.2087 - // we can easily recover it just by scanning up the node's ancestors in the DOM
60.2088 - // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
60.2089 - if (bindingContextMayDifferFromDomParentElement)
60.2090 - ko.storedBindingContextForNode(node, bindingContextInstance);
60.2091 -
60.2092 - // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
60.2093 - var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
60.2094 - parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
60.2095 -
60.2096 - if (parsedBindings) {
60.2097 - // First run all the inits, so bindings can register for notification on changes
60.2098 - if (initPhase === 0) {
60.2099 - initPhase = 1;
60.2100 - for (var bindingKey in parsedBindings) {
60.2101 - var binding = ko.bindingHandlers[bindingKey];
60.2102 - if (binding && node.nodeType === 8)
60.2103 - validateThatBindingIsAllowedForVirtualElements(bindingKey);
60.2104 -
60.2105 - if (binding && typeof binding["init"] == "function") {
60.2106 - var handlerInitFn = binding["init"];
60.2107 - var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
60.2108 -
60.2109 - // If this binding handler claims to control descendant bindings, make a note of this
60.2110 - if (initResult && initResult['controlsDescendantBindings']) {
60.2111 - if (bindingHandlerThatControlsDescendantBindings !== undefined)
60.2112 - throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
60.2113 - bindingHandlerThatControlsDescendantBindings = bindingKey;
60.2114 - }
60.2115 - }
60.2116 - }
60.2117 - initPhase = 2;
60.2118 - }
60.2119 -
60.2120 - // ... then run all the updates, which might trigger changes even on the first evaluation
60.2121 - if (initPhase === 2) {
60.2122 - for (var bindingKey in parsedBindings) {
60.2123 - var binding = ko.bindingHandlers[bindingKey];
60.2124 - if (binding && typeof binding["update"] == "function") {
60.2125 - var handlerUpdateFn = binding["update"];
60.2126 - handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
60.2127 - }
60.2128 - }
60.2129 - }
60.2130 - }
60.2131 - },
60.2132 - null,
60.2133 - { disposeWhenNodeIsRemoved : node }
60.2134 - );
60.2135 -
60.2136 - return {
60.2137 - shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
60.2138 - };
60.2139 - };
60.2140 -
60.2141 - var storedBindingContextDomDataKey = "__ko_bindingContext__";
60.2142 - ko.storedBindingContextForNode = function (node, bindingContext) {
60.2143 - if (arguments.length == 2)
60.2144 - ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
60.2145 - else
60.2146 - return ko.utils.domData.get(node, storedBindingContextDomDataKey);
60.2147 - }
60.2148 -
60.2149 - ko.applyBindingsToNode = function (node, bindings, viewModel) {
60.2150 - if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
60.2151 - ko.virtualElements.normaliseVirtualElementDomStructure(node);
60.2152 - return applyBindingsToNodeInternal(node, bindings, viewModel, true);
60.2153 - };
60.2154 -
60.2155 - ko.applyBindingsToDescendants = function(viewModel, rootNode) {
60.2156 - if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
60.2157 - applyBindingsToDescendantsInternal(viewModel, rootNode, true);
60.2158 - };
60.2159 -
60.2160 - ko.applyBindings = function (viewModel, rootNode) {
60.2161 - if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
60.2162 - throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
60.2163 - rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
60.2164 -
60.2165 - applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
60.2166 - };
60.2167 -
60.2168 - // Retrieving binding context from arbitrary nodes
60.2169 - ko.contextFor = function(node) {
60.2170 - // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
60.2171 - switch (node.nodeType) {
60.2172 - case 1:
60.2173 - case 8:
60.2174 - var context = ko.storedBindingContextForNode(node);
60.2175 - if (context) return context;
60.2176 - if (node.parentNode) return ko.contextFor(node.parentNode);
60.2177 - break;
60.2178 - }
60.2179 - return undefined;
60.2180 - };
60.2181 - ko.dataFor = function(node) {
60.2182 - var context = ko.contextFor(node);
60.2183 - return context ? context['$data'] : undefined;
60.2184 - };
60.2185 -
60.2186 - ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
60.2187 - ko.exportSymbol('applyBindings', ko.applyBindings);
60.2188 - ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
60.2189 - ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
60.2190 - ko.exportSymbol('contextFor', ko.contextFor);
60.2191 - ko.exportSymbol('dataFor', ko.dataFor);
60.2192 -})();
60.2193 -var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
60.2194 -ko.bindingHandlers['attr'] = {
60.2195 - 'update': function(element, valueAccessor, allBindingsAccessor) {
60.2196 - var value = ko.utils.unwrapObservable(valueAccessor()) || {};
60.2197 - for (var attrName in value) {
60.2198 - if (typeof attrName == "string") {
60.2199 - var attrValue = ko.utils.unwrapObservable(value[attrName]);
60.2200 -
60.2201 - // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
60.2202 - // when someProp is a "no value"-like value (strictly null, false, or undefined)
60.2203 - // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
60.2204 - var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
60.2205 - if (toRemove)
60.2206 - element.removeAttribute(attrName);
60.2207 -
60.2208 - // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
60.2209 - // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
60.2210 - // but instead of figuring out the mode, we'll just set the attribute through the Javascript
60.2211 - // property for IE <= 8.
60.2212 - if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
60.2213 - attrName = attrHtmlToJavascriptMap[attrName];
60.2214 - if (toRemove)
60.2215 - element.removeAttribute(attrName);
60.2216 - else
60.2217 - element[attrName] = attrValue;
60.2218 - } else if (!toRemove) {
60.2219 - try {
60.2220 - element.setAttribute(attrName, attrValue.toString());
60.2221 - } catch (err) {
60.2222 - // ignore for now
60.2223 - if (console) {
60.2224 - console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
60.2225 - }
60.2226 - }
60.2227 - }
60.2228 -
60.2229 - // Treat "name" specially - although you can think of it as an attribute, it also needs
60.2230 - // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
60.2231 - // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
60.2232 - // entirely, and there's no strong reason to allow for such casing in HTML.
60.2233 - if (attrName === "name") {
60.2234 - ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
60.2235 - }
60.2236 - }
60.2237 - }
60.2238 - }
60.2239 -};
60.2240 -ko.bindingHandlers['checked'] = {
60.2241 - 'init': function (element, valueAccessor, allBindingsAccessor) {
60.2242 - var updateHandler = function() {
60.2243 - var valueToWrite;
60.2244 - if (element.type == "checkbox") {
60.2245 - valueToWrite = element.checked;
60.2246 - } else if ((element.type == "radio") && (element.checked)) {
60.2247 - valueToWrite = element.value;
60.2248 - } else {
60.2249 - return; // "checked" binding only responds to checkboxes and selected radio buttons
60.2250 - }
60.2251 -
60.2252 - var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
60.2253 - if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
60.2254 - // For checkboxes bound to an array, we add/remove the checkbox value to that array
60.2255 - // This works for both observable and non-observable arrays
60.2256 - var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
60.2257 - if (element.checked && (existingEntryIndex < 0))
60.2258 - modelValue.push(element.value);
60.2259 - else if ((!element.checked) && (existingEntryIndex >= 0))
60.2260 - modelValue.splice(existingEntryIndex, 1);
60.2261 - } else {
60.2262 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
60.2263 - }
60.2264 - };
60.2265 - ko.utils.registerEventHandler(element, "click", updateHandler);
60.2266 -
60.2267 - // IE 6 won't allow radio buttons to be selected unless they have a name
60.2268 - if ((element.type == "radio") && !element.name)
60.2269 - ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
60.2270 - },
60.2271 - 'update': function (element, valueAccessor) {
60.2272 - var value = ko.utils.unwrapObservable(valueAccessor());
60.2273 -
60.2274 - if (element.type == "checkbox") {
60.2275 - if (value instanceof Array) {
60.2276 - // When bound to an array, the checkbox being checked represents its value being present in that array
60.2277 - element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
60.2278 - } else {
60.2279 - // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
60.2280 - element.checked = value;
60.2281 - }
60.2282 - } else if (element.type == "radio") {
60.2283 - element.checked = (element.value == value);
60.2284 - }
60.2285 - }
60.2286 -};
60.2287 -var classesWrittenByBindingKey = '__ko__cssValue';
60.2288 -ko.bindingHandlers['css'] = {
60.2289 - 'update': function (element, valueAccessor) {
60.2290 - var value = ko.utils.unwrapObservable(valueAccessor());
60.2291 - if (typeof value == "object") {
60.2292 - for (var className in value) {
60.2293 - var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
60.2294 - ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
60.2295 - }
60.2296 - } else {
60.2297 - value = String(value || ''); // Make sure we don't try to store or set a non-string value
60.2298 - ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
60.2299 - element[classesWrittenByBindingKey] = value;
60.2300 - ko.utils.toggleDomNodeCssClass(element, value, true);
60.2301 - }
60.2302 - }
60.2303 -};
60.2304 -ko.bindingHandlers['enable'] = {
60.2305 - 'update': function (element, valueAccessor) {
60.2306 - var value = ko.utils.unwrapObservable(valueAccessor());
60.2307 - if (value && element.disabled)
60.2308 - element.removeAttribute("disabled");
60.2309 - else if ((!value) && (!element.disabled))
60.2310 - element.disabled = true;
60.2311 - }
60.2312 -};
60.2313 -
60.2314 -ko.bindingHandlers['disable'] = {
60.2315 - 'update': function (element, valueAccessor) {
60.2316 - ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
60.2317 - }
60.2318 -};
60.2319 -// For certain common events (currently just 'click'), allow a simplified data-binding syntax
60.2320 -// e.g. click:handler instead of the usual full-length event:{click:handler}
60.2321 -function makeEventHandlerShortcut(eventName) {
60.2322 - ko.bindingHandlers[eventName] = {
60.2323 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
60.2324 - var newValueAccessor = function () {
60.2325 - var result = {};
60.2326 - result[eventName] = valueAccessor();
60.2327 - return result;
60.2328 - };
60.2329 - return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
60.2330 - }
60.2331 - }
60.2332 -}
60.2333 -
60.2334 -ko.bindingHandlers['event'] = {
60.2335 - 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
60.2336 - var eventsToHandle = valueAccessor() || {};
60.2337 - for(var eventNameOutsideClosure in eventsToHandle) {
60.2338 - (function() {
60.2339 - var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
60.2340 - if (typeof eventName == "string") {
60.2341 - ko.utils.registerEventHandler(element, eventName, function (event) {
60.2342 - var handlerReturnValue;
60.2343 - var handlerFunction = valueAccessor()[eventName];
60.2344 - if (!handlerFunction)
60.2345 - return;
60.2346 - var allBindings = allBindingsAccessor();
60.2347 -
60.2348 - try {
60.2349 - // Take all the event args, and prefix with the viewmodel
60.2350 - var argsForHandler = ko.utils.makeArray(arguments);
60.2351 - argsForHandler.unshift(viewModel);
60.2352 - handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
60.2353 - } finally {
60.2354 - if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
60.2355 - if (event.preventDefault)
60.2356 - event.preventDefault();
60.2357 - else
60.2358 - event.returnValue = false;
60.2359 - }
60.2360 - }
60.2361 -
60.2362 - var bubble = allBindings[eventName + 'Bubble'] !== false;
60.2363 - if (!bubble) {
60.2364 - event.cancelBubble = true;
60.2365 - if (event.stopPropagation)
60.2366 - event.stopPropagation();
60.2367 - }
60.2368 - });
60.2369 - }
60.2370 - })();
60.2371 - }
60.2372 - }
60.2373 -};
60.2374 -// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
60.2375 -// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
60.2376 -ko.bindingHandlers['foreach'] = {
60.2377 - makeTemplateValueAccessor: function(valueAccessor) {
60.2378 - return function() {
60.2379 - var modelValue = valueAccessor(),
60.2380 - unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
60.2381 -
60.2382 - // If unwrappedValue is the array, pass in the wrapped value on its own
60.2383 - // The value will be unwrapped and tracked within the template binding
60.2384 - // (See https://github.com/SteveSanderson/knockout/issues/523)
60.2385 - if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
60.2386 - return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
60.2387 -
60.2388 - // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
60.2389 - ko.utils.unwrapObservable(modelValue);
60.2390 - return {
60.2391 - 'foreach': unwrappedValue['data'],
60.2392 - 'as': unwrappedValue['as'],
60.2393 - 'includeDestroyed': unwrappedValue['includeDestroyed'],
60.2394 - 'afterAdd': unwrappedValue['afterAdd'],
60.2395 - 'beforeRemove': unwrappedValue['beforeRemove'],
60.2396 - 'afterRender': unwrappedValue['afterRender'],
60.2397 - 'beforeMove': unwrappedValue['beforeMove'],
60.2398 - 'afterMove': unwrappedValue['afterMove'],
60.2399 - 'templateEngine': ko.nativeTemplateEngine.instance
60.2400 - };
60.2401 - };
60.2402 - },
60.2403 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
60.2404 - return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
60.2405 - },
60.2406 - 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
60.2407 - return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
60.2408 - }
60.2409 -};
60.2410 -ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
60.2411 -ko.virtualElements.allowedBindings['foreach'] = true;
60.2412 -var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
60.2413 -ko.bindingHandlers['hasfocus'] = {
60.2414 - 'init': function(element, valueAccessor, allBindingsAccessor) {
60.2415 - var handleElementFocusChange = function(isFocused) {
60.2416 - // Where possible, ignore which event was raised and determine focus state using activeElement,
60.2417 - // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
60.2418 - // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
60.2419 - // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
60.2420 - // from calling 'blur()' on the element when it loses focus.
60.2421 - // Discussion at https://github.com/SteveSanderson/knockout/pull/352
60.2422 - element[hasfocusUpdatingProperty] = true;
60.2423 - var ownerDoc = element.ownerDocument;
60.2424 - if ("activeElement" in ownerDoc) {
60.2425 - isFocused = (ownerDoc.activeElement === element);
60.2426 - }
60.2427 - var modelValue = valueAccessor();
60.2428 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
60.2429 - element[hasfocusUpdatingProperty] = false;
60.2430 - };
60.2431 - var handleElementFocusIn = handleElementFocusChange.bind(null, true);
60.2432 - var handleElementFocusOut = handleElementFocusChange.bind(null, false);
60.2433 -
60.2434 - ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
60.2435 - ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
60.2436 - ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
60.2437 - ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
60.2438 - },
60.2439 - 'update': function(element, valueAccessor) {
60.2440 - var value = ko.utils.unwrapObservable(valueAccessor());
60.2441 - if (!element[hasfocusUpdatingProperty]) {
60.2442 - value ? element.focus() : element.blur();
60.2443 - ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
60.2444 - }
60.2445 - }
60.2446 -};
60.2447 -ko.bindingHandlers['html'] = {
60.2448 - 'init': function() {
60.2449 - // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
60.2450 - return { 'controlsDescendantBindings': true };
60.2451 - },
60.2452 - 'update': function (element, valueAccessor) {
60.2453 - // setHtml will unwrap the value if needed
60.2454 - ko.utils.setHtml(element, valueAccessor());
60.2455 - }
60.2456 -};
60.2457 -var withIfDomDataKey = '__ko_withIfBindingData';
60.2458 -// Makes a binding like with or if
60.2459 -function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
60.2460 - ko.bindingHandlers[bindingKey] = {
60.2461 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
60.2462 - ko.utils.domData.set(element, withIfDomDataKey, {});
60.2463 - return { 'controlsDescendantBindings': true };
60.2464 - },
60.2465 - 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
60.2466 - var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
60.2467 - dataValue = ko.utils.unwrapObservable(valueAccessor()),
60.2468 - shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
60.2469 - isFirstRender = !withIfData.savedNodes,
60.2470 - needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
60.2471 -
60.2472 - if (needsRefresh) {
60.2473 - if (isFirstRender) {
60.2474 - withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
60.2475 - }
60.2476 -
60.2477 - if (shouldDisplay) {
60.2478 - if (!isFirstRender) {
60.2479 - ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
60.2480 - }
60.2481 - ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
60.2482 - } else {
60.2483 - ko.virtualElements.emptyNode(element);
60.2484 - }
60.2485 -
60.2486 - withIfData.didDisplayOnLastUpdate = shouldDisplay;
60.2487 - }
60.2488 - }
60.2489 - };
60.2490 - ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
60.2491 - ko.virtualElements.allowedBindings[bindingKey] = true;
60.2492 -}
60.2493 -
60.2494 -// Construct the actual binding handlers
60.2495 -makeWithIfBinding('if');
60.2496 -makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
60.2497 -makeWithIfBinding('with', true /* isWith */, false /* isNot */,
60.2498 - function(bindingContext, dataValue) {
60.2499 - return bindingContext['createChildContext'](dataValue);
60.2500 - }
60.2501 -);
60.2502 -function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
60.2503 - if (preferModelValue) {
60.2504 - if (modelValue !== ko.selectExtensions.readValue(element))
60.2505 - ko.selectExtensions.writeValue(element, modelValue);
60.2506 - }
60.2507 -
60.2508 - // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
60.2509 - // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
60.2510 - // change the model value to match the dropdown.
60.2511 - if (modelValue !== ko.selectExtensions.readValue(element))
60.2512 - ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
60.2513 -};
60.2514 -
60.2515 -ko.bindingHandlers['options'] = {
60.2516 - 'update': function (element, valueAccessor, allBindingsAccessor) {
60.2517 - if (ko.utils.tagNameLower(element) !== "select")
60.2518 - throw new Error("options binding applies only to SELECT elements");
60.2519 -
60.2520 - var selectWasPreviouslyEmpty = element.length == 0;
60.2521 - var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
60.2522 - return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
60.2523 - }), function (node) {
60.2524 - return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
60.2525 - });
60.2526 - var previousScrollTop = element.scrollTop;
60.2527 -
60.2528 - var value = ko.utils.unwrapObservable(valueAccessor());
60.2529 - var selectedValue = element.value;
60.2530 -
60.2531 - // Remove all existing <option>s.
60.2532 - // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
60.2533 - while (element.length > 0) {
60.2534 - ko.cleanNode(element.options[0]);
60.2535 - element.remove(0);
60.2536 - }
60.2537 -
60.2538 - if (value) {
60.2539 - var allBindings = allBindingsAccessor(),
60.2540 - includeDestroyed = allBindings['optionsIncludeDestroyed'];
60.2541 -
60.2542 - if (typeof value.length != "number")
60.2543 - value = [value];
60.2544 - if (allBindings['optionsCaption']) {
60.2545 - var option = document.createElement("option");
60.2546 - ko.utils.setHtml(option, allBindings['optionsCaption']);
60.2547 - ko.selectExtensions.writeValue(option, undefined);
60.2548 - element.appendChild(option);
60.2549 - }
60.2550 -
60.2551 - for (var i = 0, j = value.length; i < j; i++) {
60.2552 - // Skip destroyed items
60.2553 - var arrayEntry = value[i];
60.2554 - if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
60.2555 - continue;
60.2556 -
60.2557 - var option = document.createElement("option");
60.2558 -
60.2559 - function applyToObject(object, predicate, defaultValue) {
60.2560 - var predicateType = typeof predicate;
60.2561 - if (predicateType == "function") // Given a function; run it against the data value
60.2562 - return predicate(object);
60.2563 - else if (predicateType == "string") // Given a string; treat it as a property name on the data value
60.2564 - return object[predicate];
60.2565 - else // Given no optionsText arg; use the data value itself
60.2566 - return defaultValue;
60.2567 - }
60.2568 -
60.2569 - // Apply a value to the option element
60.2570 - var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
60.2571 - ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
60.2572 -
60.2573 - // Apply some text to the option element
60.2574 - var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
60.2575 - ko.utils.setTextContent(option, optionText);
60.2576 -
60.2577 - element.appendChild(option);
60.2578 - }
60.2579 -
60.2580 - // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
60.2581 - // That's why we first added them without selection. Now it's time to set the selection.
60.2582 - var newOptions = element.getElementsByTagName("option");
60.2583 - var countSelectionsRetained = 0;
60.2584 - for (var i = 0, j = newOptions.length; i < j; i++) {
60.2585 - if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
60.2586 - ko.utils.setOptionNodeSelectionState(newOptions[i], true);
60.2587 - countSelectionsRetained++;
60.2588 - }
60.2589 - }
60.2590 -
60.2591 - element.scrollTop = previousScrollTop;
60.2592 -
60.2593 - if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
60.2594 - // Ensure consistency between model value and selected option.
60.2595 - // If the dropdown is being populated for the first time here (or was otherwise previously empty),
60.2596 - // the dropdown selection state is meaningless, so we preserve the model value.
60.2597 - ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
60.2598 - }
60.2599 -
60.2600 - // Workaround for IE9 bug
60.2601 - ko.utils.ensureSelectElementIsRenderedCorrectly(element);
60.2602 - }
60.2603 - }
60.2604 -};
60.2605 -ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
60.2606 -ko.bindingHandlers['selectedOptions'] = {
60.2607 - 'init': function (element, valueAccessor, allBindingsAccessor) {
60.2608 - ko.utils.registerEventHandler(element, "change", function () {
60.2609 - var value = valueAccessor(), valueToWrite = [];
60.2610 - ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
60.2611 - if (node.selected)
60.2612 - valueToWrite.push(ko.selectExtensions.readValue(node));
60.2613 - });
60.2614 - ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
60.2615 - });
60.2616 - },
60.2617 - 'update': function (element, valueAccessor) {
60.2618 - if (ko.utils.tagNameLower(element) != "select")
60.2619 - throw new Error("values binding applies only to SELECT elements");
60.2620 -
60.2621 - var newValue = ko.utils.unwrapObservable(valueAccessor());
60.2622 - if (newValue && typeof newValue.length == "number") {
60.2623 - ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
60.2624 - var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
60.2625 - ko.utils.setOptionNodeSelectionState(node, isSelected);
60.2626 - });
60.2627 - }
60.2628 - }
60.2629 -};
60.2630 -ko.bindingHandlers['style'] = {
60.2631 - 'update': function (element, valueAccessor) {
60.2632 - var value = ko.utils.unwrapObservable(valueAccessor() || {});
60.2633 - for (var styleName in value) {
60.2634 - if (typeof styleName == "string") {
60.2635 - var styleValue = ko.utils.unwrapObservable(value[styleName]);
60.2636 - element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
60.2637 - }
60.2638 - }
60.2639 - }
60.2640 -};
60.2641 -ko.bindingHandlers['submit'] = {
60.2642 - 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
60.2643 - if (typeof valueAccessor() != "function")
60.2644 - throw new Error("The value for a submit binding must be a function");
60.2645 - ko.utils.registerEventHandler(element, "submit", function (event) {
60.2646 - var handlerReturnValue;
60.2647 - var value = valueAccessor();
60.2648 - try { handlerReturnValue = value.call(viewModel, element); }
60.2649 - finally {
60.2650 - if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
60.2651 - if (event.preventDefault)
60.2652 - event.preventDefault();
60.2653 - else
60.2654 - event.returnValue = false;
60.2655 - }
60.2656 - }
60.2657 - });
60.2658 - }
60.2659 -};
60.2660 -ko.bindingHandlers['text'] = {
60.2661 - 'update': function (element, valueAccessor) {
60.2662 - ko.utils.setTextContent(element, valueAccessor());
60.2663 - }
60.2664 -};
60.2665 -ko.virtualElements.allowedBindings['text'] = true;
60.2666 -ko.bindingHandlers['uniqueName'] = {
60.2667 - 'init': function (element, valueAccessor) {
60.2668 - if (valueAccessor()) {
60.2669 - var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
60.2670 - ko.utils.setElementName(element, name);
60.2671 - }
60.2672 - }
60.2673 -};
60.2674 -ko.bindingHandlers['uniqueName'].currentIndex = 0;
60.2675 -ko.bindingHandlers['value'] = {
60.2676 - 'init': function (element, valueAccessor, allBindingsAccessor) {
60.2677 - // Always catch "change" event; possibly other events too if asked
60.2678 - var eventsToCatch = ["change"];
60.2679 - var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
60.2680 - var propertyChangedFired = false;
60.2681 - if (requestedEventsToCatch) {
60.2682 - if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
60.2683 - requestedEventsToCatch = [requestedEventsToCatch];
60.2684 - ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
60.2685 - eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
60.2686 - }
60.2687 -
60.2688 - var valueUpdateHandler = function() {
60.2689 - propertyChangedFired = false;
60.2690 - var modelValue = valueAccessor();
60.2691 - var elementValue = ko.selectExtensions.readValue(element);
60.2692 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
60.2693 - }
60.2694 -
60.2695 - // Workaround for https://github.com/SteveSanderson/knockout/issues/122
60.2696 - // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
60.2697 - var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
60.2698 - && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
60.2699 - if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
60.2700 - ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
60.2701 - ko.utils.registerEventHandler(element, "blur", function() {
60.2702 - if (propertyChangedFired) {
60.2703 - valueUpdateHandler();
60.2704 - }
60.2705 - });
60.2706 - }
60.2707 -
60.2708 - ko.utils.arrayForEach(eventsToCatch, function(eventName) {
60.2709 - // The syntax "after<eventname>" means "run the handler asynchronously after the event"
60.2710 - // This is useful, for example, to catch "keydown" events after the browser has updated the control
60.2711 - // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
60.2712 - var handler = valueUpdateHandler;
60.2713 - if (ko.utils.stringStartsWith(eventName, "after")) {
60.2714 - handler = function() { setTimeout(valueUpdateHandler, 0) };
60.2715 - eventName = eventName.substring("after".length);
60.2716 - }
60.2717 - ko.utils.registerEventHandler(element, eventName, handler);
60.2718 - });
60.2719 - },
60.2720 - 'update': function (element, valueAccessor) {
60.2721 - var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
60.2722 - var newValue = ko.utils.unwrapObservable(valueAccessor());
60.2723 - var elementValue = ko.selectExtensions.readValue(element);
60.2724 - var valueHasChanged = (newValue != elementValue);
60.2725 -
60.2726 - // JavaScript's 0 == "" behavious is unfortunate here as it prevents writing 0 to an empty text box (loose equality suggests the values are the same).
60.2727 - // We don't want to do a strict equality comparison as that is more confusing for developers in certain cases, so we specifically special case 0 != "" here.
60.2728 - if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
60.2729 - valueHasChanged = true;
60.2730 -
60.2731 - if (valueHasChanged) {
60.2732 - var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
60.2733 - applyValueAction();
60.2734 -
60.2735 - // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
60.2736 - // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
60.2737 - // to apply the value as well.
60.2738 - var alsoApplyAsynchronously = valueIsSelectOption;
60.2739 - if (alsoApplyAsynchronously)
60.2740 - setTimeout(applyValueAction, 0);
60.2741 - }
60.2742 -
60.2743 - // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
60.2744 - // because you're not allowed to have a model value that disagrees with a visible UI selection.
60.2745 - if (valueIsSelectOption && (element.length > 0))
60.2746 - ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
60.2747 - }
60.2748 -};
60.2749 -ko.bindingHandlers['visible'] = {
60.2750 - 'update': function (element, valueAccessor) {
60.2751 - var value = ko.utils.unwrapObservable(valueAccessor());
60.2752 - var isCurrentlyVisible = !(element.style.display == "none");
60.2753 - if (value && !isCurrentlyVisible)
60.2754 - element.style.display = "";
60.2755 - else if ((!value) && isCurrentlyVisible)
60.2756 - element.style.display = "none";
60.2757 - }
60.2758 -};
60.2759 -// 'click' is just a shorthand for the usual full-length event:{click:handler}
60.2760 -makeEventHandlerShortcut('click');
60.2761 -// If you want to make a custom template engine,
60.2762 -//
60.2763 -// [1] Inherit from this class (like ko.nativeTemplateEngine does)
60.2764 -// [2] Override 'renderTemplateSource', supplying a function with this signature:
60.2765 -//
60.2766 -// function (templateSource, bindingContext, options) {
60.2767 -// // - templateSource.text() is the text of the template you should render
60.2768 -// // - bindingContext.$data is the data you should pass into the template
60.2769 -// // - you might also want to make bindingContext.$parent, bindingContext.$parents,
60.2770 -// // and bindingContext.$root available in the template too
60.2771 -// // - options gives you access to any other properties set on "data-bind: { template: options }"
60.2772 -// //
60.2773 -// // Return value: an array of DOM nodes
60.2774 -// }
60.2775 -//
60.2776 -// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
60.2777 -//
60.2778 -// function (script) {
60.2779 -// // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
60.2780 -// // For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
60.2781 -// }
60.2782 -//
60.2783 -// This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
60.2784 -// If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
60.2785 -// and then you don't need to override 'createJavaScriptEvaluatorBlock'.
60.2786 -
60.2787 -ko.templateEngine = function () { };
60.2788 -
60.2789 -ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
60.2790 - throw new Error("Override renderTemplateSource");
60.2791 -};
60.2792 -
60.2793 -ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
60.2794 - throw new Error("Override createJavaScriptEvaluatorBlock");
60.2795 -};
60.2796 -
60.2797 -ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
60.2798 - // Named template
60.2799 - if (typeof template == "string") {
60.2800 - templateDocument = templateDocument || document;
60.2801 - var elem = templateDocument.getElementById(template);
60.2802 - if (!elem)
60.2803 - throw new Error("Cannot find template with ID " + template);
60.2804 - return new ko.templateSources.domElement(elem);
60.2805 - } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
60.2806 - // Anonymous template
60.2807 - return new ko.templateSources.anonymousTemplate(template);
60.2808 - } else
60.2809 - throw new Error("Unknown template type: " + template);
60.2810 -};
60.2811 -
60.2812 -ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
60.2813 - var templateSource = this['makeTemplateSource'](template, templateDocument);
60.2814 - return this['renderTemplateSource'](templateSource, bindingContext, options);
60.2815 -};
60.2816 -
60.2817 -ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
60.2818 - // Skip rewriting if requested
60.2819 - if (this['allowTemplateRewriting'] === false)
60.2820 - return true;
60.2821 - return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
60.2822 -};
60.2823 -
60.2824 -ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
60.2825 - var templateSource = this['makeTemplateSource'](template, templateDocument);
60.2826 - var rewritten = rewriterCallback(templateSource['text']());
60.2827 - templateSource['text'](rewritten);
60.2828 - templateSource['data']("isRewritten", true);
60.2829 -};
60.2830 -
60.2831 -ko.exportSymbol('templateEngine', ko.templateEngine);
60.2832 -
60.2833 -ko.templateRewriting = (function () {
60.2834 - var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
60.2835 - var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
60.2836 -
60.2837 - function validateDataBindValuesForRewriting(keyValueArray) {
60.2838 - var allValidators = ko.expressionRewriting.bindingRewriteValidators;
60.2839 - for (var i = 0; i < keyValueArray.length; i++) {
60.2840 - var key = keyValueArray[i]['key'];
60.2841 - if (allValidators.hasOwnProperty(key)) {
60.2842 - var validator = allValidators[key];
60.2843 -
60.2844 - if (typeof validator === "function") {
60.2845 - var possibleErrorMessage = validator(keyValueArray[i]['value']);
60.2846 - if (possibleErrorMessage)
60.2847 - throw new Error(possibleErrorMessage);
60.2848 - } else if (!validator) {
60.2849 - throw new Error("This template engine does not support the '" + key + "' binding within its templates");
60.2850 - }
60.2851 - }
60.2852 - }
60.2853 - }
60.2854 -
60.2855 - function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
60.2856 - var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
60.2857 - validateDataBindValuesForRewriting(dataBindKeyValueArray);
60.2858 - var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
60.2859 -
60.2860 - // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
60.2861 - // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
60.2862 - // extra indirection.
60.2863 - var applyBindingsToNextSiblingScript =
60.2864 - "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
60.2865 - return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
60.2866 - }
60.2867 -
60.2868 - return {
60.2869 - ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
60.2870 - if (!templateEngine['isTemplateRewritten'](template, templateDocument))
60.2871 - templateEngine['rewriteTemplate'](template, function (htmlString) {
60.2872 - return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
60.2873 - }, templateDocument);
60.2874 - },
60.2875 -
60.2876 - memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
60.2877 - return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
60.2878 - return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
60.2879 - }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
60.2880 - return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
60.2881 - });
60.2882 - },
60.2883 -
60.2884 - applyMemoizedBindingsToNextSibling: function (bindings) {
60.2885 - return ko.memoization.memoize(function (domNode, bindingContext) {
60.2886 - if (domNode.nextSibling)
60.2887 - ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
60.2888 - });
60.2889 - }
60.2890 - }
60.2891 -})();
60.2892 -
60.2893 -
60.2894 -// Exported only because it has to be referenced by string lookup from within rewritten template
60.2895 -ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
60.2896 -(function() {
60.2897 - // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
60.2898 - // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
60.2899 - //
60.2900 - // Two are provided by default:
60.2901 - // 1. ko.templateSources.domElement - reads/writes the text content of an arbitrary DOM element
60.2902 - // 2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
60.2903 - // without reading/writing the actual element text content, since it will be overwritten
60.2904 - // with the rendered template output.
60.2905 - // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
60.2906 - // Template sources need to have the following functions:
60.2907 - // text() - returns the template text from your storage location
60.2908 - // text(value) - writes the supplied template text to your storage location
60.2909 - // data(key) - reads values stored using data(key, value) - see below
60.2910 - // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
60.2911 - //
60.2912 - // Optionally, template sources can also have the following functions:
60.2913 - // nodes() - returns a DOM element containing the nodes of this template, where available
60.2914 - // nodes(value) - writes the given DOM element to your storage location
60.2915 - // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
60.2916 - // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
60.2917 - //
60.2918 - // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
60.2919 - // using and overriding "makeTemplateSource" to return an instance of your custom template source.
60.2920 -
60.2921 - ko.templateSources = {};
60.2922 -
60.2923 - // ---- ko.templateSources.domElement -----
60.2924 -
60.2925 - ko.templateSources.domElement = function(element) {
60.2926 - this.domElement = element;
60.2927 - }
60.2928 -
60.2929 - ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
60.2930 - var tagNameLower = ko.utils.tagNameLower(this.domElement),
60.2931 - elemContentsProperty = tagNameLower === "script" ? "text"
60.2932 - : tagNameLower === "textarea" ? "value"
60.2933 - : "innerHTML";
60.2934 -
60.2935 - if (arguments.length == 0) {
60.2936 - return this.domElement[elemContentsProperty];
60.2937 - } else {
60.2938 - var valueToWrite = arguments[0];
60.2939 - if (elemContentsProperty === "innerHTML")
60.2940 - ko.utils.setHtml(this.domElement, valueToWrite);
60.2941 - else
60.2942 - this.domElement[elemContentsProperty] = valueToWrite;
60.2943 - }
60.2944 - };
60.2945 -
60.2946 - ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
60.2947 - if (arguments.length === 1) {
60.2948 - return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
60.2949 - } else {
60.2950 - ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
60.2951 - }
60.2952 - };
60.2953 -
60.2954 - // ---- ko.templateSources.anonymousTemplate -----
60.2955 - // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
60.2956 - // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
60.2957 - // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
60.2958 -
60.2959 - var anonymousTemplatesDomDataKey = "__ko_anon_template__";
60.2960 - ko.templateSources.anonymousTemplate = function(element) {
60.2961 - this.domElement = element;
60.2962 - }
60.2963 - ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
60.2964 - ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
60.2965 - if (arguments.length == 0) {
60.2966 - var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
60.2967 - if (templateData.textData === undefined && templateData.containerData)
60.2968 - templateData.textData = templateData.containerData.innerHTML;
60.2969 - return templateData.textData;
60.2970 - } else {
60.2971 - var valueToWrite = arguments[0];
60.2972 - ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
60.2973 - }
60.2974 - };
60.2975 - ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
60.2976 - if (arguments.length == 0) {
60.2977 - var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
60.2978 - return templateData.containerData;
60.2979 - } else {
60.2980 - var valueToWrite = arguments[0];
60.2981 - ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
60.2982 - }
60.2983 - };
60.2984 -
60.2985 - ko.exportSymbol('templateSources', ko.templateSources);
60.2986 - ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
60.2987 - ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
60.2988 -})();
60.2989 -(function () {
60.2990 - var _templateEngine;
60.2991 - ko.setTemplateEngine = function (templateEngine) {
60.2992 - if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
60.2993 - throw new Error("templateEngine must inherit from ko.templateEngine");
60.2994 - _templateEngine = templateEngine;
60.2995 - }
60.2996 -
60.2997 - function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
60.2998 - var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
60.2999 - while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
60.3000 - nextInQueue = ko.virtualElements.nextSibling(node);
60.3001 - if (node.nodeType === 1 || node.nodeType === 8)
60.3002 - action(node);
60.3003 - }
60.3004 - }
60.3005 -
60.3006 - function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
60.3007 - // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
60.3008 - // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
60.3009 - // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
60.3010 - // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
60.3011 - // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
60.3012 -
60.3013 - if (continuousNodeArray.length) {
60.3014 - var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
60.3015 -
60.3016 - // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
60.3017 - // whereas a regular applyBindings won't introduce new memoized nodes
60.3018 - invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
60.3019 - ko.applyBindings(bindingContext, node);
60.3020 - });
60.3021 - invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
60.3022 - ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
60.3023 - });
60.3024 - }
60.3025 - }
60.3026 -
60.3027 - function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
60.3028 - return nodeOrNodeArray.nodeType ? nodeOrNodeArray
60.3029 - : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
60.3030 - : null;
60.3031 - }
60.3032 -
60.3033 - function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
60.3034 - options = options || {};
60.3035 - var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
60.3036 - var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
60.3037 - var templateEngineToUse = (options['templateEngine'] || _templateEngine);
60.3038 - ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
60.3039 - var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
60.3040 -
60.3041 - // Loosely check result is an array of DOM nodes
60.3042 - if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
60.3043 - throw new Error("Template engine must return an array of DOM nodes");
60.3044 -
60.3045 - var haveAddedNodesToParent = false;
60.3046 - switch (renderMode) {
60.3047 - case "replaceChildren":
60.3048 - ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
60.3049 - haveAddedNodesToParent = true;
60.3050 - break;
60.3051 - case "replaceNode":
60.3052 - ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
60.3053 - haveAddedNodesToParent = true;
60.3054 - break;
60.3055 - case "ignoreTargetNode": break;
60.3056 - default:
60.3057 - throw new Error("Unknown renderMode: " + renderMode);
60.3058 - }
60.3059 -
60.3060 - if (haveAddedNodesToParent) {
60.3061 - activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
60.3062 - if (options['afterRender'])
60.3063 - ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
60.3064 - }
60.3065 -
60.3066 - return renderedNodesArray;
60.3067 - }
60.3068 -
60.3069 - ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
60.3070 - options = options || {};
60.3071 - if ((options['templateEngine'] || _templateEngine) == undefined)
60.3072 - throw new Error("Set a template engine before calling renderTemplate");
60.3073 - renderMode = renderMode || "replaceChildren";
60.3074 -
60.3075 - if (targetNodeOrNodeArray) {
60.3076 - var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
60.3077 -
60.3078 - var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
60.3079 - var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
60.3080 -
60.3081 - return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
60.3082 - function () {
60.3083 - // Ensure we've got a proper binding context to work with
60.3084 - var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
60.3085 - ? dataOrBindingContext
60.3086 - : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
60.3087 -
60.3088 - // Support selecting template as a function of the data being rendered
60.3089 - var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
60.3090 -
60.3091 - var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
60.3092 - if (renderMode == "replaceNode") {
60.3093 - targetNodeOrNodeArray = renderedNodesArray;
60.3094 - firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
60.3095 - }
60.3096 - },
60.3097 - null,
60.3098 - { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
60.3099 - );
60.3100 - } else {
60.3101 - // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node
60.3102 - return ko.memoization.memoize(function (domNode) {
60.3103 - ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
60.3104 - });
60.3105 - }
60.3106 - };
60.3107 -
60.3108 - ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
60.3109 - // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
60.3110 - // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
60.3111 - var arrayItemContext;
60.3112 -
60.3113 - // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
60.3114 - var executeTemplateForArrayItem = function (arrayValue, index) {
60.3115 - // Support selecting template as a function of the data being rendered
60.3116 - arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
60.3117 - arrayItemContext['$index'] = index;
60.3118 - var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
60.3119 - return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
60.3120 - }
60.3121 -
60.3122 - // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
60.3123 - var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
60.3124 - activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
60.3125 - if (options['afterRender'])
60.3126 - options['afterRender'](addedNodesArray, arrayValue);
60.3127 - };
60.3128 -
60.3129 - return ko.dependentObservable(function () {
60.3130 - var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
60.3131 - if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
60.3132 - unwrappedArray = [unwrappedArray];
60.3133 -
60.3134 - // Filter out any entries marked as destroyed
60.3135 - var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
60.3136 - return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
60.3137 - });
60.3138 -
60.3139 - // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
60.3140 - // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
60.3141 - ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
60.3142 -
60.3143 - }, null, { disposeWhenNodeIsRemoved: targetNode });
60.3144 - };
60.3145 -
60.3146 - var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
60.3147 - function disposeOldComputedAndStoreNewOne(element, newComputed) {
60.3148 - var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
60.3149 - if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
60.3150 - oldComputed.dispose();
60.3151 - ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
60.3152 - }
60.3153 -
60.3154 - ko.bindingHandlers['template'] = {
60.3155 - 'init': function(element, valueAccessor) {
60.3156 - // Support anonymous templates
60.3157 - var bindingValue = ko.utils.unwrapObservable(valueAccessor());
60.3158 - if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
60.3159 - // It's an anonymous template - store the element contents, then clear the element
60.3160 - var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
60.3161 - container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
60.3162 - new ko.templateSources.anonymousTemplate(element)['nodes'](container);
60.3163 - }
60.3164 - return { 'controlsDescendantBindings': true };
60.3165 - },
60.3166 - 'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
60.3167 - var templateName = ko.utils.unwrapObservable(valueAccessor()),
60.3168 - options = {},
60.3169 - shouldDisplay = true,
60.3170 - dataValue,
60.3171 - templateComputed = null;
60.3172 -
60.3173 - if (typeof templateName != "string") {
60.3174 - options = templateName;
60.3175 - templateName = options['name'];
60.3176 -
60.3177 - // Support "if"/"ifnot" conditions
60.3178 - if ('if' in options)
60.3179 - shouldDisplay = ko.utils.unwrapObservable(options['if']);
60.3180 - if (shouldDisplay && 'ifnot' in options)
60.3181 - shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
60.3182 -
60.3183 - dataValue = ko.utils.unwrapObservable(options['data']);
60.3184 - }
60.3185 -
60.3186 - if ('foreach' in options) {
60.3187 - // Render once for each data point (treating data set as empty if shouldDisplay==false)
60.3188 - var dataArray = (shouldDisplay && options['foreach']) || [];
60.3189 - templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
60.3190 - } else if (!shouldDisplay) {
60.3191 - ko.virtualElements.emptyNode(element);
60.3192 - } else {
60.3193 - // Render once for this single data point (or use the viewModel if no data was provided)
60.3194 - var innerBindingContext = ('data' in options) ?
60.3195 - bindingContext['createChildContext'](dataValue, options['as']) : // Given an explitit 'data' value, we create a child binding context for it
60.3196 - bindingContext; // Given no explicit 'data' value, we retain the same binding context
60.3197 - templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
60.3198 - }
60.3199 -
60.3200 - // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
60.3201 - disposeOldComputedAndStoreNewOne(element, templateComputed);
60.3202 - }
60.3203 - };
60.3204 -
60.3205 - // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
60.3206 - ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
60.3207 - var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
60.3208 -
60.3209 - if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
60.3210 - return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)
60.3211 -
60.3212 - if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
60.3213 - return null; // Named templates can be rewritten, so return "no error"
60.3214 - return "This template engine does not support anonymous templates nested within its templates";
60.3215 - };
60.3216 -
60.3217 - ko.virtualElements.allowedBindings['template'] = true;
60.3218 -})();
60.3219 -
60.3220 -ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
60.3221 -ko.exportSymbol('renderTemplate', ko.renderTemplate);
60.3222 -
60.3223 -ko.utils.compareArrays = (function () {
60.3224 - var statusNotInOld = 'added', statusNotInNew = 'deleted';
60.3225 -
60.3226 - // Simple calculation based on Levenshtein distance.
60.3227 - function compareArrays(oldArray, newArray, dontLimitMoves) {
60.3228 - oldArray = oldArray || [];
60.3229 - newArray = newArray || [];
60.3230 -
60.3231 - if (oldArray.length <= newArray.length)
60.3232 - return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
60.3233 - else
60.3234 - return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
60.3235 - }
60.3236 -
60.3237 - function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
60.3238 - var myMin = Math.min,
60.3239 - myMax = Math.max,
60.3240 - editDistanceMatrix = [],
60.3241 - smlIndex, smlIndexMax = smlArray.length,
60.3242 - bigIndex, bigIndexMax = bigArray.length,
60.3243 - compareRange = (bigIndexMax - smlIndexMax) || 1,
60.3244 - maxDistance = smlIndexMax + bigIndexMax + 1,
60.3245 - thisRow, lastRow,
60.3246 - bigIndexMaxForRow, bigIndexMinForRow;
60.3247 -
60.3248 - for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
60.3249 - lastRow = thisRow;
60.3250 - editDistanceMatrix.push(thisRow = []);
60.3251 - bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
60.3252 - bigIndexMinForRow = myMax(0, smlIndex - 1);
60.3253 - for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
60.3254 - if (!bigIndex)
60.3255 - thisRow[bigIndex] = smlIndex + 1;
60.3256 - else if (!smlIndex) // Top row - transform empty array into new array via additions
60.3257 - thisRow[bigIndex] = bigIndex + 1;
60.3258 - else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
60.3259 - thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
60.3260 - else {
60.3261 - var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
60.3262 - var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
60.3263 - thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
60.3264 - }
60.3265 - }
60.3266 - }
60.3267 -
60.3268 - var editScript = [], meMinusOne, notInSml = [], notInBig = [];
60.3269 - for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
60.3270 - meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
60.3271 - if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
60.3272 - notInSml.push(editScript[editScript.length] = { // added
60.3273 - 'status': statusNotInSml,
60.3274 - 'value': bigArray[--bigIndex],
60.3275 - 'index': bigIndex });
60.3276 - } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
60.3277 - notInBig.push(editScript[editScript.length] = { // deleted
60.3278 - 'status': statusNotInBig,
60.3279 - 'value': smlArray[--smlIndex],
60.3280 - 'index': smlIndex });
60.3281 - } else {
60.3282 - editScript.push({
60.3283 - 'status': "retained",
60.3284 - 'value': bigArray[--bigIndex] });
60.3285 - --smlIndex;
60.3286 - }
60.3287 - }
60.3288 -
60.3289 - if (notInSml.length && notInBig.length) {
60.3290 - // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
60.3291 - // smlIndexMax keeps the time complexity of this algorithm linear.
60.3292 - var limitFailedCompares = smlIndexMax * 10, failedCompares,
60.3293 - a, d, notInSmlItem, notInBigItem;
60.3294 - // Go through the items that have been added and deleted and try to find matches between them.
60.3295 - for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
60.3296 - for (d = 0; notInBigItem = notInBig[d]; d++) {
60.3297 - if (notInSmlItem['value'] === notInBigItem['value']) {
60.3298 - notInSmlItem['moved'] = notInBigItem['index'];
60.3299 - notInBigItem['moved'] = notInSmlItem['index'];
60.3300 - notInBig.splice(d,1); // This item is marked as moved; so remove it from notInBig list
60.3301 - failedCompares = d = 0; // Reset failed compares count because we're checking for consecutive failures
60.3302 - break;
60.3303 - }
60.3304 - }
60.3305 - failedCompares += d;
60.3306 - }
60.3307 - }
60.3308 - return editScript.reverse();
60.3309 - }
60.3310 -
60.3311 - return compareArrays;
60.3312 -})();
60.3313 -
60.3314 -ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
60.3315 -
60.3316 -(function () {
60.3317 - // Objective:
60.3318 - // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
60.3319 - // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
60.3320 - // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
60.3321 - // so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
60.3322 - // previously mapped - retain those nodes, and just insert/delete other ones
60.3323 -
60.3324 - // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
60.3325 - // You can use this, for example, to activate bindings on those nodes.
60.3326 -
60.3327 - function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
60.3328 - // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
60.3329 - // them against what is in the DOM right now. It may be that some of the nodes have already been removed from the document,
60.3330 - // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
60.3331 - // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
60.3332 - // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
60.3333 - //
60.3334 - // Rules:
60.3335 - // [A] Any leading nodes that aren't in the document any more should be ignored
60.3336 - // These most likely correspond to memoization nodes that were already removed during binding
60.3337 - // See https://github.com/SteveSanderson/knockout/pull/440
60.3338 - // [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
60.3339 - // have already been removed, and include any nodes that have been inserted among the previous collection
60.3340 -
60.3341 - // Rule [A]
60.3342 - while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
60.3343 - contiguousNodeArray.splice(0, 1);
60.3344 -
60.3345 - // Rule [B]
60.3346 - if (contiguousNodeArray.length > 1) {
60.3347 - // Build up the actual new contiguous node set
60.3348 - var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
60.3349 - while (current !== last) {
60.3350 - current = current.nextSibling;
60.3351 - if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
60.3352 - return;
60.3353 - newContiguousSet.push(current);
60.3354 - }
60.3355 -
60.3356 - // ... then mutate the input array to match this.
60.3357 - // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
60.3358 - Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
60.3359 - }
60.3360 - return contiguousNodeArray;
60.3361 - }
60.3362 -
60.3363 - function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
60.3364 - // Map this array value inside a dependentObservable so we re-map when any dependency changes
60.3365 - var mappedNodes = [];
60.3366 - var dependentObservable = ko.dependentObservable(function() {
60.3367 - var newMappedNodes = mapping(valueToMap, index) || [];
60.3368 -
60.3369 - // On subsequent evaluations, just replace the previously-inserted DOM nodes
60.3370 - if (mappedNodes.length > 0) {
60.3371 - ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
60.3372 - if (callbackAfterAddingNodes)
60.3373 - ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
60.3374 - }
60.3375 -
60.3376 - // Replace the contents of the mappedNodes array, thereby updating the record
60.3377 - // of which nodes would be deleted if valueToMap was itself later removed
60.3378 - mappedNodes.splice(0, mappedNodes.length);
60.3379 - ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
60.3380 - }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
60.3381 - return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
60.3382 - }
60.3383 -
60.3384 - var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
60.3385 -
60.3386 - ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
60.3387 - // Compare the provided array against the previous one
60.3388 - array = array || [];
60.3389 - options = options || {};
60.3390 - var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
60.3391 - var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
60.3392 - var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
60.3393 - var editScript = ko.utils.compareArrays(lastArray, array);
60.3394 -
60.3395 - // Build the new mapping result
60.3396 - var newMappingResult = [];
60.3397 - var lastMappingResultIndex = 0;
60.3398 - var newMappingResultIndex = 0;
60.3399 -
60.3400 - var nodesToDelete = [];
60.3401 - var itemsToProcess = [];
60.3402 - var itemsForBeforeRemoveCallbacks = [];
60.3403 - var itemsForMoveCallbacks = [];
60.3404 - var itemsForAfterAddCallbacks = [];
60.3405 - var mapData;
60.3406 -
60.3407 - function itemMovedOrRetained(editScriptIndex, oldPosition) {
60.3408 - mapData = lastMappingResult[oldPosition];
60.3409 - if (newMappingResultIndex !== oldPosition)
60.3410 - itemsForMoveCallbacks[editScriptIndex] = mapData;
60.3411 - // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
60.3412 - mapData.indexObservable(newMappingResultIndex++);
60.3413 - fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
60.3414 - newMappingResult.push(mapData);
60.3415 - itemsToProcess.push(mapData);
60.3416 - }
60.3417 -
60.3418 - function callCallback(callback, items) {
60.3419 - if (callback) {
60.3420 - for (var i = 0, n = items.length; i < n; i++) {
60.3421 - if (items[i]) {
60.3422 - ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
60.3423 - callback(node, i, items[i].arrayEntry);
60.3424 - });
60.3425 - }
60.3426 - }
60.3427 - }
60.3428 - }
60.3429 -
60.3430 - for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
60.3431 - movedIndex = editScriptItem['moved'];
60.3432 - switch (editScriptItem['status']) {
60.3433 - case "deleted":
60.3434 - if (movedIndex === undefined) {
60.3435 - mapData = lastMappingResult[lastMappingResultIndex];
60.3436 -
60.3437 - // Stop tracking changes to the mapping for these nodes
60.3438 - if (mapData.dependentObservable)
60.3439 - mapData.dependentObservable.dispose();
60.3440 -
60.3441 - // Queue these nodes for later removal
60.3442 - nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
60.3443 - if (options['beforeRemove']) {
60.3444 - itemsForBeforeRemoveCallbacks[i] = mapData;
60.3445 - itemsToProcess.push(mapData);
60.3446 - }
60.3447 - }
60.3448 - lastMappingResultIndex++;
60.3449 - break;
60.3450 -
60.3451 - case "retained":
60.3452 - itemMovedOrRetained(i, lastMappingResultIndex++);
60.3453 - break;
60.3454 -
60.3455 - case "added":
60.3456 - if (movedIndex !== undefined) {
60.3457 - itemMovedOrRetained(i, movedIndex);
60.3458 - } else {
60.3459 - mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
60.3460 - newMappingResult.push(mapData);
60.3461 - itemsToProcess.push(mapData);
60.3462 - if (!isFirstExecution)
60.3463 - itemsForAfterAddCallbacks[i] = mapData;
60.3464 - }
60.3465 - break;
60.3466 - }
60.3467 - }
60.3468 -
60.3469 - // Call beforeMove first before any changes have been made to the DOM
60.3470 - callCallback(options['beforeMove'], itemsForMoveCallbacks);
60.3471 -
60.3472 - // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
60.3473 - ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
60.3474 -
60.3475 - // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
60.3476 - for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
60.3477 - // Get nodes for newly added items
60.3478 - if (!mapData.mappedNodes)
60.3479 - ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
60.3480 -
60.3481 - // Put nodes in the right place if they aren't there already
60.3482 - for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
60.3483 - if (node !== nextNode)
60.3484 - ko.virtualElements.insertAfter(domNode, node, lastNode);
60.3485 - }
60.3486 -
60.3487 - // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
60.3488 - if (!mapData.initialized && callbackAfterAddingNodes) {
60.3489 - callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
60.3490 - mapData.initialized = true;
60.3491 - }
60.3492 - }
60.3493 -
60.3494 - // If there's a beforeRemove callback, call it after reordering.
60.3495 - // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
60.3496 - // some sort of animation, which is why we first reorder the nodes that will be removed. If the
60.3497 - // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
60.3498 - // Perhaps we'll make that change in the future if this scenario becomes more common.
60.3499 - callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
60.3500 -
60.3501 - // Finally call afterMove and afterAdd callbacks
60.3502 - callCallback(options['afterMove'], itemsForMoveCallbacks);
60.3503 - callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
60.3504 -
60.3505 - // Store a copy of the array items we just considered so we can difference it next time
60.3506 - ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
60.3507 - }
60.3508 -})();
60.3509 -
60.3510 -ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
60.3511 -ko.nativeTemplateEngine = function () {
60.3512 - this['allowTemplateRewriting'] = false;
60.3513 -}
60.3514 -
60.3515 -ko.nativeTemplateEngine.prototype = new ko.templateEngine();
60.3516 -ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
60.3517 - var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
60.3518 - templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
60.3519 - templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
60.3520 -
60.3521 - if (templateNodes) {
60.3522 - return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
60.3523 - } else {
60.3524 - var templateText = templateSource['text']();
60.3525 - return ko.utils.parseHtmlFragment(templateText);
60.3526 - }
60.3527 -};
60.3528 -
60.3529 -ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
60.3530 -ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
60.3531 -
60.3532 -ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
60.3533 -(function() {
60.3534 - ko.jqueryTmplTemplateEngine = function () {
60.3535 - // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
60.3536 - // doesn't expose a version number, so we have to infer it.
60.3537 - // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
60.3538 - // which KO internally refers to as version "2", so older versions are no longer detected.
60.3539 - var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
60.3540 - if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
60.3541 - return 0;
60.3542 - // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
60.3543 - try {
60.3544 - if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
60.3545 - // Since 1.0.0pre, custom tags should append markup to an array called "__"
60.3546 - return 2; // Final version of jquery.tmpl
60.3547 - }
60.3548 - } catch(ex) { /* Apparently not the version we were looking for */ }
60.3549 -
60.3550 - return 1; // Any older version that we don't support
60.3551 - })();
60.3552 -
60.3553 - function ensureHasReferencedJQueryTemplates() {
60.3554 - if (jQueryTmplVersion < 2)
60.3555 - throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
60.3556 - }
60.3557 -
60.3558 - function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
60.3559 - return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
60.3560 - }
60.3561 -
60.3562 - this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
60.3563 - options = options || {};
60.3564 - ensureHasReferencedJQueryTemplates();
60.3565 -
60.3566 - // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
60.3567 - var precompiled = templateSource['data']('precompiled');
60.3568 - if (!precompiled) {
60.3569 - var templateText = templateSource['text']() || "";
60.3570 - // Wrap in "with($whatever.koBindingContext) { ... }"
60.3571 - templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
60.3572 -
60.3573 - precompiled = jQuery['template'](null, templateText);
60.3574 - templateSource['data']('precompiled', precompiled);
60.3575 - }
60.3576 -
60.3577 - var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
60.3578 - var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
60.3579 -
60.3580 - var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
60.3581 - resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
60.3582 -
60.3583 - jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
60.3584 - return resultNodes;
60.3585 - };
60.3586 -
60.3587 - this['createJavaScriptEvaluatorBlock'] = function(script) {
60.3588 - return "{{ko_code ((function() { return " + script + " })()) }}";
60.3589 - };
60.3590 -
60.3591 - this['addTemplate'] = function(templateName, templateMarkup) {
60.3592 - document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
60.3593 - };
60.3594 -
60.3595 - if (jQueryTmplVersion > 0) {
60.3596 - jQuery['tmpl']['tag']['ko_code'] = {
60.3597 - open: "__.push($1 || '');"
60.3598 - };
60.3599 - jQuery['tmpl']['tag']['ko_with'] = {
60.3600 - open: "with($1) {",
60.3601 - close: "} "
60.3602 - };
60.3603 - }
60.3604 - };
60.3605 -
60.3606 - ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
60.3607 -
60.3608 - // Use this one by default *only if jquery.tmpl is referenced*
60.3609 - var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
60.3610 - if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
60.3611 - ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
60.3612 -
60.3613 - ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
60.3614 -})();
60.3615 -});
60.3616 -})(window,document,navigator,window["jQuery"]);
60.3617 -})();
60.3618 \ No newline at end of file
61.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
61.2 +++ b/ko/bck2brwsr/src/test/java/org/apidesign/bck2brwsr/ko2brwsr/Bck2BrwsrJavaScriptBodyTest.java Tue Feb 11 13:31:42 2014 +0100
61.3 @@ -0,0 +1,34 @@
61.4 +/**
61.5 + * Back 2 Browser Bytecode Translator
61.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
61.7 + *
61.8 + * This program is free software: you can redistribute it and/or modify
61.9 + * it under the terms of the GNU General Public License as published by
61.10 + * the Free Software Foundation, version 2 of the License.
61.11 + *
61.12 + * This program is distributed in the hope that it will be useful,
61.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
61.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
61.15 + * GNU General Public License for more details.
61.16 + *
61.17 + * You should have received a copy of the GNU General Public License
61.18 + * along with this program. Look for COPYING file in the top folder.
61.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
61.20 + */
61.21 +package org.apidesign.bck2brwsr.ko2brwsr;
61.22 +
61.23 +import org.apidesign.bck2brwsr.vmtest.VMTest;
61.24 +import org.apidesign.html.json.tck.JavaScriptTCK;
61.25 +import org.apidesign.html.json.tck.KOTest;
61.26 +import org.testng.annotations.Factory;
61.27 +
61.28 +/**
61.29 + *
61.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
61.31 + */
61.32 +public class Bck2BrwsrJavaScriptBodyTest extends JavaScriptTCK {
61.33 + @Factory public static Object[] tests() {
61.34 + return VMTest.newTests().withClasses(testClasses())
61.35 + .withTestAnnotation(KOTest.class).build();
61.36 + }
61.37 +}
62.1 --- a/ko/bck2brwsr/src/test/java/org/apidesign/bck2brwsr/ko2brwsr/Bck2BrwsrKnockoutTest.java Tue Feb 11 10:48:24 2014 +0100
62.2 +++ b/ko/bck2brwsr/src/test/java/org/apidesign/bck2brwsr/ko2brwsr/Bck2BrwsrKnockoutTest.java Tue Feb 11 13:31:42 2014 +0100
62.3 @@ -31,6 +31,7 @@
62.4 import org.apidesign.html.json.spi.WSTransfer;
62.5 import org.apidesign.html.json.tck.KOTest;
62.6 import org.apidesign.html.json.tck.KnockoutTCK;
62.7 +import org.netbeans.html.ko4j.KO4J;
62.8 import org.openide.util.lookup.ServiceProvider;
62.9 import org.testng.annotations.Factory;
62.10
62.11 @@ -50,10 +51,11 @@
62.12
62.13 @Override
62.14 public BrwsrCtx createContext() {
62.15 + KO4J ko = new KO4J(null);
62.16 return Contexts.newBuilder().
62.17 - register(Transfer.class, BrwsrCtxImpl.DEFAULT, 9).
62.18 - register(WSTransfer.class, BrwsrCtxImpl.DEFAULT, 9).
62.19 - register(Technology.class, BrwsrCtxImpl.DEFAULT, 9).build();
62.20 + register(Transfer.class, ko.transfer(), 9).
62.21 + register(WSTransfer.class, ko.websockets(), 9).
62.22 + register(Technology.class, ko.knockout(), 9).build();
62.23 }
62.24
62.25
63.1 --- a/ko/fx/pom.xml Tue Feb 11 10:48:24 2014 +0100
63.2 +++ b/ko/fx/pom.xml Tue Feb 11 13:31:42 2014 +0100
63.3 @@ -40,7 +40,7 @@
63.4 <type>jar</type>
63.5 </dependency>
63.6 <dependency>
63.7 - <groupId>org.apidesign.html</groupId>
63.8 + <groupId>org.netbeans.html</groupId>
63.9 <artifactId>net.java.html.json</artifactId>
63.10 <version>${net.java.html.version}</version>
63.11 </dependency>
63.12 @@ -50,7 +50,7 @@
63.13 <scope>test</scope>
63.14 </dependency>
63.15 <dependency>
63.16 - <groupId>org.apidesign.html</groupId>
63.17 + <groupId>org.netbeans.html</groupId>
63.18 <artifactId>net.java.html.json.tck</artifactId>
63.19 <version>${net.java.html.version}</version>
63.20 <scope>test</scope>
63.21 @@ -67,14 +67,14 @@
63.22 <scope>test</scope>
63.23 </dependency>
63.24 <dependency>
63.25 - <groupId>org.apidesign.html</groupId>
63.26 + <groupId>org.netbeans.html</groupId>
63.27 <artifactId>net.java.html.boot</artifactId>
63.28 <version>${net.java.html.version}</version>
63.29 <type>jar</type>
63.30 </dependency>
63.31 <dependency>
63.32 - <groupId>org.apidesign.html</groupId>
63.33 - <artifactId>ko-fx</artifactId>
63.34 + <groupId>org.netbeans.html</groupId>
63.35 + <artifactId>ko4j</artifactId>
63.36 <version>${net.java.html.version}</version>
63.37 <type>jar</type>
63.38 </dependency>
63.39 @@ -86,7 +86,7 @@
63.40 <type>jar</type>
63.41 </dependency>
63.42 <dependency>
63.43 - <groupId>org.apidesign.html</groupId>
63.44 + <groupId>org.netbeans.html</groupId>
63.45 <artifactId>ko-ws-tyrus</artifactId>
63.46 <version>${net.java.html.version}</version>
63.47 <scope>test</scope>
64.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
64.2 +++ b/ko/fx/src/test/java/org/apidesign/bck2brwsr/kofx/JavaScriptBodyFXBrwsrTest.java Tue Feb 11 13:31:42 2014 +0100
64.3 @@ -0,0 +1,36 @@
64.4 +/**
64.5 + * Back 2 Browser Bytecode Translator
64.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
64.7 + *
64.8 + * This program is free software: you can redistribute it and/or modify
64.9 + * it under the terms of the GNU General Public License as published by
64.10 + * the Free Software Foundation, version 2 of the License.
64.11 + *
64.12 + * This program is distributed in the hope that it will be useful,
64.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
64.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
64.15 + * GNU General Public License for more details.
64.16 + *
64.17 + * You should have received a copy of the GNU General Public License
64.18 + * along with this program. Look for COPYING file in the top folder.
64.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
64.20 + */
64.21 +package org.apidesign.bck2brwsr.kofx;
64.22 +
64.23 +import org.apidesign.bck2brwsr.vmtest.VMTest;
64.24 +import org.apidesign.html.json.tck.JavaScriptTCK;
64.25 +import org.apidesign.html.json.tck.KOTest;
64.26 +import org.testng.annotations.Factory;
64.27 +
64.28 +/**
64.29 + *
64.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
64.31 + */
64.32 +public class JavaScriptBodyFXBrwsrTest extends JavaScriptTCK {
64.33 + @Factory public static Object[] create() {
64.34 + return VMTest.newTests().
64.35 + withLaunchers("fxbrwsr").
64.36 + withClasses(testClasses()).
64.37 + withTestAnnotation(KOTest.class).build();
64.38 + }
64.39 +}
65.1 --- a/ko/fx/src/test/java/org/apidesign/bck2brwsr/kofx/KnockoutFXTest.java Tue Feb 11 10:48:24 2014 +0100
65.2 +++ b/ko/fx/src/test/java/org/apidesign/bck2brwsr/kofx/KnockoutFXTest.java Tue Feb 11 13:31:42 2014 +0100
65.3 @@ -28,17 +28,17 @@
65.4 import net.java.html.BrwsrCtx;
65.5 import net.java.html.js.JavaScriptBody;
65.6 import org.apidesign.bck2brwsr.vmtest.VMTest;
65.7 -import org.apidesign.html.boot.impl.FnUtils;
65.8 +import org.apidesign.html.boot.spi.Fn;
65.9 import org.apidesign.html.context.spi.Contexts;
65.10 import org.apidesign.html.json.spi.Technology;
65.11 import org.apidesign.html.json.spi.Transfer;
65.12 import org.apidesign.html.json.spi.WSTransfer;
65.13 import org.apidesign.html.json.tck.KOTest;
65.14 import org.apidesign.html.json.tck.KnockoutTCK;
65.15 -import org.apidesign.html.kofx.FXContext;
65.16 -import org.apidesign.html.wstyrus.TyrusContext;
65.17 import org.json.JSONException;
65.18 import org.json.JSONObject;
65.19 +import org.netbeans.html.ko4j.KO4J;
65.20 +import org.netbeans.html.wstyrus.TyrusContext;
65.21 import org.openide.util.lookup.ServiceProvider;
65.22 import org.testng.annotations.Factory;
65.23
65.24 @@ -60,34 +60,36 @@
65.25
65.26 @Override
65.27 public BrwsrCtx createContext() {
65.28 - FXContext fx = new FXContext(FnUtils.currentPresenter());
65.29 + KO4J ko = new KO4J(Fn.activePresenter());
65.30 TyrusContext tc = new TyrusContext();
65.31 Contexts.Builder b = Contexts.newBuilder().
65.32 - register(Technology.class, fx, 10).
65.33 - register(Transfer.class, fx, 10);
65.34 + register(Technology.class, ko.knockout(), 10).
65.35 + register(Transfer.class, ko.transfer(), 10);
65.36 try {
65.37 Class.forName("java.util.function.Function");
65.38 // prefer WebView's WebSockets on JDK8
65.39 - b.register(WSTransfer.class, fx, 10);
65.40 + b.register(WSTransfer.class, ko.websockets(), 10);
65.41 } catch (ClassNotFoundException ex) {
65.42 // ok, JDK7 needs tyrus
65.43 b.register(WSTransfer.class, tc, 20);
65.44 + b.register(Transfer.class, tc, 5);
65.45 }
65.46 return b.build();
65.47 }
65.48
65.49 @Override
65.50 public Object createJSON(Map<String, Object> values) {
65.51 - JSONObject json = new JSONObject();
65.52 + Object json = createJSON();
65.53 for (Map.Entry<String, Object> entry : values.entrySet()) {
65.54 - try {
65.55 - json.put(entry.getKey(), entry.getValue());
65.56 - } catch (JSONException ex) {
65.57 - throw new IllegalStateException(ex);
65.58 - }
65.59 + setProperty(json, entry.getKey(), entry.getValue());
65.60 }
65.61 return json;
65.62 }
65.63 +
65.64 + @JavaScriptBody(args = {}, body = "return new Object();")
65.65 + private static native Object createJSON();
65.66 + @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
65.67 + private static native void setProperty(Object json, String key, Object value);
65.68
65.69 @Override
65.70 @JavaScriptBody(args = { "s", "args" }, body = ""
66.1 --- a/launcher/fx/pom.xml Tue Feb 11 10:48:24 2014 +0100
66.2 +++ b/launcher/fx/pom.xml Tue Feb 11 13:31:42 2014 +0100
66.3 @@ -75,7 +75,7 @@
66.4 <version>4.1</version>
66.5 </dependency>
66.6 <dependency>
66.7 - <groupId>org.apidesign.html</groupId>
66.8 + <groupId>org.netbeans.html</groupId>
66.9 <artifactId>net.java.html.boot</artifactId>
66.10 <version>${net.java.html.version}</version>
66.11 </dependency>
67.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Tue Feb 11 10:48:24 2014 +0100
67.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Tue Feb 11 13:31:42 2014 +0100
67.3 @@ -572,7 +572,10 @@
67.4 }
67.5
67.6 class Res {
67.7 - public InputStream get(String resource) throws IOException {
67.8 + public InputStream get(String resource, int skip) throws IOException {
67.9 + if (!resource.endsWith(".class")) {
67.10 + return getResource(resource, skip);
67.11 + }
67.12 URL u = null;
67.13 for (ClassLoader l : loaders) {
67.14 Enumeration<URL> en = l.getResources(resource);
67.15 @@ -585,12 +588,30 @@
67.16 }
67.17 if (u != null) {
67.18 if (u.toExternalForm().contains("rt.jar")) {
67.19 - LOG.log(Level.WARNING, "Fallback to bootclasspath for {0}", u);
67.20 + LOG.log(Level.WARNING, "No fallback to bootclasspath for {0}", u);
67.21 + return null;
67.22 }
67.23 return u.openStream();
67.24 }
67.25 throw new IOException("Can't find " + resource);
67.26 }
67.27 + private InputStream getResource(String resource, int skip) throws IOException {
67.28 + for (ClassLoader l : loaders) {
67.29 + Enumeration<URL> en = l.getResources(resource);
67.30 + while (en.hasMoreElements()) {
67.31 + final URL now = en.nextElement();
67.32 + if (now.toExternalForm().contains("sisu-inject-bean")) {
67.33 + // certainly we don't want this resource, as that
67.34 + // module is not compiled with target 1.6, currently
67.35 + continue;
67.36 + }
67.37 + if (--skip < 0) {
67.38 + return now.openStream();
67.39 + }
67.40 + }
67.41 + }
67.42 + throw new IOException("Not found (anymore of) " + resource);
67.43 + }
67.44 }
67.45
67.46 private static class Page extends HttpHandler {
67.47 @@ -623,7 +644,7 @@
67.48 }
67.49 OutputStream os = response.getOutputStream();
67.50 try {
67.51 - InputStream is = res.get(r);
67.52 + InputStream is = res.get(r, 0);
67.53 copyStream(is, os, request.getRequestURL().toString(), replace);
67.54 } catch (IOException ex) {
67.55 response.setDetailMessage(ex.getLocalizedMessage());
67.56 @@ -695,7 +716,9 @@
67.57 }
67.58 InputStream is = null;
67.59 try {
67.60 - is = loader.get(res);
67.61 + String skip = request.getParameter("skip");
67.62 + int skipCnt = skip == null ? 0 : Integer.parseInt(skip);
67.63 + is = loader.get(res, skipCnt);
67.64 response.setContentType("text/javascript");
67.65 Writer w = response.getWriter();
67.66 w.append("[");
68.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/BrowserToolbar.java Tue Feb 11 10:48:24 2014 +0100
68.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/BrowserToolbar.java Tue Feb 11 13:31:42 2014 +0100
68.3 @@ -117,7 +117,9 @@
68.4 zoom = Math.abs( zoom )/100;
68.5 if( zoom <= 0.0 )
68.6 return null;
68.7 - webView.impl_setScale( zoom );
68.8 + webView.setScaleX(zoom);
68.9 + webView.setScaleY(zoom);
68.10 + webView.setScaleZ(zoom);
68.11 return (int)(100*zoom) + "%"; //NOI18N
68.12 } catch( NumberFormatException nfe ) {
68.13 //ignore
69.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Tue Feb 11 10:48:24 2014 +0100
69.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Tue Feb 11 13:31:42 2014 +0100
69.3 @@ -25,8 +25,8 @@
69.4 import java.lang.reflect.Modifier;
69.5 import java.net.URL;
69.6 import java.util.Enumeration;
69.7 +import net.java.html.js.JavaScriptBody;
69.8 import netscape.javascript.JSObject;
69.9 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
69.10
69.11 /**
69.12 *
69.13 @@ -60,8 +60,7 @@
69.14 }
69.15
69.16 private static void beginTest(Case c) {
69.17 - Object[] arr = new Object[2];
69.18 - beginTest(c.getClassName() + "." + c.getMethodName(), c, arr);
69.19 + Object[] arr = beginTest(c.getClassName() + "." + c.getMethodName(), c, new Object[2]);
69.20 textArea = arr[0];
69.21 statusArea = arr[1];
69.22 }
69.23 @@ -102,23 +101,23 @@
69.24 + "ul.appendChild(li);\n"
69.25 + "arr[0] = pre;\n"
69.26 + "arr[1] = status;\n"
69.27 + + "return arr;"
69.28 )
69.29 - private static native void beginTest(String test, Case c, Object[] arr);
69.30 + private static native Object[] beginTest(String test, Case c, Object[] arr);
69.31
69.32 - @JavaScriptBody(args = { "url", "callback", "arr" }, body =
69.33 + @JavaScriptBody(args = { "url", "callback" }, javacall = true, body =
69.34 "var request = new XMLHttpRequest();\n"
69.35 + "request.open('GET', url, true);\n"
69.36 + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
69.37 + "request.onreadystatechange = function() {\n"
69.38 + " if (this.readyState!==4) return;\n"
69.39 + " try {\n"
69.40 - + " arr[0] = this.responseText;\n"
69.41 - + " callback.run();\n"
69.42 + + " callback.@org.apidesign.bck2brwsr.launcher.fximpl.OnMessage::onMessage(Ljava/lang/String;)(this.responseText);\n"
69.43 + " } catch (e) { alert(e); }\n"
69.44 + "};\n"
69.45 + "request.send();\n"
69.46 )
69.47 - private static native void loadText(String url, Runnable callback, String[] arr) throws IOException;
69.48 + private static native void loadText(String url, OnMessage callback) throws IOException;
69.49
69.50 public static void runHarness(String url) throws IOException {
69.51 new Console().harness(url);
69.52 @@ -129,7 +128,7 @@
69.53 Request r = new Request(url);
69.54 }
69.55
69.56 - private static class Request implements Runnable {
69.57 + private static class Request implements Runnable, OnMessage {
69.58 private final String[] arr = { null };
69.59 private final String url;
69.60 private Case c;
69.61 @@ -137,11 +136,17 @@
69.62
69.63 private Request(String url) throws IOException {
69.64 this.url = url;
69.65 - loadText(url, new Run(this), arr);
69.66 + loadText(url, this);
69.67 }
69.68 private Request(String url, String u) throws IOException {
69.69 this.url = url;
69.70 - loadText(u, new Run(this), arr);
69.71 + loadText(u, this);
69.72 + }
69.73 +
69.74 + @Override
69.75 + public void onMessage(String msg) {
69.76 + arr[0] = msg;
69.77 + run();
69.78 }
69.79
69.80 @Override
69.81 @@ -177,7 +182,7 @@
69.82 } catch (Exception ex) {
69.83 if (ex instanceof InterruptedException) {
69.84 log("Re-scheduling in 100ms");
69.85 - schedule(new Run(this), 100);
69.86 + schedule(this, 100);
69.87 return;
69.88 }
69.89 log(ex.getClass().getName() + ":" + ex.getMessage());
69.90 @@ -250,7 +255,11 @@
69.91 private static void turnAssetionStatusOn() {
69.92 }
69.93
69.94 - @JavaScriptBody(args = { "r", "time" }, body = "return window.setTimeout(function() { r.run(); }, time);")
69.95 + @JavaScriptBody(args = { "r", "time" }, javacall = true, body =
69.96 + "return window.setTimeout(function() { "
69.97 + + "r.@java.lang.Runnable::run()(); "
69.98 + + "}, time);"
69.99 + )
69.100 private static native Object schedule(Runnable r, int time);
69.101
69.102 private static final class Case {
70.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Tue Feb 11 10:48:24 2014 +0100
70.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Tue Feb 11 13:31:42 2014 +0100
70.3 @@ -34,6 +34,7 @@
70.4 import javafx.scene.control.ToolBar;
70.5 import javafx.scene.layout.BorderPane;
70.6 import javafx.scene.layout.HBox;
70.7 +import javafx.scene.layout.Priority;
70.8 import javafx.scene.layout.VBox;
70.9 import javafx.scene.text.Text;
70.10 import javafx.scene.web.WebEngine;
70.11 @@ -68,7 +69,9 @@
70.12 hbox.setStyle( "-fx-background-color: #808080;");
70.13 hbox.setAlignment(Pos.CENTER);
70.14 hbox.getChildren().add(vbox);
70.15 + HBox.setHgrow(vbox, Priority.ALWAYS);
70.16 vbox.getChildren().add(view);
70.17 + VBox.setVgrow(view, Priority.ALWAYS);
70.18
70.19 BorderPane root = new BorderPane();
70.20 final boolean showToolbar = "true".equals(this.getParameters().getNamed().get("toolbar")); // NOI18N
71.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Tue Feb 11 10:48:24 2014 +0100
71.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Tue Feb 11 13:31:42 2014 +0100
71.3 @@ -19,24 +19,30 @@
71.4
71.5 import java.io.BufferedReader;
71.6 import java.io.Reader;
71.7 -import org.apidesign.html.boot.spi.Fn;
71.8 import java.net.URL;
71.9 import java.util.ArrayList;
71.10 import java.util.Arrays;
71.11 import java.util.Collection;
71.12 import java.util.List;
71.13 import java.util.TooManyListenersException;
71.14 +import java.util.concurrent.Executor;
71.15 +import java.util.logging.Level;
71.16 +import java.util.logging.Logger;
71.17 +import javafx.application.Platform;
71.18 import javafx.beans.value.ChangeListener;
71.19 import javafx.scene.web.WebEngine;
71.20 import netscape.javascript.JSObject;
71.21 -import org.apidesign.html.boot.impl.FindResources;
71.22 -import org.apidesign.html.boot.impl.FnUtils;
71.23 +import org.apidesign.html.boot.spi.Fn;
71.24 +import org.netbeans.html.boot.impl.FindResources;
71.25 +import org.netbeans.html.boot.impl.FnUtils;
71.26
71.27 /**
71.28 *
71.29 * @author Jaroslav Tulach <jtulach@netbeans.org>
71.30 */
71.31 public final class JVMBridge {
71.32 + static final Logger LOG = Logger.getLogger(JVMBridge.class.getName());
71.33 +
71.34 private final WebEngine engine;
71.35 private final ClassLoader cl;
71.36 private final WebPresenter presenter;
71.37 @@ -70,11 +76,12 @@
71.38 }
71.39
71.40 public Class<?> loadClass(String name) throws ClassNotFoundException {
71.41 - FnUtils.currentPresenter(presenter);
71.42 + Fn.activate(presenter);
71.43 return Class.forName(name, true, cl);
71.44 }
71.45
71.46 - private final class WebPresenter implements FindResources, Fn.Presenter {
71.47 + private final class WebPresenter
71.48 + implements FindResources, Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor {
71.49 @Override
71.50 public void findResources(String name, Collection<? super URL> results, boolean oneIsEnough) {
71.51 if (ldrs != null) for (ClassLoader l : ldrs) {
71.52 @@ -87,6 +94,9 @@
71.53
71.54 @Override
71.55 public Fn defineFn(String code, String... names) {
71.56 + return defineJSFn(code, names);
71.57 + }
71.58 + private JSFn defineJSFn(String code, String... names) {
71.59 StringBuilder sb = new StringBuilder();
71.60 sb.append("(function() {");
71.61 sb.append(" return function(");
71.62 @@ -122,6 +132,91 @@
71.63 }
71.64 engine.executeScript(sb.toString());
71.65 }
71.66 +
71.67 + @Override
71.68 + public Object toJava(Object js) {
71.69 + return checkArray(js);
71.70 + }
71.71 +
71.72 + @Override
71.73 + public Object toJavaScript(Object toReturn) {
71.74 + if (toReturn instanceof Object[]) {
71.75 + return convertArrays((Object[]) toReturn);
71.76 + }
71.77 + return toReturn;
71.78 + }
71.79 +
71.80 + @Override
71.81 + public void execute(Runnable command) {
71.82 + if (Platform.isFxApplicationThread()) {
71.83 + command.run();
71.84 + } else {
71.85 + Platform.runLater(command);
71.86 + }
71.87 + }
71.88 +
71.89 + final JSObject convertArrays(Object[] arr) {
71.90 + for (int i = 0; i < arr.length; i++) {
71.91 + if (arr[i] instanceof Object[]) {
71.92 + arr[i] = convertArrays((Object[]) arr[i]);
71.93 + }
71.94 + }
71.95 + final JSObject wrapArr = (JSObject) wrapArrFn().call("array", arr); // NOI18N
71.96 + return wrapArr;
71.97 + }
71.98 +
71.99 + private JSObject wrapArrImpl;
71.100 +
71.101 + private final JSObject wrapArrFn() {
71.102 + if (wrapArrImpl == null) {
71.103 + try {
71.104 + wrapArrImpl = (JSObject) defineJSFn(" var k = {};"
71.105 + + " k.array= function() {"
71.106 + + " return Array.prototype.slice.call(arguments);"
71.107 + + " };"
71.108 + + " return k;"
71.109 + ).invokeImpl(null, false);
71.110 + } catch (Exception ex) {
71.111 + throw new IllegalStateException(ex);
71.112 + }
71.113 + }
71.114 + return wrapArrImpl;
71.115 + }
71.116 +
71.117 + final Object checkArray(Object val) {
71.118 + int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
71.119 + if (length == -1) {
71.120 + return val;
71.121 + }
71.122 + Object[] arr = new Object[length];
71.123 + arraySizeFn().call("array", val, arr);
71.124 + return arr;
71.125 + }
71.126 + private JSObject arraySize;
71.127 +
71.128 + private final JSObject arraySizeFn() {
71.129 + if (arraySize == null) {
71.130 + try {
71.131 + arraySize = (JSObject) defineJSFn(" var k = {};"
71.132 + + " k.array = function(arr, to) {"
71.133 + + " if (to === null) {"
71.134 + + " if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;"
71.135 + + " else return -1;"
71.136 + + " } else {"
71.137 + + " var l = arr.length;"
71.138 + + " for (var i = 0; i < l; i++) to[i] = arr[i];"
71.139 + + " return l;"
71.140 + + " }"
71.141 + + " };"
71.142 + + " return k;"
71.143 + ).invokeImpl(null, false);
71.144 + } catch (Exception ex) {
71.145 + throw new IllegalStateException(ex);
71.146 + }
71.147 + }
71.148 + return arraySize;
71.149 + }
71.150 +
71.151 }
71.152
71.153 private static final class JSFn extends Fn {
71.154 @@ -134,12 +229,29 @@
71.155
71.156 @Override
71.157 public Object invoke(Object thiz, Object... args) throws Exception {
71.158 + return invokeImpl(thiz, true, args);
71.159 + }
71.160 +
71.161 + final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
71.162 try {
71.163 List<Object> all = new ArrayList<Object>(args.length + 1);
71.164 all.add(thiz == null ? fn : thiz);
71.165 - all.addAll(Arrays.asList(args));
71.166 + for (int i = 0; i < args.length; i++) {
71.167 + if (arrayChecks && args[i] instanceof Object[]) {
71.168 + Object[] arr = (Object[]) args[i];
71.169 + Object conv = ((WebPresenter) presenter()).convertArrays(arr);
71.170 + args[i] = conv;
71.171 + }
71.172 + all.add(args[i]);
71.173 + }
71.174 Object ret = fn.call("call", all.toArray()); // NOI18N
71.175 - return ret == fn ? null : ret;
71.176 + if (ret == fn) {
71.177 + return null;
71.178 + }
71.179 + if (!arrayChecks) {
71.180 + return ret;
71.181 + }
71.182 + return ((WebPresenter) presenter()).checkArray(ret);
71.183 } catch (Error t) {
71.184 t.printStackTrace();
71.185 throw t;
72.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
72.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/OnMessage.java Tue Feb 11 13:31:42 2014 +0100
72.3 @@ -0,0 +1,26 @@
72.4 +/**
72.5 + * Back 2 Browser Bytecode Translator
72.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
72.7 + *
72.8 + * This program is free software: you can redistribute it and/or modify
72.9 + * it under the terms of the GNU General Public License as published by
72.10 + * the Free Software Foundation, version 2 of the License.
72.11 + *
72.12 + * This program is distributed in the hope that it will be useful,
72.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
72.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
72.15 + * GNU General Public License for more details.
72.16 + *
72.17 + * You should have received a copy of the GNU General Public License
72.18 + * along with this program. Look for COPYING file in the top folder.
72.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
72.20 + */
72.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
72.22 +
72.23 +/**
72.24 + *
72.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
72.26 + */
72.27 +interface OnMessage {
72.28 + public void onMessage(String msg);
72.29 +}
73.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Run.java Tue Feb 11 10:48:24 2014 +0100
73.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
73.3 @@ -1,35 +0,0 @@
73.4 -/**
73.5 - * Back 2 Browser Bytecode Translator
73.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
73.7 - *
73.8 - * This program is free software: you can redistribute it and/or modify
73.9 - * it under the terms of the GNU General Public License as published by
73.10 - * the Free Software Foundation, version 2 of the License.
73.11 - *
73.12 - * This program is distributed in the hope that it will be useful,
73.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
73.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
73.15 - * GNU General Public License for more details.
73.16 - *
73.17 - * You should have received a copy of the GNU General Public License
73.18 - * along with this program. Look for COPYING file in the top folder.
73.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
73.20 - */
73.21 -
73.22 -package org.apidesign.bck2brwsr.launcher.fximpl;
73.23 -
73.24 -/**
73.25 - *
73.26 - * @author Jaroslav Tulach <jtulach@netbeans.org>
73.27 - */
73.28 -public final class Run implements Runnable {
73.29 - private final Runnable r;
73.30 - Run(Runnable r) {
73.31 - this.r = r;
73.32 - }
73.33 -
73.34 - @Override
73.35 - public void run() {
73.36 - r.run();
73.37 - }
73.38 -}
74.1 --- a/launcher/fx/src/test/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoaderTest.java Tue Feb 11 10:48:24 2014 +0100
74.2 +++ b/launcher/fx/src/test/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoaderTest.java Tue Feb 11 13:31:42 2014 +0100
74.3 @@ -31,8 +31,8 @@
74.4 import javax.script.ScriptEngineManager;
74.5 import javax.script.ScriptException;
74.6 import org.apidesign.html.boot.spi.Fn;
74.7 -import org.apidesign.html.boot.impl.FindResources;
74.8 -import org.apidesign.html.boot.impl.FnUtils;
74.9 +import org.netbeans.html.boot.impl.FindResources;
74.10 +import org.netbeans.html.boot.impl.FnUtils;
74.11 import static org.testng.Assert.*;
74.12 import org.testng.annotations.BeforeClass;
74.13 import org.testng.annotations.BeforeMethod;
74.14 @@ -92,7 +92,7 @@
74.15 all.addAll(Arrays.asList(args));
74.16 Invocable inv = (Invocable)eng;
74.17 Object ret = inv.invokeMethod(val, "call", all.toArray());
74.18 - return ret == val ? null : ret;
74.19 + return val.equals(ret) ? null : ret;
74.20 }
74.21 };
74.22 } catch (ScriptException ex) {
74.23 @@ -118,7 +118,7 @@
74.24 }
74.25
74.26 @BeforeMethod public void registerPresenter() {
74.27 - FnUtils.currentPresenter(presenter);
74.28 + Fn.activate(presenter);
74.29 }
74.30
74.31 @Test public void noParamMethod() throws Throwable {
75.1 --- a/launcher/fx/src/test/java/org/apidesign/bck2brwsr/launcher/fximpl/JsMethods.java Tue Feb 11 10:48:24 2014 +0100
75.2 +++ b/launcher/fx/src/test/java/org/apidesign/bck2brwsr/launcher/fximpl/JsMethods.java Tue Feb 11 13:31:42 2014 +0100
75.3 @@ -17,7 +17,7 @@
75.4 */
75.5 package org.apidesign.bck2brwsr.launcher.fximpl;
75.6
75.7 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
75.8 +import net.java.html.js.JavaScriptBody;
75.9
75.10 /**
75.11 *
76.1 --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Tue Feb 11 10:48:24 2014 +0100
76.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Tue Feb 11 13:31:42 2014 +0100
76.3 @@ -17,8 +17,15 @@
76.4 */
76.5 package org.apidesign.bck2brwsr.launcher;
76.6
76.7 +import java.io.File;
76.8 +import java.io.FileReader;
76.9 import java.io.IOException;
76.10 import java.io.InputStream;
76.11 +import java.io.InputStreamReader;
76.12 +import java.io.Reader;
76.13 +import java.net.MalformedURLException;
76.14 +import java.net.URL;
76.15 +import java.util.logging.Level;
76.16 import org.apidesign.vm4brwsr.Bck2Brwsr;
76.17
76.18 /**
76.19 @@ -42,19 +49,57 @@
76.20 class R implements Bck2Brwsr.Resources {
76.21 @Override
76.22 public InputStream get(String resource) throws IOException {
76.23 - return loader.get(resource);
76.24 + return loader.get(resource, 0);
76.25 }
76.26 }
76.27 -
76.28 - Bck2Brwsr.generate(sb, new R());
76.29 + String b2b = System.getProperty("bck2brwsr.js");
76.30 + if (b2b != null) {
76.31 + LOG.log(Level.INFO, "Serving bck2brwsr.js from {0}", b2b);
76.32 + URL bu;
76.33 + try {
76.34 + bu = new URL(b2b);
76.35 + } catch (MalformedURLException ex) {
76.36 + File f = new File(b2b);
76.37 + if (f.exists()) {
76.38 + bu = f.toURI().toURL();
76.39 + } else {
76.40 + throw ex;
76.41 + }
76.42 + }
76.43 + try (Reader r = new InputStreamReader(bu.openStream())) {
76.44 + char[] arr = new char[4096];
76.45 + for (;;) {
76.46 + int len = r.read(arr);
76.47 + if (len == -1) {
76.48 + break;
76.49 + }
76.50 + sb.append(arr, 0, len);
76.51 + }
76.52 + }
76.53 + } else {
76.54 + LOG.log(Level.INFO, "Generating bck2brwsr.js from scratch", b2b);
76.55 + Bck2Brwsr.generate(sb, new R());
76.56 + }
76.57 sb.append(
76.58 - "(function WrapperVM(global) {"
76.59 - + " function ldCls(res) {\n"
76.60 + "(function WrapperVM(global) {\n"
76.61 + + " var cache = {};\n"
76.62 + + " function ldCls(res, skip) {\n"
76.63 + + " var c = cache[res];\n"
76.64 + + " if (c) {\n"
76.65 + + " if (c[skip]) return c[skip];\n"
76.66 + + " if (c[skip] === null) return null;\n"
76.67 + + " } else {\n"
76.68 + + " cache[res] = c = new Array();\n"
76.69 + + " }\n"
76.70 + " var request = new XMLHttpRequest();\n"
76.71 - + " request.open('GET', '/classes/' + res, false);\n"
76.72 + + " request.open('GET', '/classes/' + res + '?skip=' + skip, false);\n"
76.73 + " request.send();\n"
76.74 - + " if (request.status !== 200) return null;\n"
76.75 + + " if (request.status !== 200) {\n"
76.76 + + " c[skip] = null;\n"
76.77 + + " return null;\n"
76.78 + + " }\n"
76.79 + " var arr = eval('(' + request.responseText + ')');\n"
76.80 + + " c[skip] = arr;\n"
76.81 + " return arr;\n"
76.82 + " }\n"
76.83 + " var prevvm = global.bck2brwsr;\n"
76.84 @@ -65,6 +110,7 @@
76.85 + " };\n"
76.86 + "})(this);\n"
76.87 );
76.88 + LOG.log(Level.INFO, "Serving bck2brwsr.js", b2b);
76.89 }
76.90
76.91 }
77.1 --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Tue Feb 11 10:48:24 2014 +0100
77.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Tue Feb 11 13:31:42 2014 +0100
77.3 @@ -126,6 +126,10 @@
77.4 u = en.nextElement();
77.5 }
77.6 if (u != null) {
77.7 + if (u.toExternalForm().contains("rt.jar")) {
77.8 + LOG.log(Level.WARNING, "No fallback to bootclasspath for {0}", u);
77.9 + return null;
77.10 + }
77.11 return u.openStream();
77.12 }
77.13 }
78.1 --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Tue Feb 11 10:48:24 2014 +0100
78.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Tue Feb 11 13:31:42 2014 +0100
78.3 @@ -220,14 +220,22 @@
78.4 * @return the array of bytes in the given resource
78.5 * @throws IOException I/O in case something goes wrong
78.6 */
78.7 - public static byte[] read(String name) throws IOException {
78.8 + public static byte[] read(String name, int skip) throws IOException {
78.9 URL u = null;
78.10 - Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
78.11 - while (en.hasMoreElements()) {
78.12 - u = en.nextElement();
78.13 + if (!name.endsWith(".class")) {
78.14 + u = getResource(name, skip);
78.15 + } else {
78.16 + Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
78.17 + while (en.hasMoreElements()) {
78.18 + u = en.nextElement();
78.19 + }
78.20 }
78.21 if (u == null) {
78.22 - throw new IOException("Can't find " + name);
78.23 + if (name.endsWith(".class")) {
78.24 + throw new IOException("Can't find " + name);
78.25 + } else {
78.26 + return null;
78.27 + }
78.28 }
78.29 try (InputStream is = u.openStream()) {
78.30 byte[] arr;
78.31 @@ -244,6 +252,19 @@
78.32 }
78.33 }
78.34
78.35 + private static URL getResource(String resource, int skip) throws IOException {
78.36 + URL u = null;
78.37 + Enumeration<URL> en = Console.class.getClassLoader().getResources(resource);
78.38 + while (en.hasMoreElements()) {
78.39 + final URL now = en.nextElement();
78.40 + if (--skip < 0) {
78.41 + u = now;
78.42 + break;
78.43 + }
78.44 + }
78.45 + return u;
78.46 + }
78.47 +
78.48 @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;")
78.49 private static void turnAssetionStatusOn() {
78.50 }
79.1 --- a/pom.xml Tue Feb 11 10:48:24 2014 +0100
79.2 +++ b/pom.xml Tue Feb 11 13:31:42 2014 +0100
79.3 @@ -13,15 +13,14 @@
79.4 </parent>
79.5 <properties>
79.6 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
79.7 - <netbeans.version>RELEASE73</netbeans.version>
79.8 + <netbeans.version>RELEASE74</netbeans.version>
79.9 <license>COPYING</license>
79.10 - <net.java.html.version>0.6</net.java.html.version>
79.11 + <net.java.html.version>0.7.5</net.java.html.version>
79.12 + <netbeans.compile.on.save>none</netbeans.compile.on.save>
79.13 </properties>
79.14 <modules>
79.15 - <module>dew</module>
79.16 <module>javaquery</module>
79.17 <module>benchmarks</module>
79.18 - <module>ide</module>
79.19 <module>ko</module>
79.20 <module>launcher</module>
79.21 <module>rt</module>
79.22 @@ -90,10 +89,8 @@
79.23 <exclude>rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java</exclude>
79.24 <exclude>rt/archetype/src/main/resources/archetype-resources/**</exclude>
79.25 <exclude>rt/emul/compact/src/test/resources/**</exclude>
79.26 - <exclude>dew/src/main/resources/org/apidesign/bck2brwsr/dew/**</exclude>
79.27 <exclude>javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout*.js</exclude>
79.28 <exclude>ko/archetype/src/main/resources/archetype-resources/**</exclude>
79.29 - <exclude>ko/*/src/main/resources/org/apidesign/*/*/knockout-2.2.1.js</exclude>
79.30 </excludes>
79.31 </configuration>
79.32 </plugin>-->
80.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
80.2 +++ b/rt/emul/compact/src/main/java/java/io/BufferedInputStream.java Tue Feb 11 13:31:42 2014 +0100
80.3 @@ -0,0 +1,458 @@
80.4 +/*
80.5 + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
80.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
80.7 + *
80.8 + * This code is free software; you can redistribute it and/or modify it
80.9 + * under the terms of the GNU General Public License version 2 only, as
80.10 + * published by the Free Software Foundation. Oracle designates this
80.11 + * particular file as subject to the "Classpath" exception as provided
80.12 + * by Oracle in the LICENSE file that accompanied this code.
80.13 + *
80.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
80.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
80.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
80.17 + * version 2 for more details (a copy is included in the LICENSE file that
80.18 + * accompanied this code).
80.19 + *
80.20 + * You should have received a copy of the GNU General Public License version
80.21 + * 2 along with this work; if not, write to the Free Software Foundation,
80.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
80.23 + *
80.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
80.25 + * or visit www.oracle.com if you need additional information or have any
80.26 + * questions.
80.27 + */
80.28 +
80.29 +package java.io;
80.30 +
80.31 +/**
80.32 + * A <code>BufferedInputStream</code> adds
80.33 + * functionality to another input stream-namely,
80.34 + * the ability to buffer the input and to
80.35 + * support the <code>mark</code> and <code>reset</code>
80.36 + * methods. When the <code>BufferedInputStream</code>
80.37 + * is created, an internal buffer array is
80.38 + * created. As bytes from the stream are read
80.39 + * or skipped, the internal buffer is refilled
80.40 + * as necessary from the contained input stream,
80.41 + * many bytes at a time. The <code>mark</code>
80.42 + * operation remembers a point in the input
80.43 + * stream and the <code>reset</code> operation
80.44 + * causes all the bytes read since the most
80.45 + * recent <code>mark</code> operation to be
80.46 + * reread before new bytes are taken from
80.47 + * the contained input stream.
80.48 + *
80.49 + * @author Arthur van Hoff
80.50 + * @since JDK1.0
80.51 + */
80.52 +public
80.53 +class BufferedInputStream extends FilterInputStream {
80.54 +
80.55 + private static int defaultBufferSize = 8192;
80.56 +
80.57 + /**
80.58 + * The internal buffer array where the data is stored. When necessary,
80.59 + * it may be replaced by another array of
80.60 + * a different size.
80.61 + */
80.62 + protected volatile byte buf[];
80.63 +
80.64 +
80.65 + /**
80.66 + * The index one greater than the index of the last valid byte in
80.67 + * the buffer.
80.68 + * This value is always
80.69 + * in the range <code>0</code> through <code>buf.length</code>;
80.70 + * elements <code>buf[0]</code> through <code>buf[count-1]
80.71 + * </code>contain buffered input data obtained
80.72 + * from the underlying input stream.
80.73 + */
80.74 + protected int count;
80.75 +
80.76 + /**
80.77 + * The current position in the buffer. This is the index of the next
80.78 + * character to be read from the <code>buf</code> array.
80.79 + * <p>
80.80 + * This value is always in the range <code>0</code>
80.81 + * through <code>count</code>. If it is less
80.82 + * than <code>count</code>, then <code>buf[pos]</code>
80.83 + * is the next byte to be supplied as input;
80.84 + * if it is equal to <code>count</code>, then
80.85 + * the next <code>read</code> or <code>skip</code>
80.86 + * operation will require more bytes to be
80.87 + * read from the contained input stream.
80.88 + *
80.89 + * @see java.io.BufferedInputStream#buf
80.90 + */
80.91 + protected int pos;
80.92 +
80.93 + /**
80.94 + * The value of the <code>pos</code> field at the time the last
80.95 + * <code>mark</code> method was called.
80.96 + * <p>
80.97 + * This value is always
80.98 + * in the range <code>-1</code> through <code>pos</code>.
80.99 + * If there is no marked position in the input
80.100 + * stream, this field is <code>-1</code>. If
80.101 + * there is a marked position in the input
80.102 + * stream, then <code>buf[markpos]</code>
80.103 + * is the first byte to be supplied as input
80.104 + * after a <code>reset</code> operation. If
80.105 + * <code>markpos</code> is not <code>-1</code>,
80.106 + * then all bytes from positions <code>buf[markpos]</code>
80.107 + * through <code>buf[pos-1]</code> must remain
80.108 + * in the buffer array (though they may be
80.109 + * moved to another place in the buffer array,
80.110 + * with suitable adjustments to the values
80.111 + * of <code>count</code>, <code>pos</code>,
80.112 + * and <code>markpos</code>); they may not
80.113 + * be discarded unless and until the difference
80.114 + * between <code>pos</code> and <code>markpos</code>
80.115 + * exceeds <code>marklimit</code>.
80.116 + *
80.117 + * @see java.io.BufferedInputStream#mark(int)
80.118 + * @see java.io.BufferedInputStream#pos
80.119 + */
80.120 + protected int markpos = -1;
80.121 +
80.122 + /**
80.123 + * The maximum read ahead allowed after a call to the
80.124 + * <code>mark</code> method before subsequent calls to the
80.125 + * <code>reset</code> method fail.
80.126 + * Whenever the difference between <code>pos</code>
80.127 + * and <code>markpos</code> exceeds <code>marklimit</code>,
80.128 + * then the mark may be dropped by setting
80.129 + * <code>markpos</code> to <code>-1</code>.
80.130 + *
80.131 + * @see java.io.BufferedInputStream#mark(int)
80.132 + * @see java.io.BufferedInputStream#reset()
80.133 + */
80.134 + protected int marklimit;
80.135 +
80.136 + /**
80.137 + * Check to make sure that underlying input stream has not been
80.138 + * nulled out due to close; if not return it;
80.139 + */
80.140 + private InputStream getInIfOpen() throws IOException {
80.141 + InputStream input = in;
80.142 + if (input == null)
80.143 + throw new IOException("Stream closed");
80.144 + return input;
80.145 + }
80.146 +
80.147 + /**
80.148 + * Check to make sure that buffer has not been nulled out due to
80.149 + * close; if not return it;
80.150 + */
80.151 + private byte[] getBufIfOpen() throws IOException {
80.152 + byte[] buffer = buf;
80.153 + if (buffer == null)
80.154 + throw new IOException("Stream closed");
80.155 + return buffer;
80.156 + }
80.157 +
80.158 + /**
80.159 + * Creates a <code>BufferedInputStream</code>
80.160 + * and saves its argument, the input stream
80.161 + * <code>in</code>, for later use. An internal
80.162 + * buffer array is created and stored in <code>buf</code>.
80.163 + *
80.164 + * @param in the underlying input stream.
80.165 + */
80.166 + public BufferedInputStream(InputStream in) {
80.167 + this(in, defaultBufferSize);
80.168 + }
80.169 +
80.170 + /**
80.171 + * Creates a <code>BufferedInputStream</code>
80.172 + * with the specified buffer size,
80.173 + * and saves its argument, the input stream
80.174 + * <code>in</code>, for later use. An internal
80.175 + * buffer array of length <code>size</code>
80.176 + * is created and stored in <code>buf</code>.
80.177 + *
80.178 + * @param in the underlying input stream.
80.179 + * @param size the buffer size.
80.180 + * @exception IllegalArgumentException if size <= 0.
80.181 + */
80.182 + public BufferedInputStream(InputStream in, int size) {
80.183 + super(in);
80.184 + if (size <= 0) {
80.185 + throw new IllegalArgumentException("Buffer size <= 0");
80.186 + }
80.187 + buf = new byte[size];
80.188 + }
80.189 +
80.190 + /**
80.191 + * Fills the buffer with more data, taking into account
80.192 + * shuffling and other tricks for dealing with marks.
80.193 + * Assumes that it is being called by a synchronized method.
80.194 + * This method also assumes that all data has already been read in,
80.195 + * hence pos > count.
80.196 + */
80.197 + private void fill() throws IOException {
80.198 + byte[] buffer = getBufIfOpen();
80.199 + if (markpos < 0)
80.200 + pos = 0; /* no mark: throw away the buffer */
80.201 + else if (pos >= buffer.length) /* no room left in buffer */
80.202 + if (markpos > 0) { /* can throw away early part of the buffer */
80.203 + int sz = pos - markpos;
80.204 + System.arraycopy(buffer, markpos, buffer, 0, sz);
80.205 + pos = sz;
80.206 + markpos = 0;
80.207 + } else if (buffer.length >= marklimit) {
80.208 + markpos = -1; /* buffer got too big, invalidate mark */
80.209 + pos = 0; /* drop buffer contents */
80.210 + } else { /* grow buffer */
80.211 + int nsz = pos * 2;
80.212 + if (nsz > marklimit)
80.213 + nsz = marklimit;
80.214 + byte nbuf[] = new byte[nsz];
80.215 + System.arraycopy(buffer, 0, nbuf, 0, pos);
80.216 + buffer = nbuf;
80.217 + }
80.218 + count = pos;
80.219 + int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
80.220 + if (n > 0)
80.221 + count = n + pos;
80.222 + }
80.223 +
80.224 + /**
80.225 + * See
80.226 + * the general contract of the <code>read</code>
80.227 + * method of <code>InputStream</code>.
80.228 + *
80.229 + * @return the next byte of data, or <code>-1</code> if the end of the
80.230 + * stream is reached.
80.231 + * @exception IOException if this input stream has been closed by
80.232 + * invoking its {@link #close()} method,
80.233 + * or an I/O error occurs.
80.234 + * @see java.io.FilterInputStream#in
80.235 + */
80.236 + public synchronized int read() throws IOException {
80.237 + if (pos >= count) {
80.238 + fill();
80.239 + if (pos >= count)
80.240 + return -1;
80.241 + }
80.242 + return getBufIfOpen()[pos++] & 0xff;
80.243 + }
80.244 +
80.245 + /**
80.246 + * Read characters into a portion of an array, reading from the underlying
80.247 + * stream at most once if necessary.
80.248 + */
80.249 + private int read1(byte[] b, int off, int len) throws IOException {
80.250 + int avail = count - pos;
80.251 + if (avail <= 0) {
80.252 + /* If the requested length is at least as large as the buffer, and
80.253 + if there is no mark/reset activity, do not bother to copy the
80.254 + bytes into the local buffer. In this way buffered streams will
80.255 + cascade harmlessly. */
80.256 + if (len >= getBufIfOpen().length && markpos < 0) {
80.257 + return getInIfOpen().read(b, off, len);
80.258 + }
80.259 + fill();
80.260 + avail = count - pos;
80.261 + if (avail <= 0) return -1;
80.262 + }
80.263 + int cnt = (avail < len) ? avail : len;
80.264 + System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
80.265 + pos += cnt;
80.266 + return cnt;
80.267 + }
80.268 +
80.269 + /**
80.270 + * Reads bytes from this byte-input stream into the specified byte array,
80.271 + * starting at the given offset.
80.272 + *
80.273 + * <p> This method implements the general contract of the corresponding
80.274 + * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
80.275 + * the <code>{@link InputStream}</code> class. As an additional
80.276 + * convenience, it attempts to read as many bytes as possible by repeatedly
80.277 + * invoking the <code>read</code> method of the underlying stream. This
80.278 + * iterated <code>read</code> continues until one of the following
80.279 + * conditions becomes true: <ul>
80.280 + *
80.281 + * <li> The specified number of bytes have been read,
80.282 + *
80.283 + * <li> The <code>read</code> method of the underlying stream returns
80.284 + * <code>-1</code>, indicating end-of-file, or
80.285 + *
80.286 + * <li> The <code>available</code> method of the underlying stream
80.287 + * returns zero, indicating that further input requests would block.
80.288 + *
80.289 + * </ul> If the first <code>read</code> on the underlying stream returns
80.290 + * <code>-1</code> to indicate end-of-file then this method returns
80.291 + * <code>-1</code>. Otherwise this method returns the number of bytes
80.292 + * actually read.
80.293 + *
80.294 + * <p> Subclasses of this class are encouraged, but not required, to
80.295 + * attempt to read as many bytes as possible in the same fashion.
80.296 + *
80.297 + * @param b destination buffer.
80.298 + * @param off offset at which to start storing bytes.
80.299 + * @param len maximum number of bytes to read.
80.300 + * @return the number of bytes read, or <code>-1</code> if the end of
80.301 + * the stream has been reached.
80.302 + * @exception IOException if this input stream has been closed by
80.303 + * invoking its {@link #close()} method,
80.304 + * or an I/O error occurs.
80.305 + */
80.306 + public synchronized int read(byte b[], int off, int len)
80.307 + throws IOException
80.308 + {
80.309 + getBufIfOpen(); // Check for closed stream
80.310 + if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
80.311 + throw new IndexOutOfBoundsException();
80.312 + } else if (len == 0) {
80.313 + return 0;
80.314 + }
80.315 +
80.316 + int n = 0;
80.317 + for (;;) {
80.318 + int nread = read1(b, off + n, len - n);
80.319 + if (nread <= 0)
80.320 + return (n == 0) ? nread : n;
80.321 + n += nread;
80.322 + if (n >= len)
80.323 + return n;
80.324 + // if not closed but no bytes available, return
80.325 + InputStream input = in;
80.326 + if (input != null && input.available() <= 0)
80.327 + return n;
80.328 + }
80.329 + }
80.330 +
80.331 + /**
80.332 + * See the general contract of the <code>skip</code>
80.333 + * method of <code>InputStream</code>.
80.334 + *
80.335 + * @exception IOException if the stream does not support seek,
80.336 + * or if this input stream has been closed by
80.337 + * invoking its {@link #close()} method, or an
80.338 + * I/O error occurs.
80.339 + */
80.340 + public synchronized long skip(long n) throws IOException {
80.341 + getBufIfOpen(); // Check for closed stream
80.342 + if (n <= 0) {
80.343 + return 0;
80.344 + }
80.345 + long avail = count - pos;
80.346 +
80.347 + if (avail <= 0) {
80.348 + // If no mark position set then don't keep in buffer
80.349 + if (markpos <0)
80.350 + return getInIfOpen().skip(n);
80.351 +
80.352 + // Fill in buffer to save bytes for reset
80.353 + fill();
80.354 + avail = count - pos;
80.355 + if (avail <= 0)
80.356 + return 0;
80.357 + }
80.358 +
80.359 + long skipped = (avail < n) ? avail : n;
80.360 + pos += skipped;
80.361 + return skipped;
80.362 + }
80.363 +
80.364 + /**
80.365 + * Returns an estimate of the number of bytes that can be read (or
80.366 + * skipped over) from this input stream without blocking by the next
80.367 + * invocation of a method for this input stream. The next invocation might be
80.368 + * the same thread or another thread. A single read or skip of this
80.369 + * many bytes will not block, but may read or skip fewer bytes.
80.370 + * <p>
80.371 + * This method returns the sum of the number of bytes remaining to be read in
80.372 + * the buffer (<code>count - pos</code>) and the result of calling the
80.373 + * {@link java.io.FilterInputStream#in in}.available().
80.374 + *
80.375 + * @return an estimate of the number of bytes that can be read (or skipped
80.376 + * over) from this input stream without blocking.
80.377 + * @exception IOException if this input stream has been closed by
80.378 + * invoking its {@link #close()} method,
80.379 + * or an I/O error occurs.
80.380 + */
80.381 + public synchronized int available() throws IOException {
80.382 + int n = count - pos;
80.383 + int avail = getInIfOpen().available();
80.384 + return n > (Integer.MAX_VALUE - avail)
80.385 + ? Integer.MAX_VALUE
80.386 + : n + avail;
80.387 + }
80.388 +
80.389 + /**
80.390 + * See the general contract of the <code>mark</code>
80.391 + * method of <code>InputStream</code>.
80.392 + *
80.393 + * @param readlimit the maximum limit of bytes that can be read before
80.394 + * the mark position becomes invalid.
80.395 + * @see java.io.BufferedInputStream#reset()
80.396 + */
80.397 + public synchronized void mark(int readlimit) {
80.398 + marklimit = readlimit;
80.399 + markpos = pos;
80.400 + }
80.401 +
80.402 + /**
80.403 + * See the general contract of the <code>reset</code>
80.404 + * method of <code>InputStream</code>.
80.405 + * <p>
80.406 + * If <code>markpos</code> is <code>-1</code>
80.407 + * (no mark has been set or the mark has been
80.408 + * invalidated), an <code>IOException</code>
80.409 + * is thrown. Otherwise, <code>pos</code> is
80.410 + * set equal to <code>markpos</code>.
80.411 + *
80.412 + * @exception IOException if this stream has not been marked or,
80.413 + * if the mark has been invalidated, or the stream
80.414 + * has been closed by invoking its {@link #close()}
80.415 + * method, or an I/O error occurs.
80.416 + * @see java.io.BufferedInputStream#mark(int)
80.417 + */
80.418 + public synchronized void reset() throws IOException {
80.419 + getBufIfOpen(); // Cause exception if closed
80.420 + if (markpos < 0)
80.421 + throw new IOException("Resetting to invalid mark");
80.422 + pos = markpos;
80.423 + }
80.424 +
80.425 + /**
80.426 + * Tests if this input stream supports the <code>mark</code>
80.427 + * and <code>reset</code> methods. The <code>markSupported</code>
80.428 + * method of <code>BufferedInputStream</code> returns
80.429 + * <code>true</code>.
80.430 + *
80.431 + * @return a <code>boolean</code> indicating if this stream type supports
80.432 + * the <code>mark</code> and <code>reset</code> methods.
80.433 + * @see java.io.InputStream#mark(int)
80.434 + * @see java.io.InputStream#reset()
80.435 + */
80.436 + public boolean markSupported() {
80.437 + return true;
80.438 + }
80.439 +
80.440 + /**
80.441 + * Closes this input stream and releases any system resources
80.442 + * associated with the stream.
80.443 + * Once the stream has been closed, further read(), available(), reset(),
80.444 + * or skip() invocations will throw an IOException.
80.445 + * Closing a previously closed stream has no effect.
80.446 + *
80.447 + * @exception IOException if an I/O error occurs.
80.448 + */
80.449 + public void close() throws IOException {
80.450 + byte[] buffer;
80.451 + while ( (buffer = buf) != null) {
80.452 + InputStream input = in;
80.453 + buf = null;
80.454 + in = null;
80.455 + if (input != null)
80.456 + input.close();
80.457 + return;
80.458 + // Else retry in case a new buf was CASed in fill()
80.459 + }
80.460 + }
80.461 +}
81.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
81.2 +++ b/rt/emul/compact/src/main/java/java/io/FileDescriptor.java Tue Feb 11 13:31:42 2014 +0100
81.3 @@ -0,0 +1,146 @@
81.4 +/*
81.5 + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
81.6 + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
81.7 + *
81.8 + *
81.9 + *
81.10 + *
81.11 + *
81.12 + *
81.13 + *
81.14 + *
81.15 + *
81.16 + *
81.17 + *
81.18 + *
81.19 + *
81.20 + *
81.21 + *
81.22 + *
81.23 + *
81.24 + *
81.25 + *
81.26 + *
81.27 + */
81.28 +
81.29 +package java.io;
81.30 +
81.31 +import java.util.concurrent.atomic.AtomicInteger;
81.32 +
81.33 +/**
81.34 + * Instances of the file descriptor class serve as an opaque handle
81.35 + * to the underlying machine-specific structure representing an open
81.36 + * file, an open socket, or another source or sink of bytes. The
81.37 + * main practical use for a file descriptor is to create a
81.38 + * <code>FileInputStream</code> or <code>FileOutputStream</code> to
81.39 + * contain it.
81.40 + * <p>
81.41 + * Applications should not create their own file descriptors.
81.42 + *
81.43 + * @author Pavani Diwanji
81.44 + * @see java.io.FileInputStream
81.45 + * @see java.io.FileOutputStream
81.46 + * @since JDK1.0
81.47 + */
81.48 +public final class FileDescriptor {
81.49 +
81.50 + private int fd;
81.51 +
81.52 + /**
81.53 + * A counter for tracking the FIS/FOS/RAF instances that
81.54 + * use this FileDescriptor. The FIS/FOS.finalize() will not release
81.55 + * the FileDescriptor if it is still under user by a stream.
81.56 + */
81.57 + private AtomicInteger useCount;
81.58 +
81.59 + /**
81.60 + * Constructs an (invalid) FileDescriptor
81.61 + * object.
81.62 + */
81.63 + public /**/ FileDescriptor() {
81.64 + fd = -1;
81.65 + useCount = new AtomicInteger();
81.66 + }
81.67 +
81.68 + private /* */ FileDescriptor(int fd) {
81.69 + this.fd = fd;
81.70 + useCount = new AtomicInteger();
81.71 + }
81.72 +
81.73 + /**
81.74 + * A handle to the standard input stream. Usually, this file
81.75 + * descriptor is not used directly, but rather via the input stream
81.76 + * known as <code>System.in</code>.
81.77 + *
81.78 + * @see java.lang.System#in
81.79 + */
81.80 + public static final FileDescriptor in = new FileDescriptor(0);
81.81 +
81.82 + /**
81.83 + * A handle to the standard output stream. Usually, this file
81.84 + * descriptor is not used directly, but rather via the output stream
81.85 + * known as <code>System.out</code>.
81.86 + * @see java.lang.System#out
81.87 + */
81.88 + public static final FileDescriptor out = new FileDescriptor(1);
81.89 +
81.90 + /**
81.91 + * A handle to the standard error stream. Usually, this file
81.92 + * descriptor is not used directly, but rather via the output stream
81.93 + * known as <code>System.err</code>.
81.94 + *
81.95 + * @see java.lang.System#err
81.96 + */
81.97 + public static final FileDescriptor err = new FileDescriptor(2);
81.98 +
81.99 + /**
81.100 + * Tests if this file descriptor object is valid.
81.101 + *
81.102 + * @return <code>true</code> if the file descriptor object represents a
81.103 + * valid, open file, socket, or other active I/O connection;
81.104 + * <code>false</code> otherwise.
81.105 + */
81.106 + public boolean valid() {
81.107 + return fd != -1;
81.108 + }
81.109 +
81.110 + /**
81.111 + * Force all system buffers to synchronize with the underlying
81.112 + * device. This method returns after all modified data and
81.113 + * attributes of this FileDescriptor have been written to the
81.114 + * relevant device(s). In particular, if this FileDescriptor
81.115 + * refers to a physical storage medium, such as a file in a file
81.116 + * system, sync will not return until all in-memory modified copies
81.117 + * of buffers associated with this FileDescriptor have been
81.118 + * written to the physical medium.
81.119 + *
81.120 + * sync is meant to be used by code that requires physical
81.121 + * storage (such as a file) to be in a known state For
81.122 + * example, a class that provided a simple transaction facility
81.123 + * might use sync to ensure that all changes to a file caused
81.124 + * by a given transaction were recorded on a storage medium.
81.125 + *
81.126 + * sync only affects buffers downstream of this FileDescriptor. If
81.127 + * any in-memory buffering is being done by the application (for
81.128 + * example, by a BufferedOutputStream object), those buffers must
81.129 + * be flushed into the FileDescriptor (for example, by invoking
81.130 + * OutputStream.flush) before that data will be affected by sync.
81.131 + *
81.132 + * @exception SyncFailedException
81.133 + * Thrown when the buffers cannot be flushed,
81.134 + * or because the system cannot guarantee that all the
81.135 + * buffers have been synchronized with physical media.
81.136 + * @since JDK1.1
81.137 + */
81.138 + public native void sync() throws SyncFailedException;
81.139 +
81.140 + // package private methods used by FIS, FOS and RAF
81.141 +
81.142 + int incrementAndGetUseCount() {
81.143 + return useCount.incrementAndGet();
81.144 + }
81.145 +
81.146 + int decrementAndGetUseCount() {
81.147 + return useCount.decrementAndGet();
81.148 + }
81.149 +}
82.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
82.2 +++ b/rt/emul/compact/src/main/java/java/io/FileInputStream.java Tue Feb 11 13:31:42 2014 +0100
82.3 @@ -0,0 +1,382 @@
82.4 +/*
82.5 + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
82.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
82.7 + *
82.8 + * This code is free software; you can redistribute it and/or modify it
82.9 + * under the terms of the GNU General Public License version 2 only, as
82.10 + * published by the Free Software Foundation. Oracle designates this
82.11 + * particular file as subject to the "Classpath" exception as provided
82.12 + * by Oracle in the LICENSE file that accompanied this code.
82.13 + *
82.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
82.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
82.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
82.17 + * version 2 for more details (a copy is included in the LICENSE file that
82.18 + * accompanied this code).
82.19 + *
82.20 + * You should have received a copy of the GNU General Public License version
82.21 + * 2 along with this work; if not, write to the Free Software Foundation,
82.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
82.23 + *
82.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
82.25 + * or visit www.oracle.com if you need additional information or have any
82.26 + * questions.
82.27 + */
82.28 +
82.29 +package java.io;
82.30 +
82.31 +
82.32 +
82.33 +/**
82.34 + * A <code>FileInputStream</code> obtains input bytes
82.35 + * from a file in a file system. What files
82.36 + * are available depends on the host environment.
82.37 + *
82.38 + * <p><code>FileInputStream</code> is meant for reading streams of raw bytes
82.39 + * such as image data. For reading streams of characters, consider using
82.40 + * <code>FileReader</code>.
82.41 + *
82.42 + * @author Arthur van Hoff
82.43 + * @see java.io.File
82.44 + * @see java.io.FileDescriptor
82.45 + * @see java.io.FileOutputStream
82.46 + * @see java.nio.file.Files#newInputStream
82.47 + * @since JDK1.0
82.48 + */
82.49 +public
82.50 +class FileInputStream extends InputStream
82.51 +{
82.52 + /* File Descriptor - handle to the open file */
82.53 + private final FileDescriptor fd;
82.54 +
82.55 +// private FileChannel channel = null;
82.56 +
82.57 + private final Object closeLock = new Object();
82.58 + private volatile boolean closed = false;
82.59 +
82.60 + private static final ThreadLocal<Boolean> runningFinalize =
82.61 + new ThreadLocal<>();
82.62 +
82.63 + private static boolean isRunningFinalize() {
82.64 + Boolean val;
82.65 + if ((val = runningFinalize.get()) != null)
82.66 + return val.booleanValue();
82.67 + return false;
82.68 + }
82.69 +
82.70 + /**
82.71 + * Creates a <code>FileInputStream</code> by
82.72 + * opening a connection to an actual file,
82.73 + * the file named by the path name <code>name</code>
82.74 + * in the file system. A new <code>FileDescriptor</code>
82.75 + * object is created to represent this file
82.76 + * connection.
82.77 + * <p>
82.78 + * First, if there is a security
82.79 + * manager, its <code>checkRead</code> method
82.80 + * is called with the <code>name</code> argument
82.81 + * as its argument.
82.82 + * <p>
82.83 + * If the named file does not exist, is a directory rather than a regular
82.84 + * file, or for some other reason cannot be opened for reading then a
82.85 + * <code>FileNotFoundException</code> is thrown.
82.86 + *
82.87 + * @param name the system-dependent file name.
82.88 + * @exception FileNotFoundException if the file does not exist,
82.89 + * is a directory rather than a regular file,
82.90 + * or for some other reason cannot be opened for
82.91 + * reading.
82.92 + * @exception SecurityException if a security manager exists and its
82.93 + * <code>checkRead</code> method denies read access
82.94 + * to the file.
82.95 + * @see java.lang.SecurityManager#checkRead(java.lang.String)
82.96 + */
82.97 + public FileInputStream(String name) throws FileNotFoundException {
82.98 + this(name != null ? new File(name) : null);
82.99 + }
82.100 +
82.101 + /**
82.102 + * Creates a <code>FileInputStream</code> by
82.103 + * opening a connection to an actual file,
82.104 + * the file named by the <code>File</code>
82.105 + * object <code>file</code> in the file system.
82.106 + * A new <code>FileDescriptor</code> object
82.107 + * is created to represent this file connection.
82.108 + * <p>
82.109 + * First, if there is a security manager,
82.110 + * its <code>checkRead</code> method is called
82.111 + * with the path represented by the <code>file</code>
82.112 + * argument as its argument.
82.113 + * <p>
82.114 + * If the named file does not exist, is a directory rather than a regular
82.115 + * file, or for some other reason cannot be opened for reading then a
82.116 + * <code>FileNotFoundException</code> is thrown.
82.117 + *
82.118 + * @param file the file to be opened for reading.
82.119 + * @exception FileNotFoundException if the file does not exist,
82.120 + * is a directory rather than a regular file,
82.121 + * or for some other reason cannot be opened for
82.122 + * reading.
82.123 + * @exception SecurityException if a security manager exists and its
82.124 + * <code>checkRead</code> method denies read access to the file.
82.125 + * @see java.io.File#getPath()
82.126 + * @see java.lang.SecurityManager#checkRead(java.lang.String)
82.127 + */
82.128 + public FileInputStream(File file) throws FileNotFoundException {
82.129 + throw new SecurityException();
82.130 + }
82.131 +
82.132 + /**
82.133 + * Creates a <code>FileInputStream</code> by using the file descriptor
82.134 + * <code>fdObj</code>, which represents an existing connection to an
82.135 + * actual file in the file system.
82.136 + * <p>
82.137 + * If there is a security manager, its <code>checkRead</code> method is
82.138 + * called with the file descriptor <code>fdObj</code> as its argument to
82.139 + * see if it's ok to read the file descriptor. If read access is denied
82.140 + * to the file descriptor a <code>SecurityException</code> is thrown.
82.141 + * <p>
82.142 + * If <code>fdObj</code> is null then a <code>NullPointerException</code>
82.143 + * is thrown.
82.144 + * <p>
82.145 + * This constructor does not throw an exception if <code>fdObj</code>
82.146 + * is {@link java.io.FileDescriptor#valid() invalid}.
82.147 + * However, if the methods are invoked on the resulting stream to attempt
82.148 + * I/O on the stream, an <code>IOException</code> is thrown.
82.149 + *
82.150 + * @param fdObj the file descriptor to be opened for reading.
82.151 + * @throws SecurityException if a security manager exists and its
82.152 + * <code>checkRead</code> method denies read access to the
82.153 + * file descriptor.
82.154 + * @see SecurityManager#checkRead(java.io.FileDescriptor)
82.155 + */
82.156 + public FileInputStream(FileDescriptor fdObj) {
82.157 + throw new SecurityException();
82.158 + }
82.159 +
82.160 + /**
82.161 + * Opens the specified file for reading.
82.162 + * @param name the name of the file
82.163 + */
82.164 + private native void open(String name) throws FileNotFoundException;
82.165 +
82.166 + /**
82.167 + * Reads a byte of data from this input stream. This method blocks
82.168 + * if no input is yet available.
82.169 + *
82.170 + * @return the next byte of data, or <code>-1</code> if the end of the
82.171 + * file is reached.
82.172 + * @exception IOException if an I/O error occurs.
82.173 + */
82.174 + public native int read() throws IOException;
82.175 +
82.176 + /**
82.177 + * Reads a subarray as a sequence of bytes.
82.178 + * @param b the data to be written
82.179 + * @param off the start offset in the data
82.180 + * @param len the number of bytes that are written
82.181 + * @exception IOException If an I/O error has occurred.
82.182 + */
82.183 + private native int readBytes(byte b[], int off, int len) throws IOException;
82.184 +
82.185 + /**
82.186 + * Reads up to <code>b.length</code> bytes of data from this input
82.187 + * stream into an array of bytes. This method blocks until some input
82.188 + * is available.
82.189 + *
82.190 + * @param b the buffer into which the data is read.
82.191 + * @return the total number of bytes read into the buffer, or
82.192 + * <code>-1</code> if there is no more data because the end of
82.193 + * the file has been reached.
82.194 + * @exception IOException if an I/O error occurs.
82.195 + */
82.196 + public int read(byte b[]) throws IOException {
82.197 + return readBytes(b, 0, b.length);
82.198 + }
82.199 +
82.200 + /**
82.201 + * Reads up to <code>len</code> bytes of data from this input stream
82.202 + * into an array of bytes. If <code>len</code> is not zero, the method
82.203 + * blocks until some input is available; otherwise, no
82.204 + * bytes are read and <code>0</code> is returned.
82.205 + *
82.206 + * @param b the buffer into which the data is read.
82.207 + * @param off the start offset in the destination array <code>b</code>
82.208 + * @param len the maximum number of bytes read.
82.209 + * @return the total number of bytes read into the buffer, or
82.210 + * <code>-1</code> if there is no more data because the end of
82.211 + * the file has been reached.
82.212 + * @exception NullPointerException If <code>b</code> is <code>null</code>.
82.213 + * @exception IndexOutOfBoundsException If <code>off</code> is negative,
82.214 + * <code>len</code> is negative, or <code>len</code> is greater than
82.215 + * <code>b.length - off</code>
82.216 + * @exception IOException if an I/O error occurs.
82.217 + */
82.218 + public int read(byte b[], int off, int len) throws IOException {
82.219 + return readBytes(b, off, len);
82.220 + }
82.221 +
82.222 + /**
82.223 + * Skips over and discards <code>n</code> bytes of data from the
82.224 + * input stream.
82.225 + *
82.226 + * <p>The <code>skip</code> method may, for a variety of
82.227 + * reasons, end up skipping over some smaller number of bytes,
82.228 + * possibly <code>0</code>. If <code>n</code> is negative, an
82.229 + * <code>IOException</code> is thrown, even though the <code>skip</code>
82.230 + * method of the {@link InputStream} superclass does nothing in this case.
82.231 + * The actual number of bytes skipped is returned.
82.232 + *
82.233 + * <p>This method may skip more bytes than are remaining in the backing
82.234 + * file. This produces no exception and the number of bytes skipped
82.235 + * may include some number of bytes that were beyond the EOF of the
82.236 + * backing file. Attempting to read from the stream after skipping past
82.237 + * the end will result in -1 indicating the end of the file.
82.238 + *
82.239 + * @param n the number of bytes to be skipped.
82.240 + * @return the actual number of bytes skipped.
82.241 + * @exception IOException if n is negative, if the stream does not
82.242 + * support seek, or if an I/O error occurs.
82.243 + */
82.244 + public native long skip(long n) throws IOException;
82.245 +
82.246 + /**
82.247 + * Returns an estimate of the number of remaining bytes that can be read (or
82.248 + * skipped over) from this input stream without blocking by the next
82.249 + * invocation of a method for this input stream. The next invocation might be
82.250 + * the same thread or another thread. A single read or skip of this
82.251 + * many bytes will not block, but may read or skip fewer bytes.
82.252 + *
82.253 + * <p> In some cases, a non-blocking read (or skip) may appear to be
82.254 + * blocked when it is merely slow, for example when reading large
82.255 + * files over slow networks.
82.256 + *
82.257 + * @return an estimate of the number of remaining bytes that can be read
82.258 + * (or skipped over) from this input stream without blocking.
82.259 + * @exception IOException if this file input stream has been closed by calling
82.260 + * {@code close} or an I/O error occurs.
82.261 + */
82.262 + public native int available() throws IOException;
82.263 +
82.264 + /**
82.265 + * Closes this file input stream and releases any system resources
82.266 + * associated with the stream.
82.267 + *
82.268 + * <p> If this stream has an associated channel then the channel is closed
82.269 + * as well.
82.270 + *
82.271 + * @exception IOException if an I/O error occurs.
82.272 + *
82.273 + * @revised 1.4
82.274 + * @spec JSR-51
82.275 + */
82.276 + public void close() throws IOException {
82.277 + synchronized (closeLock) {
82.278 + if (closed) {
82.279 + return;
82.280 + }
82.281 + closed = true;
82.282 + }
82.283 +// if (channel != null) {
82.284 +// /*
82.285 +// * Decrement the FD use count associated with the channel
82.286 +// * The use count is incremented whenever a new channel
82.287 +// * is obtained from this stream.
82.288 +// */
82.289 +// fd.decrementAndGetUseCount();
82.290 +// channel.close();
82.291 +// }
82.292 +
82.293 + /*
82.294 + * Decrement the FD use count associated with this stream
82.295 + */
82.296 + int useCount = fd.decrementAndGetUseCount();
82.297 +
82.298 + /*
82.299 + * If FileDescriptor is still in use by another stream, the finalizer
82.300 + * will not close it.
82.301 + */
82.302 + if ((useCount <= 0) || !isRunningFinalize()) {
82.303 + close0();
82.304 + }
82.305 + }
82.306 +
82.307 + /**
82.308 + * Returns the <code>FileDescriptor</code>
82.309 + * object that represents the connection to
82.310 + * the actual file in the file system being
82.311 + * used by this <code>FileInputStream</code>.
82.312 + *
82.313 + * @return the file descriptor object associated with this stream.
82.314 + * @exception IOException if an I/O error occurs.
82.315 + * @see java.io.FileDescriptor
82.316 + */
82.317 + public final FileDescriptor getFD() throws IOException {
82.318 + if (fd != null) return fd;
82.319 + throw new IOException();
82.320 + }
82.321 +
82.322 + /**
82.323 + * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
82.324 + * object associated with this file input stream.
82.325 + *
82.326 + * <p> The initial {@link java.nio.channels.FileChannel#position()
82.327 + * </code>position<code>} of the returned channel will be equal to the
82.328 + * number of bytes read from the file so far. Reading bytes from this
82.329 + * stream will increment the channel's position. Changing the channel's
82.330 + * position, either explicitly or by reading, will change this stream's
82.331 + * file position.
82.332 + *
82.333 + * @return the file channel associated with this file input stream
82.334 + *
82.335 + * @since 1.4
82.336 + * @spec JSR-51
82.337 + */
82.338 +// public FileChannel getChannel() {
82.339 +// synchronized (this) {
82.340 +// if (channel == null) {
82.341 +// channel = FileChannelImpl.open(fd, true, false, this);
82.342 +//
82.343 +// /*
82.344 +// * Increment fd's use count. Invoking the channel's close()
82.345 +// * method will result in decrementing the use count set for
82.346 +// * the channel.
82.347 +// */
82.348 +// fd.incrementAndGetUseCount();
82.349 +// }
82.350 +// return channel;
82.351 +// }
82.352 +// }
82.353 +
82.354 + private static native void initIDs();
82.355 +
82.356 + private native void close0() throws IOException;
82.357 +
82.358 + static {
82.359 + initIDs();
82.360 + }
82.361 +
82.362 + /**
82.363 + * Ensures that the <code>close</code> method of this file input stream is
82.364 + * called when there are no more references to it.
82.365 + *
82.366 + * @exception IOException if an I/O error occurs.
82.367 + * @see java.io.FileInputStream#close()
82.368 + */
82.369 + protected void finalize() throws IOException {
82.370 + if ((fd != null) && (fd != FileDescriptor.in)) {
82.371 +
82.372 + /*
82.373 + * Finalizer should not release the FileDescriptor if another
82.374 + * stream is still using it. If the user directly invokes
82.375 + * close() then the FileDescriptor is also released.
82.376 + */
82.377 + runningFinalize.set(Boolean.TRUE);
82.378 + try {
82.379 + close();
82.380 + } finally {
82.381 + runningFinalize.set(Boolean.FALSE);
82.382 + }
82.383 + }
82.384 + }
82.385 +}
83.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
83.2 +++ b/rt/emul/compact/src/main/java/java/io/FileOutputStream.java Tue Feb 11 13:31:42 2014 +0100
83.3 @@ -0,0 +1,422 @@
83.4 +/*
83.5 + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
83.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
83.7 + *
83.8 + * This code is free software; you can redistribute it and/or modify it
83.9 + * under the terms of the GNU General Public License version 2 only, as
83.10 + * published by the Free Software Foundation. Oracle designates this
83.11 + * particular file as subject to the "Classpath" exception as provided
83.12 + * by Oracle in the LICENSE file that accompanied this code.
83.13 + *
83.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
83.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
83.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
83.17 + * version 2 for more details (a copy is included in the LICENSE file that
83.18 + * accompanied this code).
83.19 + *
83.20 + * You should have received a copy of the GNU General Public License version
83.21 + * 2 along with this work; if not, write to the Free Software Foundation,
83.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
83.23 + *
83.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
83.25 + * or visit www.oracle.com if you need additional information or have any
83.26 + * questions.
83.27 + */
83.28 +
83.29 +package java.io;
83.30 +
83.31 +
83.32 +
83.33 +/**
83.34 + * A file output stream is an output stream for writing data to a
83.35 + * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not
83.36 + * a file is available or may be created depends upon the underlying
83.37 + * platform. Some platforms, in particular, allow a file to be opened
83.38 + * for writing by only one <tt>FileOutputStream</tt> (or other
83.39 + * file-writing object) at a time. In such situations the constructors in
83.40 + * this class will fail if the file involved is already open.
83.41 + *
83.42 + * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes
83.43 + * such as image data. For writing streams of characters, consider using
83.44 + * <code>FileWriter</code>.
83.45 + *
83.46 + * @author Arthur van Hoff
83.47 + * @see java.io.File
83.48 + * @see java.io.FileDescriptor
83.49 + * @see java.io.FileInputStream
83.50 + * @see java.nio.file.Files#newOutputStream
83.51 + * @since JDK1.0
83.52 + */
83.53 +public
83.54 +class FileOutputStream extends OutputStream
83.55 +{
83.56 + /**
83.57 + * The system dependent file descriptor.
83.58 + */
83.59 + private final FileDescriptor fd;
83.60 +
83.61 + /**
83.62 + * True if the file is opened for append.
83.63 + */
83.64 + private final boolean append;
83.65 +
83.66 + /**
83.67 + * The associated channel, initalized lazily.
83.68 + */
83.69 +// private FileChannel channel;
83.70 +
83.71 + private final Object closeLock = new Object();
83.72 + private volatile boolean closed = false;
83.73 + private static final ThreadLocal<Boolean> runningFinalize =
83.74 + new ThreadLocal<>();
83.75 +
83.76 + private static boolean isRunningFinalize() {
83.77 + Boolean val;
83.78 + if ((val = runningFinalize.get()) != null)
83.79 + return val.booleanValue();
83.80 + return false;
83.81 + }
83.82 +
83.83 + /**
83.84 + * Creates a file output stream to write to the file with the
83.85 + * specified name. A new <code>FileDescriptor</code> object is
83.86 + * created to represent this file connection.
83.87 + * <p>
83.88 + * First, if there is a security manager, its <code>checkWrite</code>
83.89 + * method is called with <code>name</code> as its argument.
83.90 + * <p>
83.91 + * If the file exists but is a directory rather than a regular file, does
83.92 + * not exist but cannot be created, or cannot be opened for any other
83.93 + * reason then a <code>FileNotFoundException</code> is thrown.
83.94 + *
83.95 + * @param name the system-dependent filename
83.96 + * @exception FileNotFoundException if the file exists but is a directory
83.97 + * rather than a regular file, does not exist but cannot
83.98 + * be created, or cannot be opened for any other reason
83.99 + * @exception SecurityException if a security manager exists and its
83.100 + * <code>checkWrite</code> method denies write access
83.101 + * to the file.
83.102 + * @see java.lang.SecurityManager#checkWrite(java.lang.String)
83.103 + */
83.104 + public FileOutputStream(String name) throws FileNotFoundException {
83.105 + this(name != null ? new File(name) : null, false);
83.106 + }
83.107 +
83.108 + /**
83.109 + * Creates a file output stream to write to the file with the specified
83.110 + * name. If the second argument is <code>true</code>, then
83.111 + * bytes will be written to the end of the file rather than the beginning.
83.112 + * A new <code>FileDescriptor</code> object is created to represent this
83.113 + * file connection.
83.114 + * <p>
83.115 + * First, if there is a security manager, its <code>checkWrite</code>
83.116 + * method is called with <code>name</code> as its argument.
83.117 + * <p>
83.118 + * If the file exists but is a directory rather than a regular file, does
83.119 + * not exist but cannot be created, or cannot be opened for any other
83.120 + * reason then a <code>FileNotFoundException</code> is thrown.
83.121 + *
83.122 + * @param name the system-dependent file name
83.123 + * @param append if <code>true</code>, then bytes will be written
83.124 + * to the end of the file rather than the beginning
83.125 + * @exception FileNotFoundException if the file exists but is a directory
83.126 + * rather than a regular file, does not exist but cannot
83.127 + * be created, or cannot be opened for any other reason.
83.128 + * @exception SecurityException if a security manager exists and its
83.129 + * <code>checkWrite</code> method denies write access
83.130 + * to the file.
83.131 + * @see java.lang.SecurityManager#checkWrite(java.lang.String)
83.132 + * @since JDK1.1
83.133 + */
83.134 + public FileOutputStream(String name, boolean append)
83.135 + throws FileNotFoundException
83.136 + {
83.137 + this(name != null ? new File(name) : null, append);
83.138 + }
83.139 +
83.140 + /**
83.141 + * Creates a file output stream to write to the file represented by
83.142 + * the specified <code>File</code> object. A new
83.143 + * <code>FileDescriptor</code> object is created to represent this
83.144 + * file connection.
83.145 + * <p>
83.146 + * First, if there is a security manager, its <code>checkWrite</code>
83.147 + * method is called with the path represented by the <code>file</code>
83.148 + * argument as its argument.
83.149 + * <p>
83.150 + * If the file exists but is a directory rather than a regular file, does
83.151 + * not exist but cannot be created, or cannot be opened for any other
83.152 + * reason then a <code>FileNotFoundException</code> is thrown.
83.153 + *
83.154 + * @param file the file to be opened for writing.
83.155 + * @exception FileNotFoundException if the file exists but is a directory
83.156 + * rather than a regular file, does not exist but cannot
83.157 + * be created, or cannot be opened for any other reason
83.158 + * @exception SecurityException if a security manager exists and its
83.159 + * <code>checkWrite</code> method denies write access
83.160 + * to the file.
83.161 + * @see java.io.File#getPath()
83.162 + * @see java.lang.SecurityException
83.163 + * @see java.lang.SecurityManager#checkWrite(java.lang.String)
83.164 + */
83.165 + public FileOutputStream(File file) throws FileNotFoundException {
83.166 + this(file, false);
83.167 + }
83.168 +
83.169 + /**
83.170 + * Creates a file output stream to write to the file represented by
83.171 + * the specified <code>File</code> object. If the second argument is
83.172 + * <code>true</code>, then bytes will be written to the end of the file
83.173 + * rather than the beginning. A new <code>FileDescriptor</code> object is
83.174 + * created to represent this file connection.
83.175 + * <p>
83.176 + * First, if there is a security manager, its <code>checkWrite</code>
83.177 + * method is called with the path represented by the <code>file</code>
83.178 + * argument as its argument.
83.179 + * <p>
83.180 + * If the file exists but is a directory rather than a regular file, does
83.181 + * not exist but cannot be created, or cannot be opened for any other
83.182 + * reason then a <code>FileNotFoundException</code> is thrown.
83.183 + *
83.184 + * @param file the file to be opened for writing.
83.185 + * @param append if <code>true</code>, then bytes will be written
83.186 + * to the end of the file rather than the beginning
83.187 + * @exception FileNotFoundException if the file exists but is a directory
83.188 + * rather than a regular file, does not exist but cannot
83.189 + * be created, or cannot be opened for any other reason
83.190 + * @exception SecurityException if a security manager exists and its
83.191 + * <code>checkWrite</code> method denies write access
83.192 + * to the file.
83.193 + * @see java.io.File#getPath()
83.194 + * @see java.lang.SecurityException
83.195 + * @see java.lang.SecurityManager#checkWrite(java.lang.String)
83.196 + * @since 1.4
83.197 + */
83.198 + public FileOutputStream(File file, boolean append)
83.199 + throws FileNotFoundException
83.200 + {
83.201 + throw new SecurityException();
83.202 + }
83.203 +
83.204 + /**
83.205 + * Creates a file output stream to write to the specified file
83.206 + * descriptor, which represents an existing connection to an actual
83.207 + * file in the file system.
83.208 + * <p>
83.209 + * First, if there is a security manager, its <code>checkWrite</code>
83.210 + * method is called with the file descriptor <code>fdObj</code>
83.211 + * argument as its argument.
83.212 + * <p>
83.213 + * If <code>fdObj</code> is null then a <code>NullPointerException</code>
83.214 + * is thrown.
83.215 + * <p>
83.216 + * This constructor does not throw an exception if <code>fdObj</code>
83.217 + * is {@link java.io.FileDescriptor#valid() invalid}.
83.218 + * However, if the methods are invoked on the resulting stream to attempt
83.219 + * I/O on the stream, an <code>IOException</code> is thrown.
83.220 + *
83.221 + * @param fdObj the file descriptor to be opened for writing
83.222 + * @exception SecurityException if a security manager exists and its
83.223 + * <code>checkWrite</code> method denies
83.224 + * write access to the file descriptor
83.225 + * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
83.226 + */
83.227 + public FileOutputStream(FileDescriptor fdObj) {
83.228 + throw new SecurityException();
83.229 + }
83.230 +
83.231 + /**
83.232 + * Opens a file, with the specified name, for overwriting or appending.
83.233 + * @param name name of file to be opened
83.234 + * @param append whether the file is to be opened in append mode
83.235 + */
83.236 + private native void open(String name, boolean append)
83.237 + throws FileNotFoundException;
83.238 +
83.239 + /**
83.240 + * Writes the specified byte to this file output stream.
83.241 + *
83.242 + * @param b the byte to be written.
83.243 + * @param append {@code true} if the write operation first
83.244 + * advances the position to the end of file
83.245 + */
83.246 + private native void write(int b, boolean append) throws IOException;
83.247 +
83.248 + /**
83.249 + * Writes the specified byte to this file output stream. Implements
83.250 + * the <code>write</code> method of <code>OutputStream</code>.
83.251 + *
83.252 + * @param b the byte to be written.
83.253 + * @exception IOException if an I/O error occurs.
83.254 + */
83.255 + public void write(int b) throws IOException {
83.256 + write(b, append);
83.257 + }
83.258 +
83.259 + /**
83.260 + * Writes a sub array as a sequence of bytes.
83.261 + * @param b the data to be written
83.262 + * @param off the start offset in the data
83.263 + * @param len the number of bytes that are written
83.264 + * @param append {@code true} to first advance the position to the
83.265 + * end of file
83.266 + * @exception IOException If an I/O error has occurred.
83.267 + */
83.268 + private native void writeBytes(byte b[], int off, int len, boolean append)
83.269 + throws IOException;
83.270 +
83.271 + /**
83.272 + * Writes <code>b.length</code> bytes from the specified byte array
83.273 + * to this file output stream.
83.274 + *
83.275 + * @param b the data.
83.276 + * @exception IOException if an I/O error occurs.
83.277 + */
83.278 + public void write(byte b[]) throws IOException {
83.279 + writeBytes(b, 0, b.length, append);
83.280 + }
83.281 +
83.282 + /**
83.283 + * Writes <code>len</code> bytes from the specified byte array
83.284 + * starting at offset <code>off</code> to this file output stream.
83.285 + *
83.286 + * @param b the data.
83.287 + * @param off the start offset in the data.
83.288 + * @param len the number of bytes to write.
83.289 + * @exception IOException if an I/O error occurs.
83.290 + */
83.291 + public void write(byte b[], int off, int len) throws IOException {
83.292 + writeBytes(b, off, len, append);
83.293 + }
83.294 +
83.295 + /**
83.296 + * Closes this file output stream and releases any system resources
83.297 + * associated with this stream. This file output stream may no longer
83.298 + * be used for writing bytes.
83.299 + *
83.300 + * <p> If this stream has an associated channel then the channel is closed
83.301 + * as well.
83.302 + *
83.303 + * @exception IOException if an I/O error occurs.
83.304 + *
83.305 + * @revised 1.4
83.306 + * @spec JSR-51
83.307 + */
83.308 + public void close() throws IOException {
83.309 + synchronized (closeLock) {
83.310 + if (closed) {
83.311 + return;
83.312 + }
83.313 + closed = true;
83.314 + }
83.315 +//
83.316 +// if (channel != null) {
83.317 +// /*
83.318 +// * Decrement FD use count associated with the channel
83.319 +// * The use count is incremented whenever a new channel
83.320 +// * is obtained from this stream.
83.321 +// */
83.322 +// fd.decrementAndGetUseCount();
83.323 +// channel.close();
83.324 +// }
83.325 +
83.326 + /*
83.327 + * Decrement FD use count associated with this stream
83.328 + */
83.329 + int useCount = fd.decrementAndGetUseCount();
83.330 +
83.331 + /*
83.332 + * If FileDescriptor is still in use by another stream, the finalizer
83.333 + * will not close it.
83.334 + */
83.335 + if ((useCount <= 0) || !isRunningFinalize()) {
83.336 + close0();
83.337 + }
83.338 + }
83.339 +
83.340 + /**
83.341 + * Returns the file descriptor associated with this stream.
83.342 + *
83.343 + * @return the <code>FileDescriptor</code> object that represents
83.344 + * the connection to the file in the file system being used
83.345 + * by this <code>FileOutputStream</code> object.
83.346 + *
83.347 + * @exception IOException if an I/O error occurs.
83.348 + * @see java.io.FileDescriptor
83.349 + */
83.350 + public final FileDescriptor getFD() throws IOException {
83.351 + if (fd != null) return fd;
83.352 + throw new IOException();
83.353 + }
83.354 +
83.355 + /**
83.356 + * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
83.357 + * object associated with this file output stream. </p>
83.358 + *
83.359 + * <p> The initial {@link java.nio.channels.FileChannel#position()
83.360 + * </code>position<code>} of the returned channel will be equal to the
83.361 + * number of bytes written to the file so far unless this stream is in
83.362 + * append mode, in which case it will be equal to the size of the file.
83.363 + * Writing bytes to this stream will increment the channel's position
83.364 + * accordingly. Changing the channel's position, either explicitly or by
83.365 + * writing, will change this stream's file position.
83.366 + *
83.367 + * @return the file channel associated with this file output stream
83.368 + *
83.369 + * @since 1.4
83.370 + * @spec JSR-51
83.371 + */
83.372 +// public FileChannel getChannel() {
83.373 +// synchronized (this) {
83.374 +// if (channel == null) {
83.375 +// channel = FileChannelImpl.open(fd, false, true, append, this);
83.376 +//
83.377 +// /*
83.378 +// * Increment fd's use count. Invoking the channel's close()
83.379 +// * method will result in decrementing the use count set for
83.380 +// * the channel.
83.381 +// */
83.382 +// fd.incrementAndGetUseCount();
83.383 +// }
83.384 +// return channel;
83.385 +// }
83.386 +// }
83.387 +
83.388 + /**
83.389 + * Cleans up the connection to the file, and ensures that the
83.390 + * <code>close</code> method of this file output stream is
83.391 + * called when there are no more references to this stream.
83.392 + *
83.393 + * @exception IOException if an I/O error occurs.
83.394 + * @see java.io.FileInputStream#close()
83.395 + */
83.396 + protected void finalize() throws IOException {
83.397 + if (fd != null) {
83.398 + if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
83.399 + flush();
83.400 + } else {
83.401 +
83.402 + /*
83.403 + * Finalizer should not release the FileDescriptor if another
83.404 + * stream is still using it. If the user directly invokes
83.405 + * close() then the FileDescriptor is also released.
83.406 + */
83.407 + runningFinalize.set(Boolean.TRUE);
83.408 + try {
83.409 + close();
83.410 + } finally {
83.411 + runningFinalize.set(Boolean.FALSE);
83.412 + }
83.413 + }
83.414 + }
83.415 + }
83.416 +
83.417 + private native void close0() throws IOException;
83.418 +
83.419 + private static native void initIDs();
83.420 +
83.421 + static {
83.422 + initIDs();
83.423 + }
83.424 +
83.425 +}
84.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
84.2 +++ b/rt/emul/compact/src/main/java/java/io/FileReader.java Tue Feb 11 13:31:42 2014 +0100
84.3 @@ -0,0 +1,85 @@
84.4 +/*
84.5 + * Copyright (c) 1996, 2001, Oracle and/or its affiliates. All rights reserved.
84.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
84.7 + *
84.8 + * This code is free software; you can redistribute it and/or modify it
84.9 + * under the terms of the GNU General Public License version 2 only, as
84.10 + * published by the Free Software Foundation. Oracle designates this
84.11 + * particular file as subject to the "Classpath" exception as provided
84.12 + * by Oracle in the LICENSE file that accompanied this code.
84.13 + *
84.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
84.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
84.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
84.17 + * version 2 for more details (a copy is included in the LICENSE file that
84.18 + * accompanied this code).
84.19 + *
84.20 + * You should have received a copy of the GNU General Public License version
84.21 + * 2 along with this work; if not, write to the Free Software Foundation,
84.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
84.23 + *
84.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
84.25 + * or visit www.oracle.com if you need additional information or have any
84.26 + * questions.
84.27 + */
84.28 +
84.29 +package java.io;
84.30 +
84.31 +
84.32 +/**
84.33 + * Convenience class for reading character files. The constructors of this
84.34 + * class assume that the default character encoding and the default byte-buffer
84.35 + * size are appropriate. To specify these values yourself, construct an
84.36 + * InputStreamReader on a FileInputStream.
84.37 + *
84.38 + * <p><code>FileReader</code> is meant for reading streams of characters.
84.39 + * For reading streams of raw bytes, consider using a
84.40 + * <code>FileInputStream</code>.
84.41 + *
84.42 + * @see InputStreamReader
84.43 + * @see FileInputStream
84.44 + *
84.45 + * @author Mark Reinhold
84.46 + * @since JDK1.1
84.47 + */
84.48 +public class FileReader extends InputStreamReader {
84.49 +
84.50 + /**
84.51 + * Creates a new <tt>FileReader</tt>, given the name of the
84.52 + * file to read from.
84.53 + *
84.54 + * @param fileName the name of the file to read from
84.55 + * @exception FileNotFoundException if the named file does not exist,
84.56 + * is a directory rather than a regular file,
84.57 + * or for some other reason cannot be opened for
84.58 + * reading.
84.59 + */
84.60 + public FileReader(String fileName) throws FileNotFoundException {
84.61 + super(new FileInputStream(fileName));
84.62 + }
84.63 +
84.64 + /**
84.65 + * Creates a new <tt>FileReader</tt>, given the <tt>File</tt>
84.66 + * to read from.
84.67 + *
84.68 + * @param file the <tt>File</tt> to read from
84.69 + * @exception FileNotFoundException if the file does not exist,
84.70 + * is a directory rather than a regular file,
84.71 + * or for some other reason cannot be opened for
84.72 + * reading.
84.73 + */
84.74 + public FileReader(File file) throws FileNotFoundException {
84.75 + super(new FileInputStream(file));
84.76 + }
84.77 +
84.78 + /**
84.79 + * Creates a new <tt>FileReader</tt>, given the
84.80 + * <tt>FileDescriptor</tt> to read from.
84.81 + *
84.82 + * @param fd the FileDescriptor to read from
84.83 + */
84.84 + public FileReader(FileDescriptor fd) {
84.85 + super(new FileInputStream(fd));
84.86 + }
84.87 +
84.88 +}
85.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
85.2 +++ b/rt/emul/compact/src/main/java/java/io/FileWriter.java Tue Feb 11 13:31:42 2014 +0100
85.3 @@ -0,0 +1,119 @@
85.4 +/*
85.5 + * Copyright (c) 1996, 2001, Oracle and/or its affiliates. All rights reserved.
85.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
85.7 + *
85.8 + * This code is free software; you can redistribute it and/or modify it
85.9 + * under the terms of the GNU General Public License version 2 only, as
85.10 + * published by the Free Software Foundation. Oracle designates this
85.11 + * particular file as subject to the "Classpath" exception as provided
85.12 + * by Oracle in the LICENSE file that accompanied this code.
85.13 + *
85.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
85.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
85.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
85.17 + * version 2 for more details (a copy is included in the LICENSE file that
85.18 + * accompanied this code).
85.19 + *
85.20 + * You should have received a copy of the GNU General Public License version
85.21 + * 2 along with this work; if not, write to the Free Software Foundation,
85.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
85.23 + *
85.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
85.25 + * or visit www.oracle.com if you need additional information or have any
85.26 + * questions.
85.27 + */
85.28 +
85.29 +package java.io;
85.30 +
85.31 +
85.32 +/**
85.33 + * Convenience class for writing character files. The constructors of this
85.34 + * class assume that the default character encoding and the default byte-buffer
85.35 + * size are acceptable. To specify these values yourself, construct an
85.36 + * OutputStreamWriter on a FileOutputStream.
85.37 + *
85.38 + * <p>Whether or not a file is available or may be created depends upon the
85.39 + * underlying platform. Some platforms, in particular, allow a file to be
85.40 + * opened for writing by only one <tt>FileWriter</tt> (or other file-writing
85.41 + * object) at a time. In such situations the constructors in this class
85.42 + * will fail if the file involved is already open.
85.43 + *
85.44 + * <p><code>FileWriter</code> is meant for writing streams of characters.
85.45 + * For writing streams of raw bytes, consider using a
85.46 + * <code>FileOutputStream</code>.
85.47 + *
85.48 + * @see OutputStreamWriter
85.49 + * @see FileOutputStream
85.50 + *
85.51 + * @author Mark Reinhold
85.52 + * @since JDK1.1
85.53 + */
85.54 +
85.55 +public class FileWriter extends OutputStreamWriter {
85.56 +
85.57 + /**
85.58 + * Constructs a FileWriter object given a file name.
85.59 + *
85.60 + * @param fileName String The system-dependent filename.
85.61 + * @throws IOException if the named file exists but is a directory rather
85.62 + * than a regular file, does not exist but cannot be
85.63 + * created, or cannot be opened for any other reason
85.64 + */
85.65 + public FileWriter(String fileName) throws IOException {
85.66 + super(new FileOutputStream(fileName));
85.67 + }
85.68 +
85.69 + /**
85.70 + * Constructs a FileWriter object given a file name with a boolean
85.71 + * indicating whether or not to append the data written.
85.72 + *
85.73 + * @param fileName String The system-dependent filename.
85.74 + * @param append boolean if <code>true</code>, then data will be written
85.75 + * to the end of the file rather than the beginning.
85.76 + * @throws IOException if the named file exists but is a directory rather
85.77 + * than a regular file, does not exist but cannot be
85.78 + * created, or cannot be opened for any other reason
85.79 + */
85.80 + public FileWriter(String fileName, boolean append) throws IOException {
85.81 + super(new FileOutputStream(fileName, append));
85.82 + }
85.83 +
85.84 + /**
85.85 + * Constructs a FileWriter object given a File object.
85.86 + *
85.87 + * @param file a File object to write to.
85.88 + * @throws IOException if the file exists but is a directory rather than
85.89 + * a regular file, does not exist but cannot be created,
85.90 + * or cannot be opened for any other reason
85.91 + */
85.92 + public FileWriter(File file) throws IOException {
85.93 + super(new FileOutputStream(file));
85.94 + }
85.95 +
85.96 + /**
85.97 + * Constructs a FileWriter object given a File object. If the second
85.98 + * argument is <code>true</code>, then bytes will be written to the end
85.99 + * of the file rather than the beginning.
85.100 + *
85.101 + * @param file a File object to write to
85.102 + * @param append if <code>true</code>, then bytes will be written
85.103 + * to the end of the file rather than the beginning
85.104 + * @throws IOException if the file exists but is a directory rather than
85.105 + * a regular file, does not exist but cannot be created,
85.106 + * or cannot be opened for any other reason
85.107 + * @since 1.4
85.108 + */
85.109 + public FileWriter(File file, boolean append) throws IOException {
85.110 + super(new FileOutputStream(file, append));
85.111 + }
85.112 +
85.113 + /**
85.114 + * Constructs a FileWriter object associated with a file descriptor.
85.115 + *
85.116 + * @param fd FileDescriptor object to write to.
85.117 + */
85.118 + public FileWriter(FileDescriptor fd) {
85.119 + super(new FileOutputStream(fd));
85.120 + }
85.121 +
85.122 +}
86.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
86.2 +++ b/rt/emul/compact/src/main/java/java/io/FilterReader.java Tue Feb 11 13:31:42 2014 +0100
86.3 @@ -0,0 +1,124 @@
86.4 +/*
86.5 + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
86.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
86.7 + *
86.8 + * This code is free software; you can redistribute it and/or modify it
86.9 + * under the terms of the GNU General Public License version 2 only, as
86.10 + * published by the Free Software Foundation. Oracle designates this
86.11 + * particular file as subject to the "Classpath" exception as provided
86.12 + * by Oracle in the LICENSE file that accompanied this code.
86.13 + *
86.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
86.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
86.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
86.17 + * version 2 for more details (a copy is included in the LICENSE file that
86.18 + * accompanied this code).
86.19 + *
86.20 + * You should have received a copy of the GNU General Public License version
86.21 + * 2 along with this work; if not, write to the Free Software Foundation,
86.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
86.23 + *
86.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
86.25 + * or visit www.oracle.com if you need additional information or have any
86.26 + * questions.
86.27 + */
86.28 +
86.29 +package java.io;
86.30 +
86.31 +
86.32 +/**
86.33 + * Abstract class for reading filtered character streams.
86.34 + * The abstract class <code>FilterReader</code> itself
86.35 + * provides default methods that pass all requests to
86.36 + * the contained stream. Subclasses of <code>FilterReader</code>
86.37 + * should override some of these methods and may also provide
86.38 + * additional methods and fields.
86.39 + *
86.40 + * @author Mark Reinhold
86.41 + * @since JDK1.1
86.42 + */
86.43 +
86.44 +public abstract class FilterReader extends Reader {
86.45 +
86.46 + /**
86.47 + * The underlying character-input stream.
86.48 + */
86.49 + protected Reader in;
86.50 +
86.51 + /**
86.52 + * Creates a new filtered reader.
86.53 + *
86.54 + * @param in a Reader object providing the underlying stream.
86.55 + * @throws NullPointerException if <code>in</code> is <code>null</code>
86.56 + */
86.57 + protected FilterReader(Reader in) {
86.58 + super(in);
86.59 + this.in = in;
86.60 + }
86.61 +
86.62 + /**
86.63 + * Reads a single character.
86.64 + *
86.65 + * @exception IOException If an I/O error occurs
86.66 + */
86.67 + public int read() throws IOException {
86.68 + return in.read();
86.69 + }
86.70 +
86.71 + /**
86.72 + * Reads characters into a portion of an array.
86.73 + *
86.74 + * @exception IOException If an I/O error occurs
86.75 + */
86.76 + public int read(char cbuf[], int off, int len) throws IOException {
86.77 + return in.read(cbuf, off, len);
86.78 + }
86.79 +
86.80 + /**
86.81 + * Skips characters.
86.82 + *
86.83 + * @exception IOException If an I/O error occurs
86.84 + */
86.85 + public long skip(long n) throws IOException {
86.86 + return in.skip(n);
86.87 + }
86.88 +
86.89 + /**
86.90 + * Tells whether this stream is ready to be read.
86.91 + *
86.92 + * @exception IOException If an I/O error occurs
86.93 + */
86.94 + public boolean ready() throws IOException {
86.95 + return in.ready();
86.96 + }
86.97 +
86.98 + /**
86.99 + * Tells whether this stream supports the mark() operation.
86.100 + */
86.101 + public boolean markSupported() {
86.102 + return in.markSupported();
86.103 + }
86.104 +
86.105 + /**
86.106 + * Marks the present position in the stream.
86.107 + *
86.108 + * @exception IOException If an I/O error occurs
86.109 + */
86.110 + public void mark(int readAheadLimit) throws IOException {
86.111 + in.mark(readAheadLimit);
86.112 + }
86.113 +
86.114 + /**
86.115 + * Resets the stream.
86.116 + *
86.117 + * @exception IOException If an I/O error occurs
86.118 + */
86.119 + public void reset() throws IOException {
86.120 + in.reset();
86.121 + }
86.122 +
86.123 + public void close() throws IOException {
86.124 + in.close();
86.125 + }
86.126 +
86.127 +}
87.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
87.2 +++ b/rt/emul/compact/src/main/java/java/io/FilterWriter.java Tue Feb 11 13:31:42 2014 +0100
87.3 @@ -0,0 +1,107 @@
87.4 +/*
87.5 + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
87.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
87.7 + *
87.8 + * This code is free software; you can redistribute it and/or modify it
87.9 + * under the terms of the GNU General Public License version 2 only, as
87.10 + * published by the Free Software Foundation. Oracle designates this
87.11 + * particular file as subject to the "Classpath" exception as provided
87.12 + * by Oracle in the LICENSE file that accompanied this code.
87.13 + *
87.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
87.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
87.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
87.17 + * version 2 for more details (a copy is included in the LICENSE file that
87.18 + * accompanied this code).
87.19 + *
87.20 + * You should have received a copy of the GNU General Public License version
87.21 + * 2 along with this work; if not, write to the Free Software Foundation,
87.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
87.23 + *
87.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
87.25 + * or visit www.oracle.com if you need additional information or have any
87.26 + * questions.
87.27 + */
87.28 +
87.29 +package java.io;
87.30 +
87.31 +
87.32 +/**
87.33 + * Abstract class for writing filtered character streams.
87.34 + * The abstract class <code>FilterWriter</code> itself
87.35 + * provides default methods that pass all requests to the
87.36 + * contained stream. Subclasses of <code>FilterWriter</code>
87.37 + * should override some of these methods and may also
87.38 + * provide additional methods and fields.
87.39 + *
87.40 + * @author Mark Reinhold
87.41 + * @since JDK1.1
87.42 + */
87.43 +
87.44 +public abstract class FilterWriter extends Writer {
87.45 +
87.46 + /**
87.47 + * The underlying character-output stream.
87.48 + */
87.49 + protected Writer out;
87.50 +
87.51 + /**
87.52 + * Create a new filtered writer.
87.53 + *
87.54 + * @param out a Writer object to provide the underlying stream.
87.55 + * @throws NullPointerException if <code>out</code> is <code>null</code>
87.56 + */
87.57 + protected FilterWriter(Writer out) {
87.58 + super(out);
87.59 + this.out = out;
87.60 + }
87.61 +
87.62 + /**
87.63 + * Writes a single character.
87.64 + *
87.65 + * @exception IOException If an I/O error occurs
87.66 + */
87.67 + public void write(int c) throws IOException {
87.68 + out.write(c);
87.69 + }
87.70 +
87.71 + /**
87.72 + * Writes a portion of an array of characters.
87.73 + *
87.74 + * @param cbuf Buffer of characters to be written
87.75 + * @param off Offset from which to start reading characters
87.76 + * @param len Number of characters to be written
87.77 + *
87.78 + * @exception IOException If an I/O error occurs
87.79 + */
87.80 + public void write(char cbuf[], int off, int len) throws IOException {
87.81 + out.write(cbuf, off, len);
87.82 + }
87.83 +
87.84 + /**
87.85 + * Writes a portion of a string.
87.86 + *
87.87 + * @param str String to be written
87.88 + * @param off Offset from which to start reading characters
87.89 + * @param len Number of characters to be written
87.90 + *
87.91 + * @exception IOException If an I/O error occurs
87.92 + */
87.93 + public void write(String str, int off, int len) throws IOException {
87.94 + out.write(str, off, len);
87.95 + }
87.96 +
87.97 + /**
87.98 + * Flushes the stream.
87.99 + *
87.100 + * @exception IOException If an I/O error occurs
87.101 + */
87.102 + public void flush() throws IOException {
87.103 + out.flush();
87.104 + }
87.105 +
87.106 + public void close() throws IOException {
87.107 + out.close();
87.108 + }
87.109 +
87.110 +}
88.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
88.2 +++ b/rt/emul/compact/src/main/java/java/io/LineNumberInputStream.java Tue Feb 11 13:31:42 2014 +0100
88.3 @@ -0,0 +1,292 @@
88.4 +/*
88.5 + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved.
88.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
88.7 + *
88.8 + * This code is free software; you can redistribute it and/or modify it
88.9 + * under the terms of the GNU General Public License version 2 only, as
88.10 + * published by the Free Software Foundation. Oracle designates this
88.11 + * particular file as subject to the "Classpath" exception as provided
88.12 + * by Oracle in the LICENSE file that accompanied this code.
88.13 + *
88.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
88.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
88.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
88.17 + * version 2 for more details (a copy is included in the LICENSE file that
88.18 + * accompanied this code).
88.19 + *
88.20 + * You should have received a copy of the GNU General Public License version
88.21 + * 2 along with this work; if not, write to the Free Software Foundation,
88.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
88.23 + *
88.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
88.25 + * or visit www.oracle.com if you need additional information or have any
88.26 + * questions.
88.27 + */
88.28 +
88.29 +package java.io;
88.30 +
88.31 +/**
88.32 + * This class is an input stream filter that provides the added
88.33 + * functionality of keeping track of the current line number.
88.34 + * <p>
88.35 + * A line is a sequence of bytes ending with a carriage return
88.36 + * character (<code>'\r'</code>), a newline character
88.37 + * (<code>'\n'</code>), or a carriage return character followed
88.38 + * immediately by a linefeed character. In all three cases, the line
88.39 + * terminating character(s) are returned as a single newline character.
88.40 + * <p>
88.41 + * The line number begins at <code>0</code>, and is incremented by
88.42 + * <code>1</code> when a <code>read</code> returns a newline character.
88.43 + *
88.44 + * @author Arthur van Hoff
88.45 + * @see java.io.LineNumberReader
88.46 + * @since JDK1.0
88.47 + * @deprecated This class incorrectly assumes that bytes adequately represent
88.48 + * characters. As of JDK 1.1, the preferred way to operate on
88.49 + * character streams is via the new character-stream classes, which
88.50 + * include a class for counting line numbers.
88.51 + */
88.52 +@Deprecated
88.53 +public
88.54 +class LineNumberInputStream extends FilterInputStream {
88.55 + int pushBack = -1;
88.56 + int lineNumber;
88.57 + int markLineNumber;
88.58 + int markPushBack = -1;
88.59 +
88.60 + /**
88.61 + * Constructs a newline number input stream that reads its input
88.62 + * from the specified input stream.
88.63 + *
88.64 + * @param in the underlying input stream.
88.65 + */
88.66 + public LineNumberInputStream(InputStream in) {
88.67 + super(in);
88.68 + }
88.69 +
88.70 + /**
88.71 + * Reads the next byte of data from this input stream. The value
88.72 + * byte is returned as an <code>int</code> in the range
88.73 + * <code>0</code> to <code>255</code>. If no byte is available
88.74 + * because the end of the stream has been reached, the value
88.75 + * <code>-1</code> is returned. This method blocks until input data
88.76 + * is available, the end of the stream is detected, or an exception
88.77 + * is thrown.
88.78 + * <p>
88.79 + * The <code>read</code> method of
88.80 + * <code>LineNumberInputStream</code> calls the <code>read</code>
88.81 + * method of the underlying input stream. It checks for carriage
88.82 + * returns and newline characters in the input, and modifies the
88.83 + * current line number as appropriate. A carriage-return character or
88.84 + * a carriage return followed by a newline character are both
88.85 + * converted into a single newline character.
88.86 + *
88.87 + * @return the next byte of data, or <code>-1</code> if the end of this
88.88 + * stream is reached.
88.89 + * @exception IOException if an I/O error occurs.
88.90 + * @see java.io.FilterInputStream#in
88.91 + * @see java.io.LineNumberInputStream#getLineNumber()
88.92 + */
88.93 + public int read() throws IOException {
88.94 + int c = pushBack;
88.95 +
88.96 + if (c != -1) {
88.97 + pushBack = -1;
88.98 + } else {
88.99 + c = in.read();
88.100 + }
88.101 +
88.102 + switch (c) {
88.103 + case '\r':
88.104 + pushBack = in.read();
88.105 + if (pushBack == '\n') {
88.106 + pushBack = -1;
88.107 + }
88.108 + case '\n':
88.109 + lineNumber++;
88.110 + return '\n';
88.111 + }
88.112 + return c;
88.113 + }
88.114 +
88.115 + /**
88.116 + * Reads up to <code>len</code> bytes of data from this input stream
88.117 + * into an array of bytes. This method blocks until some input is available.
88.118 + * <p>
88.119 + * The <code>read</code> method of
88.120 + * <code>LineNumberInputStream</code> repeatedly calls the
88.121 + * <code>read</code> method of zero arguments to fill in the byte array.
88.122 + *
88.123 + * @param b the buffer into which the data is read.
88.124 + * @param off the start offset of the data.
88.125 + * @param len the maximum number of bytes read.
88.126 + * @return the total number of bytes read into the buffer, or
88.127 + * <code>-1</code> if there is no more data because the end of
88.128 + * this stream has been reached.
88.129 + * @exception IOException if an I/O error occurs.
88.130 + * @see java.io.LineNumberInputStream#read()
88.131 + */
88.132 + public int read(byte b[], int off, int len) throws IOException {
88.133 + if (b == null) {
88.134 + throw new NullPointerException();
88.135 + } else if ((off < 0) || (off > b.length) || (len < 0) ||
88.136 + ((off + len) > b.length) || ((off + len) < 0)) {
88.137 + throw new IndexOutOfBoundsException();
88.138 + } else if (len == 0) {
88.139 + return 0;
88.140 + }
88.141 +
88.142 + int c = read();
88.143 + if (c == -1) {
88.144 + return -1;
88.145 + }
88.146 + b[off] = (byte)c;
88.147 +
88.148 + int i = 1;
88.149 + try {
88.150 + for (; i < len ; i++) {
88.151 + c = read();
88.152 + if (c == -1) {
88.153 + break;
88.154 + }
88.155 + if (b != null) {
88.156 + b[off + i] = (byte)c;
88.157 + }
88.158 + }
88.159 + } catch (IOException ee) {
88.160 + }
88.161 + return i;
88.162 + }
88.163 +
88.164 + /**
88.165 + * Skips over and discards <code>n</code> bytes of data from this
88.166 + * input stream. The <code>skip</code> method may, for a variety of
88.167 + * reasons, end up skipping over some smaller number of bytes,
88.168 + * possibly <code>0</code>. The actual number of bytes skipped is
88.169 + * returned. If <code>n</code> is negative, no bytes are skipped.
88.170 + * <p>
88.171 + * The <code>skip</code> method of <code>LineNumberInputStream</code> creates
88.172 + * a byte array and then repeatedly reads into it until
88.173 + * <code>n</code> bytes have been read or the end of the stream has
88.174 + * been reached.
88.175 + *
88.176 + * @param n the number of bytes to be skipped.
88.177 + * @return the actual number of bytes skipped.
88.178 + * @exception IOException if an I/O error occurs.
88.179 + * @see java.io.FilterInputStream#in
88.180 + */
88.181 + public long skip(long n) throws IOException {
88.182 + int chunk = 2048;
88.183 + long remaining = n;
88.184 + byte data[];
88.185 + int nr;
88.186 +
88.187 + if (n <= 0) {
88.188 + return 0;
88.189 + }
88.190 +
88.191 + data = new byte[chunk];
88.192 + while (remaining > 0) {
88.193 + nr = read(data, 0, (int) Math.min(chunk, remaining));
88.194 + if (nr < 0) {
88.195 + break;
88.196 + }
88.197 + remaining -= nr;
88.198 + }
88.199 +
88.200 + return n - remaining;
88.201 + }
88.202 +
88.203 + /**
88.204 + * Sets the line number to the specified argument.
88.205 + *
88.206 + * @param lineNumber the new line number.
88.207 + * @see #getLineNumber
88.208 + */
88.209 + public void setLineNumber(int lineNumber) {
88.210 + this.lineNumber = lineNumber;
88.211 + }
88.212 +
88.213 + /**
88.214 + * Returns the current line number.
88.215 + *
88.216 + * @return the current line number.
88.217 + * @see #setLineNumber
88.218 + */
88.219 + public int getLineNumber() {
88.220 + return lineNumber;
88.221 + }
88.222 +
88.223 +
88.224 + /**
88.225 + * Returns the number of bytes that can be read from this input
88.226 + * stream without blocking.
88.227 + * <p>
88.228 + * Note that if the underlying input stream is able to supply
88.229 + * <i>k</i> input characters without blocking, the
88.230 + * <code>LineNumberInputStream</code> can guarantee only to provide
88.231 + * <i>k</i>/2 characters without blocking, because the
88.232 + * <i>k</i> characters from the underlying input stream might
88.233 + * consist of <i>k</i>/2 pairs of <code>'\r'</code> and
88.234 + * <code>'\n'</code>, which are converted to just
88.235 + * <i>k</i>/2 <code>'\n'</code> characters.
88.236 + *
88.237 + * @return the number of bytes that can be read from this input stream
88.238 + * without blocking.
88.239 + * @exception IOException if an I/O error occurs.
88.240 + * @see java.io.FilterInputStream#in
88.241 + */
88.242 + public int available() throws IOException {
88.243 + return (pushBack == -1) ? super.available()/2 : super.available()/2 + 1;
88.244 + }
88.245 +
88.246 + /**
88.247 + * Marks the current position in this input stream. A subsequent
88.248 + * call to the <code>reset</code> method repositions this stream at
88.249 + * the last marked position so that subsequent reads re-read the same bytes.
88.250 + * <p>
88.251 + * The <code>mark</code> method of
88.252 + * <code>LineNumberInputStream</code> remembers the current line
88.253 + * number in a private variable, and then calls the <code>mark</code>
88.254 + * method of the underlying input stream.
88.255 + *
88.256 + * @param readlimit the maximum limit of bytes that can be read before
88.257 + * the mark position becomes invalid.
88.258 + * @see java.io.FilterInputStream#in
88.259 + * @see java.io.LineNumberInputStream#reset()
88.260 + */
88.261 + public void mark(int readlimit) {
88.262 + markLineNumber = lineNumber;
88.263 + markPushBack = pushBack;
88.264 + in.mark(readlimit);
88.265 + }
88.266 +
88.267 + /**
88.268 + * Repositions this stream to the position at the time the
88.269 + * <code>mark</code> method was last called on this input stream.
88.270 + * <p>
88.271 + * The <code>reset</code> method of
88.272 + * <code>LineNumberInputStream</code> resets the line number to be
88.273 + * the line number at the time the <code>mark</code> method was
88.274 + * called, and then calls the <code>reset</code> method of the
88.275 + * underlying input stream.
88.276 + * <p>
88.277 + * Stream marks are intended to be used in
88.278 + * situations where you need to read ahead a little to see what's in
88.279 + * the stream. Often this is most easily done by invoking some
88.280 + * general parser. If the stream is of the type handled by the
88.281 + * parser, it just chugs along happily. If the stream is not of
88.282 + * that type, the parser should toss an exception when it fails,
88.283 + * which, if it happens within readlimit bytes, allows the outer
88.284 + * code to reset the stream and try another parser.
88.285 + *
88.286 + * @exception IOException if an I/O error occurs.
88.287 + * @see java.io.FilterInputStream#in
88.288 + * @see java.io.LineNumberInputStream#mark(int)
88.289 + */
88.290 + public void reset() throws IOException {
88.291 + lineNumber = markLineNumber;
88.292 + pushBack = markPushBack;
88.293 + in.reset();
88.294 + }
88.295 +}
89.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
89.2 +++ b/rt/emul/compact/src/main/java/java/io/LineNumberReader.java Tue Feb 11 13:31:42 2014 +0100
89.3 @@ -0,0 +1,281 @@
89.4 +/*
89.5 + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
89.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
89.7 + *
89.8 + * This code is free software; you can redistribute it and/or modify it
89.9 + * under the terms of the GNU General Public License version 2 only, as
89.10 + * published by the Free Software Foundation. Oracle designates this
89.11 + * particular file as subject to the "Classpath" exception as provided
89.12 + * by Oracle in the LICENSE file that accompanied this code.
89.13 + *
89.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
89.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
89.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
89.17 + * version 2 for more details (a copy is included in the LICENSE file that
89.18 + * accompanied this code).
89.19 + *
89.20 + * You should have received a copy of the GNU General Public License version
89.21 + * 2 along with this work; if not, write to the Free Software Foundation,
89.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
89.23 + *
89.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
89.25 + * or visit www.oracle.com if you need additional information or have any
89.26 + * questions.
89.27 + */
89.28 +
89.29 +package java.io;
89.30 +
89.31 +
89.32 +/**
89.33 + * A buffered character-input stream that keeps track of line numbers. This
89.34 + * class defines methods {@link #setLineNumber(int)} and {@link
89.35 + * #getLineNumber()} for setting and getting the current line number
89.36 + * respectively.
89.37 + *
89.38 + * <p> By default, line numbering begins at 0. This number increments at every
89.39 + * <a href="#lt">line terminator</a> as the data is read, and can be changed
89.40 + * with a call to <tt>setLineNumber(int)</tt>. Note however, that
89.41 + * <tt>setLineNumber(int)</tt> does not actually change the current position in
89.42 + * the stream; it only changes the value that will be returned by
89.43 + * <tt>getLineNumber()</tt>.
89.44 + *
89.45 + * <p> A line is considered to be <a name="lt">terminated</a> by any one of a
89.46 + * line feed ('\n'), a carriage return ('\r'), or a carriage return followed
89.47 + * immediately by a linefeed.
89.48 + *
89.49 + * @author Mark Reinhold
89.50 + * @since JDK1.1
89.51 + */
89.52 +
89.53 +public class LineNumberReader extends BufferedReader {
89.54 +
89.55 + /** The current line number */
89.56 + private int lineNumber = 0;
89.57 +
89.58 + /** The line number of the mark, if any */
89.59 + private int markedLineNumber; // Defaults to 0
89.60 +
89.61 + /** If the next character is a line feed, skip it */
89.62 + private boolean skipLF;
89.63 +
89.64 + /** The skipLF flag when the mark was set */
89.65 + private boolean markedSkipLF;
89.66 +
89.67 + /**
89.68 + * Create a new line-numbering reader, using the default input-buffer
89.69 + * size.
89.70 + *
89.71 + * @param in
89.72 + * A Reader object to provide the underlying stream
89.73 + */
89.74 + public LineNumberReader(Reader in) {
89.75 + super(in);
89.76 + }
89.77 +
89.78 + /**
89.79 + * Create a new line-numbering reader, reading characters into a buffer of
89.80 + * the given size.
89.81 + *
89.82 + * @param in
89.83 + * A Reader object to provide the underlying stream
89.84 + *
89.85 + * @param sz
89.86 + * An int specifying the size of the buffer
89.87 + */
89.88 + public LineNumberReader(Reader in, int sz) {
89.89 + super(in, sz);
89.90 + }
89.91 +
89.92 + /**
89.93 + * Set the current line number.
89.94 + *
89.95 + * @param lineNumber
89.96 + * An int specifying the line number
89.97 + *
89.98 + * @see #getLineNumber
89.99 + */
89.100 + public void setLineNumber(int lineNumber) {
89.101 + this.lineNumber = lineNumber;
89.102 + }
89.103 +
89.104 + /**
89.105 + * Get the current line number.
89.106 + *
89.107 + * @return The current line number
89.108 + *
89.109 + * @see #setLineNumber
89.110 + */
89.111 + public int getLineNumber() {
89.112 + return lineNumber;
89.113 + }
89.114 +
89.115 + /**
89.116 + * Read a single character. <a href="#lt">Line terminators</a> are
89.117 + * compressed into single newline ('\n') characters. Whenever a line
89.118 + * terminator is read the current line number is incremented.
89.119 + *
89.120 + * @return The character read, or -1 if the end of the stream has been
89.121 + * reached
89.122 + *
89.123 + * @throws IOException
89.124 + * If an I/O error occurs
89.125 + */
89.126 + public int read() throws IOException {
89.127 + synchronized (lock) {
89.128 + int c = super.read();
89.129 + if (skipLF) {
89.130 + if (c == '\n')
89.131 + c = super.read();
89.132 + skipLF = false;
89.133 + }
89.134 + switch (c) {
89.135 + case '\r':
89.136 + skipLF = true;
89.137 + case '\n': /* Fall through */
89.138 + lineNumber++;
89.139 + return '\n';
89.140 + }
89.141 + return c;
89.142 + }
89.143 + }
89.144 +
89.145 + /**
89.146 + * Read characters into a portion of an array. Whenever a <a
89.147 + * href="#lt">line terminator</a> is read the current line number is
89.148 + * incremented.
89.149 + *
89.150 + * @param cbuf
89.151 + * Destination buffer
89.152 + *
89.153 + * @param off
89.154 + * Offset at which to start storing characters
89.155 + *
89.156 + * @param len
89.157 + * Maximum number of characters to read
89.158 + *
89.159 + * @return The number of bytes read, or -1 if the end of the stream has
89.160 + * already been reached
89.161 + *
89.162 + * @throws IOException
89.163 + * If an I/O error occurs
89.164 + */
89.165 + public int read(char cbuf[], int off, int len) throws IOException {
89.166 + synchronized (lock) {
89.167 + int n = super.read(cbuf, off, len);
89.168 +
89.169 + for (int i = off; i < off + n; i++) {
89.170 + int c = cbuf[i];
89.171 + if (skipLF) {
89.172 + skipLF = false;
89.173 + if (c == '\n')
89.174 + continue;
89.175 + }
89.176 + switch (c) {
89.177 + case '\r':
89.178 + skipLF = true;
89.179 + case '\n': /* Fall through */
89.180 + lineNumber++;
89.181 + break;
89.182 + }
89.183 + }
89.184 +
89.185 + return n;
89.186 + }
89.187 + }
89.188 +
89.189 + /**
89.190 + * Read a line of text. Whenever a <a href="#lt">line terminator</a> is
89.191 + * read the current line number is incremented.
89.192 + *
89.193 + * @return A String containing the contents of the line, not including
89.194 + * any <a href="#lt">line termination characters</a>, or
89.195 + * <tt>null</tt> if the end of the stream has been reached
89.196 + *
89.197 + * @throws IOException
89.198 + * If an I/O error occurs
89.199 + */
89.200 + public String readLine() throws IOException {
89.201 + synchronized (lock) {
89.202 + String l = super.readLine(skipLF);
89.203 + skipLF = false;
89.204 + if (l != null)
89.205 + lineNumber++;
89.206 + return l;
89.207 + }
89.208 + }
89.209 +
89.210 + /** Maximum skip-buffer size */
89.211 + private static final int maxSkipBufferSize = 8192;
89.212 +
89.213 + /** Skip buffer, null until allocated */
89.214 + private char skipBuffer[] = null;
89.215 +
89.216 + /**
89.217 + * Skip characters.
89.218 + *
89.219 + * @param n
89.220 + * The number of characters to skip
89.221 + *
89.222 + * @return The number of characters actually skipped
89.223 + *
89.224 + * @throws IOException
89.225 + * If an I/O error occurs
89.226 + *
89.227 + * @throws IllegalArgumentException
89.228 + * If <tt>n</tt> is negative
89.229 + */
89.230 + public long skip(long n) throws IOException {
89.231 + if (n < 0)
89.232 + throw new IllegalArgumentException("skip() value is negative");
89.233 + int nn = (int) Math.min(n, maxSkipBufferSize);
89.234 + synchronized (lock) {
89.235 + if ((skipBuffer == null) || (skipBuffer.length < nn))
89.236 + skipBuffer = new char[nn];
89.237 + long r = n;
89.238 + while (r > 0) {
89.239 + int nc = read(skipBuffer, 0, (int) Math.min(r, nn));
89.240 + if (nc == -1)
89.241 + break;
89.242 + r -= nc;
89.243 + }
89.244 + return n - r;
89.245 + }
89.246 + }
89.247 +
89.248 + /**
89.249 + * Mark the present position in the stream. Subsequent calls to reset()
89.250 + * will attempt to reposition the stream to this point, and will also reset
89.251 + * the line number appropriately.
89.252 + *
89.253 + * @param readAheadLimit
89.254 + * Limit on the number of characters that may be read while still
89.255 + * preserving the mark. After reading this many characters,
89.256 + * attempting to reset the stream may fail.
89.257 + *
89.258 + * @throws IOException
89.259 + * If an I/O error occurs
89.260 + */
89.261 + public void mark(int readAheadLimit) throws IOException {
89.262 + synchronized (lock) {
89.263 + super.mark(readAheadLimit);
89.264 + markedLineNumber = lineNumber;
89.265 + markedSkipLF = skipLF;
89.266 + }
89.267 + }
89.268 +
89.269 + /**
89.270 + * Reset the stream to the most recent mark.
89.271 + *
89.272 + * @throws IOException
89.273 + * If the stream has not been marked, or if the mark has been
89.274 + * invalidated
89.275 + */
89.276 + public void reset() throws IOException {
89.277 + synchronized (lock) {
89.278 + super.reset();
89.279 + lineNumber = markedLineNumber;
89.280 + skipLF = markedSkipLF;
89.281 + }
89.282 + }
89.283 +
89.284 +}
90.1 --- a/rt/emul/compact/src/main/java/java/io/OutputStreamWriter.java Tue Feb 11 10:48:24 2014 +0100
90.2 +++ b/rt/emul/compact/src/main/java/java/io/OutputStreamWriter.java Tue Feb 11 13:31:42 2014 +0100
90.3 @@ -25,6 +25,8 @@
90.4
90.5 package java.io;
90.6
90.7 +import java.nio.charset.Charset;
90.8 +
90.9 /**
90.10 * An OutputStreamWriter is a bridge from character streams to byte streams:
90.11 * Characters written to it are encoded into bytes using a specified {@link
90.12 @@ -116,12 +118,9 @@
90.13 * @since 1.4
90.14 * @spec JSR-51
90.15 */
90.16 -// public OutputStreamWriter(OutputStream out, Charset cs) {
90.17 -// super(out);
90.18 -// if (cs == null)
90.19 -// throw new NullPointerException("charset");
90.20 -// se = StreamEncoder.forOutputStreamWriter(out, this, cs);
90.21 -// }
90.22 + public OutputStreamWriter(OutputStream out, Charset cs) {
90.23 + this(out);
90.24 + }
90.25
90.26 /**
90.27 * Creates an OutputStreamWriter that uses the given charset encoder. </p>
91.1 --- a/rt/emul/compact/src/main/java/java/io/PrintStream.java Tue Feb 11 10:48:24 2014 +0100
91.2 +++ b/rt/emul/compact/src/main/java/java/io/PrintStream.java Tue Feb 11 13:31:42 2014 +0100
91.3 @@ -25,6 +25,7 @@
91.4
91.5 package java.io;
91.6
91.7 +import java.nio.charset.Charset;
91.8 import java.util.Arrays;
91.9
91.10
91.11 @@ -88,9 +89,6 @@
91.12 static final class Formatter {
91.13 }
91.14
91.15 - static final class Charset {
91.16 - }
91.17 -
91.18 static Charset toCharset(String ch) throws UnsupportedEncodingException {
91.19 if (!"UTF-8".equals(ch)) {
91.20 throw new UnsupportedEncodingException();
92.1 --- a/rt/emul/compact/src/main/java/java/io/PrintWriter.java Tue Feb 11 10:48:24 2014 +0100
92.2 +++ b/rt/emul/compact/src/main/java/java/io/PrintWriter.java Tue Feb 11 13:31:42 2014 +0100
92.3 @@ -25,10 +25,9 @@
92.4
92.5 package java.io;
92.6
92.7 -import java.io.PrintStream.Charset;
92.8 import java.io.PrintStream.Formatter;
92.9 +import java.nio.charset.Charset;
92.10 import java.util.Arrays;
92.11 -import java.util.Objects;
92.12
92.13 /**
92.14 * Prints formatted representations of objects to a text-output stream. This
93.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
93.2 +++ b/rt/emul/compact/src/main/java/java/io/StringReader.java Tue Feb 11 13:31:42 2014 +0100
93.3 @@ -0,0 +1,201 @@
93.4 +/*
93.5 + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
93.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
93.7 + *
93.8 + * This code is free software; you can redistribute it and/or modify it
93.9 + * under the terms of the GNU General Public License version 2 only, as
93.10 + * published by the Free Software Foundation. Oracle designates this
93.11 + * particular file as subject to the "Classpath" exception as provided
93.12 + * by Oracle in the LICENSE file that accompanied this code.
93.13 + *
93.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
93.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
93.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
93.17 + * version 2 for more details (a copy is included in the LICENSE file that
93.18 + * accompanied this code).
93.19 + *
93.20 + * You should have received a copy of the GNU General Public License version
93.21 + * 2 along with this work; if not, write to the Free Software Foundation,
93.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
93.23 + *
93.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
93.25 + * or visit www.oracle.com if you need additional information or have any
93.26 + * questions.
93.27 + */
93.28 +
93.29 +package java.io;
93.30 +
93.31 +
93.32 +/**
93.33 + * A character stream whose source is a string.
93.34 + *
93.35 + * @author Mark Reinhold
93.36 + * @since JDK1.1
93.37 + */
93.38 +
93.39 +public class StringReader extends Reader {
93.40 +
93.41 + private String str;
93.42 + private int length;
93.43 + private int next = 0;
93.44 + private int mark = 0;
93.45 +
93.46 + /**
93.47 + * Creates a new string reader.
93.48 + *
93.49 + * @param s String providing the character stream.
93.50 + */
93.51 + public StringReader(String s) {
93.52 + this.str = s;
93.53 + this.length = s.length();
93.54 + }
93.55 +
93.56 + /** Check to make sure that the stream has not been closed */
93.57 + private void ensureOpen() throws IOException {
93.58 + if (str == null)
93.59 + throw new IOException("Stream closed");
93.60 + }
93.61 +
93.62 + /**
93.63 + * Reads a single character.
93.64 + *
93.65 + * @return The character read, or -1 if the end of the stream has been
93.66 + * reached
93.67 + *
93.68 + * @exception IOException If an I/O error occurs
93.69 + */
93.70 + public int read() throws IOException {
93.71 + synchronized (lock) {
93.72 + ensureOpen();
93.73 + if (next >= length)
93.74 + return -1;
93.75 + return str.charAt(next++);
93.76 + }
93.77 + }
93.78 +
93.79 + /**
93.80 + * Reads characters into a portion of an array.
93.81 + *
93.82 + * @param cbuf Destination buffer
93.83 + * @param off Offset at which to start writing characters
93.84 + * @param len Maximum number of characters to read
93.85 + *
93.86 + * @return The number of characters read, or -1 if the end of the
93.87 + * stream has been reached
93.88 + *
93.89 + * @exception IOException If an I/O error occurs
93.90 + */
93.91 + public int read(char cbuf[], int off, int len) throws IOException {
93.92 + synchronized (lock) {
93.93 + ensureOpen();
93.94 + if ((off < 0) || (off > cbuf.length) || (len < 0) ||
93.95 + ((off + len) > cbuf.length) || ((off + len) < 0)) {
93.96 + throw new IndexOutOfBoundsException();
93.97 + } else if (len == 0) {
93.98 + return 0;
93.99 + }
93.100 + if (next >= length)
93.101 + return -1;
93.102 + int n = Math.min(length - next, len);
93.103 + str.getChars(next, next + n, cbuf, off);
93.104 + next += n;
93.105 + return n;
93.106 + }
93.107 + }
93.108 +
93.109 + /**
93.110 + * Skips the specified number of characters in the stream. Returns
93.111 + * the number of characters that were skipped.
93.112 + *
93.113 + * <p>The <code>ns</code> parameter may be negative, even though the
93.114 + * <code>skip</code> method of the {@link Reader} superclass throws
93.115 + * an exception in this case. Negative values of <code>ns</code> cause the
93.116 + * stream to skip backwards. Negative return values indicate a skip
93.117 + * backwards. It is not possible to skip backwards past the beginning of
93.118 + * the string.
93.119 + *
93.120 + * <p>If the entire string has been read or skipped, then this method has
93.121 + * no effect and always returns 0.
93.122 + *
93.123 + * @exception IOException If an I/O error occurs
93.124 + */
93.125 + public long skip(long ns) throws IOException {
93.126 + synchronized (lock) {
93.127 + ensureOpen();
93.128 + if (next >= length)
93.129 + return 0;
93.130 + // Bound skip by beginning and end of the source
93.131 + long n = Math.min(length - next, ns);
93.132 + n = Math.max(-next, n);
93.133 + next += n;
93.134 + return n;
93.135 + }
93.136 + }
93.137 +
93.138 + /**
93.139 + * Tells whether this stream is ready to be read.
93.140 + *
93.141 + * @return True if the next read() is guaranteed not to block for input
93.142 + *
93.143 + * @exception IOException If the stream is closed
93.144 + */
93.145 + public boolean ready() throws IOException {
93.146 + synchronized (lock) {
93.147 + ensureOpen();
93.148 + return true;
93.149 + }
93.150 + }
93.151 +
93.152 + /**
93.153 + * Tells whether this stream supports the mark() operation, which it does.
93.154 + */
93.155 + public boolean markSupported() {
93.156 + return true;
93.157 + }
93.158 +
93.159 + /**
93.160 + * Marks the present position in the stream. Subsequent calls to reset()
93.161 + * will reposition the stream to this point.
93.162 + *
93.163 + * @param readAheadLimit Limit on the number of characters that may be
93.164 + * read while still preserving the mark. Because
93.165 + * the stream's input comes from a string, there
93.166 + * is no actual limit, so this argument must not
93.167 + * be negative, but is otherwise ignored.
93.168 + *
93.169 + * @exception IllegalArgumentException If readAheadLimit is < 0
93.170 + * @exception IOException If an I/O error occurs
93.171 + */
93.172 + public void mark(int readAheadLimit) throws IOException {
93.173 + if (readAheadLimit < 0){
93.174 + throw new IllegalArgumentException("Read-ahead limit < 0");
93.175 + }
93.176 + synchronized (lock) {
93.177 + ensureOpen();
93.178 + mark = next;
93.179 + }
93.180 + }
93.181 +
93.182 + /**
93.183 + * Resets the stream to the most recent mark, or to the beginning of the
93.184 + * string if it has never been marked.
93.185 + *
93.186 + * @exception IOException If an I/O error occurs
93.187 + */
93.188 + public void reset() throws IOException {
93.189 + synchronized (lock) {
93.190 + ensureOpen();
93.191 + next = mark;
93.192 + }
93.193 + }
93.194 +
93.195 + /**
93.196 + * Closes the stream and releases any system resources associated with
93.197 + * it. Once the stream has been closed, further read(),
93.198 + * ready(), mark(), or reset() invocations will throw an IOException.
93.199 + * Closing a previously closed stream has no effect.
93.200 + */
93.201 + public void close() {
93.202 + str = null;
93.203 + }
93.204 +}
94.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
94.2 +++ b/rt/emul/compact/src/main/java/java/io/StringWriter.java Tue Feb 11 13:31:42 2014 +0100
94.3 @@ -0,0 +1,236 @@
94.4 +/*
94.5 + * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
94.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
94.7 + *
94.8 + * This code is free software; you can redistribute it and/or modify it
94.9 + * under the terms of the GNU General Public License version 2 only, as
94.10 + * published by the Free Software Foundation. Oracle designates this
94.11 + * particular file as subject to the "Classpath" exception as provided
94.12 + * by Oracle in the LICENSE file that accompanied this code.
94.13 + *
94.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
94.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
94.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
94.17 + * version 2 for more details (a copy is included in the LICENSE file that
94.18 + * accompanied this code).
94.19 + *
94.20 + * You should have received a copy of the GNU General Public License version
94.21 + * 2 along with this work; if not, write to the Free Software Foundation,
94.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
94.23 + *
94.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
94.25 + * or visit www.oracle.com if you need additional information or have any
94.26 + * questions.
94.27 + */
94.28 +
94.29 +package java.io;
94.30 +
94.31 +
94.32 +/**
94.33 + * A character stream that collects its output in a string buffer, which can
94.34 + * then be used to construct a string.
94.35 + * <p>
94.36 + * Closing a <tt>StringWriter</tt> has no effect. The methods in this class
94.37 + * can be called after the stream has been closed without generating an
94.38 + * <tt>IOException</tt>.
94.39 + *
94.40 + * @author Mark Reinhold
94.41 + * @since JDK1.1
94.42 + */
94.43 +
94.44 +public class StringWriter extends Writer {
94.45 +
94.46 + private StringBuffer buf;
94.47 +
94.48 + /**
94.49 + * Create a new string writer using the default initial string-buffer
94.50 + * size.
94.51 + */
94.52 + public StringWriter() {
94.53 + buf = new StringBuffer();
94.54 + lock = buf;
94.55 + }
94.56 +
94.57 + /**
94.58 + * Create a new string writer using the specified initial string-buffer
94.59 + * size.
94.60 + *
94.61 + * @param initialSize
94.62 + * The number of <tt>char</tt> values that will fit into this buffer
94.63 + * before it is automatically expanded
94.64 + *
94.65 + * @throws IllegalArgumentException
94.66 + * If <tt>initialSize</tt> is negative
94.67 + */
94.68 + public StringWriter(int initialSize) {
94.69 + if (initialSize < 0) {
94.70 + throw new IllegalArgumentException("Negative buffer size");
94.71 + }
94.72 + buf = new StringBuffer(initialSize);
94.73 + lock = buf;
94.74 + }
94.75 +
94.76 + /**
94.77 + * Write a single character.
94.78 + */
94.79 + public void write(int c) {
94.80 + buf.append((char) c);
94.81 + }
94.82 +
94.83 + /**
94.84 + * Write a portion of an array of characters.
94.85 + *
94.86 + * @param cbuf Array of characters
94.87 + * @param off Offset from which to start writing characters
94.88 + * @param len Number of characters to write
94.89 + */
94.90 + public void write(char cbuf[], int off, int len) {
94.91 + if ((off < 0) || (off > cbuf.length) || (len < 0) ||
94.92 + ((off + len) > cbuf.length) || ((off + len) < 0)) {
94.93 + throw new IndexOutOfBoundsException();
94.94 + } else if (len == 0) {
94.95 + return;
94.96 + }
94.97 + buf.append(cbuf, off, len);
94.98 + }
94.99 +
94.100 + /**
94.101 + * Write a string.
94.102 + */
94.103 + public void write(String str) {
94.104 + buf.append(str);
94.105 + }
94.106 +
94.107 + /**
94.108 + * Write a portion of a string.
94.109 + *
94.110 + * @param str String to be written
94.111 + * @param off Offset from which to start writing characters
94.112 + * @param len Number of characters to write
94.113 + */
94.114 + public void write(String str, int off, int len) {
94.115 + buf.append(str.substring(off, off + len));
94.116 + }
94.117 +
94.118 + /**
94.119 + * Appends the specified character sequence to this writer.
94.120 + *
94.121 + * <p> An invocation of this method of the form <tt>out.append(csq)</tt>
94.122 + * behaves in exactly the same way as the invocation
94.123 + *
94.124 + * <pre>
94.125 + * out.write(csq.toString()) </pre>
94.126 + *
94.127 + * <p> Depending on the specification of <tt>toString</tt> for the
94.128 + * character sequence <tt>csq</tt>, the entire sequence may not be
94.129 + * appended. For instance, invoking the <tt>toString</tt> method of a
94.130 + * character buffer will return a subsequence whose content depends upon
94.131 + * the buffer's position and limit.
94.132 + *
94.133 + * @param csq
94.134 + * The character sequence to append. If <tt>csq</tt> is
94.135 + * <tt>null</tt>, then the four characters <tt>"null"</tt> are
94.136 + * appended to this writer.
94.137 + *
94.138 + * @return This writer
94.139 + *
94.140 + * @since 1.5
94.141 + */
94.142 + public StringWriter append(CharSequence csq) {
94.143 + if (csq == null)
94.144 + write("null");
94.145 + else
94.146 + write(csq.toString());
94.147 + return this;
94.148 + }
94.149 +
94.150 + /**
94.151 + * Appends a subsequence of the specified character sequence to this writer.
94.152 + *
94.153 + * <p> An invocation of this method of the form <tt>out.append(csq, start,
94.154 + * end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
94.155 + * exactly the same way as the invocation
94.156 + *
94.157 + * <pre>
94.158 + * out.write(csq.subSequence(start, end).toString()) </pre>
94.159 + *
94.160 + * @param csq
94.161 + * The character sequence from which a subsequence will be
94.162 + * appended. If <tt>csq</tt> is <tt>null</tt>, then characters
94.163 + * will be appended as if <tt>csq</tt> contained the four
94.164 + * characters <tt>"null"</tt>.
94.165 + *
94.166 + * @param start
94.167 + * The index of the first character in the subsequence
94.168 + *
94.169 + * @param end
94.170 + * The index of the character following the last character in the
94.171 + * subsequence
94.172 + *
94.173 + * @return This writer
94.174 + *
94.175 + * @throws IndexOutOfBoundsException
94.176 + * If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
94.177 + * is greater than <tt>end</tt>, or <tt>end</tt> is greater than
94.178 + * <tt>csq.length()</tt>
94.179 + *
94.180 + * @since 1.5
94.181 + */
94.182 + public StringWriter append(CharSequence csq, int start, int end) {
94.183 + CharSequence cs = (csq == null ? "null" : csq);
94.184 + write(cs.subSequence(start, end).toString());
94.185 + return this;
94.186 + }
94.187 +
94.188 + /**
94.189 + * Appends the specified character to this writer.
94.190 + *
94.191 + * <p> An invocation of this method of the form <tt>out.append(c)</tt>
94.192 + * behaves in exactly the same way as the invocation
94.193 + *
94.194 + * <pre>
94.195 + * out.write(c) </pre>
94.196 + *
94.197 + * @param c
94.198 + * The 16-bit character to append
94.199 + *
94.200 + * @return This writer
94.201 + *
94.202 + * @since 1.5
94.203 + */
94.204 + public StringWriter append(char c) {
94.205 + write(c);
94.206 + return this;
94.207 + }
94.208 +
94.209 + /**
94.210 + * Return the buffer's current value as a string.
94.211 + */
94.212 + public String toString() {
94.213 + return buf.toString();
94.214 + }
94.215 +
94.216 + /**
94.217 + * Return the string buffer itself.
94.218 + *
94.219 + * @return StringBuffer holding the current buffer value.
94.220 + */
94.221 + public StringBuffer getBuffer() {
94.222 + return buf;
94.223 + }
94.224 +
94.225 + /**
94.226 + * Flush the stream.
94.227 + */
94.228 + public void flush() {
94.229 + }
94.230 +
94.231 + /**
94.232 + * Closing a <tt>StringWriter</tt> has no effect. The methods in this
94.233 + * class can be called after the stream has been closed without generating
94.234 + * an <tt>IOException</tt>.
94.235 + */
94.236 + public void close() throws IOException {
94.237 + }
94.238 +
94.239 +}
95.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
95.2 +++ b/rt/emul/compact/src/main/java/java/io/SyncFailedException.java Tue Feb 11 13:31:42 2014 +0100
95.3 @@ -0,0 +1,48 @@
95.4 +/*
95.5 + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
95.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
95.7 + *
95.8 + * This code is free software; you can redistribute it and/or modify it
95.9 + * under the terms of the GNU General Public License version 2 only, as
95.10 + * published by the Free Software Foundation. Oracle designates this
95.11 + * particular file as subject to the "Classpath" exception as provided
95.12 + * by Oracle in the LICENSE file that accompanied this code.
95.13 + *
95.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
95.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
95.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
95.17 + * version 2 for more details (a copy is included in the LICENSE file that
95.18 + * accompanied this code).
95.19 + *
95.20 + * You should have received a copy of the GNU General Public License version
95.21 + * 2 along with this work; if not, write to the Free Software Foundation,
95.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
95.23 + *
95.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
95.25 + * or visit www.oracle.com if you need additional information or have any
95.26 + * questions.
95.27 + */
95.28 +
95.29 +package java.io;
95.30 +
95.31 +/**
95.32 + * Signals that a sync operation has failed.
95.33 + *
95.34 + * @author Ken Arnold
95.35 + * @see java.io.FileDescriptor#sync
95.36 + * @see java.io.IOException
95.37 + * @since JDK1.1
95.38 + */
95.39 +public class SyncFailedException extends IOException {
95.40 + private static final long serialVersionUID = -2353342684412443330L;
95.41 +
95.42 + /**
95.43 + * Constructs an SyncFailedException with a detail message.
95.44 + * A detail message is a String that describes this particular exception.
95.45 + *
95.46 + * @param desc a String describing the exception.
95.47 + */
95.48 + public SyncFailedException(String desc) {
95.49 + super(desc);
95.50 + }
95.51 +}
96.1 --- a/rt/emul/compact/src/main/java/java/lang/System.java Tue Feb 11 10:48:24 2014 +0100
96.2 +++ b/rt/emul/compact/src/main/java/java/lang/System.java Tue Feb 11 13:31:42 2014 +0100
96.3 @@ -17,6 +17,13 @@
96.4 */
96.5 package java.lang;
96.6
96.7 +import java.io.BufferedOutputStream;
96.8 +import java.io.ByteArrayInputStream;
96.9 +import java.io.IOException;
96.10 +import java.io.InputStream;
96.11 +import java.io.OutputStream;
96.12 +import java.io.PrintStream;
96.13 +import java.util.Properties;
96.14 import org.apidesign.bck2brwsr.core.JavaScriptBody;
96.15
96.16 /** Poor man's re-implementation of most important System methods.
96.17 @@ -45,13 +52,27 @@
96.18 }
96.19
96.20 public static String getProperty(String name) {
96.21 + if ("os.name".equals(name)) {
96.22 + return userAgent();
96.23 + }
96.24 return null;
96.25 }
96.26
96.27 + @JavaScriptBody(args = {}, body = "return (typeof navigator !== 'undefined') ? navigator.userAgent : 'unknown';")
96.28 + private static native String userAgent();
96.29 +
96.30 public static String getProperty(String key, String def) {
96.31 return def;
96.32 }
96.33
96.34 + public static Properties getProperties() {
96.35 + throw new SecurityException();
96.36 + }
96.37 +
96.38 + public static void setProperties(Properties p) {
96.39 + throw new SecurityException();
96.40 + }
96.41 +
96.42 /**
96.43 * Returns the system-dependent line separator string. It always
96.44 * returns the same value - the initial value of the {@linkplain
96.45 @@ -67,4 +88,49 @@
96.46 @JavaScriptBody(args = { "exitCode" }, body = "window.close();")
96.47 public static void exit(int exitCode) {
96.48 }
96.49 +
96.50 + public final static InputStream in;
96.51 +
96.52 + public final static PrintStream out;
96.53 +
96.54 + public final static PrintStream err;
96.55 +
96.56 + public static void setOut(PrintStream out) {
96.57 + throw new SecurityException();
96.58 + }
96.59 +
96.60 + public static void setIn(InputStream in) {
96.61 + throw new SecurityException();
96.62 + }
96.63 +
96.64 + public static void setErr(PrintStream err) {
96.65 + throw new SecurityException();
96.66 + }
96.67 +
96.68 + static {
96.69 + in = new ByteArrayInputStream(new byte[0]);
96.70 + out = new PrintStream(new BufferedOutputStream(new SystemStream("log")));
96.71 + err = new PrintStream(new BufferedOutputStream(new SystemStream("warn")));
96.72 + }
96.73 +
96.74 + private static final class SystemStream extends OutputStream {
96.75 + private final String method;
96.76 +
96.77 + public SystemStream(String method) {
96.78 + this.method = method;
96.79 + }
96.80 +
96.81 + @Override
96.82 + public void write(byte b[], int off, int len) throws IOException {
96.83 + write(method, new String(b, off, len, "UTF-8"));
96.84 + }
96.85 +
96.86 + @JavaScriptBody(args = { "method", "b" }, body = "if (typeof console !== 'undefined') console[method](b.toString());")
96.87 + private static native void write(String method, String b);
96.88 +
96.89 + @Override
96.90 + public void write(int b) throws IOException {
96.91 + write(new byte[] { (byte)b });
96.92 + }
96.93 + } // end of SystemStream
96.94 }
97.1 --- a/rt/emul/compact/src/main/java/java/lang/Thread.java Tue Feb 11 10:48:24 2014 +0100
97.2 +++ b/rt/emul/compact/src/main/java/java/lang/Thread.java Tue Feb 11 13:31:42 2014 +0100
97.3 @@ -1165,7 +1165,7 @@
97.4 * @since 1.2
97.5 */
97.6 public ClassLoader getContextClassLoader() {
97.7 - return null;
97.8 + return ClassLoader.getSystemClassLoader();
97.9 }
97.10
97.11 /**
97.12 @@ -1191,6 +1191,9 @@
97.13 * @since 1.2
97.14 */
97.15 public void setContextClassLoader(ClassLoader cl) {
97.16 + if (cl == ClassLoader.getSystemClassLoader()) {
97.17 + return;
97.18 + }
97.19 throw new SecurityException();
97.20 }
97.21
98.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
98.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/Annotation.java Tue Feb 11 13:31:42 2014 +0100
98.3 @@ -0,0 +1,131 @@
98.4 +/*
98.5 + * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
98.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
98.7 + *
98.8 + * This code is free software; you can redistribute it and/or modify it
98.9 + * under the terms of the GNU General Public License version 2 only, as
98.10 + * published by the Free Software Foundation. Oracle designates this
98.11 + * particular file as subject to the "Classpath" exception as provided
98.12 + * by Oracle in the LICENSE file that accompanied this code.
98.13 + *
98.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
98.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
98.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
98.17 + * version 2 for more details (a copy is included in the LICENSE file that
98.18 + * accompanied this code).
98.19 + *
98.20 + * You should have received a copy of the GNU General Public License version
98.21 + * 2 along with this work; if not, write to the Free Software Foundation,
98.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
98.23 + *
98.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
98.25 + * or visit www.oracle.com if you need additional information or have any
98.26 + * questions.
98.27 + */
98.28 +
98.29 +package java.lang.annotation;
98.30 +
98.31 +/**
98.32 + * The common interface extended by all annotation types. Note that an
98.33 + * interface that manually extends this one does <i>not</i> define
98.34 + * an annotation type. Also note that this interface does not itself
98.35 + * define an annotation type.
98.36 + *
98.37 + * More information about annotation types can be found in section 9.6 of
98.38 + * <cite>The Java™ Language Specification</cite>.
98.39 + *
98.40 + * @author Josh Bloch
98.41 + * @since 1.5
98.42 + */
98.43 +public interface Annotation {
98.44 + /**
98.45 + * Returns true if the specified object represents an annotation
98.46 + * that is logically equivalent to this one. In other words,
98.47 + * returns true if the specified object is an instance of the same
98.48 + * annotation type as this instance, all of whose members are equal
98.49 + * to the corresponding member of this annotation, as defined below:
98.50 + * <ul>
98.51 + * <li>Two corresponding primitive typed members whose values are
98.52 + * <tt>x</tt> and <tt>y</tt> are considered equal if <tt>x == y</tt>,
98.53 + * unless their type is <tt>float</tt> or <tt>double</tt>.
98.54 + *
98.55 + * <li>Two corresponding <tt>float</tt> members whose values
98.56 + * are <tt>x</tt> and <tt>y</tt> are considered equal if
98.57 + * <tt>Float.valueOf(x).equals(Float.valueOf(y))</tt>.
98.58 + * (Unlike the <tt>==</tt> operator, NaN is considered equal
98.59 + * to itself, and <tt>0.0f</tt> unequal to <tt>-0.0f</tt>.)
98.60 + *
98.61 + * <li>Two corresponding <tt>double</tt> members whose values
98.62 + * are <tt>x</tt> and <tt>y</tt> are considered equal if
98.63 + * <tt>Double.valueOf(x).equals(Double.valueOf(y))</tt>.
98.64 + * (Unlike the <tt>==</tt> operator, NaN is considered equal
98.65 + * to itself, and <tt>0.0</tt> unequal to <tt>-0.0</tt>.)
98.66 + *
98.67 + * <li>Two corresponding <tt>String</tt>, <tt>Class</tt>, enum, or
98.68 + * annotation typed members whose values are <tt>x</tt> and <tt>y</tt>
98.69 + * are considered equal if <tt>x.equals(y)</tt>. (Note that this
98.70 + * definition is recursive for annotation typed members.)
98.71 + *
98.72 + * <li>Two corresponding array typed members <tt>x</tt> and <tt>y</tt>
98.73 + * are considered equal if <tt>Arrays.equals(x, y)</tt>, for the
98.74 + * appropriate overloading of {@link java.util.Arrays#equals}.
98.75 + * </ul>
98.76 + *
98.77 + * @return true if the specified object represents an annotation
98.78 + * that is logically equivalent to this one, otherwise false
98.79 + */
98.80 + boolean equals(Object obj);
98.81 +
98.82 + /**
98.83 + * Returns the hash code of this annotation, as defined below:
98.84 + *
98.85 + * <p>The hash code of an annotation is the sum of the hash codes
98.86 + * of its members (including those with default values), as defined
98.87 + * below:
98.88 + *
98.89 + * The hash code of an annotation member is (127 times the hash code
98.90 + * of the member-name as computed by {@link String#hashCode()}) XOR
98.91 + * the hash code of the member-value, as defined below:
98.92 + *
98.93 + * <p>The hash code of a member-value depends on its type:
98.94 + * <ul>
98.95 + * <li>The hash code of a primitive value <tt><i>v</i></tt> is equal to
98.96 + * <tt><i>WrapperType</i>.valueOf(<i>v</i>).hashCode()</tt>, where
98.97 + * <tt><i>WrapperType</i></tt> is the wrapper type corresponding
98.98 + * to the primitive type of <tt><i>v</i></tt> ({@link Byte},
98.99 + * {@link Character}, {@link Double}, {@link Float}, {@link Integer},
98.100 + * {@link Long}, {@link Short}, or {@link Boolean}).
98.101 + *
98.102 + * <li>The hash code of a string, enum, class, or annotation member-value
98.103 + I <tt><i>v</i></tt> is computed as by calling
98.104 + * <tt><i>v</i>.hashCode()</tt>. (In the case of annotation
98.105 + * member values, this is a recursive definition.)
98.106 + *
98.107 + * <li>The hash code of an array member-value is computed by calling
98.108 + * the appropriate overloading of
98.109 + * {@link java.util.Arrays#hashCode(long[]) Arrays.hashCode}
98.110 + * on the value. (There is one overloading for each primitive
98.111 + * type, and one for object reference types.)
98.112 + * </ul>
98.113 + *
98.114 + * @return the hash code of this annotation
98.115 + */
98.116 + int hashCode();
98.117 +
98.118 + /**
98.119 + * Returns a string representation of this annotation. The details
98.120 + * of the representation are implementation-dependent, but the following
98.121 + * may be regarded as typical:
98.122 + * <pre>
98.123 + * @com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
98.124 + * </pre>
98.125 + *
98.126 + * @return a string representation of this annotation
98.127 + */
98.128 + String toString();
98.129 +
98.130 + /**
98.131 + * Returns the annotation type of this annotation.
98.132 + */
98.133 + Class<? extends Annotation> annotationType();
98.134 +}
99.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
99.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/AnnotationFormatError.java Tue Feb 11 13:31:42 2014 +0100
99.3 @@ -0,0 +1,79 @@
99.4 +/*
99.5 + * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
99.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
99.7 + *
99.8 + * This code is free software; you can redistribute it and/or modify it
99.9 + * under the terms of the GNU General Public License version 2 only, as
99.10 + * published by the Free Software Foundation. Oracle designates this
99.11 + * particular file as subject to the "Classpath" exception as provided
99.12 + * by Oracle in the LICENSE file that accompanied this code.
99.13 + *
99.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
99.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
99.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
99.17 + * version 2 for more details (a copy is included in the LICENSE file that
99.18 + * accompanied this code).
99.19 + *
99.20 + * You should have received a copy of the GNU General Public License version
99.21 + * 2 along with this work; if not, write to the Free Software Foundation,
99.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
99.23 + *
99.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
99.25 + * or visit www.oracle.com if you need additional information or have any
99.26 + * questions.
99.27 + */
99.28 +
99.29 +package java.lang.annotation;
99.30 +
99.31 +/**
99.32 + * Thrown when the annotation parser attempts to read an annotation
99.33 + * from a class file and determines that the annotation is malformed.
99.34 + * This error can be thrown by the {@linkplain
99.35 + * java.lang.reflect.AnnotatedElement API used to read annotations
99.36 + * reflectively}.
99.37 + *
99.38 + * @author Josh Bloch
99.39 + * @see java.lang.reflect.AnnotatedElement
99.40 + * @since 1.5
99.41 + */
99.42 +public class AnnotationFormatError extends Error {
99.43 + private static final long serialVersionUID = -4256701562333669892L;
99.44 +
99.45 + /**
99.46 + * Constructs a new <tt>AnnotationFormatError</tt> with the specified
99.47 + * detail message.
99.48 + *
99.49 + * @param message the detail message.
99.50 + */
99.51 + public AnnotationFormatError(String message) {
99.52 + super(message);
99.53 + }
99.54 +
99.55 + /**
99.56 + * Constructs a new <tt>AnnotationFormatError</tt> with the specified
99.57 + * detail message and cause. Note that the detail message associated
99.58 + * with <code>cause</code> is <i>not</i> automatically incorporated in
99.59 + * this error's detail message.
99.60 + *
99.61 + * @param message the detail message
99.62 + * @param cause the cause (A <tt>null</tt> value is permitted, and
99.63 + * indicates that the cause is nonexistent or unknown.)
99.64 + */
99.65 + public AnnotationFormatError(String message, Throwable cause) {
99.66 + super(message, cause);
99.67 + }
99.68 +
99.69 +
99.70 + /**
99.71 + * Constructs a new <tt>AnnotationFormatError</tt> with the specified
99.72 + * cause and a detail message of
99.73 + * <tt>(cause == null ? null : cause.toString())</tt> (which
99.74 + * typically contains the class and detail message of <tt>cause</tt>).
99.75 + *
99.76 + * @param cause the cause (A <tt>null</tt> value is permitted, and
99.77 + * indicates that the cause is nonexistent or unknown.)
99.78 + */
99.79 + public AnnotationFormatError(Throwable cause) {
99.80 + super(cause);
99.81 + }
99.82 +}
100.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
100.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java Tue Feb 11 13:31:42 2014 +0100
100.3 @@ -0,0 +1,91 @@
100.4 +/*
100.5 + * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
100.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
100.7 + *
100.8 + * This code is free software; you can redistribute it and/or modify it
100.9 + * under the terms of the GNU General Public License version 2 only, as
100.10 + * published by the Free Software Foundation. Oracle designates this
100.11 + * particular file as subject to the "Classpath" exception as provided
100.12 + * by Oracle in the LICENSE file that accompanied this code.
100.13 + *
100.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
100.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
100.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
100.17 + * version 2 for more details (a copy is included in the LICENSE file that
100.18 + * accompanied this code).
100.19 + *
100.20 + * You should have received a copy of the GNU General Public License version
100.21 + * 2 along with this work; if not, write to the Free Software Foundation,
100.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
100.23 + *
100.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
100.25 + * or visit www.oracle.com if you need additional information or have any
100.26 + * questions.
100.27 + */
100.28 +
100.29 +package java.lang.annotation;
100.30 +import java.lang.reflect.Method;
100.31 +
100.32 +/**
100.33 + * Thrown to indicate that a program has attempted to access an element of
100.34 + * an annotation whose type has changed after the annotation was compiled
100.35 + * (or serialized).
100.36 + * This exception can be thrown by the {@linkplain
100.37 + * java.lang.reflect.AnnotatedElement API used to read annotations
100.38 + * reflectively}.
100.39 + *
100.40 + * @author Josh Bloch
100.41 + * @see java.lang.reflect.AnnotatedElement
100.42 + * @since 1.5
100.43 + */
100.44 +public class AnnotationTypeMismatchException extends RuntimeException {
100.45 + private static final long serialVersionUID = 8125925355765570191L;
100.46 +
100.47 + /**
100.48 + * The <tt>Method</tt> object for the annotation element.
100.49 + */
100.50 + private final Method element;
100.51 +
100.52 + /**
100.53 + * The (erroneous) type of data found in the annotation. This string
100.54 + * may, but is not required to, contain the value as well. The exact
100.55 + * format of the string is unspecified.
100.56 + */
100.57 + private final String foundType;
100.58 +
100.59 + /**
100.60 + * Constructs an AnnotationTypeMismatchException for the specified
100.61 + * annotation type element and found data type.
100.62 + *
100.63 + * @param element the <tt>Method</tt> object for the annotation element
100.64 + * @param foundType the (erroneous) type of data found in the annotation.
100.65 + * This string may, but is not required to, contain the value
100.66 + * as well. The exact format of the string is unspecified.
100.67 + */
100.68 + public AnnotationTypeMismatchException(Method element, String foundType) {
100.69 + super("Incorrectly typed data found for annotation element " + element
100.70 + + " (Found data of type " + foundType + ")");
100.71 + this.element = element;
100.72 + this.foundType = foundType;
100.73 + }
100.74 +
100.75 + /**
100.76 + * Returns the <tt>Method</tt> object for the incorrectly typed element.
100.77 + *
100.78 + * @return the <tt>Method</tt> object for the incorrectly typed element
100.79 + */
100.80 + public Method element() {
100.81 + return this.element;
100.82 + }
100.83 +
100.84 + /**
100.85 + * Returns the type of data found in the incorrectly typed element.
100.86 + * The returned string may, but is not required to, contain the value
100.87 + * as well. The exact format of the string is unspecified.
100.88 + *
100.89 + * @return the type of data found in the incorrectly typed element
100.90 + */
100.91 + public String foundType() {
100.92 + return this.foundType;
100.93 + }
100.94 +}
101.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
101.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/Documented.java Tue Feb 11 13:31:42 2014 +0100
101.3 @@ -0,0 +1,43 @@
101.4 +/*
101.5 + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
101.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
101.7 + *
101.8 + * This code is free software; you can redistribute it and/or modify it
101.9 + * under the terms of the GNU General Public License version 2 only, as
101.10 + * published by the Free Software Foundation. Oracle designates this
101.11 + * particular file as subject to the "Classpath" exception as provided
101.12 + * by Oracle in the LICENSE file that accompanied this code.
101.13 + *
101.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
101.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
101.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
101.17 + * version 2 for more details (a copy is included in the LICENSE file that
101.18 + * accompanied this code).
101.19 + *
101.20 + * You should have received a copy of the GNU General Public License version
101.21 + * 2 along with this work; if not, write to the Free Software Foundation,
101.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
101.23 + *
101.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
101.25 + * or visit www.oracle.com if you need additional information or have any
101.26 + * questions.
101.27 + */
101.28 +
101.29 +package java.lang.annotation;
101.30 +
101.31 +/**
101.32 + * Indicates that annotations with a type are to be documented by javadoc
101.33 + * and similar tools by default. This type should be used to annotate the
101.34 + * declarations of types whose annotations affect the use of annotated
101.35 + * elements by their clients. If a type declaration is annotated with
101.36 + * Documented, its annotations become part of the public API
101.37 + * of the annotated elements.
101.38 + *
101.39 + * @author Joshua Bloch
101.40 + * @since 1.5
101.41 + */
101.42 +@Documented
101.43 +@Retention(RetentionPolicy.RUNTIME)
101.44 +@Target(ElementType.ANNOTATION_TYPE)
101.45 +public @interface Documented {
101.46 +}
102.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
102.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/ElementType.java Tue Feb 11 13:31:42 2014 +0100
102.3 @@ -0,0 +1,63 @@
102.4 +/*
102.5 + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
102.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
102.7 + *
102.8 + * This code is free software; you can redistribute it and/or modify it
102.9 + * under the terms of the GNU General Public License version 2 only, as
102.10 + * published by the Free Software Foundation. Oracle designates this
102.11 + * particular file as subject to the "Classpath" exception as provided
102.12 + * by Oracle in the LICENSE file that accompanied this code.
102.13 + *
102.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
102.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
102.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
102.17 + * version 2 for more details (a copy is included in the LICENSE file that
102.18 + * accompanied this code).
102.19 + *
102.20 + * You should have received a copy of the GNU General Public License version
102.21 + * 2 along with this work; if not, write to the Free Software Foundation,
102.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
102.23 + *
102.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
102.25 + * or visit www.oracle.com if you need additional information or have any
102.26 + * questions.
102.27 + */
102.28 +
102.29 +package java.lang.annotation;
102.30 +
102.31 +/**
102.32 + * A program element type. The constants of this enumerated type
102.33 + * provide a simple classification of the declared elements in a
102.34 + * Java program.
102.35 + *
102.36 + * <p>These constants are used with the {@link Target} meta-annotation type
102.37 + * to specify where it is legal to use an annotation type.
102.38 + *
102.39 + * @author Joshua Bloch
102.40 + * @since 1.5
102.41 + */
102.42 +public enum ElementType {
102.43 + /** Class, interface (including annotation type), or enum declaration */
102.44 + TYPE,
102.45 +
102.46 + /** Field declaration (includes enum constants) */
102.47 + FIELD,
102.48 +
102.49 + /** Method declaration */
102.50 + METHOD,
102.51 +
102.52 + /** Parameter declaration */
102.53 + PARAMETER,
102.54 +
102.55 + /** Constructor declaration */
102.56 + CONSTRUCTOR,
102.57 +
102.58 + /** Local variable declaration */
102.59 + LOCAL_VARIABLE,
102.60 +
102.61 + /** Annotation type declaration */
102.62 + ANNOTATION_TYPE,
102.63 +
102.64 + /** Package declaration */
102.65 + PACKAGE
102.66 +}
103.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
103.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/IncompleteAnnotationException.java Tue Feb 11 13:31:42 2014 +0100
103.3 @@ -0,0 +1,83 @@
103.4 +/*
103.5 + * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
103.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
103.7 + *
103.8 + * This code is free software; you can redistribute it and/or modify it
103.9 + * under the terms of the GNU General Public License version 2 only, as
103.10 + * published by the Free Software Foundation. Oracle designates this
103.11 + * particular file as subject to the "Classpath" exception as provided
103.12 + * by Oracle in the LICENSE file that accompanied this code.
103.13 + *
103.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
103.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
103.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
103.17 + * version 2 for more details (a copy is included in the LICENSE file that
103.18 + * accompanied this code).
103.19 + *
103.20 + * You should have received a copy of the GNU General Public License version
103.21 + * 2 along with this work; if not, write to the Free Software Foundation,
103.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
103.23 + *
103.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
103.25 + * or visit www.oracle.com if you need additional information or have any
103.26 + * questions.
103.27 + */
103.28 +
103.29 +package java.lang.annotation;
103.30 +
103.31 +/**
103.32 + * Thrown to indicate that a program has attempted to access an element of
103.33 + * an annotation type that was added to the annotation type definition after
103.34 + * the annotation was compiled (or serialized). This exception will not be
103.35 + * thrown if the new element has a default value.
103.36 + * This exception can be thrown by the {@linkplain
103.37 + * java.lang.reflect.AnnotatedElement API used to read annotations
103.38 + * reflectively}.
103.39 + *
103.40 + * @author Josh Bloch
103.41 + * @see java.lang.reflect.AnnotatedElement
103.42 + * @since 1.5
103.43 + */
103.44 +public class IncompleteAnnotationException extends RuntimeException {
103.45 + private static final long serialVersionUID = 8445097402741811912L;
103.46 +
103.47 + private Class annotationType;
103.48 + private String elementName;
103.49 +
103.50 +
103.51 + /**
103.52 + * Constructs an IncompleteAnnotationException to indicate that
103.53 + * the named element was missing from the specified annotation type.
103.54 + *
103.55 + * @param annotationType the Class object for the annotation type
103.56 + * @param elementName the name of the missing element
103.57 + */
103.58 + public IncompleteAnnotationException(
103.59 + Class<? extends Annotation> annotationType,
103.60 + String elementName) {
103.61 + super(annotationType.getName() + " missing element " + elementName);
103.62 +
103.63 + this.annotationType = annotationType;
103.64 + this.elementName = elementName;
103.65 + }
103.66 +
103.67 + /**
103.68 + * Returns the Class object for the annotation type with the
103.69 + * missing element.
103.70 + *
103.71 + * @return the Class object for the annotation type with the
103.72 + * missing element
103.73 + */
103.74 + public Class<? extends Annotation> annotationType() {
103.75 + return annotationType;
103.76 + }
103.77 +
103.78 + /**
103.79 + * Returns the name of the missing element.
103.80 + *
103.81 + * @return the name of the missing element
103.82 + */
103.83 + public String elementName() {
103.84 + return elementName;
103.85 + }
103.86 +}
104.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
104.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/Inherited.java Tue Feb 11 13:31:42 2014 +0100
104.3 @@ -0,0 +1,52 @@
104.4 +/*
104.5 + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
104.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
104.7 + *
104.8 + * This code is free software; you can redistribute it and/or modify it
104.9 + * under the terms of the GNU General Public License version 2 only, as
104.10 + * published by the Free Software Foundation. Oracle designates this
104.11 + * particular file as subject to the "Classpath" exception as provided
104.12 + * by Oracle in the LICENSE file that accompanied this code.
104.13 + *
104.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
104.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
104.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
104.17 + * version 2 for more details (a copy is included in the LICENSE file that
104.18 + * accompanied this code).
104.19 + *
104.20 + * You should have received a copy of the GNU General Public License version
104.21 + * 2 along with this work; if not, write to the Free Software Foundation,
104.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
104.23 + *
104.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
104.25 + * or visit www.oracle.com if you need additional information or have any
104.26 + * questions.
104.27 + */
104.28 +
104.29 +package java.lang.annotation;
104.30 +
104.31 +/**
104.32 + * Indicates that an annotation type is automatically inherited. If
104.33 + * an Inherited meta-annotation is present on an annotation type
104.34 + * declaration, and the user queries the annotation type on a class
104.35 + * declaration, and the class declaration has no annotation for this type,
104.36 + * then the class's superclass will automatically be queried for the
104.37 + * annotation type. This process will be repeated until an annotation for this
104.38 + * type is found, or the top of the class hierarchy (Object)
104.39 + * is reached. If no superclass has an annotation for this type, then
104.40 + * the query will indicate that the class in question has no such annotation.
104.41 + *
104.42 + * <p>Note that this meta-annotation type has no effect if the annotated
104.43 + * type is used to annotate anything other than a class. Note also
104.44 + * that this meta-annotation only causes annotations to be inherited
104.45 + * from superclasses; annotations on implemented interfaces have no
104.46 + * effect.
104.47 + *
104.48 + * @author Joshua Bloch
104.49 + * @since 1.5
104.50 + */
104.51 +@Documented
104.52 +@Retention(RetentionPolicy.RUNTIME)
104.53 +@Target(ElementType.ANNOTATION_TYPE)
104.54 +public @interface Inherited {
104.55 +}
105.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
105.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/Retention.java Tue Feb 11 13:31:42 2014 +0100
105.3 @@ -0,0 +1,47 @@
105.4 +/*
105.5 + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
105.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
105.7 + *
105.8 + * This code is free software; you can redistribute it and/or modify it
105.9 + * under the terms of the GNU General Public License version 2 only, as
105.10 + * published by the Free Software Foundation. Oracle designates this
105.11 + * particular file as subject to the "Classpath" exception as provided
105.12 + * by Oracle in the LICENSE file that accompanied this code.
105.13 + *
105.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
105.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
105.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
105.17 + * version 2 for more details (a copy is included in the LICENSE file that
105.18 + * accompanied this code).
105.19 + *
105.20 + * You should have received a copy of the GNU General Public License version
105.21 + * 2 along with this work; if not, write to the Free Software Foundation,
105.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
105.23 + *
105.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
105.25 + * or visit www.oracle.com if you need additional information or have any
105.26 + * questions.
105.27 + */
105.28 +
105.29 +package java.lang.annotation;
105.30 +
105.31 +/**
105.32 + * Indicates how long annotations with the annotated type are to
105.33 + * be retained. If no Retention annotation is present on
105.34 + * an annotation type declaration, the retention policy defaults to
105.35 + * {@code RetentionPolicy.CLASS}.
105.36 + *
105.37 + * <p>A Retention meta-annotation has effect only if the
105.38 + * meta-annotated type is used directly for annotation. It has no
105.39 + * effect if the meta-annotated type is used as a member type in
105.40 + * another annotation type.
105.41 + *
105.42 + * @author Joshua Bloch
105.43 + * @since 1.5
105.44 + */
105.45 +@Documented
105.46 +@Retention(RetentionPolicy.RUNTIME)
105.47 +@Target(ElementType.ANNOTATION_TYPE)
105.48 +public @interface Retention {
105.49 + RetentionPolicy value();
105.50 +}
106.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
106.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/RetentionPolicy.java Tue Feb 11 13:31:42 2014 +0100
106.3 @@ -0,0 +1,57 @@
106.4 +/*
106.5 + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
106.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
106.7 + *
106.8 + * This code is free software; you can redistribute it and/or modify it
106.9 + * under the terms of the GNU General Public License version 2 only, as
106.10 + * published by the Free Software Foundation. Oracle designates this
106.11 + * particular file as subject to the "Classpath" exception as provided
106.12 + * by Oracle in the LICENSE file that accompanied this code.
106.13 + *
106.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
106.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
106.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
106.17 + * version 2 for more details (a copy is included in the LICENSE file that
106.18 + * accompanied this code).
106.19 + *
106.20 + * You should have received a copy of the GNU General Public License version
106.21 + * 2 along with this work; if not, write to the Free Software Foundation,
106.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
106.23 + *
106.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
106.25 + * or visit www.oracle.com if you need additional information or have any
106.26 + * questions.
106.27 + */
106.28 +
106.29 +package java.lang.annotation;
106.30 +
106.31 +/**
106.32 + * Annotation retention policy. The constants of this enumerated type
106.33 + * describe the various policies for retaining annotations. They are used
106.34 + * in conjunction with the {@link Retention} meta-annotation type to specify
106.35 + * how long annotations are to be retained.
106.36 + *
106.37 + * @author Joshua Bloch
106.38 + * @since 1.5
106.39 + */
106.40 +public enum RetentionPolicy {
106.41 + /**
106.42 + * Annotations are to be discarded by the compiler.
106.43 + */
106.44 + SOURCE,
106.45 +
106.46 + /**
106.47 + * Annotations are to be recorded in the class file by the compiler
106.48 + * but need not be retained by the VM at run time. This is the default
106.49 + * behavior.
106.50 + */
106.51 + CLASS,
106.52 +
106.53 + /**
106.54 + * Annotations are to be recorded in the class file by the compiler and
106.55 + * retained by the VM at run time, so they may be read reflectively.
106.56 + *
106.57 + * @see java.lang.reflect.AnnotatedElement
106.58 + */
106.59 + RUNTIME
106.60 +}
107.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
107.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/Target.java Tue Feb 11 13:31:42 2014 +0100
107.3 @@ -0,0 +1,68 @@
107.4 +/*
107.5 + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
107.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
107.7 + *
107.8 + * This code is free software; you can redistribute it and/or modify it
107.9 + * under the terms of the GNU General Public License version 2 only, as
107.10 + * published by the Free Software Foundation. Oracle designates this
107.11 + * particular file as subject to the "Classpath" exception as provided
107.12 + * by Oracle in the LICENSE file that accompanied this code.
107.13 + *
107.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
107.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
107.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
107.17 + * version 2 for more details (a copy is included in the LICENSE file that
107.18 + * accompanied this code).
107.19 + *
107.20 + * You should have received a copy of the GNU General Public License version
107.21 + * 2 along with this work; if not, write to the Free Software Foundation,
107.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
107.23 + *
107.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
107.25 + * or visit www.oracle.com if you need additional information or have any
107.26 + * questions.
107.27 + */
107.28 +
107.29 +package java.lang.annotation;
107.30 +
107.31 +/**
107.32 + * Indicates the kinds of program element to which an annotation type
107.33 + * is applicable. If a Target meta-annotation is not present on an
107.34 + * annotation type declaration, the declared type may be used on any
107.35 + * program element. If such a meta-annotation is present, the compiler
107.36 + * will enforce the specified usage restriction.
107.37 + *
107.38 + * For example, this meta-annotation indicates that the declared type is
107.39 + * itself a meta-annotation type. It can only be used on annotation type
107.40 + * declarations:
107.41 + * <pre>
107.42 + * @Target(ElementType.ANNOTATION_TYPE)
107.43 + * public @interface MetaAnnotationType {
107.44 + * ...
107.45 + * }
107.46 + * </pre>
107.47 + * This meta-annotation indicates that the declared type is intended solely
107.48 + * for use as a member type in complex annotation type declarations. It
107.49 + * cannot be used to annotate anything directly:
107.50 + * <pre>
107.51 + * @Target({})
107.52 + * public @interface MemberType {
107.53 + * ...
107.54 + * }
107.55 + * </pre>
107.56 + * It is a compile-time error for a single ElementType constant to
107.57 + * appear more than once in a Target annotation. For example, the
107.58 + * following meta-annotation is illegal:
107.59 + * <pre>
107.60 + * @Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
107.61 + * public @interface Bogus {
107.62 + * ...
107.63 + * }
107.64 + * </pre>
107.65 + */
107.66 +@Documented
107.67 +@Retention(RetentionPolicy.RUNTIME)
107.68 +@Target(ElementType.ANNOTATION_TYPE)
107.69 +public @interface Target {
107.70 + ElementType[] value();
107.71 +}
108.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
108.2 +++ b/rt/emul/compact/src/main/java/java/lang/annotation/package-info.java Tue Feb 11 13:31:42 2014 +0100
108.3 @@ -0,0 +1,33 @@
108.4 +/*
108.5 + * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
108.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
108.7 + *
108.8 + * This code is free software; you can redistribute it and/or modify it
108.9 + * under the terms of the GNU General Public License version 2 only, as
108.10 + * published by the Free Software Foundation. Oracle designates this
108.11 + * particular file as subject to the "Classpath" exception as provided
108.12 + * by Oracle in the LICENSE file that accompanied this code.
108.13 + *
108.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
108.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
108.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
108.17 + * version 2 for more details (a copy is included in the LICENSE file that
108.18 + * accompanied this code).
108.19 + *
108.20 + * You should have received a copy of the GNU General Public License version
108.21 + * 2 along with this work; if not, write to the Free Software Foundation,
108.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
108.23 + *
108.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
108.25 + * or visit www.oracle.com if you need additional information or have any
108.26 + * questions.
108.27 + */
108.28 +
108.29 +/**
108.30 + * Provides library support for the Java programming language
108.31 + * annotation facility.
108.32 + *
108.33 + * @author Josh Bloch
108.34 + * @since 1.5
108.35 + */
108.36 +package java.lang.annotation;
109.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
109.2 +++ b/rt/emul/compact/src/main/java/java/net/URLConnection.java Tue Feb 11 13:31:42 2014 +0100
109.3 @@ -0,0 +1,2314 @@
109.4 +/*
109.5 + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
109.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
109.7 + *
109.8 + * This code is free software; you can redistribute it and/or modify it
109.9 + * under the terms of the GNU General Public License version 2 only, as
109.10 + * published by the Free Software Foundation. Oracle designates this
109.11 + * particular file as subject to the "Classpath" exception as provided
109.12 + * by Oracle in the LICENSE file that accompanied this code.
109.13 + *
109.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
109.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
109.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
109.17 + * version 2 for more details (a copy is included in the LICENSE file that
109.18 + * accompanied this code).
109.19 + *
109.20 + * You should have received a copy of the GNU General Public License version
109.21 + * 2 along with this work; if not, write to the Free Software Foundation,
109.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
109.23 + *
109.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
109.25 + * or visit www.oracle.com if you need additional information or have any
109.26 + * questions.
109.27 + */
109.28 +
109.29 +package java.net;
109.30 +
109.31 +import java.io.IOException;
109.32 +import java.io.InputStream;
109.33 +import java.io.OutputStream;
109.34 +import java.io.PrintStream;
109.35 +import java.util.ArrayList;
109.36 +import java.util.Date;
109.37 +import java.util.StringTokenizer;
109.38 +import java.util.Collections;
109.39 +import java.util.HashMap;
109.40 +import java.util.Hashtable;
109.41 +import java.util.Iterator;
109.42 +import java.util.Map;
109.43 +import java.util.List;
109.44 +import java.util.NoSuchElementException;
109.45 +
109.46 +/**
109.47 + * The abstract class <code>URLConnection</code> is the superclass
109.48 + * of all classes that represent a communications link between the
109.49 + * application and a URL. Instances of this class can be used both to
109.50 + * read from and to write to the resource referenced by the URL. In
109.51 + * general, creating a connection to a URL is a multistep process:
109.52 + * <p>
109.53 + * <center><table border=2 summary="Describes the process of creating a connection to a URL: openConnection() and connect() over time.">
109.54 + * <tr><th><code>openConnection()</code></th>
109.55 + * <th><code>connect()</code></th></tr>
109.56 + * <tr><td>Manipulate parameters that affect the connection to the remote
109.57 + * resource.</td>
109.58 + * <td>Interact with the resource; query header fields and
109.59 + * contents.</td></tr>
109.60 + * </table>
109.61 + * ---------------------------->
109.62 + * <br>time</center>
109.63 + *
109.64 + * <ol>
109.65 + * <li>The connection object is created by invoking the
109.66 + * <code>openConnection</code> method on a URL.
109.67 + * <li>The setup parameters and general request properties are manipulated.
109.68 + * <li>The actual connection to the remote object is made, using the
109.69 + * <code>connect</code> method.
109.70 + * <li>The remote object becomes available. The header fields and the contents
109.71 + * of the remote object can be accessed.
109.72 + * </ol>
109.73 + * <p>
109.74 + * The setup parameters are modified using the following methods:
109.75 + * <ul>
109.76 + * <li><code>setAllowUserInteraction</code>
109.77 + * <li><code>setDoInput</code>
109.78 + * <li><code>setDoOutput</code>
109.79 + * <li><code>setIfModifiedSince</code>
109.80 + * <li><code>setUseCaches</code>
109.81 + * </ul>
109.82 + * <p>
109.83 + * and the general request properties are modified using the method:
109.84 + * <ul>
109.85 + * <li><code>setRequestProperty</code>
109.86 + * </ul>
109.87 + * <p>
109.88 + * Default values for the <code>AllowUserInteraction</code> and
109.89 + * <code>UseCaches</code> parameters can be set using the methods
109.90 + * <code>setDefaultAllowUserInteraction</code> and
109.91 + * <code>setDefaultUseCaches</code>.
109.92 + * <p>
109.93 + * Each of the above <code>set</code> methods has a corresponding
109.94 + * <code>get</code> method to retrieve the value of the parameter or
109.95 + * general request property. The specific parameters and general
109.96 + * request properties that are applicable are protocol specific.
109.97 + * <p>
109.98 + * The following methods are used to access the header fields and
109.99 + * the contents after the connection is made to the remote object:
109.100 + * <ul>
109.101 + * <li><code>getContent</code>
109.102 + * <li><code>getHeaderField</code>
109.103 + * <li><code>getInputStream</code>
109.104 + * <li><code>getOutputStream</code>
109.105 + * </ul>
109.106 + * <p>
109.107 + * Certain header fields are accessed frequently. The methods:
109.108 + * <ul>
109.109 + * <li><code>getContentEncoding</code>
109.110 + * <li><code>getContentLength</code>
109.111 + * <li><code>getContentType</code>
109.112 + * <li><code>getDate</code>
109.113 + * <li><code>getExpiration</code>
109.114 + * <li><code>getLastModifed</code>
109.115 + * </ul>
109.116 + * <p>
109.117 + * provide convenient access to these fields. The
109.118 + * <code>getContentType</code> method is used by the
109.119 + * <code>getContent</code> method to determine the type of the remote
109.120 + * object; subclasses may find it convenient to override the
109.121 + * <code>getContentType</code> method.
109.122 + * <p>
109.123 + * In the common case, all of the pre-connection parameters and
109.124 + * general request properties can be ignored: the pre-connection
109.125 + * parameters and request properties default to sensible values. For
109.126 + * most clients of this interface, there are only two interesting
109.127 + * methods: <code>getInputStream</code> and <code>getContent</code>,
109.128 + * which are mirrored in the <code>URL</code> class by convenience methods.
109.129 + * <p>
109.130 + * More information on the request properties and header fields of
109.131 + * an <code>http</code> connection can be found at:
109.132 + * <blockquote><pre>
109.133 + * <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a>
109.134 + * </pre></blockquote>
109.135 + *
109.136 + * Note about <code>fileNameMap</code>: In versions prior to JDK 1.1.6,
109.137 + * field <code>fileNameMap</code> of <code>URLConnection</code> was public.
109.138 + * In JDK 1.1.6 and later, <code>fileNameMap</code> is private; accessor
109.139 + * and mutator methods {@link #getFileNameMap() getFileNameMap} and
109.140 + * {@link #setFileNameMap(java.net.FileNameMap) setFileNameMap} are added
109.141 + * to access it. This change is also described on the <a href=
109.142 + * "http://java.sun.com/products/jdk/1.2/compatibility.html">
109.143 + * Compatibility</a> page.
109.144 + *
109.145 + * Invoking the <tt>close()</tt> methods on the <tt>InputStream</tt> or <tt>OutputStream</tt> of an
109.146 + * <tt>URLConnection</tt> after a request may free network resources associated with this
109.147 + * instance, unless particular protocol specifications specify different behaviours
109.148 + * for it.
109.149 + *
109.150 + * @author James Gosling
109.151 + * @see java.net.URL#openConnection()
109.152 + * @see java.net.URLConnection#connect()
109.153 + * @see java.net.URLConnection#getContent()
109.154 + * @see java.net.URLConnection#getContentEncoding()
109.155 + * @see java.net.URLConnection#getContentLength()
109.156 + * @see java.net.URLConnection#getContentType()
109.157 + * @see java.net.URLConnection#getDate()
109.158 + * @see java.net.URLConnection#getExpiration()
109.159 + * @see java.net.URLConnection#getHeaderField(int)
109.160 + * @see java.net.URLConnection#getHeaderField(java.lang.String)
109.161 + * @see java.net.URLConnection#getInputStream()
109.162 + * @see java.net.URLConnection#getLastModified()
109.163 + * @see java.net.URLConnection#getOutputStream()
109.164 + * @see java.net.URLConnection#setAllowUserInteraction(boolean)
109.165 + * @see java.net.URLConnection#setDefaultUseCaches(boolean)
109.166 + * @see java.net.URLConnection#setDoInput(boolean)
109.167 + * @see java.net.URLConnection#setDoOutput(boolean)
109.168 + * @see java.net.URLConnection#setIfModifiedSince(long)
109.169 + * @see java.net.URLConnection#setRequestProperty(java.lang.String, java.lang.String)
109.170 + * @see java.net.URLConnection#setUseCaches(boolean)
109.171 + * @since JDK1.0
109.172 + */
109.173 +public abstract class URLConnection {
109.174 +
109.175 + /**
109.176 + * The URL represents the remote object on the World Wide Web to
109.177 + * which this connection is opened.
109.178 + * <p>
109.179 + * The value of this field can be accessed by the
109.180 + * <code>getURL</code> method.
109.181 + * <p>
109.182 + * The default value of this variable is the value of the URL
109.183 + * argument in the <code>URLConnection</code> constructor.
109.184 + *
109.185 + * @see java.net.URLConnection#getURL()
109.186 + * @see java.net.URLConnection#url
109.187 + */
109.188 + protected URL url;
109.189 +
109.190 + /**
109.191 + * This variable is set by the <code>setDoInput</code> method. Its
109.192 + * value is returned by the <code>getDoInput</code> method.
109.193 + * <p>
109.194 + * A URL connection can be used for input and/or output. Setting the
109.195 + * <code>doInput</code> flag to <code>true</code> indicates that
109.196 + * the application intends to read data from the URL connection.
109.197 + * <p>
109.198 + * The default value of this field is <code>true</code>.
109.199 + *
109.200 + * @see java.net.URLConnection#getDoInput()
109.201 + * @see java.net.URLConnection#setDoInput(boolean)
109.202 + */
109.203 + protected boolean doInput = true;
109.204 +
109.205 + /**
109.206 + * This variable is set by the <code>setDoOutput</code> method. Its
109.207 + * value is returned by the <code>getDoOutput</code> method.
109.208 + * <p>
109.209 + * A URL connection can be used for input and/or output. Setting the
109.210 + * <code>doOutput</code> flag to <code>true</code> indicates
109.211 + * that the application intends to write data to the URL connection.
109.212 + * <p>
109.213 + * The default value of this field is <code>false</code>.
109.214 + *
109.215 + * @see java.net.URLConnection#getDoOutput()
109.216 + * @see java.net.URLConnection#setDoOutput(boolean)
109.217 + */
109.218 + protected boolean doOutput = false;
109.219 +
109.220 + private static boolean defaultAllowUserInteraction = false;
109.221 +
109.222 + /**
109.223 + * If <code>true</code>, this <code>URL</code> is being examined in
109.224 + * a context in which it makes sense to allow user interactions such
109.225 + * as popping up an authentication dialog. If <code>false</code>,
109.226 + * then no user interaction is allowed.
109.227 + * <p>
109.228 + * The value of this field can be set by the
109.229 + * <code>setAllowUserInteraction</code> method.
109.230 + * Its value is returned by the
109.231 + * <code>getAllowUserInteraction</code> method.
109.232 + * Its default value is the value of the argument in the last invocation
109.233 + * of the <code>setDefaultAllowUserInteraction</code> method.
109.234 + *
109.235 + * @see java.net.URLConnection#getAllowUserInteraction()
109.236 + * @see java.net.URLConnection#setAllowUserInteraction(boolean)
109.237 + * @see java.net.URLConnection#setDefaultAllowUserInteraction(boolean)
109.238 + */
109.239 + protected boolean allowUserInteraction = defaultAllowUserInteraction;
109.240 +
109.241 + private static boolean defaultUseCaches = true;
109.242 +
109.243 + /**
109.244 + * If <code>true</code>, the protocol is allowed to use caching
109.245 + * whenever it can. If <code>false</code>, the protocol must always
109.246 + * try to get a fresh copy of the object.
109.247 + * <p>
109.248 + * This field is set by the <code>setUseCaches</code> method. Its
109.249 + * value is returned by the <code>getUseCaches</code> method.
109.250 + * <p>
109.251 + * Its default value is the value given in the last invocation of the
109.252 + * <code>setDefaultUseCaches</code> method.
109.253 + *
109.254 + * @see java.net.URLConnection#setUseCaches(boolean)
109.255 + * @see java.net.URLConnection#getUseCaches()
109.256 + * @see java.net.URLConnection#setDefaultUseCaches(boolean)
109.257 + */
109.258 + protected boolean useCaches = defaultUseCaches;
109.259 +
109.260 + /**
109.261 + * Some protocols support skipping the fetching of the object unless
109.262 + * the object has been modified more recently than a certain time.
109.263 + * <p>
109.264 + * A nonzero value gives a time as the number of milliseconds since
109.265 + * January 1, 1970, GMT. The object is fetched only if it has been
109.266 + * modified more recently than that time.
109.267 + * <p>
109.268 + * This variable is set by the <code>setIfModifiedSince</code>
109.269 + * method. Its value is returned by the
109.270 + * <code>getIfModifiedSince</code> method.
109.271 + * <p>
109.272 + * The default value of this field is <code>0</code>, indicating
109.273 + * that the fetching must always occur.
109.274 + *
109.275 + * @see java.net.URLConnection#getIfModifiedSince()
109.276 + * @see java.net.URLConnection#setIfModifiedSince(long)
109.277 + */
109.278 + protected long ifModifiedSince = 0;
109.279 +
109.280 + /**
109.281 + * If <code>false</code>, this connection object has not created a
109.282 + * communications link to the specified URL. If <code>true</code>,
109.283 + * the communications link has been established.
109.284 + */
109.285 + protected boolean connected = false;
109.286 +
109.287 + /**
109.288 + * @since 1.5
109.289 + */
109.290 + private int connectTimeout;
109.291 + private int readTimeout;
109.292 +
109.293 + /**
109.294 + * @since 1.6
109.295 + */
109.296 + private MessageHeader requests;
109.297 +
109.298 + /**
109.299 + * @since JDK1.1
109.300 + */
109.301 + private static FileNameMap fileNameMap;
109.302 +
109.303 + /**
109.304 + * @since 1.2.2
109.305 + */
109.306 + private static boolean fileNameMapLoaded = false;
109.307 +
109.308 + /**
109.309 + * Loads filename map (a mimetable) from a data file. It will
109.310 + * first try to load the user-specific table, defined
109.311 + * by "content.types.user.table" property. If that fails,
109.312 + * it tries to load the default built-in table at
109.313 + * lib/content-types.properties under java home.
109.314 + *
109.315 + * @return the FileNameMap
109.316 + * @since 1.2
109.317 + * @see #setFileNameMap(java.net.FileNameMap)
109.318 + */
109.319 + public static synchronized FileNameMap getFileNameMap() {
109.320 + if ((fileNameMap == null) && !fileNameMapLoaded) {
109.321 + fileNameMap = new FileNameMap() {
109.322 + @Override
109.323 + public String getContentTypeFor(String fileName) {
109.324 + return "text/plain";
109.325 + }
109.326 + };
109.327 + fileNameMapLoaded = true;
109.328 + }
109.329 +
109.330 + return new FileNameMap() {
109.331 + private FileNameMap map = fileNameMap;
109.332 + public String getContentTypeFor(String fileName) {
109.333 + return map.getContentTypeFor(fileName);
109.334 + }
109.335 + };
109.336 + }
109.337 +
109.338 + /**
109.339 + * Sets the FileNameMap.
109.340 + * <p>
109.341 + * If there is a security manager, this method first calls
109.342 + * the security manager's <code>checkSetFactory</code> method
109.343 + * to ensure the operation is allowed.
109.344 + * This could result in a SecurityException.
109.345 + *
109.346 + * @param map the FileNameMap to be set
109.347 + * @exception SecurityException if a security manager exists and its
109.348 + * <code>checkSetFactory</code> method doesn't allow the operation.
109.349 + * @see SecurityManager#checkSetFactory
109.350 + * @see #getFileNameMap()
109.351 + * @since 1.2
109.352 + */
109.353 + public static void setFileNameMap(FileNameMap map) {
109.354 + throw new SecurityException();
109.355 + }
109.356 +
109.357 + /**
109.358 + * Opens a communications link to the resource referenced by this
109.359 + * URL, if such a connection has not already been established.
109.360 + * <p>
109.361 + * If the <code>connect</code> method is called when the connection
109.362 + * has already been opened (indicated by the <code>connected</code>
109.363 + * field having the value <code>true</code>), the call is ignored.
109.364 + * <p>
109.365 + * URLConnection objects go through two phases: first they are
109.366 + * created, then they are connected. After being created, and
109.367 + * before being connected, various options can be specified
109.368 + * (e.g., doInput and UseCaches). After connecting, it is an
109.369 + * error to try to set them. Operations that depend on being
109.370 + * connected, like getContentLength, will implicitly perform the
109.371 + * connection, if necessary.
109.372 + *
109.373 + * @throws SocketTimeoutException if the timeout expires before
109.374 + * the connection can be established
109.375 + * @exception IOException if an I/O error occurs while opening the
109.376 + * connection.
109.377 + * @see java.net.URLConnection#connected
109.378 + * @see #getConnectTimeout()
109.379 + * @see #setConnectTimeout(int)
109.380 + */
109.381 + abstract public void connect() throws IOException;
109.382 +
109.383 + /**
109.384 + * Sets a specified timeout value, in milliseconds, to be used
109.385 + * when opening a communications link to the resource referenced
109.386 + * by this URLConnection. If the timeout expires before the
109.387 + * connection can be established, a
109.388 + * java.net.SocketTimeoutException is raised. A timeout of zero is
109.389 + * interpreted as an infinite timeout.
109.390 +
109.391 + * <p> Some non-standard implmentation of this method may ignore
109.392 + * the specified timeout. To see the connect timeout set, please
109.393 + * call getConnectTimeout().
109.394 + *
109.395 + * @param timeout an <code>int</code> that specifies the connect
109.396 + * timeout value in milliseconds
109.397 + * @throws IllegalArgumentException if the timeout parameter is negative
109.398 + *
109.399 + * @see #getConnectTimeout()
109.400 + * @see #connect()
109.401 + * @since 1.5
109.402 + */
109.403 + public void setConnectTimeout(int timeout) {
109.404 + if (timeout < 0) {
109.405 + throw new IllegalArgumentException("timeout can not be negative");
109.406 + }
109.407 + connectTimeout = timeout;
109.408 + }
109.409 +
109.410 + /**
109.411 + * Returns setting for connect timeout.
109.412 + * <p>
109.413 + * 0 return implies that the option is disabled
109.414 + * (i.e., timeout of infinity).
109.415 + *
109.416 + * @return an <code>int</code> that indicates the connect timeout
109.417 + * value in milliseconds
109.418 + * @see #setConnectTimeout(int)
109.419 + * @see #connect()
109.420 + * @since 1.5
109.421 + */
109.422 + public int getConnectTimeout() {
109.423 + return connectTimeout;
109.424 + }
109.425 +
109.426 + /**
109.427 + * Sets the read timeout to a specified timeout, in
109.428 + * milliseconds. A non-zero value specifies the timeout when
109.429 + * reading from Input stream when a connection is established to a
109.430 + * resource. If the timeout expires before there is data available
109.431 + * for read, a java.net.SocketTimeoutException is raised. A
109.432 + * timeout of zero is interpreted as an infinite timeout.
109.433 + *
109.434 + *<p> Some non-standard implementation of this method ignores the
109.435 + * specified timeout. To see the read timeout set, please call
109.436 + * getReadTimeout().
109.437 + *
109.438 + * @param timeout an <code>int</code> that specifies the timeout
109.439 + * value to be used in milliseconds
109.440 + * @throws IllegalArgumentException if the timeout parameter is negative
109.441 + *
109.442 + * @see #getReadTimeout()
109.443 + * @see InputStream#read()
109.444 + * @since 1.5
109.445 + */
109.446 + public void setReadTimeout(int timeout) {
109.447 + if (timeout < 0) {
109.448 + throw new IllegalArgumentException("timeout can not be negative");
109.449 + }
109.450 + readTimeout = timeout;
109.451 + }
109.452 +
109.453 + /**
109.454 + * Returns setting for read timeout. 0 return implies that the
109.455 + * option is disabled (i.e., timeout of infinity).
109.456 + *
109.457 + * @return an <code>int</code> that indicates the read timeout
109.458 + * value in milliseconds
109.459 + *
109.460 + * @see #setReadTimeout(int)
109.461 + * @see InputStream#read()
109.462 + * @since 1.5
109.463 + */
109.464 + public int getReadTimeout() {
109.465 + return readTimeout;
109.466 + }
109.467 +
109.468 + /**
109.469 + * Constructs a URL connection to the specified URL. A connection to
109.470 + * the object referenced by the URL is not created.
109.471 + *
109.472 + * @param url the specified URL.
109.473 + */
109.474 + protected URLConnection(URL url) {
109.475 + this.url = url;
109.476 + }
109.477 +
109.478 + /**
109.479 + * Returns the value of this <code>URLConnection</code>'s <code>URL</code>
109.480 + * field.
109.481 + *
109.482 + * @return the value of this <code>URLConnection</code>'s <code>URL</code>
109.483 + * field.
109.484 + * @see java.net.URLConnection#url
109.485 + */
109.486 + public URL getURL() {
109.487 + return url;
109.488 + }
109.489 +
109.490 + /**
109.491 + * Returns the value of the <code>content-length</code> header field.
109.492 + * <P>
109.493 + * <B>Note</B>: {@link #getContentLengthLong() getContentLengthLong()}
109.494 + * should be preferred over this method, since it returns a {@code long}
109.495 + * instead and is therefore more portable.</P>
109.496 + *
109.497 + * @return the content length of the resource that this connection's URL
109.498 + * references, {@code -1} if the content length is not known,
109.499 + * or if the content length is greater than Integer.MAX_VALUE.
109.500 + */
109.501 + public int getContentLength() {
109.502 + long l = getContentLengthLong();
109.503 + if (l > Integer.MAX_VALUE)
109.504 + return -1;
109.505 + return (int) l;
109.506 + }
109.507 +
109.508 + /**
109.509 + * Returns the value of the <code>content-length</code> header field as a
109.510 + * long.
109.511 + *
109.512 + * @return the content length of the resource that this connection's URL
109.513 + * references, or <code>-1</code> if the content length is
109.514 + * not known.
109.515 + * @since 7.0
109.516 + */
109.517 + public long getContentLengthLong() {
109.518 + return getHeaderFieldLong("content-length", -1);
109.519 + }
109.520 +
109.521 + /**
109.522 + * Returns the value of the <code>content-type</code> header field.
109.523 + *
109.524 + * @return the content type of the resource that the URL references,
109.525 + * or <code>null</code> if not known.
109.526 + * @see java.net.URLConnection#getHeaderField(java.lang.String)
109.527 + */
109.528 + public String getContentType() {
109.529 + return getHeaderField("content-type");
109.530 + }
109.531 +
109.532 + /**
109.533 + * Returns the value of the <code>content-encoding</code> header field.
109.534 + *
109.535 + * @return the content encoding of the resource that the URL references,
109.536 + * or <code>null</code> if not known.
109.537 + * @see java.net.URLConnection#getHeaderField(java.lang.String)
109.538 + */
109.539 + public String getContentEncoding() {
109.540 + return getHeaderField("content-encoding");
109.541 + }
109.542 +
109.543 + /**
109.544 + * Returns the value of the <code>expires</code> header field.
109.545 + *
109.546 + * @return the expiration date of the resource that this URL references,
109.547 + * or 0 if not known. The value is the number of milliseconds since
109.548 + * January 1, 1970 GMT.
109.549 + * @see java.net.URLConnection#getHeaderField(java.lang.String)
109.550 + */
109.551 + public long getExpiration() {
109.552 + return getHeaderFieldDate("expires", 0);
109.553 + }
109.554 +
109.555 + /**
109.556 + * Returns the value of the <code>date</code> header field.
109.557 + *
109.558 + * @return the sending date of the resource that the URL references,
109.559 + * or <code>0</code> if not known. The value returned is the
109.560 + * number of milliseconds since January 1, 1970 GMT.
109.561 + * @see java.net.URLConnection#getHeaderField(java.lang.String)
109.562 + */
109.563 + public long getDate() {
109.564 + return getHeaderFieldDate("date", 0);
109.565 + }
109.566 +
109.567 + /**
109.568 + * Returns the value of the <code>last-modified</code> header field.
109.569 + * The result is the number of milliseconds since January 1, 1970 GMT.
109.570 + *
109.571 + * @return the date the resource referenced by this
109.572 + * <code>URLConnection</code> was last modified, or 0 if not known.
109.573 + * @see java.net.URLConnection#getHeaderField(java.lang.String)
109.574 + */
109.575 + public long getLastModified() {
109.576 + return getHeaderFieldDate("last-modified", 0);
109.577 + }
109.578 +
109.579 + /**
109.580 + * Returns the value of the named header field.
109.581 + * <p>
109.582 + * If called on a connection that sets the same header multiple times
109.583 + * with possibly different values, only the last value is returned.
109.584 + *
109.585 + *
109.586 + * @param name the name of a header field.
109.587 + * @return the value of the named header field, or <code>null</code>
109.588 + * if there is no such field in the header.
109.589 + */
109.590 + public String getHeaderField(String name) {
109.591 + return null;
109.592 + }
109.593 +
109.594 + /**
109.595 + * Returns an unmodifiable Map of the header fields.
109.596 + * The Map keys are Strings that represent the
109.597 + * response-header field names. Each Map value is an
109.598 + * unmodifiable List of Strings that represents
109.599 + * the corresponding field values.
109.600 + *
109.601 + * @return a Map of header fields
109.602 + * @since 1.4
109.603 + */
109.604 + public Map<String,List<String>> getHeaderFields() {
109.605 + return Collections.EMPTY_MAP;
109.606 + }
109.607 +
109.608 + /**
109.609 + * Returns the value of the named field parsed as a number.
109.610 + * <p>
109.611 + * This form of <code>getHeaderField</code> exists because some
109.612 + * connection types (e.g., <code>http-ng</code>) have pre-parsed
109.613 + * headers. Classes for that connection type can override this method
109.614 + * and short-circuit the parsing.
109.615 + *
109.616 + * @param name the name of the header field.
109.617 + * @param Default the default value.
109.618 + * @return the value of the named field, parsed as an integer. The
109.619 + * <code>Default</code> value is returned if the field is
109.620 + * missing or malformed.
109.621 + */
109.622 + public int getHeaderFieldInt(String name, int Default) {
109.623 + String value = getHeaderField(name);
109.624 + try {
109.625 + return Integer.parseInt(value);
109.626 + } catch (Exception e) { }
109.627 + return Default;
109.628 + }
109.629 +
109.630 + /**
109.631 + * Returns the value of the named field parsed as a number.
109.632 + * <p>
109.633 + * This form of <code>getHeaderField</code> exists because some
109.634 + * connection types (e.g., <code>http-ng</code>) have pre-parsed
109.635 + * headers. Classes for that connection type can override this method
109.636 + * and short-circuit the parsing.
109.637 + *
109.638 + * @param name the name of the header field.
109.639 + * @param Default the default value.
109.640 + * @return the value of the named field, parsed as a long. The
109.641 + * <code>Default</code> value is returned if the field is
109.642 + * missing or malformed.
109.643 + * @since 7.0
109.644 + */
109.645 + public long getHeaderFieldLong(String name, long Default) {
109.646 + String value = getHeaderField(name);
109.647 + try {
109.648 + return Long.parseLong(value);
109.649 + } catch (Exception e) { }
109.650 + return Default;
109.651 + }
109.652 +
109.653 + /**
109.654 + * Returns the value of the named field parsed as date.
109.655 + * The result is the number of milliseconds since January 1, 1970 GMT
109.656 + * represented by the named field.
109.657 + * <p>
109.658 + * This form of <code>getHeaderField</code> exists because some
109.659 + * connection types (e.g., <code>http-ng</code>) have pre-parsed
109.660 + * headers. Classes for that connection type can override this method
109.661 + * and short-circuit the parsing.
109.662 + *
109.663 + * @param name the name of the header field.
109.664 + * @param Default a default value.
109.665 + * @return the value of the field, parsed as a date. The value of the
109.666 + * <code>Default</code> argument is returned if the field is
109.667 + * missing or malformed.
109.668 + */
109.669 + public long getHeaderFieldDate(String name, long Default) {
109.670 + String value = getHeaderField(name);
109.671 + try {
109.672 + return Date.parse(value);
109.673 + } catch (Exception e) { }
109.674 + return Default;
109.675 + }
109.676 +
109.677 + /**
109.678 + * Returns the key for the <code>n</code><sup>th</sup> header field.
109.679 + * It returns <code>null</code> if there are fewer than <code>n+1</code> fields.
109.680 + *
109.681 + * @param n an index, where n>=0
109.682 + * @return the key for the <code>n</code><sup>th</sup> header field,
109.683 + * or <code>null</code> if there are fewer than <code>n+1</code>
109.684 + * fields.
109.685 + */
109.686 + public String getHeaderFieldKey(int n) {
109.687 + return null;
109.688 + }
109.689 +
109.690 + /**
109.691 + * Returns the value for the <code>n</code><sup>th</sup> header field.
109.692 + * It returns <code>null</code> if there are fewer than
109.693 + * <code>n+1</code>fields.
109.694 + * <p>
109.695 + * This method can be used in conjunction with the
109.696 + * {@link #getHeaderFieldKey(int) getHeaderFieldKey} method to iterate through all
109.697 + * the headers in the message.
109.698 + *
109.699 + * @param n an index, where n>=0
109.700 + * @return the value of the <code>n</code><sup>th</sup> header field
109.701 + * or <code>null</code> if there are fewer than <code>n+1</code> fields
109.702 + * @see java.net.URLConnection#getHeaderFieldKey(int)
109.703 + */
109.704 + public String getHeaderField(int n) {
109.705 + return null;
109.706 + }
109.707 +
109.708 + /**
109.709 + * Retrieves the contents of this URL connection.
109.710 + * <p>
109.711 + * This method first determines the content type of the object by
109.712 + * calling the <code>getContentType</code> method. If this is
109.713 + * the first time that the application has seen that specific content
109.714 + * type, a content handler for that content type is created:
109.715 + * <ol>
109.716 + * <li>If the application has set up a content handler factory instance
109.717 + * using the <code>setContentHandlerFactory</code> method, the
109.718 + * <code>createContentHandler</code> method of that instance is called
109.719 + * with the content type as an argument; the result is a content
109.720 + * handler for that content type.
109.721 + * <li>If no content handler factory has yet been set up, or if the
109.722 + * factory's <code>createContentHandler</code> method returns
109.723 + * <code>null</code>, then the application loads the class named:
109.724 + * <blockquote><pre>
109.725 + * sun.net.www.content.<<i>contentType</i>>
109.726 + * </pre></blockquote>
109.727 + * where <<i>contentType</i>> is formed by taking the
109.728 + * content-type string, replacing all slash characters with a
109.729 + * <code>period</code> ('.'), and all other non-alphanumeric characters
109.730 + * with the underscore character '<code>_</code>'. The alphanumeric
109.731 + * characters are specifically the 26 uppercase ASCII letters
109.732 + * '<code>A</code>' through '<code>Z</code>', the 26 lowercase ASCII
109.733 + * letters '<code>a</code>' through '<code>z</code>', and the 10 ASCII
109.734 + * digits '<code>0</code>' through '<code>9</code>'. If the specified
109.735 + * class does not exist, or is not a subclass of
109.736 + * <code>ContentHandler</code>, then an
109.737 + * <code>UnknownServiceException</code> is thrown.
109.738 + * </ol>
109.739 + *
109.740 + * @return the object fetched. The <code>instanceof</code> operator
109.741 + * should be used to determine the specific kind of object
109.742 + * returned.
109.743 + * @exception IOException if an I/O error occurs while
109.744 + * getting the content.
109.745 + * @exception UnknownServiceException if the protocol does not support
109.746 + * the content type.
109.747 + * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
109.748 + * @see java.net.URLConnection#getContentType()
109.749 + * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
109.750 + */
109.751 + public Object getContent() throws IOException {
109.752 + // Must call getInputStream before GetHeaderField gets called
109.753 + // so that FileNotFoundException has a chance to be thrown up
109.754 + // from here without being caught.
109.755 + getInputStream();
109.756 + return getContentHandler().getContent(this);
109.757 + }
109.758 +
109.759 + /**
109.760 + * Retrieves the contents of this URL connection.
109.761 + *
109.762 + * @param classes the <code>Class</code> array
109.763 + * indicating the requested types
109.764 + * @return the object fetched that is the first match of the type
109.765 + * specified in the classes array. null if none of
109.766 + * the requested types are supported.
109.767 + * The <code>instanceof</code> operator should be used to
109.768 + * determine the specific kind of object returned.
109.769 + * @exception IOException if an I/O error occurs while
109.770 + * getting the content.
109.771 + * @exception UnknownServiceException if the protocol does not support
109.772 + * the content type.
109.773 + * @see java.net.URLConnection#getContent()
109.774 + * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
109.775 + * @see java.net.URLConnection#getContent(java.lang.Class[])
109.776 + * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
109.777 + * @since 1.3
109.778 + */
109.779 + public Object getContent(Class[] classes) throws IOException {
109.780 + // Must call getInputStream before GetHeaderField gets called
109.781 + // so that FileNotFoundException has a chance to be thrown up
109.782 + // from here without being caught.
109.783 + getInputStream();
109.784 + return getContentHandler().getContent(this, classes);
109.785 + }
109.786 +
109.787 + /**
109.788 + * Returns a permission object representing the permission
109.789 + * necessary to make the connection represented by this
109.790 + * object. This method returns null if no permission is
109.791 + * required to make the connection. By default, this method
109.792 + * returns <code>java.security.AllPermission</code>. Subclasses
109.793 + * should override this method and return the permission
109.794 + * that best represents the permission required to make a
109.795 + * a connection to the URL. For example, a <code>URLConnection</code>
109.796 + * representing a <code>file:</code> URL would return a
109.797 + * <code>java.io.FilePermission</code> object.
109.798 + *
109.799 + * <p>The permission returned may dependent upon the state of the
109.800 + * connection. For example, the permission before connecting may be
109.801 + * different from that after connecting. For example, an HTTP
109.802 + * sever, say foo.com, may redirect the connection to a different
109.803 + * host, say bar.com. Before connecting the permission returned by
109.804 + * the connection will represent the permission needed to connect
109.805 + * to foo.com, while the permission returned after connecting will
109.806 + * be to bar.com.
109.807 + *
109.808 + * <p>Permissions are generally used for two purposes: to protect
109.809 + * caches of objects obtained through URLConnections, and to check
109.810 + * the right of a recipient to learn about a particular URL. In
109.811 + * the first case, the permission should be obtained
109.812 + * <em>after</em> the object has been obtained. For example, in an
109.813 + * HTTP connection, this will represent the permission to connect
109.814 + * to the host from which the data was ultimately fetched. In the
109.815 + * second case, the permission should be obtained and tested
109.816 + * <em>before</em> connecting.
109.817 + *
109.818 + * @return the permission object representing the permission
109.819 + * necessary to make the connection represented by this
109.820 + * URLConnection.
109.821 + *
109.822 + * @exception IOException if the computation of the permission
109.823 + * requires network or file I/O and an exception occurs while
109.824 + * computing it.
109.825 + */
109.826 +// public Permission getPermission() throws IOException {
109.827 +// return SecurityConstants.ALL_PERMISSION;
109.828 +// }
109.829 +
109.830 + /**
109.831 + * Returns an input stream that reads from this open connection.
109.832 + *
109.833 + * A SocketTimeoutException can be thrown when reading from the
109.834 + * returned input stream if the read timeout expires before data
109.835 + * is available for read.
109.836 + *
109.837 + * @return an input stream that reads from this open connection.
109.838 + * @exception IOException if an I/O error occurs while
109.839 + * creating the input stream.
109.840 + * @exception UnknownServiceException if the protocol does not support
109.841 + * input.
109.842 + * @see #setReadTimeout(int)
109.843 + * @see #getReadTimeout()
109.844 + */
109.845 + public InputStream getInputStream() throws IOException {
109.846 + throw new UnknownServiceException("protocol doesn't support input");
109.847 + }
109.848 +
109.849 + /**
109.850 + * Returns an output stream that writes to this connection.
109.851 + *
109.852 + * @return an output stream that writes to this connection.
109.853 + * @exception IOException if an I/O error occurs while
109.854 + * creating the output stream.
109.855 + * @exception UnknownServiceException if the protocol does not support
109.856 + * output.
109.857 + */
109.858 + public OutputStream getOutputStream() throws IOException {
109.859 + throw new UnknownServiceException("protocol doesn't support output");
109.860 + }
109.861 +
109.862 + /**
109.863 + * Returns a <code>String</code> representation of this URL connection.
109.864 + *
109.865 + * @return a string representation of this <code>URLConnection</code>.
109.866 + */
109.867 + public String toString() {
109.868 + return this.getClass().getName() + ":" + url;
109.869 + }
109.870 +
109.871 + /**
109.872 + * Sets the value of the <code>doInput</code> field for this
109.873 + * <code>URLConnection</code> to the specified value.
109.874 + * <p>
109.875 + * A URL connection can be used for input and/or output. Set the DoInput
109.876 + * flag to true if you intend to use the URL connection for input,
109.877 + * false if not. The default is true.
109.878 + *
109.879 + * @param doinput the new value.
109.880 + * @throws IllegalStateException if already connected
109.881 + * @see java.net.URLConnection#doInput
109.882 + * @see #getDoInput()
109.883 + */
109.884 + public void setDoInput(boolean doinput) {
109.885 + if (connected)
109.886 + throw new IllegalStateException("Already connected");
109.887 + doInput = doinput;
109.888 + }
109.889 +
109.890 + /**
109.891 + * Returns the value of this <code>URLConnection</code>'s
109.892 + * <code>doInput</code> flag.
109.893 + *
109.894 + * @return the value of this <code>URLConnection</code>'s
109.895 + * <code>doInput</code> flag.
109.896 + * @see #setDoInput(boolean)
109.897 + */
109.898 + public boolean getDoInput() {
109.899 + return doInput;
109.900 + }
109.901 +
109.902 + /**
109.903 + * Sets the value of the <code>doOutput</code> field for this
109.904 + * <code>URLConnection</code> to the specified value.
109.905 + * <p>
109.906 + * A URL connection can be used for input and/or output. Set the DoOutput
109.907 + * flag to true if you intend to use the URL connection for output,
109.908 + * false if not. The default is false.
109.909 + *
109.910 + * @param dooutput the new value.
109.911 + * @throws IllegalStateException if already connected
109.912 + * @see #getDoOutput()
109.913 + */
109.914 + public void setDoOutput(boolean dooutput) {
109.915 + if (connected)
109.916 + throw new IllegalStateException("Already connected");
109.917 + doOutput = dooutput;
109.918 + }
109.919 +
109.920 + /**
109.921 + * Returns the value of this <code>URLConnection</code>'s
109.922 + * <code>doOutput</code> flag.
109.923 + *
109.924 + * @return the value of this <code>URLConnection</code>'s
109.925 + * <code>doOutput</code> flag.
109.926 + * @see #setDoOutput(boolean)
109.927 + */
109.928 + public boolean getDoOutput() {
109.929 + return doOutput;
109.930 + }
109.931 +
109.932 + /**
109.933 + * Set the value of the <code>allowUserInteraction</code> field of
109.934 + * this <code>URLConnection</code>.
109.935 + *
109.936 + * @param allowuserinteraction the new value.
109.937 + * @throws IllegalStateException if already connected
109.938 + * @see #getAllowUserInteraction()
109.939 + */
109.940 + public void setAllowUserInteraction(boolean allowuserinteraction) {
109.941 + if (connected)
109.942 + throw new IllegalStateException("Already connected");
109.943 + allowUserInteraction = allowuserinteraction;
109.944 + }
109.945 +
109.946 + /**
109.947 + * Returns the value of the <code>allowUserInteraction</code> field for
109.948 + * this object.
109.949 + *
109.950 + * @return the value of the <code>allowUserInteraction</code> field for
109.951 + * this object.
109.952 + * @see #setAllowUserInteraction(boolean)
109.953 + */
109.954 + public boolean getAllowUserInteraction() {
109.955 + return allowUserInteraction;
109.956 + }
109.957 +
109.958 + /**
109.959 + * Sets the default value of the
109.960 + * <code>allowUserInteraction</code> field for all future
109.961 + * <code>URLConnection</code> objects to the specified value.
109.962 + *
109.963 + * @param defaultallowuserinteraction the new value.
109.964 + * @see #getDefaultAllowUserInteraction()
109.965 + */
109.966 + public static void setDefaultAllowUserInteraction(boolean defaultallowuserinteraction) {
109.967 + defaultAllowUserInteraction = defaultallowuserinteraction;
109.968 + }
109.969 +
109.970 + /**
109.971 + * Returns the default value of the <code>allowUserInteraction</code>
109.972 + * field.
109.973 + * <p>
109.974 + * Ths default is "sticky", being a part of the static state of all
109.975 + * URLConnections. This flag applies to the next, and all following
109.976 + * URLConnections that are created.
109.977 + *
109.978 + * @return the default value of the <code>allowUserInteraction</code>
109.979 + * field.
109.980 + * @see #setDefaultAllowUserInteraction(boolean)
109.981 + */
109.982 + public static boolean getDefaultAllowUserInteraction() {
109.983 + return defaultAllowUserInteraction;
109.984 + }
109.985 +
109.986 + /**
109.987 + * Sets the value of the <code>useCaches</code> field of this
109.988 + * <code>URLConnection</code> to the specified value.
109.989 + * <p>
109.990 + * Some protocols do caching of documents. Occasionally, it is important
109.991 + * to be able to "tunnel through" and ignore the caches (e.g., the
109.992 + * "reload" button in a browser). If the UseCaches flag on a connection
109.993 + * is true, the connection is allowed to use whatever caches it can.
109.994 + * If false, caches are to be ignored.
109.995 + * The default value comes from DefaultUseCaches, which defaults to
109.996 + * true.
109.997 + *
109.998 + * @param usecaches a <code>boolean</code> indicating whether
109.999 + * or not to allow caching
109.1000 + * @throws IllegalStateException if already connected
109.1001 + * @see #getUseCaches()
109.1002 + */
109.1003 + public void setUseCaches(boolean usecaches) {
109.1004 + if (connected)
109.1005 + throw new IllegalStateException("Already connected");
109.1006 + useCaches = usecaches;
109.1007 + }
109.1008 +
109.1009 + /**
109.1010 + * Returns the value of this <code>URLConnection</code>'s
109.1011 + * <code>useCaches</code> field.
109.1012 + *
109.1013 + * @return the value of this <code>URLConnection</code>'s
109.1014 + * <code>useCaches</code> field.
109.1015 + * @see #setUseCaches(boolean)
109.1016 + */
109.1017 + public boolean getUseCaches() {
109.1018 + return useCaches;
109.1019 + }
109.1020 +
109.1021 + /**
109.1022 + * Sets the value of the <code>ifModifiedSince</code> field of
109.1023 + * this <code>URLConnection</code> to the specified value.
109.1024 + *
109.1025 + * @param ifmodifiedsince the new value.
109.1026 + * @throws IllegalStateException if already connected
109.1027 + * @see #getIfModifiedSince()
109.1028 + */
109.1029 + public void setIfModifiedSince(long ifmodifiedsince) {
109.1030 + if (connected)
109.1031 + throw new IllegalStateException("Already connected");
109.1032 + ifModifiedSince = ifmodifiedsince;
109.1033 + }
109.1034 +
109.1035 + /**
109.1036 + * Returns the value of this object's <code>ifModifiedSince</code> field.
109.1037 + *
109.1038 + * @return the value of this object's <code>ifModifiedSince</code> field.
109.1039 + * @see #setIfModifiedSince(long)
109.1040 + */
109.1041 + public long getIfModifiedSince() {
109.1042 + return ifModifiedSince;
109.1043 + }
109.1044 +
109.1045 + /**
109.1046 + * Returns the default value of a <code>URLConnection</code>'s
109.1047 + * <code>useCaches</code> flag.
109.1048 + * <p>
109.1049 + * Ths default is "sticky", being a part of the static state of all
109.1050 + * URLConnections. This flag applies to the next, and all following
109.1051 + * URLConnections that are created.
109.1052 + *
109.1053 + * @return the default value of a <code>URLConnection</code>'s
109.1054 + * <code>useCaches</code> flag.
109.1055 + * @see #setDefaultUseCaches(boolean)
109.1056 + */
109.1057 + public boolean getDefaultUseCaches() {
109.1058 + return defaultUseCaches;
109.1059 + }
109.1060 +
109.1061 + /**
109.1062 + * Sets the default value of the <code>useCaches</code> field to the
109.1063 + * specified value.
109.1064 + *
109.1065 + * @param defaultusecaches the new value.
109.1066 + * @see #getDefaultUseCaches()
109.1067 + */
109.1068 + public void setDefaultUseCaches(boolean defaultusecaches) {
109.1069 + defaultUseCaches = defaultusecaches;
109.1070 + }
109.1071 +
109.1072 + /**
109.1073 + * Sets the general request property. If a property with the key already
109.1074 + * exists, overwrite its value with the new value.
109.1075 + *
109.1076 + * <p> NOTE: HTTP requires all request properties which can
109.1077 + * legally have multiple instances with the same key
109.1078 + * to use a comma-seperated list syntax which enables multiple
109.1079 + * properties to be appended into a single property.
109.1080 + *
109.1081 + * @param key the keyword by which the request is known
109.1082 + * (e.g., "<code>Accept</code>").
109.1083 + * @param value the value associated with it.
109.1084 + * @throws IllegalStateException if already connected
109.1085 + * @throws NullPointerException if key is <CODE>null</CODE>
109.1086 + * @see #getRequestProperty(java.lang.String)
109.1087 + */
109.1088 + public void setRequestProperty(String key, String value) {
109.1089 + if (connected)
109.1090 + throw new IllegalStateException("Already connected");
109.1091 + if (key == null)
109.1092 + throw new NullPointerException ("key is null");
109.1093 +
109.1094 + if (requests == null)
109.1095 + requests = new MessageHeader();
109.1096 +
109.1097 + requests.set(key, value);
109.1098 + }
109.1099 +
109.1100 + /**
109.1101 + * Adds a general request property specified by a
109.1102 + * key-value pair. This method will not overwrite
109.1103 + * existing values associated with the same key.
109.1104 + *
109.1105 + * @param key the keyword by which the request is known
109.1106 + * (e.g., "<code>Accept</code>").
109.1107 + * @param value the value associated with it.
109.1108 + * @throws IllegalStateException if already connected
109.1109 + * @throws NullPointerException if key is null
109.1110 + * @see #getRequestProperties()
109.1111 + * @since 1.4
109.1112 + */
109.1113 + public void addRequestProperty(String key, String value) {
109.1114 + if (connected)
109.1115 + throw new IllegalStateException("Already connected");
109.1116 + if (key == null)
109.1117 + throw new NullPointerException ("key is null");
109.1118 +
109.1119 + if (requests == null)
109.1120 + requests = new MessageHeader();
109.1121 +
109.1122 + requests.add(key, value);
109.1123 + }
109.1124 +
109.1125 +
109.1126 + /**
109.1127 + * Returns the value of the named general request property for this
109.1128 + * connection.
109.1129 + *
109.1130 + * @param key the keyword by which the request is known (e.g., "Accept").
109.1131 + * @return the value of the named general request property for this
109.1132 + * connection. If key is null, then null is returned.
109.1133 + * @throws IllegalStateException if already connected
109.1134 + * @see #setRequestProperty(java.lang.String, java.lang.String)
109.1135 + */
109.1136 + public String getRequestProperty(String key) {
109.1137 + if (connected)
109.1138 + throw new IllegalStateException("Already connected");
109.1139 +
109.1140 + if (requests == null)
109.1141 + return null;
109.1142 +
109.1143 + return requests.findValue(key);
109.1144 + }
109.1145 +
109.1146 + /**
109.1147 + * Returns an unmodifiable Map of general request
109.1148 + * properties for this connection. The Map keys
109.1149 + * are Strings that represent the request-header
109.1150 + * field names. Each Map value is a unmodifiable List
109.1151 + * of Strings that represents the corresponding
109.1152 + * field values.
109.1153 + *
109.1154 + * @return a Map of the general request properties for this connection.
109.1155 + * @throws IllegalStateException if already connected
109.1156 + * @since 1.4
109.1157 + */
109.1158 + public Map<String,List<String>> getRequestProperties() {
109.1159 + if (connected)
109.1160 + throw new IllegalStateException("Already connected");
109.1161 +
109.1162 + if (requests == null)
109.1163 + return Collections.EMPTY_MAP;
109.1164 +
109.1165 + return requests.getHeaders(null);
109.1166 + }
109.1167 +
109.1168 + /**
109.1169 + * Sets the default value of a general request property. When a
109.1170 + * <code>URLConnection</code> is created, it is initialized with
109.1171 + * these properties.
109.1172 + *
109.1173 + * @param key the keyword by which the request is known
109.1174 + * (e.g., "<code>Accept</code>").
109.1175 + * @param value the value associated with the key.
109.1176 + *
109.1177 + * @see java.net.URLConnection#setRequestProperty(java.lang.String,java.lang.String)
109.1178 + *
109.1179 + * @deprecated The instance specific setRequestProperty method
109.1180 + * should be used after an appropriate instance of URLConnection
109.1181 + * is obtained. Invoking this method will have no effect.
109.1182 + *
109.1183 + * @see #getDefaultRequestProperty(java.lang.String)
109.1184 + */
109.1185 + @Deprecated
109.1186 + public static void setDefaultRequestProperty(String key, String value) {
109.1187 + }
109.1188 +
109.1189 + /**
109.1190 + * Returns the value of the default request property. Default request
109.1191 + * properties are set for every connection.
109.1192 + *
109.1193 + * @param key the keyword by which the request is known (e.g., "Accept").
109.1194 + * @return the value of the default request property
109.1195 + * for the specified key.
109.1196 + *
109.1197 + * @see java.net.URLConnection#getRequestProperty(java.lang.String)
109.1198 + *
109.1199 + * @deprecated The instance specific getRequestProperty method
109.1200 + * should be used after an appropriate instance of URLConnection
109.1201 + * is obtained.
109.1202 + *
109.1203 + * @see #setDefaultRequestProperty(java.lang.String, java.lang.String)
109.1204 + */
109.1205 + @Deprecated
109.1206 + public static String getDefaultRequestProperty(String key) {
109.1207 + return null;
109.1208 + }
109.1209 +
109.1210 + /**
109.1211 + * The ContentHandler factory.
109.1212 + */
109.1213 + static ContentHandlerFactory factory;
109.1214 +
109.1215 + /**
109.1216 + * Sets the <code>ContentHandlerFactory</code> of an
109.1217 + * application. It can be called at most once by an application.
109.1218 + * <p>
109.1219 + * The <code>ContentHandlerFactory</code> instance is used to
109.1220 + * construct a content handler from a content type
109.1221 + * <p>
109.1222 + * If there is a security manager, this method first calls
109.1223 + * the security manager's <code>checkSetFactory</code> method
109.1224 + * to ensure the operation is allowed.
109.1225 + * This could result in a SecurityException.
109.1226 + *
109.1227 + * @param fac the desired factory.
109.1228 + * @exception Error if the factory has already been defined.
109.1229 + * @exception SecurityException if a security manager exists and its
109.1230 + * <code>checkSetFactory</code> method doesn't allow the operation.
109.1231 + * @see java.net.ContentHandlerFactory
109.1232 + * @see java.net.URLConnection#getContent()
109.1233 + * @see SecurityManager#checkSetFactory
109.1234 + */
109.1235 + public static synchronized void setContentHandlerFactory(ContentHandlerFactory fac) {
109.1236 + throw new SecurityException();
109.1237 + }
109.1238 +
109.1239 + private static Hashtable handlers = new Hashtable();
109.1240 +
109.1241 + /**
109.1242 + * Gets the Content Handler appropriate for this connection.
109.1243 + * @param connection the connection to use.
109.1244 + */
109.1245 + synchronized ContentHandler getContentHandler()
109.1246 + throws UnknownServiceException
109.1247 + {
109.1248 + String contentType = stripOffParameters(getContentType());
109.1249 + ContentHandler handler = null;
109.1250 + if (contentType == null)
109.1251 + throw new UnknownServiceException("no content-type");
109.1252 + try {
109.1253 + handler = (ContentHandler) handlers.get(contentType);
109.1254 + if (handler != null)
109.1255 + return handler;
109.1256 + } catch(Exception e) {
109.1257 + }
109.1258 +
109.1259 + if (factory != null)
109.1260 + handler = factory.createContentHandler(contentType);
109.1261 + if (handler == null) {
109.1262 + try {
109.1263 + handler = lookupContentHandlerClassFor(contentType);
109.1264 + } catch(Exception e) {
109.1265 + e.printStackTrace();
109.1266 + handler = UnknownContentHandler.INSTANCE;
109.1267 + }
109.1268 + handlers.put(contentType, handler);
109.1269 + }
109.1270 + return handler;
109.1271 + }
109.1272 +
109.1273 + /*
109.1274 + * Media types are in the format: type/subtype*(; parameter).
109.1275 + * For looking up the content handler, we should ignore those
109.1276 + * parameters.
109.1277 + */
109.1278 + private String stripOffParameters(String contentType)
109.1279 + {
109.1280 + if (contentType == null)
109.1281 + return null;
109.1282 + int index = contentType.indexOf(';');
109.1283 +
109.1284 + if (index > 0)
109.1285 + return contentType.substring(0, index);
109.1286 + else
109.1287 + return contentType;
109.1288 + }
109.1289 +
109.1290 + private static final String contentClassPrefix = "sun.net.www.content";
109.1291 + private static final String contentPathProp = "java.content.handler.pkgs";
109.1292 +
109.1293 + /**
109.1294 + * Looks for a content handler in a user-defineable set of places.
109.1295 + * By default it looks in sun.net.www.content, but users can define a
109.1296 + * vertical-bar delimited set of class prefixes to search through in
109.1297 + * addition by defining the java.content.handler.pkgs property.
109.1298 + * The class name must be of the form:
109.1299 + * <pre>
109.1300 + * {package-prefix}.{major}.{minor}
109.1301 + * e.g.
109.1302 + * YoyoDyne.experimental.text.plain
109.1303 + * </pre>
109.1304 + */
109.1305 + private ContentHandler lookupContentHandlerClassFor(String contentType)
109.1306 + throws InstantiationException, IllegalAccessException, ClassNotFoundException {
109.1307 + String contentHandlerClassName = typeToPackageName(contentType);
109.1308 +
109.1309 + String contentHandlerPkgPrefixes =getContentHandlerPkgPrefixes();
109.1310 +
109.1311 + StringTokenizer packagePrefixIter =
109.1312 + new StringTokenizer(contentHandlerPkgPrefixes, "|");
109.1313 +
109.1314 + while (packagePrefixIter.hasMoreTokens()) {
109.1315 + String packagePrefix = packagePrefixIter.nextToken().trim();
109.1316 +
109.1317 + try {
109.1318 + String clsName = packagePrefix + "." + contentHandlerClassName;
109.1319 + Class cls = null;
109.1320 + try {
109.1321 + cls = Class.forName(clsName);
109.1322 + } catch (ClassNotFoundException e) {
109.1323 + ClassLoader cl = ClassLoader.getSystemClassLoader();
109.1324 + if (cl != null) {
109.1325 + cls = cl.loadClass(clsName);
109.1326 + }
109.1327 + }
109.1328 + if (cls != null) {
109.1329 + ContentHandler handler =
109.1330 + (ContentHandler)cls.newInstance();
109.1331 + return handler;
109.1332 + }
109.1333 + } catch(Exception e) {
109.1334 + }
109.1335 + }
109.1336 +
109.1337 + return UnknownContentHandler.INSTANCE;
109.1338 + }
109.1339 +
109.1340 + /**
109.1341 + * Utility function to map a MIME content type into an equivalent
109.1342 + * pair of class name components. For example: "text/html" would
109.1343 + * be returned as "text.html"
109.1344 + */
109.1345 + private String typeToPackageName(String contentType) {
109.1346 + // make sure we canonicalize the class name: all lower case
109.1347 + contentType = contentType.toLowerCase();
109.1348 + int len = contentType.length();
109.1349 + char nm[] = new char[len];
109.1350 + contentType.getChars(0, len, nm, 0);
109.1351 + for (int i = 0; i < len; i++) {
109.1352 + char c = nm[i];
109.1353 + if (c == '/') {
109.1354 + nm[i] = '.';
109.1355 + } else if (!('A' <= c && c <= 'Z' ||
109.1356 + 'a' <= c && c <= 'z' ||
109.1357 + '0' <= c && c <= '9')) {
109.1358 + nm[i] = '_';
109.1359 + }
109.1360 + }
109.1361 + return new String(nm);
109.1362 + }
109.1363 +
109.1364 +
109.1365 + /**
109.1366 + * Returns a vertical bar separated list of package prefixes for potential
109.1367 + * content handlers. Tries to get the java.content.handler.pkgs property
109.1368 + * to use as a set of package prefixes to search. Whether or not
109.1369 + * that property has been defined, the sun.net.www.content is always
109.1370 + * the last one on the returned package list.
109.1371 + */
109.1372 + private String getContentHandlerPkgPrefixes() {
109.1373 + String packagePrefixList = "";
109.1374 +
109.1375 + if (packagePrefixList != "") {
109.1376 + packagePrefixList += "|";
109.1377 + }
109.1378 +
109.1379 + return packagePrefixList + contentClassPrefix;
109.1380 + }
109.1381 +
109.1382 + /**
109.1383 + * Tries to determine the content type of an object, based
109.1384 + * on the specified "file" component of a URL.
109.1385 + * This is a convenience method that can be used by
109.1386 + * subclasses that override the <code>getContentType</code> method.
109.1387 + *
109.1388 + * @param fname a filename.
109.1389 + * @return a guess as to what the content type of the object is,
109.1390 + * based upon its file name.
109.1391 + * @see java.net.URLConnection#getContentType()
109.1392 + */
109.1393 + public static String guessContentTypeFromName(String fname) {
109.1394 + return getFileNameMap().getContentTypeFor(fname);
109.1395 + }
109.1396 +
109.1397 + /**
109.1398 + * Tries to determine the type of an input stream based on the
109.1399 + * characters at the beginning of the input stream. This method can
109.1400 + * be used by subclasses that override the
109.1401 + * <code>getContentType</code> method.
109.1402 + * <p>
109.1403 + * Ideally, this routine would not be needed. But many
109.1404 + * <code>http</code> servers return the incorrect content type; in
109.1405 + * addition, there are many nonstandard extensions. Direct inspection
109.1406 + * of the bytes to determine the content type is often more accurate
109.1407 + * than believing the content type claimed by the <code>http</code> server.
109.1408 + *
109.1409 + * @param is an input stream that supports marks.
109.1410 + * @return a guess at the content type, or <code>null</code> if none
109.1411 + * can be determined.
109.1412 + * @exception IOException if an I/O error occurs while reading the
109.1413 + * input stream.
109.1414 + * @see java.io.InputStream#mark(int)
109.1415 + * @see java.io.InputStream#markSupported()
109.1416 + * @see java.net.URLConnection#getContentType()
109.1417 + */
109.1418 + static public String guessContentTypeFromStream(InputStream is)
109.1419 + throws IOException {
109.1420 + // If we can't read ahead safely, just give up on guessing
109.1421 + if (!is.markSupported())
109.1422 + return null;
109.1423 +
109.1424 + is.mark(16);
109.1425 + int c1 = is.read();
109.1426 + int c2 = is.read();
109.1427 + int c3 = is.read();
109.1428 + int c4 = is.read();
109.1429 + int c5 = is.read();
109.1430 + int c6 = is.read();
109.1431 + int c7 = is.read();
109.1432 + int c8 = is.read();
109.1433 + int c9 = is.read();
109.1434 + int c10 = is.read();
109.1435 + int c11 = is.read();
109.1436 + int c12 = is.read();
109.1437 + int c13 = is.read();
109.1438 + int c14 = is.read();
109.1439 + int c15 = is.read();
109.1440 + int c16 = is.read();
109.1441 + is.reset();
109.1442 +
109.1443 + if (c1 == 0xCA && c2 == 0xFE && c3 == 0xBA && c4 == 0xBE) {
109.1444 + return "application/java-vm";
109.1445 + }
109.1446 +
109.1447 + if (c1 == 0xAC && c2 == 0xED) {
109.1448 + // next two bytes are version number, currently 0x00 0x05
109.1449 + return "application/x-java-serialized-object";
109.1450 + }
109.1451 +
109.1452 + if (c1 == '<') {
109.1453 + if (c2 == '!'
109.1454 + || ((c2 == 'h' && (c3 == 't' && c4 == 'm' && c5 == 'l' ||
109.1455 + c3 == 'e' && c4 == 'a' && c5 == 'd') ||
109.1456 + (c2 == 'b' && c3 == 'o' && c4 == 'd' && c5 == 'y'))) ||
109.1457 + ((c2 == 'H' && (c3 == 'T' && c4 == 'M' && c5 == 'L' ||
109.1458 + c3 == 'E' && c4 == 'A' && c5 == 'D') ||
109.1459 + (c2 == 'B' && c3 == 'O' && c4 == 'D' && c5 == 'Y')))) {
109.1460 + return "text/html";
109.1461 + }
109.1462 +
109.1463 + if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l' && c6 == ' ') {
109.1464 + return "application/xml";
109.1465 + }
109.1466 + }
109.1467 +
109.1468 + // big and little (identical) endian UTF-8 encodings, with BOM
109.1469 + if (c1 == 0xef && c2 == 0xbb && c3 == 0xbf) {
109.1470 + if (c4 == '<' && c5 == '?' && c6 == 'x') {
109.1471 + return "application/xml";
109.1472 + }
109.1473 + }
109.1474 +
109.1475 + // big and little endian UTF-16 encodings, with byte order mark
109.1476 + if (c1 == 0xfe && c2 == 0xff) {
109.1477 + if (c3 == 0 && c4 == '<' && c5 == 0 && c6 == '?' &&
109.1478 + c7 == 0 && c8 == 'x') {
109.1479 + return "application/xml";
109.1480 + }
109.1481 + }
109.1482 +
109.1483 + if (c1 == 0xff && c2 == 0xfe) {
109.1484 + if (c3 == '<' && c4 == 0 && c5 == '?' && c6 == 0 &&
109.1485 + c7 == 'x' && c8 == 0) {
109.1486 + return "application/xml";
109.1487 + }
109.1488 + }
109.1489 +
109.1490 + // big and little endian UTF-32 encodings, with BOM
109.1491 + if (c1 == 0x00 && c2 == 0x00 && c3 == 0xfe && c4 == 0xff) {
109.1492 + if (c5 == 0 && c6 == 0 && c7 == 0 && c8 == '<' &&
109.1493 + c9 == 0 && c10 == 0 && c11 == 0 && c12 == '?' &&
109.1494 + c13 == 0 && c14 == 0 && c15 == 0 && c16 == 'x') {
109.1495 + return "application/xml";
109.1496 + }
109.1497 + }
109.1498 +
109.1499 + if (c1 == 0xff && c2 == 0xfe && c3 == 0x00 && c4 == 0x00) {
109.1500 + if (c5 == '<' && c6 == 0 && c7 == 0 && c8 == 0 &&
109.1501 + c9 == '?' && c10 == 0 && c11 == 0 && c12 == 0 &&
109.1502 + c13 == 'x' && c14 == 0 && c15 == 0 && c16 == 0) {
109.1503 + return "application/xml";
109.1504 + }
109.1505 + }
109.1506 +
109.1507 + if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') {
109.1508 + return "image/gif";
109.1509 + }
109.1510 +
109.1511 + if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') {
109.1512 + return "image/x-bitmap";
109.1513 + }
109.1514 +
109.1515 + if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' &&
109.1516 + c5 == 'M' && c6 == '2') {
109.1517 + return "image/x-pixmap";
109.1518 + }
109.1519 +
109.1520 + if (c1 == 137 && c2 == 80 && c3 == 78 &&
109.1521 + c4 == 71 && c5 == 13 && c6 == 10 &&
109.1522 + c7 == 26 && c8 == 10) {
109.1523 + return "image/png";
109.1524 + }
109.1525 +
109.1526 + if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) {
109.1527 + if (c4 == 0xE0) {
109.1528 + return "image/jpeg";
109.1529 + }
109.1530 +
109.1531 + /**
109.1532 + * File format used by digital cameras to store images.
109.1533 + * Exif Format can be read by any application supporting
109.1534 + * JPEG. Exif Spec can be found at:
109.1535 + * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF
109.1536 + */
109.1537 + if ((c4 == 0xE1) &&
109.1538 + (c7 == 'E' && c8 == 'x' && c9 == 'i' && c10 =='f' &&
109.1539 + c11 == 0)) {
109.1540 + return "image/jpeg";
109.1541 + }
109.1542 +
109.1543 + if (c4 == 0xEE) {
109.1544 + return "image/jpg";
109.1545 + }
109.1546 + }
109.1547 +
109.1548 + if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 &&
109.1549 + c5 == 0xA1 && c6 == 0xB1 && c7 == 0x1A && c8 == 0xE1) {
109.1550 +
109.1551 + /* Above is signature of Microsoft Structured Storage.
109.1552 + * Below this, could have tests for various SS entities.
109.1553 + * For now, just test for FlashPix.
109.1554 + */
109.1555 + if (checkfpx(is)) {
109.1556 + return "image/vnd.fpx";
109.1557 + }
109.1558 + }
109.1559 +
109.1560 + if (c1 == 0x2E && c2 == 0x73 && c3 == 0x6E && c4 == 0x64) {
109.1561 + return "audio/basic"; // .au format, big endian
109.1562 + }
109.1563 +
109.1564 + if (c1 == 0x64 && c2 == 0x6E && c3 == 0x73 && c4 == 0x2E) {
109.1565 + return "audio/basic"; // .au format, little endian
109.1566 + }
109.1567 +
109.1568 + if (c1 == 'R' && c2 == 'I' && c3 == 'F' && c4 == 'F') {
109.1569 + /* I don't know if this is official but evidence
109.1570 + * suggests that .wav files start with "RIFF" - brown
109.1571 + */
109.1572 + return "audio/x-wav";
109.1573 + }
109.1574 + return null;
109.1575 + }
109.1576 +
109.1577 + /**
109.1578 + * Check for FlashPix image data in InputStream is. Return true if
109.1579 + * the stream has FlashPix data, false otherwise. Before calling this
109.1580 + * method, the stream should have already been checked to be sure it
109.1581 + * contains Microsoft Structured Storage data.
109.1582 + */
109.1583 + static private boolean checkfpx(InputStream is) throws IOException {
109.1584 +
109.1585 + /* Test for FlashPix image data in Microsoft Structured Storage format.
109.1586 + * In general, should do this with calls to an SS implementation.
109.1587 + * Lacking that, need to dig via offsets to get to the FlashPix
109.1588 + * ClassID. Details:
109.1589 + *
109.1590 + * Offset to Fpx ClsID from beginning of stream should be:
109.1591 + *
109.1592 + * FpxClsidOffset = rootEntryOffset + clsidOffset
109.1593 + *
109.1594 + * where: clsidOffset = 0x50.
109.1595 + * rootEntryOffset = headerSize + sectorSize*sectDirStart
109.1596 + * + 128*rootEntryDirectory
109.1597 + *
109.1598 + * where: headerSize = 0x200 (always)
109.1599 + * sectorSize = 2 raised to power of uSectorShift,
109.1600 + * which is found in the header at
109.1601 + * offset 0x1E.
109.1602 + * sectDirStart = found in the header at offset 0x30.
109.1603 + * rootEntryDirectory = in general, should search for
109.1604 + * directory labelled as root.
109.1605 + * We will assume value of 0 (i.e.,
109.1606 + * rootEntry is in first directory)
109.1607 + */
109.1608 +
109.1609 + // Mark the stream so we can reset it. 0x100 is enough for the first
109.1610 + // few reads, but the mark will have to be reset and set again once
109.1611 + // the offset to the root directory entry is computed. That offset
109.1612 + // can be very large and isn't know until the stream has been read from
109.1613 + is.mark(0x100);
109.1614 +
109.1615 + // Get the byte ordering located at 0x1E. 0xFE is Intel,
109.1616 + // 0xFF is other
109.1617 + long toSkip = (long)0x1C;
109.1618 + long posn;
109.1619 +
109.1620 + if ((posn = skipForward(is, toSkip)) < toSkip) {
109.1621 + is.reset();
109.1622 + return false;
109.1623 + }
109.1624 +
109.1625 + int c[] = new int[16];
109.1626 + if (readBytes(c, 2, is) < 0) {
109.1627 + is.reset();
109.1628 + return false;
109.1629 + }
109.1630 +
109.1631 + int byteOrder = c[0];
109.1632 +
109.1633 + posn+=2;
109.1634 + int uSectorShift;
109.1635 + if (readBytes(c, 2, is) < 0) {
109.1636 + is.reset();
109.1637 + return false;
109.1638 + }
109.1639 +
109.1640 + if(byteOrder == 0xFE) {
109.1641 + uSectorShift = c[0];
109.1642 + uSectorShift += c[1] << 8;
109.1643 + }
109.1644 + else {
109.1645 + uSectorShift = c[0] << 8;
109.1646 + uSectorShift += c[1];
109.1647 + }
109.1648 +
109.1649 + posn += 2;
109.1650 + toSkip = (long)0x30 - posn;
109.1651 + long skipped = 0;
109.1652 + if ((skipped = skipForward(is, toSkip)) < toSkip) {
109.1653 + is.reset();
109.1654 + return false;
109.1655 + }
109.1656 + posn += skipped;
109.1657 +
109.1658 + if (readBytes(c, 4, is) < 0) {
109.1659 + is.reset();
109.1660 + return false;
109.1661 + }
109.1662 +
109.1663 + int sectDirStart;
109.1664 + if(byteOrder == 0xFE) {
109.1665 + sectDirStart = c[0];
109.1666 + sectDirStart += c[1] << 8;
109.1667 + sectDirStart += c[2] << 16;
109.1668 + sectDirStart += c[3] << 24;
109.1669 + } else {
109.1670 + sectDirStart = c[0] << 24;
109.1671 + sectDirStart += c[1] << 16;
109.1672 + sectDirStart += c[2] << 8;
109.1673 + sectDirStart += c[3];
109.1674 + }
109.1675 + posn += 4;
109.1676 + is.reset(); // Reset back to the beginning
109.1677 +
109.1678 + toSkip = 0x200L + (long)(1<<uSectorShift)*sectDirStart + 0x50L;
109.1679 +
109.1680 + // Sanity check!
109.1681 + if (toSkip < 0) {
109.1682 + return false;
109.1683 + }
109.1684 +
109.1685 + /*
109.1686 + * How far can we skip? Is there any performance problem here?
109.1687 + * This skip can be fairly long, at least 0x4c650 in at least
109.1688 + * one case. Have to assume that the skip will fit in an int.
109.1689 + * Leave room to read whole root dir
109.1690 + */
109.1691 + is.mark((int)toSkip+0x30);
109.1692 +
109.1693 + if ((skipForward(is, toSkip)) < toSkip) {
109.1694 + is.reset();
109.1695 + return false;
109.1696 + }
109.1697 +
109.1698 + /* should be at beginning of ClassID, which is as follows
109.1699 + * (in Intel byte order):
109.1700 + * 00 67 61 56 54 C1 CE 11 85 53 00 AA 00 A1 F9 5B
109.1701 + *
109.1702 + * This is stored from Windows as long,short,short,char[8]
109.1703 + * so for byte order changes, the order only changes for
109.1704 + * the first 8 bytes in the ClassID.
109.1705 + *
109.1706 + * Test against this, ignoring second byte (Intel) since
109.1707 + * this could change depending on part of Fpx file we have.
109.1708 + */
109.1709 +
109.1710 + if (readBytes(c, 16, is) < 0) {
109.1711 + is.reset();
109.1712 + return false;
109.1713 + }
109.1714 +
109.1715 + // intel byte order
109.1716 + if (byteOrder == 0xFE &&
109.1717 + c[0] == 0x00 && c[2] == 0x61 && c[3] == 0x56 &&
109.1718 + c[4] == 0x54 && c[5] == 0xC1 && c[6] == 0xCE &&
109.1719 + c[7] == 0x11 && c[8] == 0x85 && c[9] == 0x53 &&
109.1720 + c[10]== 0x00 && c[11]== 0xAA && c[12]== 0x00 &&
109.1721 + c[13]== 0xA1 && c[14]== 0xF9 && c[15]== 0x5B) {
109.1722 + is.reset();
109.1723 + return true;
109.1724 + }
109.1725 +
109.1726 + // non-intel byte order
109.1727 + else if (c[3] == 0x00 && c[1] == 0x61 && c[0] == 0x56 &&
109.1728 + c[5] == 0x54 && c[4] == 0xC1 && c[7] == 0xCE &&
109.1729 + c[6] == 0x11 && c[8] == 0x85 && c[9] == 0x53 &&
109.1730 + c[10]== 0x00 && c[11]== 0xAA && c[12]== 0x00 &&
109.1731 + c[13]== 0xA1 && c[14]== 0xF9 && c[15]== 0x5B) {
109.1732 + is.reset();
109.1733 + return true;
109.1734 + }
109.1735 + is.reset();
109.1736 + return false;
109.1737 + }
109.1738 +
109.1739 + /**
109.1740 + * Tries to read the specified number of bytes from the stream
109.1741 + * Returns -1, If EOF is reached before len bytes are read, returns 0
109.1742 + * otherwise
109.1743 + */
109.1744 + static private int readBytes(int c[], int len, InputStream is)
109.1745 + throws IOException {
109.1746 +
109.1747 + byte buf[] = new byte[len];
109.1748 + if (is.read(buf, 0, len) < len) {
109.1749 + return -1;
109.1750 + }
109.1751 +
109.1752 + // fill the passed in int array
109.1753 + for (int i = 0; i < len; i++) {
109.1754 + c[i] = buf[i] & 0xff;
109.1755 + }
109.1756 + return 0;
109.1757 + }
109.1758 +
109.1759 +
109.1760 + /**
109.1761 + * Skips through the specified number of bytes from the stream
109.1762 + * until either EOF is reached, or the specified
109.1763 + * number of bytes have been skipped
109.1764 + */
109.1765 + static private long skipForward(InputStream is, long toSkip)
109.1766 + throws IOException {
109.1767 +
109.1768 + long eachSkip = 0;
109.1769 + long skipped = 0;
109.1770 +
109.1771 + while (skipped != toSkip) {
109.1772 + eachSkip = is.skip(toSkip - skipped);
109.1773 +
109.1774 + // check if EOF is reached
109.1775 + if (eachSkip <= 0) {
109.1776 + if (is.read() == -1) {
109.1777 + return skipped ;
109.1778 + } else {
109.1779 + skipped++;
109.1780 + }
109.1781 + }
109.1782 + skipped += eachSkip;
109.1783 + }
109.1784 + return skipped;
109.1785 + }
109.1786 +
109.1787 +}
109.1788 +
109.1789 +
109.1790 +class UnknownContentHandler extends ContentHandler {
109.1791 + static final ContentHandler INSTANCE = new UnknownContentHandler();
109.1792 +
109.1793 + public Object getContent(URLConnection uc) throws IOException {
109.1794 + return uc.getInputStream();
109.1795 + }
109.1796 +}
109.1797 +
109.1798 +/** An RFC 844 or MIME message header. Includes methods
109.1799 + for parsing headers from incoming streams, fetching
109.1800 + values, setting values, and printing headers.
109.1801 + Key values of null are legal: they indicate lines in
109.1802 + the header that don't have a valid key, but do have
109.1803 + a value (this isn't legal according to the standard,
109.1804 + but lines like this are everywhere). */
109.1805 +class MessageHeader {
109.1806 + private String keys[];
109.1807 + private String values[];
109.1808 + private int nkeys;
109.1809 +
109.1810 + public MessageHeader () {
109.1811 + grow();
109.1812 + }
109.1813 +
109.1814 + public MessageHeader (InputStream is) throws java.io.IOException {
109.1815 + parseHeader(is);
109.1816 + }
109.1817 +
109.1818 + /**
109.1819 + * Reset a message header (all key/values removed)
109.1820 + */
109.1821 + public synchronized void reset() {
109.1822 + keys = null;
109.1823 + values = null;
109.1824 + nkeys = 0;
109.1825 + grow();
109.1826 + }
109.1827 +
109.1828 + /**
109.1829 + * Find the value that corresponds to this key.
109.1830 + * It finds only the first occurrence of the key.
109.1831 + * @param k the key to find.
109.1832 + * @return null if not found.
109.1833 + */
109.1834 + public synchronized String findValue(String k) {
109.1835 + if (k == null) {
109.1836 + for (int i = nkeys; --i >= 0;)
109.1837 + if (keys[i] == null)
109.1838 + return values[i];
109.1839 + } else
109.1840 + for (int i = nkeys; --i >= 0;) {
109.1841 + if (k.equalsIgnoreCase(keys[i]))
109.1842 + return values[i];
109.1843 + }
109.1844 + return null;
109.1845 + }
109.1846 +
109.1847 + // return the location of the key
109.1848 + public synchronized int getKey(String k) {
109.1849 + for (int i = nkeys; --i >= 0;)
109.1850 + if ((keys[i] == k) ||
109.1851 + (k != null && k.equalsIgnoreCase(keys[i])))
109.1852 + return i;
109.1853 + return -1;
109.1854 + }
109.1855 +
109.1856 + public synchronized String getKey(int n) {
109.1857 + if (n < 0 || n >= nkeys) return null;
109.1858 + return keys[n];
109.1859 + }
109.1860 +
109.1861 + public synchronized String getValue(int n) {
109.1862 + if (n < 0 || n >= nkeys) return null;
109.1863 + return values[n];
109.1864 + }
109.1865 +
109.1866 + /** Deprecated: Use multiValueIterator() instead.
109.1867 + *
109.1868 + * Find the next value that corresponds to this key.
109.1869 + * It finds the first value that follows v. To iterate
109.1870 + * over all the values of a key use:
109.1871 + * <pre>
109.1872 + * for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) {
109.1873 + * ...
109.1874 + * }
109.1875 + * </pre>
109.1876 + */
109.1877 + public synchronized String findNextValue(String k, String v) {
109.1878 + boolean foundV = false;
109.1879 + if (k == null) {
109.1880 + for (int i = nkeys; --i >= 0;)
109.1881 + if (keys[i] == null)
109.1882 + if (foundV)
109.1883 + return values[i];
109.1884 + else if (values[i] == v)
109.1885 + foundV = true;
109.1886 + } else
109.1887 + for (int i = nkeys; --i >= 0;)
109.1888 + if (k.equalsIgnoreCase(keys[i]))
109.1889 + if (foundV)
109.1890 + return values[i];
109.1891 + else if (values[i] == v)
109.1892 + foundV = true;
109.1893 + return null;
109.1894 + }
109.1895 +
109.1896 + class HeaderIterator implements Iterator<String> {
109.1897 + int index = 0;
109.1898 + int next = -1;
109.1899 + String key;
109.1900 + boolean haveNext = false;
109.1901 + Object lock;
109.1902 +
109.1903 + public HeaderIterator (String k, Object lock) {
109.1904 + key = k;
109.1905 + this.lock = lock;
109.1906 + }
109.1907 + public boolean hasNext () {
109.1908 + synchronized (lock) {
109.1909 + if (haveNext) {
109.1910 + return true;
109.1911 + }
109.1912 + while (index < nkeys) {
109.1913 + if (key.equalsIgnoreCase (keys[index])) {
109.1914 + haveNext = true;
109.1915 + next = index++;
109.1916 + return true;
109.1917 + }
109.1918 + index ++;
109.1919 + }
109.1920 + return false;
109.1921 + }
109.1922 + }
109.1923 + public String next() {
109.1924 + synchronized (lock) {
109.1925 + if (haveNext) {
109.1926 + haveNext = false;
109.1927 + return values [next];
109.1928 + }
109.1929 + if (hasNext()) {
109.1930 + return next();
109.1931 + } else {
109.1932 + throw new NoSuchElementException ("No more elements");
109.1933 + }
109.1934 + }
109.1935 + }
109.1936 + public void remove () {
109.1937 + throw new UnsupportedOperationException ("remove not allowed");
109.1938 + }
109.1939 + }
109.1940 +
109.1941 + /**
109.1942 + * return an Iterator that returns all values of a particular
109.1943 + * key in sequence
109.1944 + */
109.1945 + public Iterator<String> multiValueIterator (String k) {
109.1946 + return new HeaderIterator (k, this);
109.1947 + }
109.1948 +
109.1949 + public synchronized Map<String, List<String>> getHeaders() {
109.1950 + return getHeaders(null);
109.1951 + }
109.1952 +
109.1953 + public synchronized Map<String, List<String>> getHeaders(String[] excludeList) {
109.1954 + return filterAndAddHeaders(excludeList, null);
109.1955 + }
109.1956 +
109.1957 + public synchronized Map<String, List<String>> filterAndAddHeaders(String[] excludeList, Map<String, List<String>> include) {
109.1958 + boolean skipIt = false;
109.1959 + Map<String, List<String>> m = new HashMap<String, List<String>>();
109.1960 + for (int i = nkeys; --i >= 0;) {
109.1961 + if (excludeList != null) {
109.1962 + // check if the key is in the excludeList.
109.1963 + // if so, don't include it in the Map.
109.1964 + for (int j = 0; j < excludeList.length; j++) {
109.1965 + if ((excludeList[j] != null) &&
109.1966 + (excludeList[j].equalsIgnoreCase(keys[i]))) {
109.1967 + skipIt = true;
109.1968 + break;
109.1969 + }
109.1970 + }
109.1971 + }
109.1972 + if (!skipIt) {
109.1973 + List<String> l = m.get(keys[i]);
109.1974 + if (l == null) {
109.1975 + l = new ArrayList<String>();
109.1976 + m.put(keys[i], l);
109.1977 + }
109.1978 + l.add(values[i]);
109.1979 + } else {
109.1980 + // reset the flag
109.1981 + skipIt = false;
109.1982 + }
109.1983 + }
109.1984 +
109.1985 + if (include != null) {
109.1986 + Iterator entries = include.entrySet().iterator();
109.1987 + while (entries.hasNext()) {
109.1988 + Map.Entry entry = (Map.Entry)entries.next();
109.1989 + List l = (List)m.get(entry.getKey());
109.1990 + if (l == null) {
109.1991 + l = new ArrayList();
109.1992 + m.put((String)entry.getKey(), l);
109.1993 + }
109.1994 + l.add(entry.getValue());
109.1995 + }
109.1996 + }
109.1997 +
109.1998 + for (String key : m.keySet()) {
109.1999 + m.put(key, Collections.unmodifiableList(m.get(key)));
109.2000 + }
109.2001 +
109.2002 + return Collections.unmodifiableMap(m);
109.2003 + }
109.2004 +
109.2005 + /** Prints the key-value pairs represented by this
109.2006 + header. Also prints the RFC required blank line
109.2007 + at the end. Omits pairs with a null key. */
109.2008 + public synchronized void print(PrintStream p) {
109.2009 + for (int i = 0; i < nkeys; i++)
109.2010 + if (keys[i] != null) {
109.2011 + p.print(keys[i] +
109.2012 + (values[i] != null ? ": "+values[i]: "") + "\r\n");
109.2013 + }
109.2014 + p.print("\r\n");
109.2015 + p.flush();
109.2016 + }
109.2017 +
109.2018 + /** Adds a key value pair to the end of the
109.2019 + header. Duplicates are allowed */
109.2020 + public synchronized void add(String k, String v) {
109.2021 + grow();
109.2022 + keys[nkeys] = k;
109.2023 + values[nkeys] = v;
109.2024 + nkeys++;
109.2025 + }
109.2026 +
109.2027 + /** Prepends a key value pair to the beginning of the
109.2028 + header. Duplicates are allowed */
109.2029 + public synchronized void prepend(String k, String v) {
109.2030 + grow();
109.2031 + for (int i = nkeys; i > 0; i--) {
109.2032 + keys[i] = keys[i-1];
109.2033 + values[i] = values[i-1];
109.2034 + }
109.2035 + keys[0] = k;
109.2036 + values[0] = v;
109.2037 + nkeys++;
109.2038 + }
109.2039 +
109.2040 + /** Overwrite the previous key/val pair at location 'i'
109.2041 + * with the new k/v. If the index didn't exist before
109.2042 + * the key/val is simply tacked onto the end.
109.2043 + */
109.2044 +
109.2045 + public synchronized void set(int i, String k, String v) {
109.2046 + grow();
109.2047 + if (i < 0) {
109.2048 + return;
109.2049 + } else if (i >= nkeys) {
109.2050 + add(k, v);
109.2051 + } else {
109.2052 + keys[i] = k;
109.2053 + values[i] = v;
109.2054 + }
109.2055 + }
109.2056 +
109.2057 +
109.2058 + /** grow the key/value arrays as needed */
109.2059 +
109.2060 + private void grow() {
109.2061 + if (keys == null || nkeys >= keys.length) {
109.2062 + String[] nk = new String[nkeys + 4];
109.2063 + String[] nv = new String[nkeys + 4];
109.2064 + if (keys != null)
109.2065 + System.arraycopy(keys, 0, nk, 0, nkeys);
109.2066 + if (values != null)
109.2067 + System.arraycopy(values, 0, nv, 0, nkeys);
109.2068 + keys = nk;
109.2069 + values = nv;
109.2070 + }
109.2071 + }
109.2072 +
109.2073 + /**
109.2074 + * Remove the key from the header. If there are multiple values under
109.2075 + * the same key, they are all removed.
109.2076 + * Nothing is done if the key doesn't exist.
109.2077 + * After a remove, the other pairs' order are not changed.
109.2078 + * @param k the key to remove
109.2079 + */
109.2080 + public synchronized void remove(String k) {
109.2081 + if(k == null) {
109.2082 + for (int i = 0; i < nkeys; i++) {
109.2083 + while (keys[i] == null && i < nkeys) {
109.2084 + for(int j=i; j<nkeys-1; j++) {
109.2085 + keys[j] = keys[j+1];
109.2086 + values[j] = values[j+1];
109.2087 + }
109.2088 + nkeys--;
109.2089 + }
109.2090 + }
109.2091 + } else {
109.2092 + for (int i = 0; i < nkeys; i++) {
109.2093 + while (k.equalsIgnoreCase(keys[i]) && i < nkeys) {
109.2094 + for(int j=i; j<nkeys-1; j++) {
109.2095 + keys[j] = keys[j+1];
109.2096 + values[j] = values[j+1];
109.2097 + }
109.2098 + nkeys--;
109.2099 + }
109.2100 + }
109.2101 + }
109.2102 + }
109.2103 +
109.2104 + /** Sets the value of a key. If the key already
109.2105 + exists in the header, it's value will be
109.2106 + changed. Otherwise a new key/value pair will
109.2107 + be added to the end of the header. */
109.2108 + public synchronized void set(String k, String v) {
109.2109 + for (int i = nkeys; --i >= 0;)
109.2110 + if (k.equalsIgnoreCase(keys[i])) {
109.2111 + values[i] = v;
109.2112 + return;
109.2113 + }
109.2114 + add(k, v);
109.2115 + }
109.2116 +
109.2117 + /** Set's the value of a key only if there is no
109.2118 + * key with that value already.
109.2119 + */
109.2120 +
109.2121 + public synchronized void setIfNotSet(String k, String v) {
109.2122 + if (findValue(k) == null) {
109.2123 + add(k, v);
109.2124 + }
109.2125 + }
109.2126 +
109.2127 + /** Convert a message-id string to canonical form (strips off
109.2128 + leading and trailing <>s) */
109.2129 + public static String canonicalID(String id) {
109.2130 + if (id == null)
109.2131 + return "";
109.2132 + int st = 0;
109.2133 + int len = id.length();
109.2134 + boolean substr = false;
109.2135 + int c;
109.2136 + while (st < len && ((c = id.charAt(st)) == '<' ||
109.2137 + c <= ' ')) {
109.2138 + st++;
109.2139 + substr = true;
109.2140 + }
109.2141 + while (st < len && ((c = id.charAt(len - 1)) == '>' ||
109.2142 + c <= ' ')) {
109.2143 + len--;
109.2144 + substr = true;
109.2145 + }
109.2146 + return substr ? id.substring(st, len) : id;
109.2147 + }
109.2148 +
109.2149 + /** Parse a MIME header from an input stream. */
109.2150 + public void parseHeader(InputStream is) throws java.io.IOException {
109.2151 + synchronized (this) {
109.2152 + nkeys = 0;
109.2153 + }
109.2154 + mergeHeader(is);
109.2155 + }
109.2156 +
109.2157 + /** Parse and merge a MIME header from an input stream. */
109.2158 + public void mergeHeader(InputStream is) throws java.io.IOException {
109.2159 + if (is == null)
109.2160 + return;
109.2161 + char s[] = new char[10];
109.2162 + int firstc = is.read();
109.2163 + while (firstc != '\n' && firstc != '\r' && firstc >= 0) {
109.2164 + int len = 0;
109.2165 + int keyend = -1;
109.2166 + int c;
109.2167 + boolean inKey = firstc > ' ';
109.2168 + s[len++] = (char) firstc;
109.2169 + parseloop:{
109.2170 + while ((c = is.read()) >= 0) {
109.2171 + switch (c) {
109.2172 + case ':':
109.2173 + if (inKey && len > 0)
109.2174 + keyend = len;
109.2175 + inKey = false;
109.2176 + break;
109.2177 + case '\t':
109.2178 + c = ' ';
109.2179 + case ' ':
109.2180 + inKey = false;
109.2181 + break;
109.2182 + case '\r':
109.2183 + case '\n':
109.2184 + firstc = is.read();
109.2185 + if (c == '\r' && firstc == '\n') {
109.2186 + firstc = is.read();
109.2187 + if (firstc == '\r')
109.2188 + firstc = is.read();
109.2189 + }
109.2190 + if (firstc == '\n' || firstc == '\r' || firstc > ' ')
109.2191 + break parseloop;
109.2192 + /* continuation */
109.2193 + c = ' ';
109.2194 + break;
109.2195 + }
109.2196 + if (len >= s.length) {
109.2197 + char ns[] = new char[s.length * 2];
109.2198 + System.arraycopy(s, 0, ns, 0, len);
109.2199 + s = ns;
109.2200 + }
109.2201 + s[len++] = (char) c;
109.2202 + }
109.2203 + firstc = -1;
109.2204 + }
109.2205 + while (len > 0 && s[len - 1] <= ' ')
109.2206 + len--;
109.2207 + String k;
109.2208 + if (keyend <= 0) {
109.2209 + k = null;
109.2210 + keyend = 0;
109.2211 + } else {
109.2212 + k = String.copyValueOf(s, 0, keyend);
109.2213 + if (keyend < len && s[keyend] == ':')
109.2214 + keyend++;
109.2215 + while (keyend < len && s[keyend] <= ' ')
109.2216 + keyend++;
109.2217 + }
109.2218 + String v;
109.2219 + if (keyend >= len)
109.2220 + v = new String();
109.2221 + else
109.2222 + v = String.copyValueOf(s, keyend, len - keyend);
109.2223 + add(k, v);
109.2224 + }
109.2225 + }
109.2226 +
109.2227 + public synchronized String toString() {
109.2228 + String result = super.toString() + nkeys + " pairs: ";
109.2229 + for (int i = 0; i < keys.length && i < nkeys; i++) {
109.2230 + result += "{"+keys[i]+": "+values[i]+"}";
109.2231 + }
109.2232 + return result;
109.2233 + }
109.2234 +}
109.2235 +
109.2236 +interface ContentHandlerFactory {
109.2237 +
109.2238 + public ContentHandler createContentHandler(String contentType);
109.2239 +}
109.2240 +
109.2241 +abstract class ContentHandler {
109.2242 + /**
109.2243 + * Given a URL connect stream positioned at the beginning of the
109.2244 + * representation of an object, this method reads that stream and
109.2245 + * creates an object from it.
109.2246 + *
109.2247 + * @param urlc a URL connection.
109.2248 + * @return the object read by the <code>ContentHandler</code>.
109.2249 + * @exception IOException if an I/O error occurs while reading the object.
109.2250 + */
109.2251 + abstract public Object getContent(URLConnection urlc) throws IOException;
109.2252 +
109.2253 + /**
109.2254 + * Given a URL connect stream positioned at the beginning of the
109.2255 + * representation of an object, this method reads that stream and
109.2256 + * creates an object that matches one of the types specified.
109.2257 + *
109.2258 + * The default implementation of this method should call getContent()
109.2259 + * and screen the return type for a match of the suggested types.
109.2260 + *
109.2261 + * @param urlc a URL connection.
109.2262 + * @param classes an array of types requested
109.2263 + * @return the object read by the <code>ContentHandler</code> that is
109.2264 + * the first match of the suggested types.
109.2265 + * null if none of the requested are supported.
109.2266 + * @exception IOException if an I/O error occurs while reading the object.
109.2267 + * @since 1.3
109.2268 + */
109.2269 + public Object getContent(URLConnection urlc, Class[] classes) throws IOException {
109.2270 + Object obj = getContent(urlc);
109.2271 +
109.2272 + for (int i = 0; i < classes.length; i++) {
109.2273 + if (classes[i].isInstance(obj)) {
109.2274 + return obj;
109.2275 + }
109.2276 + }
109.2277 + return null;
109.2278 + }
109.2279 +
109.2280 +}
109.2281 +class UnknownServiceException extends IOException {
109.2282 + private static final long serialVersionUID = -4169033248853639508L;
109.2283 +
109.2284 + /**
109.2285 + * Constructs a new <code>UnknownServiceException</code> with no
109.2286 + * detail message.
109.2287 + */
109.2288 + public UnknownServiceException() {
109.2289 + }
109.2290 +
109.2291 + /**
109.2292 + * Constructs a new <code>UnknownServiceException</code> with the
109.2293 + * specified detail message.
109.2294 + *
109.2295 + * @param msg the detail message.
109.2296 + */
109.2297 + public UnknownServiceException(String msg) {
109.2298 + super(msg);
109.2299 + }
109.2300 +}
109.2301 +/**
109.2302 + * A simple interface which provides a mechanism to map
109.2303 + * between a file name and a MIME type string.
109.2304 + *
109.2305 + * @author Steven B. Byrne
109.2306 + * @since JDK1.1
109.2307 + */
109.2308 +interface FileNameMap {
109.2309 +
109.2310 + /**
109.2311 + * Gets the MIME type for the specified file name.
109.2312 + * @param fileName the specified file name
109.2313 + * @return a <code>String</code> indicating the MIME
109.2314 + * type for the specified file name.
109.2315 + */
109.2316 + public String getContentTypeFor(String fileName);
109.2317 +}
110.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
110.2 +++ b/rt/emul/compact/src/main/java/java/nio/charset/Charset.java Tue Feb 11 13:31:42 2014 +0100
110.3 @@ -0,0 +1,765 @@
110.4 +/*
110.5 + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
110.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
110.7 + *
110.8 + * This code is free software; you can redistribute it and/or modify it
110.9 + * under the terms of the GNU General Public License version 2 only, as
110.10 + * published by the Free Software Foundation. Oracle designates this
110.11 + * particular file as subject to the "Classpath" exception as provided
110.12 + * by Oracle in the LICENSE file that accompanied this code.
110.13 + *
110.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
110.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
110.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
110.17 + * version 2 for more details (a copy is included in the LICENSE file that
110.18 + * accompanied this code).
110.19 + *
110.20 + * You should have received a copy of the GNU General Public License version
110.21 + * 2 along with this work; if not, write to the Free Software Foundation,
110.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
110.23 + *
110.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
110.25 + * or visit www.oracle.com if you need additional information or have any
110.26 + * questions.
110.27 + */
110.28 +
110.29 +package java.nio.charset;
110.30 +
110.31 +//import java.nio.ByteBuffer;
110.32 +//import java.nio.CharBuffer;
110.33 +import java.util.Collections;
110.34 +import java.util.HashSet;
110.35 +import java.util.Iterator;
110.36 +import java.util.Locale;
110.37 +import java.util.Map;
110.38 +import java.util.Set;
110.39 +import java.util.SortedMap;
110.40 +import java.util.TreeMap;
110.41 +
110.42 +
110.43 +/**
110.44 + * A named mapping between sequences of sixteen-bit Unicode <a
110.45 + * href="../../lang/Character.html#unicode">code units</a> and sequences of
110.46 + * bytes. This class defines methods for creating decoders and encoders and
110.47 + * for retrieving the various names associated with a charset. Instances of
110.48 + * this class are immutable.
110.49 + *
110.50 + * <p> This class also defines static methods for testing whether a particular
110.51 + * charset is supported, for locating charset instances by name, and for
110.52 + * constructing a map that contains every charset for which support is
110.53 + * available in the current Java virtual machine. Support for new charsets can
110.54 + * be added via the service-provider interface defined in the {@link
110.55 + * java.nio.charset.spi.CharsetProvider} class.
110.56 + *
110.57 + * <p> All of the methods defined in this class are safe for use by multiple
110.58 + * concurrent threads.
110.59 + *
110.60 + *
110.61 + * <a name="names"><a name="charenc">
110.62 + * <h4>Charset names</h4>
110.63 + *
110.64 + * <p> Charsets are named by strings composed of the following characters:
110.65 + *
110.66 + * <ul>
110.67 + *
110.68 + * <li> The uppercase letters <tt>'A'</tt> through <tt>'Z'</tt>
110.69 + * (<tt>'\u0041'</tt> through <tt>'\u005a'</tt>),
110.70 + *
110.71 + * <li> The lowercase letters <tt>'a'</tt> through <tt>'z'</tt>
110.72 + * (<tt>'\u0061'</tt> through <tt>'\u007a'</tt>),
110.73 + *
110.74 + * <li> The digits <tt>'0'</tt> through <tt>'9'</tt>
110.75 + * (<tt>'\u0030'</tt> through <tt>'\u0039'</tt>),
110.76 + *
110.77 + * <li> The dash character <tt>'-'</tt>
110.78 + * (<tt>'\u002d'</tt>, <small>HYPHEN-MINUS</small>),
110.79 + *
110.80 + * <li> The plus character <tt>'+'</tt>
110.81 + * (<tt>'\u002b'</tt>, <small>PLUS SIGN</small>),
110.82 + *
110.83 + * <li> The period character <tt>'.'</tt>
110.84 + * (<tt>'\u002e'</tt>, <small>FULL STOP</small>),
110.85 + *
110.86 + * <li> The colon character <tt>':'</tt>
110.87 + * (<tt>'\u003a'</tt>, <small>COLON</small>), and
110.88 + *
110.89 + * <li> The underscore character <tt>'_'</tt>
110.90 + * (<tt>'\u005f'</tt>, <small>LOW LINE</small>).
110.91 + *
110.92 + * </ul>
110.93 + *
110.94 + * A charset name must begin with either a letter or a digit. The empty string
110.95 + * is not a legal charset name. Charset names are not case-sensitive; that is,
110.96 + * case is always ignored when comparing charset names. Charset names
110.97 + * generally follow the conventions documented in <a
110.98 + * href="http://www.ietf.org/rfc/rfc2278.txt"><i>RFC 2278: IANA Charset
110.99 + * Registration Procedures</i></a>.
110.100 + *
110.101 + * <p> Every charset has a <i>canonical name</i> and may also have one or more
110.102 + * <i>aliases</i>. The canonical name is returned by the {@link #name() name} method
110.103 + * of this class. Canonical names are, by convention, usually in upper case.
110.104 + * The aliases of a charset are returned by the {@link #aliases() aliases}
110.105 + * method.
110.106 + *
110.107 + * <a name="hn">
110.108 + *
110.109 + * <p> Some charsets have an <i>historical name</i> that is defined for
110.110 + * compatibility with previous versions of the Java platform. A charset's
110.111 + * historical name is either its canonical name or one of its aliases. The
110.112 + * historical name is returned by the <tt>getEncoding()</tt> methods of the
110.113 + * {@link java.io.InputStreamReader#getEncoding InputStreamReader} and {@link
110.114 + * java.io.OutputStreamWriter#getEncoding OutputStreamWriter} classes.
110.115 + *
110.116 + * <a name="iana">
110.117 + *
110.118 + * <p> If a charset listed in the <a
110.119 + * href="http://www.iana.org/assignments/character-sets"><i>IANA Charset
110.120 + * Registry</i></a> is supported by an implementation of the Java platform then
110.121 + * its canonical name must be the name listed in the registry. Many charsets
110.122 + * are given more than one name in the registry, in which case the registry
110.123 + * identifies one of the names as <i>MIME-preferred</i>. If a charset has more
110.124 + * than one registry name then its canonical name must be the MIME-preferred
110.125 + * name and the other names in the registry must be valid aliases. If a
110.126 + * supported charset is not listed in the IANA registry then its canonical name
110.127 + * must begin with one of the strings <tt>"X-"</tt> or <tt>"x-"</tt>.
110.128 + *
110.129 + * <p> The IANA charset registry does change over time, and so the canonical
110.130 + * name and the aliases of a particular charset may also change over time. To
110.131 + * ensure compatibility it is recommended that no alias ever be removed from a
110.132 + * charset, and that if the canonical name of a charset is changed then its
110.133 + * previous canonical name be made into an alias.
110.134 + *
110.135 + *
110.136 + * <h4>Standard charsets</h4>
110.137 + *
110.138 + * <a name="standard">
110.139 + *
110.140 + * <p> Every implementation of the Java platform is required to support the
110.141 + * following standard charsets. Consult the release documentation for your
110.142 + * implementation to see if any other charsets are supported. The behavior
110.143 + * of such optional charsets may differ between implementations.
110.144 + *
110.145 + * <blockquote><table width="80%" summary="Description of standard charsets">
110.146 + * <tr><th><p align="left">Charset</p></th><th><p align="left">Description</p></th></tr>
110.147 + * <tr><td valign=top><tt>US-ASCII</tt></td>
110.148 + * <td>Seven-bit ASCII, a.k.a. <tt>ISO646-US</tt>,
110.149 + * a.k.a. the Basic Latin block of the Unicode character set</td></tr>
110.150 + * <tr><td valign=top><tt>ISO-8859-1 </tt></td>
110.151 + * <td>ISO Latin Alphabet No. 1, a.k.a. <tt>ISO-LATIN-1</tt></td></tr>
110.152 + * <tr><td valign=top><tt>UTF-8</tt></td>
110.153 + * <td>Eight-bit UCS Transformation Format</td></tr>
110.154 + * <tr><td valign=top><tt>UTF-16BE</tt></td>
110.155 + * <td>Sixteen-bit UCS Transformation Format,
110.156 + * big-endian byte order</td></tr>
110.157 + * <tr><td valign=top><tt>UTF-16LE</tt></td>
110.158 + * <td>Sixteen-bit UCS Transformation Format,
110.159 + * little-endian byte order</td></tr>
110.160 + * <tr><td valign=top><tt>UTF-16</tt></td>
110.161 + * <td>Sixteen-bit UCS Transformation Format,
110.162 + * byte order identified by an optional byte-order mark</td></tr>
110.163 + * </table></blockquote>
110.164 + *
110.165 + * <p> The <tt>UTF-8</tt> charset is specified by <a
110.166 + * href="http://www.ietf.org/rfc/rfc2279.txt"><i>RFC 2279</i></a>; the
110.167 + * transformation format upon which it is based is specified in
110.168 + * Amendment 2 of ISO 10646-1 and is also described in the <a
110.169 + * href="http://www.unicode.org/unicode/standard/standard.html"><i>Unicode
110.170 + * Standard</i></a>.
110.171 + *
110.172 + * <p> The <tt>UTF-16</tt> charsets are specified by <a
110.173 + * href="http://www.ietf.org/rfc/rfc2781.txt"><i>RFC 2781</i></a>; the
110.174 + * transformation formats upon which they are based are specified in
110.175 + * Amendment 1 of ISO 10646-1 and are also described in the <a
110.176 + * href="http://www.unicode.org/unicode/standard/standard.html"><i>Unicode
110.177 + * Standard</i></a>.
110.178 + *
110.179 + * <p> The <tt>UTF-16</tt> charsets use sixteen-bit quantities and are
110.180 + * therefore sensitive to byte order. In these encodings the byte order of a
110.181 + * stream may be indicated by an initial <i>byte-order mark</i> represented by
110.182 + * the Unicode character <tt>'\uFEFF'</tt>. Byte-order marks are handled
110.183 + * as follows:
110.184 + *
110.185 + * <ul>
110.186 + *
110.187 + * <li><p> When decoding, the <tt>UTF-16BE</tt> and <tt>UTF-16LE</tt>
110.188 + * charsets interpret the initial byte-order marks as a <small>ZERO-WIDTH
110.189 + * NON-BREAKING SPACE</small>; when encoding, they do not write
110.190 + * byte-order marks. </p></li>
110.191 +
110.192 + *
110.193 + * <li><p> When decoding, the <tt>UTF-16</tt> charset interprets the
110.194 + * byte-order mark at the beginning of the input stream to indicate the
110.195 + * byte-order of the stream but defaults to big-endian if there is no
110.196 + * byte-order mark; when encoding, it uses big-endian byte order and writes
110.197 + * a big-endian byte-order mark. </p></li>
110.198 + *
110.199 + * </ul>
110.200 + *
110.201 + * In any case, byte order marks occuring after the first element of an
110.202 + * input sequence are not omitted since the same code is used to represent
110.203 + * <small>ZERO-WIDTH NON-BREAKING SPACE</small>.
110.204 + *
110.205 + * <p> Every instance of the Java virtual machine has a default charset, which
110.206 + * may or may not be one of the standard charsets. The default charset is
110.207 + * determined during virtual-machine startup and typically depends upon the
110.208 + * locale and charset being used by the underlying operating system. </p>
110.209 + *
110.210 + * <p>The {@link StandardCharsets} class defines constants for each of the
110.211 + * standard charsets.
110.212 + *
110.213 + * <h4>Terminology</h4>
110.214 + *
110.215 + * <p> The name of this class is taken from the terms used in
110.216 + * <a href="http://www.ietf.org/rfc/rfc2278.txt"><i>RFC 2278</i></a>.
110.217 + * In that document a <i>charset</i> is defined as the combination of
110.218 + * one or more coded character sets and a character-encoding scheme.
110.219 + * (This definition is confusing; some other software systems define
110.220 + * <i>charset</i> as a synonym for <i>coded character set</i>.)
110.221 + *
110.222 + * <p> A <i>coded character set</i> is a mapping between a set of abstract
110.223 + * characters and a set of integers. US-ASCII, ISO 8859-1,
110.224 + * JIS X 0201, and Unicode are examples of coded character sets.
110.225 + *
110.226 + * <p> Some standards have defined a <i>character set</i> to be simply a
110.227 + * set of abstract characters without an associated assigned numbering.
110.228 + * An alphabet is an example of such a character set. However, the subtle
110.229 + * distinction between <i>character set</i> and <i>coded character set</i>
110.230 + * is rarely used in practice; the former has become a short form for the
110.231 + * latter, including in the Java API specification.
110.232 + *
110.233 + * <p> A <i>character-encoding scheme</i> is a mapping between one or more
110.234 + * coded character sets and a set of octet (eight-bit byte) sequences.
110.235 + * UTF-8, UTF-16, ISO 2022, and EUC are examples of
110.236 + * character-encoding schemes. Encoding schemes are often associated with
110.237 + * a particular coded character set; UTF-8, for example, is used only to
110.238 + * encode Unicode. Some schemes, however, are associated with multiple
110.239 + * coded character sets; EUC, for example, can be used to encode
110.240 + * characters in a variety of Asian coded character sets.
110.241 + *
110.242 + * <p> When a coded character set is used exclusively with a single
110.243 + * character-encoding scheme then the corresponding charset is usually
110.244 + * named for the coded character set; otherwise a charset is usually named
110.245 + * for the encoding scheme and, possibly, the locale of the coded
110.246 + * character sets that it supports. Hence <tt>US-ASCII</tt> is both the
110.247 + * name of a coded character set and of the charset that encodes it, while
110.248 + * <tt>EUC-JP</tt> is the name of the charset that encodes the
110.249 + * JIS X 0201, JIS X 0208, and JIS X 0212
110.250 + * coded character sets for the Japanese language.
110.251 + *
110.252 + * <p> The native character encoding of the Java programming language is
110.253 + * UTF-16. A charset in the Java platform therefore defines a mapping
110.254 + * between sequences of sixteen-bit UTF-16 code units (that is, sequences
110.255 + * of chars) and sequences of bytes. </p>
110.256 + *
110.257 + *
110.258 + * @author Mark Reinhold
110.259 + * @author JSR-51 Expert Group
110.260 + * @since 1.4
110.261 + *
110.262 + * @see CharsetDecoder
110.263 + * @see CharsetEncoder
110.264 + * @see java.nio.charset.spi.CharsetProvider
110.265 + * @see java.lang.Character
110.266 + */
110.267 +
110.268 +public abstract class Charset
110.269 + implements Comparable<Charset>
110.270 +{
110.271 +
110.272 + /* -- Static methods -- */
110.273 +
110.274 + private static volatile String bugLevel = null;
110.275 +
110.276 + /**
110.277 + * Checks that the given string is a legal charset name. </p>
110.278 + *
110.279 + * @param s
110.280 + * A purported charset name
110.281 + *
110.282 + * @throws IllegalCharsetNameException
110.283 + * If the given name is not a legal charset name
110.284 + */
110.285 + private static void checkName(String s) {
110.286 + int n = s.length();
110.287 + if (n == 0)
110.288 + throw new IllegalCharsetNameException(s);
110.289 + for (int i = 0; i < n; i++) {
110.290 + char c = s.charAt(i);
110.291 + if (c >= 'A' && c <= 'Z') continue;
110.292 + if (c >= 'a' && c <= 'z') continue;
110.293 + if (c >= '0' && c <= '9') continue;
110.294 + if (c == '-' && i != 0) continue;
110.295 + if (c == '+' && i != 0) continue;
110.296 + if (c == ':' && i != 0) continue;
110.297 + if (c == '_' && i != 0) continue;
110.298 + if (c == '.' && i != 0) continue;
110.299 + throw new IllegalCharsetNameException(s);
110.300 + }
110.301 + }
110.302 +
110.303 + // Cache of the most-recently-returned charsets,
110.304 + // along with the names that were used to find them
110.305 + //
110.306 + private static volatile Object[] cache1 = null; // "Level 1" cache
110.307 + private static volatile Object[] cache2 = null; // "Level 2" cache
110.308 +
110.309 + private static void cache(String charsetName, Charset cs) {
110.310 + cache2 = cache1;
110.311 + cache1 = new Object[] { charsetName, cs };
110.312 + }
110.313 +
110.314 + // Creates an iterator that walks over the available providers, ignoring
110.315 + // those whose lookup or instantiation causes a security exception to be
110.316 + // thrown. Should be invoked with full privileges.
110.317 + //
110.318 + private static Iterator providers() {
110.319 + return Collections.emptyIterator();
110.320 + }
110.321 +
110.322 + // Thread-local gate to prevent recursive provider lookups
110.323 + private static ThreadLocal<ThreadLocal> gate = new ThreadLocal<ThreadLocal>();
110.324 +
110.325 + private static Charset lookupViaProviders(final String charsetName) {
110.326 + return null;
110.327 + }
110.328 +
110.329 + /* The extended set of charsets */
110.330 + private static Object extendedProviderLock = new Object();
110.331 + private static boolean extendedProviderProbed = false;
110.332 +
110.333 +
110.334 + private static Charset lookupExtendedCharset(String charsetName) {
110.335 + return null;
110.336 + }
110.337 +
110.338 + private static Charset lookup(String charsetName) {
110.339 + if (charsetName == null)
110.340 + throw new IllegalArgumentException("Null charset name");
110.341 +
110.342 + Object[] a;
110.343 + if ((a = cache1) != null && charsetName.equals(a[0]))
110.344 + return (Charset)a[1];
110.345 + // We expect most programs to use one Charset repeatedly.
110.346 + // We convey a hint to this effect to the VM by putting the
110.347 + // level 1 cache miss code in a separate method.
110.348 + return lookup2(charsetName);
110.349 + }
110.350 +
110.351 + private static Charset lookup2(String charsetName) {
110.352 + Object[] a;
110.353 + if ((a = cache2) != null && charsetName.equals(a[0])) {
110.354 + cache2 = cache1;
110.355 + cache1 = a;
110.356 + return (Charset)a[1];
110.357 + }
110.358 +
110.359 + /* Only need to check the name if we didn't find a charset for it */
110.360 + checkName(charsetName);
110.361 + return null;
110.362 + }
110.363 +
110.364 + /**
110.365 + * Tells whether the named charset is supported. </p>
110.366 + *
110.367 + * @param charsetName
110.368 + * The name of the requested charset; may be either
110.369 + * a canonical name or an alias
110.370 + *
110.371 + * @return <tt>true</tt> if, and only if, support for the named charset
110.372 + * is available in the current Java virtual machine
110.373 + *
110.374 + * @throws IllegalCharsetNameException
110.375 + * If the given charset name is illegal
110.376 + *
110.377 + * @throws IllegalArgumentException
110.378 + * If the given <tt>charsetName</tt> is null
110.379 + */
110.380 + public static boolean isSupported(String charsetName) {
110.381 + return (lookup(charsetName) != null);
110.382 + }
110.383 +
110.384 + /**
110.385 + * Returns a charset object for the named charset. </p>
110.386 + *
110.387 + * @param charsetName
110.388 + * The name of the requested charset; may be either
110.389 + * a canonical name or an alias
110.390 + *
110.391 + * @return A charset object for the named charset
110.392 + *
110.393 + * @throws IllegalCharsetNameException
110.394 + * If the given charset name is illegal
110.395 + *
110.396 + * @throws IllegalArgumentException
110.397 + * If the given <tt>charsetName</tt> is null
110.398 + *
110.399 + * @throws UnsupportedCharsetException
110.400 + * If no support for the named charset is available
110.401 + * in this instance of the Java virtual machine
110.402 + */
110.403 + public static Charset forName(String charsetName) {
110.404 + Charset cs = lookup(charsetName);
110.405 + if (cs != null)
110.406 + return cs;
110.407 + throw new UnsupportedCharsetException(charsetName);
110.408 + }
110.409 +
110.410 + // Fold charsets from the given iterator into the given map, ignoring
110.411 + // charsets whose names already have entries in the map.
110.412 + //
110.413 + private static void put(Iterator<Charset> i, Map<String,Charset> m) {
110.414 + while (i.hasNext()) {
110.415 + Charset cs = i.next();
110.416 + if (!m.containsKey(cs.name()))
110.417 + m.put(cs.name(), cs);
110.418 + }
110.419 + }
110.420 +
110.421 + /**
110.422 + * Constructs a sorted map from canonical charset names to charset objects.
110.423 + *
110.424 + * <p> The map returned by this method will have one entry for each charset
110.425 + * for which support is available in the current Java virtual machine. If
110.426 + * two or more supported charsets have the same canonical name then the
110.427 + * resulting map will contain just one of them; which one it will contain
110.428 + * is not specified. </p>
110.429 + *
110.430 + * <p> The invocation of this method, and the subsequent use of the
110.431 + * resulting map, may cause time-consuming disk or network I/O operations
110.432 + * to occur. This method is provided for applications that need to
110.433 + * enumerate all of the available charsets, for example to allow user
110.434 + * charset selection. This method is not used by the {@link #forName
110.435 + * forName} method, which instead employs an efficient incremental lookup
110.436 + * algorithm.
110.437 + *
110.438 + * <p> This method may return different results at different times if new
110.439 + * charset providers are dynamically made available to the current Java
110.440 + * virtual machine. In the absence of such changes, the charsets returned
110.441 + * by this method are exactly those that can be retrieved via the {@link
110.442 + * #forName forName} method. </p>
110.443 + *
110.444 + * @return An immutable, case-insensitive map from canonical charset names
110.445 + * to charset objects
110.446 + */
110.447 + public static SortedMap<String,Charset> availableCharsets() {
110.448 + TreeMap<String, Charset> tm = new TreeMap<String,Charset>();
110.449 + tm.put("UTF-8", Charset.defaultCharset());
110.450 + return tm;
110.451 + }
110.452 +
110.453 + private static volatile Charset defaultCharset;
110.454 +
110.455 + /**
110.456 + * Returns the default charset of this Java virtual machine.
110.457 + *
110.458 + * <p> The default charset is determined during virtual-machine startup and
110.459 + * typically depends upon the locale and charset of the underlying
110.460 + * operating system.
110.461 + *
110.462 + * @return A charset object for the default charset
110.463 + *
110.464 + * @since 1.5
110.465 + */
110.466 + public static Charset defaultCharset() {
110.467 + if (defaultCharset == null) {
110.468 + defaultCharset = forName("UTF-8");
110.469 + }
110.470 + return defaultCharset;
110.471 + }
110.472 +
110.473 +
110.474 + /* -- Instance fields and methods -- */
110.475 +
110.476 + private final String name; // tickles a bug in oldjavac
110.477 + private final String[] aliases; // tickles a bug in oldjavac
110.478 + private Set<String> aliasSet = null;
110.479 +
110.480 + /**
110.481 + * Initializes a new charset with the given canonical name and alias
110.482 + * set. </p>
110.483 + *
110.484 + * @param canonicalName
110.485 + * The canonical name of this charset
110.486 + *
110.487 + * @param aliases
110.488 + * An array of this charset's aliases, or null if it has no aliases
110.489 + *
110.490 + * @throws IllegalCharsetNameException
110.491 + * If the canonical name or any of the aliases are illegal
110.492 + */
110.493 + protected Charset(String canonicalName, String[] aliases) {
110.494 + checkName(canonicalName);
110.495 + String[] as = (aliases == null) ? new String[0] : aliases;
110.496 + for (int i = 0; i < as.length; i++)
110.497 + checkName(as[i]);
110.498 + this.name = canonicalName;
110.499 + this.aliases = as;
110.500 + }
110.501 +
110.502 + /**
110.503 + * Returns this charset's canonical name. </p>
110.504 + *
110.505 + * @return The canonical name of this charset
110.506 + */
110.507 + public final String name() {
110.508 + return name;
110.509 + }
110.510 +
110.511 + /**
110.512 + * Returns a set containing this charset's aliases. </p>
110.513 + *
110.514 + * @return An immutable set of this charset's aliases
110.515 + */
110.516 + public final Set<String> aliases() {
110.517 + if (aliasSet != null)
110.518 + return aliasSet;
110.519 + int n = aliases.length;
110.520 + HashSet<String> hs = new HashSet<String>(n);
110.521 + for (int i = 0; i < n; i++)
110.522 + hs.add(aliases[i]);
110.523 + aliasSet = Collections.unmodifiableSet(hs);
110.524 + return aliasSet;
110.525 + }
110.526 +
110.527 + /**
110.528 + * Returns this charset's human-readable name for the default locale.
110.529 + *
110.530 + * <p> The default implementation of this method simply returns this
110.531 + * charset's canonical name. Concrete subclasses of this class may
110.532 + * override this method in order to provide a localized display name. </p>
110.533 + *
110.534 + * @return The display name of this charset in the default locale
110.535 + */
110.536 + public String displayName() {
110.537 + return name;
110.538 + }
110.539 +
110.540 + /**
110.541 + * Tells whether or not this charset is registered in the <a
110.542 + * href="http://www.iana.org/assignments/character-sets">IANA Charset
110.543 + * Registry</a>. </p>
110.544 + *
110.545 + * @return <tt>true</tt> if, and only if, this charset is known by its
110.546 + * implementor to be registered with the IANA
110.547 + */
110.548 + public final boolean isRegistered() {
110.549 + return !name.startsWith("X-") && !name.startsWith("x-");
110.550 + }
110.551 +
110.552 + /**
110.553 + * Returns this charset's human-readable name for the given locale.
110.554 + *
110.555 + * <p> The default implementation of this method simply returns this
110.556 + * charset's canonical name. Concrete subclasses of this class may
110.557 + * override this method in order to provide a localized display name. </p>
110.558 + *
110.559 + * @param locale
110.560 + * The locale for which the display name is to be retrieved
110.561 + *
110.562 + * @return The display name of this charset in the given locale
110.563 + */
110.564 + public String displayName(Locale locale) {
110.565 + return name;
110.566 + }
110.567 +
110.568 + /**
110.569 + * Tells whether or not this charset contains the given charset.
110.570 + *
110.571 + * <p> A charset <i>C</i> is said to <i>contain</i> a charset <i>D</i> if,
110.572 + * and only if, every character representable in <i>D</i> is also
110.573 + * representable in <i>C</i>. If this relationship holds then it is
110.574 + * guaranteed that every string that can be encoded in <i>D</i> can also be
110.575 + * encoded in <i>C</i> without performing any replacements.
110.576 + *
110.577 + * <p> That <i>C</i> contains <i>D</i> does not imply that each character
110.578 + * representable in <i>C</i> by a particular byte sequence is represented
110.579 + * in <i>D</i> by the same byte sequence, although sometimes this is the
110.580 + * case.
110.581 + *
110.582 + * <p> Every charset contains itself.
110.583 + *
110.584 + * <p> This method computes an approximation of the containment relation:
110.585 + * If it returns <tt>true</tt> then the given charset is known to be
110.586 + * contained by this charset; if it returns <tt>false</tt>, however, then
110.587 + * it is not necessarily the case that the given charset is not contained
110.588 + * in this charset.
110.589 + *
110.590 + * @return <tt>true</tt> if the given charset is contained in this charset
110.591 + */
110.592 + public abstract boolean contains(Charset cs);
110.593 +
110.594 + /**
110.595 + * Constructs a new decoder for this charset. </p>
110.596 + *
110.597 + * @return A new decoder for this charset
110.598 + */
110.599 + public abstract CharsetDecoder newDecoder();
110.600 +
110.601 + /**
110.602 + * Constructs a new encoder for this charset. </p>
110.603 + *
110.604 + * @return A new encoder for this charset
110.605 + *
110.606 + * @throws UnsupportedOperationException
110.607 + * If this charset does not support encoding
110.608 + */
110.609 + public abstract CharsetEncoder newEncoder();
110.610 +
110.611 + /**
110.612 + * Tells whether or not this charset supports encoding.
110.613 + *
110.614 + * <p> Nearly all charsets support encoding. The primary exceptions are
110.615 + * special-purpose <i>auto-detect</i> charsets whose decoders can determine
110.616 + * which of several possible encoding schemes is in use by examining the
110.617 + * input byte sequence. Such charsets do not support encoding because
110.618 + * there is no way to determine which encoding should be used on output.
110.619 + * Implementations of such charsets should override this method to return
110.620 + * <tt>false</tt>. </p>
110.621 + *
110.622 + * @return <tt>true</tt> if, and only if, this charset supports encoding
110.623 + */
110.624 + public boolean canEncode() {
110.625 + return true;
110.626 + }
110.627 +
110.628 + /**
110.629 + * Convenience method that decodes bytes in this charset into Unicode
110.630 + * characters.
110.631 + *
110.632 + * <p> An invocation of this method upon a charset <tt>cs</tt> returns the
110.633 + * same result as the expression
110.634 + *
110.635 + * <pre>
110.636 + * cs.newDecoder()
110.637 + * .onMalformedInput(CodingErrorAction.REPLACE)
110.638 + * .onUnmappableCharacter(CodingErrorAction.REPLACE)
110.639 + * .decode(bb); </pre>
110.640 + *
110.641 + * except that it is potentially more efficient because it can cache
110.642 + * decoders between successive invocations.
110.643 + *
110.644 + * <p> This method always replaces malformed-input and unmappable-character
110.645 + * sequences with this charset's default replacement byte array. In order
110.646 + * to detect such sequences, use the {@link
110.647 + * CharsetDecoder#decode(java.nio.ByteBuffer)} method directly. </p>
110.648 + *
110.649 + * @param bb The byte buffer to be decoded
110.650 + *
110.651 + * @return A char buffer containing the decoded characters
110.652 + */
110.653 +// public final CharBuffer decode(ByteBuffer bb) {
110.654 +// try {
110.655 +// return ThreadLocalCoders.decoderFor(this)
110.656 +// .onMalformedInput(CodingErrorAction.REPLACE)
110.657 +// .onUnmappableCharacter(CodingErrorAction.REPLACE)
110.658 +// .decode(bb);
110.659 +// } catch (CharacterCodingException x) {
110.660 +// throw new Error(x); // Can't happen
110.661 +// }
110.662 +// }
110.663 +
110.664 + /**
110.665 + * Convenience method that encodes Unicode characters into bytes in this
110.666 + * charset.
110.667 + *
110.668 + * <p> An invocation of this method upon a charset <tt>cs</tt> returns the
110.669 + * same result as the expression
110.670 + *
110.671 + * <pre>
110.672 + * cs.newEncoder()
110.673 + * .onMalformedInput(CodingErrorAction.REPLACE)
110.674 + * .onUnmappableCharacter(CodingErrorAction.REPLACE)
110.675 + * .encode(bb); </pre>
110.676 + *
110.677 + * except that it is potentially more efficient because it can cache
110.678 + * encoders between successive invocations.
110.679 + *
110.680 + * <p> This method always replaces malformed-input and unmappable-character
110.681 + * sequences with this charset's default replacement string. In order to
110.682 + * detect such sequences, use the {@link
110.683 + * CharsetEncoder#encode(java.nio.CharBuffer)} method directly. </p>
110.684 + *
110.685 + * @param cb The char buffer to be encoded
110.686 + *
110.687 + * @return A byte buffer containing the encoded characters
110.688 + */
110.689 +// public final ByteBuffer encode(CharBuffer cb) {
110.690 +// try {
110.691 +// return ThreadLocalCoders.encoderFor(this)
110.692 +// .onMalformedInput(CodingErrorAction.REPLACE)
110.693 +// .onUnmappableCharacter(CodingErrorAction.REPLACE)
110.694 +// .encode(cb);
110.695 +// } catch (CharacterCodingException x) {
110.696 +// throw new Error(x); // Can't happen
110.697 +// }
110.698 +// }
110.699 +
110.700 + /**
110.701 + * Convenience method that encodes a string into bytes in this charset.
110.702 + *
110.703 + * <p> An invocation of this method upon a charset <tt>cs</tt> returns the
110.704 + * same result as the expression
110.705 + *
110.706 + * <pre>
110.707 + * cs.encode(CharBuffer.wrap(s)); </pre>
110.708 + *
110.709 + * @param str The string to be encoded
110.710 + *
110.711 + * @return A byte buffer containing the encoded characters
110.712 + */
110.713 +// public final ByteBuffer encode(String str) {
110.714 +// return encode(CharBuffer.wrap(str));
110.715 +// }
110.716 +
110.717 + /**
110.718 + * Compares this charset to another.
110.719 + *
110.720 + * <p> Charsets are ordered by their canonical names, without regard to
110.721 + * case. </p>
110.722 + *
110.723 + * @param that
110.724 + * The charset to which this charset is to be compared
110.725 + *
110.726 + * @return A negative integer, zero, or a positive integer as this charset
110.727 + * is less than, equal to, or greater than the specified charset
110.728 + */
110.729 + public final int compareTo(Charset that) {
110.730 + return (name().compareToIgnoreCase(that.name()));
110.731 + }
110.732 +
110.733 + /**
110.734 + * Computes a hashcode for this charset. </p>
110.735 + *
110.736 + * @return An integer hashcode
110.737 + */
110.738 + public final int hashCode() {
110.739 + return name().hashCode();
110.740 + }
110.741 +
110.742 + /**
110.743 + * Tells whether or not this object is equal to another.
110.744 + *
110.745 + * <p> Two charsets are equal if, and only if, they have the same canonical
110.746 + * names. A charset is never equal to any other type of object. </p>
110.747 + *
110.748 + * @return <tt>true</tt> if, and only if, this charset is equal to the
110.749 + * given object
110.750 + */
110.751 + public final boolean equals(Object ob) {
110.752 + if (!(ob instanceof Charset))
110.753 + return false;
110.754 + if (this == ob)
110.755 + return true;
110.756 + return name.equals(((Charset)ob).name());
110.757 + }
110.758 +
110.759 + /**
110.760 + * Returns a string describing this charset. </p>
110.761 + *
110.762 + * @return A string describing this charset
110.763 + */
110.764 + public final String toString() {
110.765 + return name();
110.766 + }
110.767 +
110.768 +}
111.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
111.2 +++ b/rt/emul/compact/src/main/java/java/nio/charset/CharsetDecoder.java Tue Feb 11 13:31:42 2014 +0100
111.3 @@ -0,0 +1,970 @@
111.4 +/*
111.5 + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
111.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
111.7 + *
111.8 + * This code is free software; you can redistribute it and/or modify it
111.9 + * under the terms of the GNU General Public License version 2 only, as
111.10 + * published by the Free Software Foundation. Oracle designates this
111.11 + * particular file as subject to the "Classpath" exception as provided
111.12 + * by Oracle in the LICENSE file that accompanied this code.
111.13 + *
111.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
111.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
111.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
111.17 + * version 2 for more details (a copy is included in the LICENSE file that
111.18 + * accompanied this code).
111.19 + *
111.20 + * You should have received a copy of the GNU General Public License version
111.21 + * 2 along with this work; if not, write to the Free Software Foundation,
111.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
111.23 + *
111.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
111.25 + * or visit www.oracle.com if you need additional information or have any
111.26 + * questions.
111.27 + */
111.28 +
111.29 +// -- This file was mechanically generated: Do not edit! -- //
111.30 +
111.31 +package java.nio.charset;
111.32 +
111.33 +//import java.nio.Buffer;
111.34 +//import java.nio.ByteBuffer;
111.35 +//import java.nio.CharBuffer;
111.36 +//import java.nio.BufferOverflowException;
111.37 +//import java.nio.BufferUnderflowException;
111.38 +import java.lang.ref.WeakReference;
111.39 +//import java.nio.charset.CoderMalfunctionError; // javadoc
111.40 +
111.41 +
111.42 +/**
111.43 + * An engine that can transform a sequence of bytes in a specific charset into a sequence of
111.44 + * sixteen-bit Unicode characters.
111.45 + *
111.46 + * <a name="steps">
111.47 + *
111.48 + * <p> The input byte sequence is provided in a byte buffer or a series
111.49 + * of such buffers. The output character sequence is written to a character buffer
111.50 + * or a series of such buffers. A decoder should always be used by making
111.51 + * the following sequence of method invocations, hereinafter referred to as a
111.52 + * <i>decoding operation</i>:
111.53 + *
111.54 + * <ol>
111.55 + *
111.56 + * <li><p> Reset the decoder via the {@link #reset reset} method, unless it
111.57 + * has not been used before; </p></li>
111.58 + *
111.59 + * <li><p> Invoke the {@link #decode decode} method zero or more times, as
111.60 + * long as additional input may be available, passing <tt>false</tt> for the
111.61 + * <tt>endOfInput</tt> argument and filling the input buffer and flushing the
111.62 + * output buffer between invocations; </p></li>
111.63 + *
111.64 + * <li><p> Invoke the {@link #decode decode} method one final time, passing
111.65 + * <tt>true</tt> for the <tt>endOfInput</tt> argument; and then </p></li>
111.66 + *
111.67 + * <li><p> Invoke the {@link #flush flush} method so that the decoder can
111.68 + * flush any internal state to the output buffer. </p></li>
111.69 + *
111.70 + * </ol>
111.71 + *
111.72 + * Each invocation of the {@link #decode decode} method will decode as many
111.73 + * bytes as possible from the input buffer, writing the resulting characters
111.74 + * to the output buffer. The {@link #decode decode} method returns when more
111.75 + * input is required, when there is not enough room in the output buffer, or
111.76 + * when a decoding error has occurred. In each case a {@link CoderResult}
111.77 + * object is returned to describe the reason for termination. An invoker can
111.78 + * examine this object and fill the input buffer, flush the output buffer, or
111.79 + * attempt to recover from a decoding error, as appropriate, and try again.
111.80 + *
111.81 + * <a name="ce">
111.82 + *
111.83 + * <p> There are two general types of decoding errors. If the input byte
111.84 + * sequence is not legal for this charset then the input is considered <i>malformed</i>. If
111.85 + * the input byte sequence is legal but cannot be mapped to a valid
111.86 + * Unicode character then an <i>unmappable character</i> has been encountered.
111.87 + *
111.88 + * <a name="cae">
111.89 + *
111.90 + * <p> How a decoding error is handled depends upon the action requested for
111.91 + * that type of error, which is described by an instance of the {@link
111.92 + * CodingErrorAction} class. The possible error actions are to {@link
111.93 + * CodingErrorAction#IGNORE </code>ignore<code>} the erroneous input, {@link
111.94 + * CodingErrorAction#REPORT </code>report<code>} the error to the invoker via
111.95 + * the returned {@link CoderResult} object, or {@link CodingErrorAction#REPLACE
111.96 + * </code>replace<code>} the erroneous input with the current value of the
111.97 + * replacement string. The replacement
111.98 + *
111.99 +
111.100 +
111.101 +
111.102 +
111.103 +
111.104 + * has the initial value <tt>"\uFFFD"</tt>;
111.105 +
111.106 + *
111.107 + * its value may be changed via the {@link #replaceWith(java.lang.String)
111.108 + * replaceWith} method.
111.109 + *
111.110 + * <p> The default action for malformed-input and unmappable-character errors
111.111 + * is to {@link CodingErrorAction#REPORT </code>report<code>} them. The
111.112 + * malformed-input error action may be changed via the {@link
111.113 + * #onMalformedInput(CodingErrorAction) onMalformedInput} method; the
111.114 + * unmappable-character action may be changed via the {@link
111.115 + * #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter} method.
111.116 + *
111.117 + * <p> This class is designed to handle many of the details of the decoding
111.118 + * process, including the implementation of error actions. A decoder for a
111.119 + * specific charset, which is a concrete subclass of this class, need only
111.120 + * implement the abstract {@link #decodeLoop decodeLoop} method, which
111.121 + * encapsulates the basic decoding loop. A subclass that maintains internal
111.122 + * state should, additionally, override the {@link #implFlush implFlush} and
111.123 + * {@link #implReset implReset} methods.
111.124 + *
111.125 + * <p> Instances of this class are not safe for use by multiple concurrent
111.126 + * threads. </p>
111.127 + *
111.128 + *
111.129 + * @author Mark Reinhold
111.130 + * @author JSR-51 Expert Group
111.131 + * @since 1.4
111.132 + *
111.133 + * @see ByteBuffer
111.134 + * @see CharBuffer
111.135 + * @see Charset
111.136 + * @see CharsetEncoder
111.137 + */
111.138 +
111.139 +public abstract class CharsetDecoder {
111.140 +
111.141 + private final Charset charset;
111.142 + private final float averageCharsPerByte;
111.143 + private final float maxCharsPerByte;
111.144 +
111.145 + private String replacement;
111.146 +// private CodingErrorAction malformedInputAction
111.147 +// = CodingErrorAction.REPORT;
111.148 +// private CodingErrorAction unmappableCharacterAction
111.149 +// = CodingErrorAction.REPORT;
111.150 +
111.151 + // Internal states
111.152 + //
111.153 + private static final int ST_RESET = 0;
111.154 + private static final int ST_CODING = 1;
111.155 + private static final int ST_END = 2;
111.156 + private static final int ST_FLUSHED = 3;
111.157 +
111.158 + private int state = ST_RESET;
111.159 +
111.160 + private static String stateNames[]
111.161 + = { "RESET", "CODING", "CODING_END", "FLUSHED" };
111.162 +
111.163 +
111.164 + /**
111.165 + * Initializes a new decoder. The new decoder will have the given
111.166 + * chars-per-byte and replacement values. </p>
111.167 + *
111.168 + * @param averageCharsPerByte
111.169 + * A positive float value indicating the expected number of
111.170 + * characters that will be produced for each input byte
111.171 + *
111.172 + * @param maxCharsPerByte
111.173 + * A positive float value indicating the maximum number of
111.174 + * characters that will be produced for each input byte
111.175 + *
111.176 + * @param replacement
111.177 + * The initial replacement; must not be <tt>null</tt>, must have
111.178 + * non-zero length, must not be longer than maxCharsPerByte,
111.179 + * and must be {@link #isLegalReplacement </code>legal<code>}
111.180 + *
111.181 + * @throws IllegalArgumentException
111.182 + * If the preconditions on the parameters do not hold
111.183 + */
111.184 + private
111.185 + CharsetDecoder(Charset cs,
111.186 + float averageCharsPerByte,
111.187 + float maxCharsPerByte,
111.188 + String replacement)
111.189 + {
111.190 + this.charset = cs;
111.191 + if (averageCharsPerByte <= 0.0f)
111.192 + throw new IllegalArgumentException("Non-positive "
111.193 + + "averageCharsPerByte");
111.194 + if (maxCharsPerByte <= 0.0f)
111.195 + throw new IllegalArgumentException("Non-positive "
111.196 + + "maxCharsPerByte");
111.197 + if (averageCharsPerByte > maxCharsPerByte)
111.198 + throw new IllegalArgumentException("averageCharsPerByte"
111.199 + + " exceeds "
111.200 + + "maxCharsPerByte");
111.201 + this.replacement = replacement;
111.202 + this.averageCharsPerByte = averageCharsPerByte;
111.203 + this.maxCharsPerByte = maxCharsPerByte;
111.204 + replaceWith(replacement);
111.205 + }
111.206 +
111.207 + /**
111.208 + * Initializes a new decoder. The new decoder will have the given
111.209 + * chars-per-byte values and its replacement will be the
111.210 + * string <tt>"\uFFFD"</tt>. </p>
111.211 + *
111.212 + * @param averageCharsPerByte
111.213 + * A positive float value indicating the expected number of
111.214 + * characters that will be produced for each input byte
111.215 + *
111.216 + * @param maxCharsPerByte
111.217 + * A positive float value indicating the maximum number of
111.218 + * characters that will be produced for each input byte
111.219 + *
111.220 + * @throws IllegalArgumentException
111.221 + * If the preconditions on the parameters do not hold
111.222 + */
111.223 + protected CharsetDecoder(Charset cs,
111.224 + float averageCharsPerByte,
111.225 + float maxCharsPerByte)
111.226 + {
111.227 + this(cs,
111.228 + averageCharsPerByte, maxCharsPerByte,
111.229 + "\uFFFD");
111.230 + }
111.231 +
111.232 + /**
111.233 + * Returns the charset that created this decoder. </p>
111.234 + *
111.235 + * @return This decoder's charset
111.236 + */
111.237 + public final Charset charset() {
111.238 + return charset;
111.239 + }
111.240 +
111.241 + /**
111.242 + * Returns this decoder's replacement value. </p>
111.243 + *
111.244 + * @return This decoder's current replacement,
111.245 + * which is never <tt>null</tt> and is never empty
111.246 + */
111.247 + public final String replacement() {
111.248 + return replacement;
111.249 + }
111.250 +
111.251 + /**
111.252 + * Changes this decoder's replacement value.
111.253 + *
111.254 + * <p> This method invokes the {@link #implReplaceWith implReplaceWith}
111.255 + * method, passing the new replacement, after checking that the new
111.256 + * replacement is acceptable. </p>
111.257 + *
111.258 + * @param newReplacement
111.259 + *
111.260 +
111.261 + * The new replacement; must not be <tt>null</tt>
111.262 + * and must have non-zero length
111.263 +
111.264 +
111.265 +
111.266 +
111.267 +
111.268 +
111.269 +
111.270 + *
111.271 + * @return This decoder
111.272 + *
111.273 + * @throws IllegalArgumentException
111.274 + * If the preconditions on the parameter do not hold
111.275 + */
111.276 + public final CharsetDecoder replaceWith(String newReplacement) {
111.277 + if (newReplacement == null)
111.278 + throw new IllegalArgumentException("Null replacement");
111.279 + int len = newReplacement.length();
111.280 + if (len == 0)
111.281 + throw new IllegalArgumentException("Empty replacement");
111.282 + if (len > maxCharsPerByte)
111.283 + throw new IllegalArgumentException("Replacement too long");
111.284 +
111.285 +
111.286 +
111.287 +
111.288 + this.replacement = newReplacement;
111.289 + implReplaceWith(newReplacement);
111.290 + return this;
111.291 + }
111.292 +
111.293 + /**
111.294 + * Reports a change to this decoder's replacement value.
111.295 + *
111.296 + * <p> The default implementation of this method does nothing. This method
111.297 + * should be overridden by decoders that require notification of changes to
111.298 + * the replacement. </p>
111.299 + *
111.300 + * @param newReplacement
111.301 + */
111.302 + protected void implReplaceWith(String newReplacement) {
111.303 + }
111.304 +
111.305 +
111.306 +
111.307 +
111.308 +
111.309 +
111.310 +
111.311 +
111.312 +
111.313 +
111.314 +
111.315 +
111.316 +
111.317 +
111.318 +
111.319 +
111.320 +
111.321 +
111.322 +
111.323 +
111.324 +
111.325 +
111.326 +
111.327 +
111.328 +
111.329 +
111.330 +
111.331 +
111.332 +
111.333 +
111.334 +
111.335 +
111.336 +
111.337 +
111.338 +
111.339 +
111.340 +
111.341 +
111.342 +
111.343 +
111.344 +
111.345 + /**
111.346 + * Returns this decoder's current action for malformed-input errors. </p>
111.347 + *
111.348 + * @return The current malformed-input action, which is never <tt>null</tt>
111.349 + */
111.350 +// public CodingErrorAction malformedInputAction() {
111.351 +// return malformedInputAction;
111.352 +// }
111.353 +
111.354 + /**
111.355 + * Changes this decoder's action for malformed-input errors. </p>
111.356 + *
111.357 + * <p> This method invokes the {@link #implOnMalformedInput
111.358 + * implOnMalformedInput} method, passing the new action. </p>
111.359 + *
111.360 + * @param newAction The new action; must not be <tt>null</tt>
111.361 + *
111.362 + * @return This decoder
111.363 + *
111.364 + * @throws IllegalArgumentException
111.365 + * If the precondition on the parameter does not hold
111.366 + */
111.367 +// public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) {
111.368 +// if (newAction == null)
111.369 +// throw new IllegalArgumentException("Null action");
111.370 +// malformedInputAction = newAction;
111.371 +// implOnMalformedInput(newAction);
111.372 +// return this;
111.373 +// }
111.374 +
111.375 + /**
111.376 + * Reports a change to this decoder's malformed-input action.
111.377 + *
111.378 + * <p> The default implementation of this method does nothing. This method
111.379 + * should be overridden by decoders that require notification of changes to
111.380 + * the malformed-input action. </p>
111.381 + */
111.382 +// protected void implOnMalformedInput(CodingErrorAction newAction) { }
111.383 +
111.384 + /**
111.385 + * Returns this decoder's current action for unmappable-character errors.
111.386 + * </p>
111.387 + *
111.388 + * @return The current unmappable-character action, which is never
111.389 + * <tt>null</tt>
111.390 + */
111.391 +// public CodingErrorAction unmappableCharacterAction() {
111.392 +// return unmappableCharacterAction;
111.393 +// }
111.394 +
111.395 + /**
111.396 + * Changes this decoder's action for unmappable-character errors.
111.397 + *
111.398 + * <p> This method invokes the {@link #implOnUnmappableCharacter
111.399 + * implOnUnmappableCharacter} method, passing the new action. </p>
111.400 + *
111.401 + * @param newAction The new action; must not be <tt>null</tt>
111.402 + *
111.403 + * @return This decoder
111.404 + *
111.405 + * @throws IllegalArgumentException
111.406 + * If the precondition on the parameter does not hold
111.407 + */
111.408 +// public final CharsetDecoder onUnmappableCharacter(CodingErrorAction
111.409 +// newAction)
111.410 +// {
111.411 +// if (newAction == null)
111.412 +// throw new IllegalArgumentException("Null action");
111.413 +// unmappableCharacterAction = newAction;
111.414 +// implOnUnmappableCharacter(newAction);
111.415 +// return this;
111.416 +// }
111.417 +
111.418 + /**
111.419 + * Reports a change to this decoder's unmappable-character action.
111.420 + *
111.421 + * <p> The default implementation of this method does nothing. This method
111.422 + * should be overridden by decoders that require notification of changes to
111.423 + * the unmappable-character action. </p>
111.424 + */
111.425 +// protected void implOnUnmappableCharacter(CodingErrorAction newAction) { }
111.426 +
111.427 + /**
111.428 + * Returns the average number of characters that will be produced for each
111.429 + * byte of input. This heuristic value may be used to estimate the size
111.430 + * of the output buffer required for a given input sequence. </p>
111.431 + *
111.432 + * @return The average number of characters produced
111.433 + * per byte of input
111.434 + */
111.435 + public final float averageCharsPerByte() {
111.436 + return averageCharsPerByte;
111.437 + }
111.438 +
111.439 + /**
111.440 + * Returns the maximum number of characters that will be produced for each
111.441 + * byte of input. This value may be used to compute the worst-case size
111.442 + * of the output buffer required for a given input sequence. </p>
111.443 + *
111.444 + * @return The maximum number of characters that will be produced per
111.445 + * byte of input
111.446 + */
111.447 + public final float maxCharsPerByte() {
111.448 + return maxCharsPerByte;
111.449 + }
111.450 +
111.451 + /**
111.452 + * Decodes as many bytes as possible from the given input buffer,
111.453 + * writing the results to the given output buffer.
111.454 + *
111.455 + * <p> The buffers are read from, and written to, starting at their current
111.456 + * positions. At most {@link Buffer#remaining in.remaining()} bytes
111.457 + * will be read and at most {@link Buffer#remaining out.remaining()}
111.458 + * characters will be written. The buffers' positions will be advanced to
111.459 + * reflect the bytes read and the characters written, but their marks and
111.460 + * limits will not be modified.
111.461 + *
111.462 + * <p> In addition to reading bytes from the input buffer and writing
111.463 + * characters to the output buffer, this method returns a {@link CoderResult}
111.464 + * object to describe its reason for termination:
111.465 + *
111.466 + * <ul>
111.467 + *
111.468 + * <li><p> {@link CoderResult#UNDERFLOW} indicates that as much of the
111.469 + * input buffer as possible has been decoded. If there is no further
111.470 + * input then the invoker can proceed to the next step of the
111.471 + * <a href="#steps">decoding operation</a>. Otherwise this method
111.472 + * should be invoked again with further input. </p></li>
111.473 + *
111.474 + * <li><p> {@link CoderResult#OVERFLOW} indicates that there is
111.475 + * insufficient space in the output buffer to decode any more bytes.
111.476 + * This method should be invoked again with an output buffer that has
111.477 + * more {@linkplain Buffer#remaining remaining} characters. This is
111.478 + * typically done by draining any decoded characters from the output
111.479 + * buffer. </p></li>
111.480 + *
111.481 + * <li><p> A {@link CoderResult#malformedForLength
111.482 + * </code>malformed-input<code>} result indicates that a malformed-input
111.483 + * error has been detected. The malformed bytes begin at the input
111.484 + * buffer's (possibly incremented) position; the number of malformed
111.485 + * bytes may be determined by invoking the result object's {@link
111.486 + * CoderResult#length() length} method. This case applies only if the
111.487 + * {@link #onMalformedInput </code>malformed action<code>} of this decoder
111.488 + * is {@link CodingErrorAction#REPORT}; otherwise the malformed input
111.489 + * will be ignored or replaced, as requested. </p></li>
111.490 + *
111.491 + * <li><p> An {@link CoderResult#unmappableForLength
111.492 + * </code>unmappable-character<code>} result indicates that an
111.493 + * unmappable-character error has been detected. The bytes that
111.494 + * decode the unmappable character begin at the input buffer's (possibly
111.495 + * incremented) position; the number of such bytes may be determined
111.496 + * by invoking the result object's {@link CoderResult#length() length}
111.497 + * method. This case applies only if the {@link #onUnmappableCharacter
111.498 + * </code>unmappable action<code>} of this decoder is {@link
111.499 + * CodingErrorAction#REPORT}; otherwise the unmappable character will be
111.500 + * ignored or replaced, as requested. </p></li>
111.501 + *
111.502 + * </ul>
111.503 + *
111.504 + * In any case, if this method is to be reinvoked in the same decoding
111.505 + * operation then care should be taken to preserve any bytes remaining
111.506 + * in the input buffer so that they are available to the next invocation.
111.507 + *
111.508 + * <p> The <tt>endOfInput</tt> parameter advises this method as to whether
111.509 + * the invoker can provide further input beyond that contained in the given
111.510 + * input buffer. If there is a possibility of providing additional input
111.511 + * then the invoker should pass <tt>false</tt> for this parameter; if there
111.512 + * is no possibility of providing further input then the invoker should
111.513 + * pass <tt>true</tt>. It is not erroneous, and in fact it is quite
111.514 + * common, to pass <tt>false</tt> in one invocation and later discover that
111.515 + * no further input was actually available. It is critical, however, that
111.516 + * the final invocation of this method in a sequence of invocations always
111.517 + * pass <tt>true</tt> so that any remaining undecoded input will be treated
111.518 + * as being malformed.
111.519 + *
111.520 + * <p> This method works by invoking the {@link #decodeLoop decodeLoop}
111.521 + * method, interpreting its results, handling error conditions, and
111.522 + * reinvoking it as necessary. </p>
111.523 + *
111.524 + *
111.525 + * @param in
111.526 + * The input byte buffer
111.527 + *
111.528 + * @param out
111.529 + * The output character buffer
111.530 + *
111.531 + * @param endOfInput
111.532 + * <tt>true</tt> if, and only if, the invoker can provide no
111.533 + * additional input bytes beyond those in the given buffer
111.534 + *
111.535 + * @return A coder-result object describing the reason for termination
111.536 + *
111.537 + * @throws IllegalStateException
111.538 + * If a decoding operation is already in progress and the previous
111.539 + * step was an invocation neither of the {@link #reset reset}
111.540 + * method, nor of this method with a value of <tt>false</tt> for
111.541 + * the <tt>endOfInput</tt> parameter, nor of this method with a
111.542 + * value of <tt>true</tt> for the <tt>endOfInput</tt> parameter
111.543 + * but a return value indicating an incomplete decoding operation
111.544 + *
111.545 + * @throws CoderMalfunctionError
111.546 + * If an invocation of the decodeLoop method threw
111.547 + * an unexpected exception
111.548 + */
111.549 +// public final CoderResult decode(ByteBuffer in, CharBuffer out,
111.550 +// boolean endOfInput)
111.551 +// {
111.552 +// int newState = endOfInput ? ST_END : ST_CODING;
111.553 +// if ((state != ST_RESET) && (state != ST_CODING)
111.554 +// && !(endOfInput && (state == ST_END)))
111.555 +// throwIllegalStateException(state, newState);
111.556 +// state = newState;
111.557 +//
111.558 +// for (;;) {
111.559 +//
111.560 +// CoderResult cr;
111.561 +// try {
111.562 +// cr = decodeLoop(in, out);
111.563 +// } catch (BufferUnderflowException x) {
111.564 +// throw new CoderMalfunctionError(x);
111.565 +// } catch (BufferOverflowException x) {
111.566 +// throw new CoderMalfunctionError(x);
111.567 +// }
111.568 +//
111.569 +// if (cr.isOverflow())
111.570 +// return cr;
111.571 +//
111.572 +// if (cr.isUnderflow()) {
111.573 +// if (endOfInput && in.hasRemaining()) {
111.574 +// cr = CoderResult.malformedForLength(in.remaining());
111.575 +// // Fall through to malformed-input case
111.576 +// } else {
111.577 +// return cr;
111.578 +// }
111.579 +// }
111.580 +//
111.581 +// CodingErrorAction action = null;
111.582 +// if (cr.isMalformed())
111.583 +// action = malformedInputAction;
111.584 +// else if (cr.isUnmappable())
111.585 +// action = unmappableCharacterAction;
111.586 +// else
111.587 +// assert false : cr.toString();
111.588 +//
111.589 +// if (action == CodingErrorAction.REPORT)
111.590 +// return cr;
111.591 +//
111.592 +// if (action == CodingErrorAction.REPLACE) {
111.593 +// if (out.remaining() < replacement.length())
111.594 +// return CoderResult.OVERFLOW;
111.595 +// out.put(replacement);
111.596 +// }
111.597 +//
111.598 +// if ((action == CodingErrorAction.IGNORE)
111.599 +// || (action == CodingErrorAction.REPLACE)) {
111.600 +// // Skip erroneous input either way
111.601 +// in.position(in.position() + cr.length());
111.602 +// continue;
111.603 +// }
111.604 +//
111.605 +// assert false;
111.606 +// }
111.607 +//
111.608 +// }
111.609 +
111.610 + /**
111.611 + * Flushes this decoder.
111.612 + *
111.613 + * <p> Some decoders maintain internal state and may need to write some
111.614 + * final characters to the output buffer once the overall input sequence has
111.615 + * been read.
111.616 + *
111.617 + * <p> Any additional output is written to the output buffer beginning at
111.618 + * its current position. At most {@link Buffer#remaining out.remaining()}
111.619 + * characters will be written. The buffer's position will be advanced
111.620 + * appropriately, but its mark and limit will not be modified.
111.621 + *
111.622 + * <p> If this method completes successfully then it returns {@link
111.623 + * CoderResult#UNDERFLOW}. If there is insufficient room in the output
111.624 + * buffer then it returns {@link CoderResult#OVERFLOW}. If this happens
111.625 + * then this method must be invoked again, with an output buffer that has
111.626 + * more room, in order to complete the current <a href="#steps">decoding
111.627 + * operation</a>.
111.628 + *
111.629 + * <p> If this decoder has already been flushed then invoking this method
111.630 + * has no effect.
111.631 + *
111.632 + * <p> This method invokes the {@link #implFlush implFlush} method to
111.633 + * perform the actual flushing operation. </p>
111.634 + *
111.635 + * @param out
111.636 + * The output character buffer
111.637 + *
111.638 + * @return A coder-result object, either {@link CoderResult#UNDERFLOW} or
111.639 + * {@link CoderResult#OVERFLOW}
111.640 + *
111.641 + * @throws IllegalStateException
111.642 + * If the previous step of the current decoding operation was an
111.643 + * invocation neither of the {@link #flush flush} method nor of
111.644 + * the three-argument {@link
111.645 + * #decode(ByteBuffer,CharBuffer,boolean) decode} method
111.646 + * with a value of <tt>true</tt> for the <tt>endOfInput</tt>
111.647 + * parameter
111.648 + */
111.649 +// public final CoderResult flush(CharBuffer out) {
111.650 +// if (state == ST_END) {
111.651 +// CoderResult cr = implFlush(out);
111.652 +// if (cr.isUnderflow())
111.653 +// state = ST_FLUSHED;
111.654 +// return cr;
111.655 +// }
111.656 +//
111.657 +// if (state != ST_FLUSHED)
111.658 +// throwIllegalStateException(state, ST_FLUSHED);
111.659 +//
111.660 +// return CoderResult.UNDERFLOW; // Already flushed
111.661 +// }
111.662 +
111.663 + /**
111.664 + * Flushes this decoder.
111.665 + *
111.666 + * <p> The default implementation of this method does nothing, and always
111.667 + * returns {@link CoderResult#UNDERFLOW}. This method should be overridden
111.668 + * by decoders that may need to write final characters to the output buffer
111.669 + * once the entire input sequence has been read. </p>
111.670 + *
111.671 + * @param out
111.672 + * The output character buffer
111.673 + *
111.674 + * @return A coder-result object, either {@link CoderResult#UNDERFLOW} or
111.675 + * {@link CoderResult#OVERFLOW}
111.676 + */
111.677 +// protected CoderResult implFlush(CharBuffer out) {
111.678 +// return CoderResult.UNDERFLOW;
111.679 +// }
111.680 +
111.681 + /**
111.682 + * Resets this decoder, clearing any internal state.
111.683 + *
111.684 + * <p> This method resets charset-independent state and also invokes the
111.685 + * {@link #implReset() implReset} method in order to perform any
111.686 + * charset-specific reset actions. </p>
111.687 + *
111.688 + * @return This decoder
111.689 + *
111.690 + */
111.691 + public final CharsetDecoder reset() {
111.692 + implReset();
111.693 + state = ST_RESET;
111.694 + return this;
111.695 + }
111.696 +
111.697 + /**
111.698 + * Resets this decoder, clearing any charset-specific internal state.
111.699 + *
111.700 + * <p> The default implementation of this method does nothing. This method
111.701 + * should be overridden by decoders that maintain internal state. </p>
111.702 + */
111.703 + protected void implReset() { }
111.704 +
111.705 + /**
111.706 + * Decodes one or more bytes into one or more characters.
111.707 + *
111.708 + * <p> This method encapsulates the basic decoding loop, decoding as many
111.709 + * bytes as possible until it either runs out of input, runs out of room
111.710 + * in the output buffer, or encounters a decoding error. This method is
111.711 + * invoked by the {@link #decode decode} method, which handles result
111.712 + * interpretation and error recovery.
111.713 + *
111.714 + * <p> The buffers are read from, and written to, starting at their current
111.715 + * positions. At most {@link Buffer#remaining in.remaining()} bytes
111.716 + * will be read, and at most {@link Buffer#remaining out.remaining()}
111.717 + * characters will be written. The buffers' positions will be advanced to
111.718 + * reflect the bytes read and the characters written, but their marks and
111.719 + * limits will not be modified.
111.720 + *
111.721 + * <p> This method returns a {@link CoderResult} object to describe its
111.722 + * reason for termination, in the same manner as the {@link #decode decode}
111.723 + * method. Most implementations of this method will handle decoding errors
111.724 + * by returning an appropriate result object for interpretation by the
111.725 + * {@link #decode decode} method. An optimized implementation may instead
111.726 + * examine the relevant error action and implement that action itself.
111.727 + *
111.728 + * <p> An implementation of this method may perform arbitrary lookahead by
111.729 + * returning {@link CoderResult#UNDERFLOW} until it receives sufficient
111.730 + * input. </p>
111.731 + *
111.732 + * @param in
111.733 + * The input byte buffer
111.734 + *
111.735 + * @param out
111.736 + * The output character buffer
111.737 + *
111.738 + * @return A coder-result object describing the reason for termination
111.739 + */
111.740 +// protected abstract CoderResult decodeLoop(ByteBuffer in,
111.741 +// CharBuffer out);
111.742 +
111.743 + /**
111.744 + * Convenience method that decodes the remaining content of a single input
111.745 + * byte buffer into a newly-allocated character buffer.
111.746 + *
111.747 + * <p> This method implements an entire <a href="#steps">decoding
111.748 + * operation</a>; that is, it resets this decoder, then it decodes the
111.749 + * bytes in the given byte buffer, and finally it flushes this
111.750 + * decoder. This method should therefore not be invoked if a decoding
111.751 + * operation is already in progress. </p>
111.752 + *
111.753 + * @param in
111.754 + * The input byte buffer
111.755 + *
111.756 + * @return A newly-allocated character buffer containing the result of the
111.757 + * decoding operation. The buffer's position will be zero and its
111.758 + * limit will follow the last character written.
111.759 + *
111.760 + * @throws IllegalStateException
111.761 + * If a decoding operation is already in progress
111.762 + *
111.763 + * @throws MalformedInputException
111.764 + * If the byte sequence starting at the input buffer's current
111.765 + * position is not legal for this charset and the current malformed-input action
111.766 + * is {@link CodingErrorAction#REPORT}
111.767 + *
111.768 + * @throws UnmappableCharacterException
111.769 + * If the byte sequence starting at the input buffer's current
111.770 + * position cannot be mapped to an equivalent character sequence and
111.771 + * the current unmappable-character action is {@link
111.772 + * CodingErrorAction#REPORT}
111.773 + */
111.774 +// public final CharBuffer decode(ByteBuffer in)
111.775 +// throws CharacterCodingException
111.776 +// {
111.777 +// int n = (int)(in.remaining() * averageCharsPerByte());
111.778 +// CharBuffer out = CharBuffer.allocate(n);
111.779 +//
111.780 +// if ((n == 0) && (in.remaining() == 0))
111.781 +// return out;
111.782 +// reset();
111.783 +// for (;;) {
111.784 +// CoderResult cr = in.hasRemaining() ?
111.785 +// decode(in, out, true) : CoderResult.UNDERFLOW;
111.786 +// if (cr.isUnderflow())
111.787 +// cr = flush(out);
111.788 +//
111.789 +// if (cr.isUnderflow())
111.790 +// break;
111.791 +// if (cr.isOverflow()) {
111.792 +// n = 2*n + 1; // Ensure progress; n might be 0!
111.793 +// CharBuffer o = CharBuffer.allocate(n);
111.794 +// out.flip();
111.795 +// o.put(out);
111.796 +// out = o;
111.797 +// continue;
111.798 +// }
111.799 +// cr.throwException();
111.800 +// }
111.801 +// out.flip();
111.802 +// return out;
111.803 +// }
111.804 +
111.805 +
111.806 +
111.807 + /**
111.808 + * Tells whether or not this decoder implements an auto-detecting charset.
111.809 + *
111.810 + * <p> The default implementation of this method always returns
111.811 + * <tt>false</tt>; it should be overridden by auto-detecting decoders to
111.812 + * return <tt>true</tt>. </p>
111.813 + *
111.814 + * @return <tt>true</tt> if, and only if, this decoder implements an
111.815 + * auto-detecting charset
111.816 + */
111.817 + public boolean isAutoDetecting() {
111.818 + return false;
111.819 + }
111.820 +
111.821 + /**
111.822 + * Tells whether or not this decoder has yet detected a
111.823 + * charset <i>(optional operation)</i>.
111.824 + *
111.825 + * <p> If this decoder implements an auto-detecting charset then at a
111.826 + * single point during a decoding operation this method may start returning
111.827 + * <tt>true</tt> to indicate that a specific charset has been detected in
111.828 + * the input byte sequence. Once this occurs, the {@link #detectedCharset
111.829 + * detectedCharset} method may be invoked to retrieve the detected charset.
111.830 + *
111.831 + * <p> That this method returns <tt>false</tt> does not imply that no bytes
111.832 + * have yet been decoded. Some auto-detecting decoders are capable of
111.833 + * decoding some, or even all, of an input byte sequence without fixing on
111.834 + * a particular charset.
111.835 + *
111.836 + * <p> The default implementation of this method always throws an {@link
111.837 + * UnsupportedOperationException}; it should be overridden by
111.838 + * auto-detecting decoders to return <tt>true</tt> once the input charset
111.839 + * has been determined. </p>
111.840 + *
111.841 + * @return <tt>true</tt> if, and only if, this decoder has detected a
111.842 + * specific charset
111.843 + *
111.844 + * @throws UnsupportedOperationException
111.845 + * If this decoder does not implement an auto-detecting charset
111.846 + */
111.847 + public boolean isCharsetDetected() {
111.848 + throw new UnsupportedOperationException();
111.849 + }
111.850 +
111.851 + /**
111.852 + * Retrieves the charset that was detected by this
111.853 + * decoder <i>(optional operation)</i>.
111.854 + *
111.855 + * <p> If this decoder implements an auto-detecting charset then this
111.856 + * method returns the actual charset once it has been detected. After that
111.857 + * point, this method returns the same value for the duration of the
111.858 + * current decoding operation. If not enough input bytes have yet been
111.859 + * read to determine the actual charset then this method throws an {@link
111.860 + * IllegalStateException}.
111.861 + *
111.862 + * <p> The default implementation of this method always throws an {@link
111.863 + * UnsupportedOperationException}; it should be overridden by
111.864 + * auto-detecting decoders to return the appropriate value. </p>
111.865 + *
111.866 + * @return The charset detected by this auto-detecting decoder,
111.867 + * or <tt>null</tt> if the charset has not yet been determined
111.868 + *
111.869 + * @throws IllegalStateException
111.870 + * If insufficient bytes have been read to determine a charset
111.871 + *
111.872 + * @throws UnsupportedOperationException
111.873 + * If this decoder does not implement an auto-detecting charset
111.874 + */
111.875 + public Charset detectedCharset() {
111.876 + throw new UnsupportedOperationException();
111.877 + }
111.878 +
111.879 +
111.880 +
111.881 +
111.882 +
111.883 +
111.884 +
111.885 +
111.886 +
111.887 +
111.888 +
111.889 +
111.890 +
111.891 +
111.892 +
111.893 +
111.894 +
111.895 +
111.896 +
111.897 +
111.898 +
111.899 +
111.900 +
111.901 +
111.902 +
111.903 +
111.904 +
111.905 +
111.906 +
111.907 +
111.908 +
111.909 +
111.910 +
111.911 +
111.912 +
111.913 +
111.914 +
111.915 +
111.916 +
111.917 +
111.918 +
111.919 +
111.920 +
111.921 +
111.922 +
111.923 +
111.924 +
111.925 +
111.926 +
111.927 +
111.928 +
111.929 +
111.930 +
111.931 +
111.932 +
111.933 +
111.934 +
111.935 +
111.936 +
111.937 +
111.938 +
111.939 +
111.940 +
111.941 +
111.942 +
111.943 +
111.944 +
111.945 +
111.946 +
111.947 +
111.948 +
111.949 +
111.950 +
111.951 +
111.952 +
111.953 +
111.954 +
111.955 +
111.956 +
111.957 +
111.958 +
111.959 +
111.960 +
111.961 +
111.962 +
111.963 +
111.964 +
111.965 +
111.966 +
111.967 +
111.968 + private void throwIllegalStateException(int from, int to) {
111.969 + throw new IllegalStateException("Current state = " + stateNames[from]
111.970 + + ", new state = " + stateNames[to]);
111.971 + }
111.972 +
111.973 +}
112.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
112.2 +++ b/rt/emul/compact/src/main/java/java/nio/charset/CharsetEncoder.java Tue Feb 11 13:31:42 2014 +0100
112.3 @@ -0,0 +1,970 @@
112.4 +/*
112.5 + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
112.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
112.7 + *
112.8 + * This code is free software; you can redistribute it and/or modify it
112.9 + * under the terms of the GNU General Public License version 2 only, as
112.10 + * published by the Free Software Foundation. Oracle designates this
112.11 + * particular file as subject to the "Classpath" exception as provided
112.12 + * by Oracle in the LICENSE file that accompanied this code.
112.13 + *
112.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
112.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
112.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
112.17 + * version 2 for more details (a copy is included in the LICENSE file that
112.18 + * accompanied this code).
112.19 + *
112.20 + * You should have received a copy of the GNU General Public License version
112.21 + * 2 along with this work; if not, write to the Free Software Foundation,
112.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
112.23 + *
112.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
112.25 + * or visit www.oracle.com if you need additional information or have any
112.26 + * questions.
112.27 + */
112.28 +
112.29 +// -- This file was mechanically generated: Do not edit! -- //
112.30 +
112.31 +package java.nio.charset;
112.32 +
112.33 +//import java.nio.Buffer;
112.34 +//import java.nio.ByteBuffer;
112.35 +//import java.nio.CharBuffer;
112.36 +//import java.nio.BufferOverflowException;
112.37 +//import java.nio.BufferUnderflowException;
112.38 +import java.lang.ref.WeakReference;
112.39 +//import java.nio.charset.CoderMalfunctionError; // javadoc
112.40 +
112.41 +
112.42 +/**
112.43 + * An engine that can transform a sequence of sixteen-bit Unicode characters into a sequence of
112.44 + * bytes in a specific charset.
112.45 + *
112.46 + * <a name="steps">
112.47 + *
112.48 + * <p> The input character sequence is provided in a character buffer or a series
112.49 + * of such buffers. The output byte sequence is written to a byte buffer
112.50 + * or a series of such buffers. An encoder should always be used by making
112.51 + * the following sequence of method invocations, hereinafter referred to as an
112.52 + * <i>encoding operation</i>:
112.53 + *
112.54 + * <ol>
112.55 + *
112.56 + * <li><p> Reset the encoder via the {@link #reset reset} method, unless it
112.57 + * has not been used before; </p></li>
112.58 + *
112.59 + * <li><p> Invoke the {@link #encode encode} method zero or more times, as
112.60 + * long as additional input may be available, passing <tt>false</tt> for the
112.61 + * <tt>endOfInput</tt> argument and filling the input buffer and flushing the
112.62 + * output buffer between invocations; </p></li>
112.63 + *
112.64 + * <li><p> Invoke the {@link #encode encode} method one final time, passing
112.65 + * <tt>true</tt> for the <tt>endOfInput</tt> argument; and then </p></li>
112.66 + *
112.67 + * <li><p> Invoke the {@link #flush flush} method so that the encoder can
112.68 + * flush any internal state to the output buffer. </p></li>
112.69 + *
112.70 + * </ol>
112.71 + *
112.72 + * Each invocation of the {@link #encode encode} method will encode as many
112.73 + * characters as possible from the input buffer, writing the resulting bytes
112.74 + * to the output buffer. The {@link #encode encode} method returns when more
112.75 + * input is required, when there is not enough room in the output buffer, or
112.76 + * when an encoding error has occurred. In each case a {@link CoderResult}
112.77 + * object is returned to describe the reason for termination. An invoker can
112.78 + * examine this object and fill the input buffer, flush the output buffer, or
112.79 + * attempt to recover from an encoding error, as appropriate, and try again.
112.80 + *
112.81 + * <a name="ce">
112.82 + *
112.83 + * <p> There are two general types of encoding errors. If the input character
112.84 + * sequence is not a legal sixteen-bit Unicode sequence then the input is considered <i>malformed</i>. If
112.85 + * the input character sequence is legal but cannot be mapped to a valid
112.86 + * byte sequence in the given charset then an <i>unmappable character</i> has been encountered.
112.87 + *
112.88 + * <a name="cae">
112.89 + *
112.90 + * <p> How an encoding error is handled depends upon the action requested for
112.91 + * that type of error, which is described by an instance of the {@link
112.92 + * CodingErrorAction} class. The possible error actions are to {@link
112.93 + * CodingErrorAction#IGNORE </code>ignore<code>} the erroneous input, {@link
112.94 + * CodingErrorAction#REPORT </code>report<code>} the error to the invoker via
112.95 + * the returned {@link CoderResult} object, or {@link CodingErrorAction#REPLACE
112.96 + * </code>replace<code>} the erroneous input with the current value of the
112.97 + * replacement byte array. The replacement
112.98 + *
112.99 +
112.100 + * is initially set to the encoder's default replacement, which often
112.101 + * (but not always) has the initial value <tt>{</tt> <tt>(byte)'?'</tt> <tt>}</tt>;
112.102 +
112.103 +
112.104 +
112.105 +
112.106 + *
112.107 + * its value may be changed via the {@link #replaceWith(byte[])
112.108 + * replaceWith} method.
112.109 + *
112.110 + * <p> The default action for malformed-input and unmappable-character errors
112.111 + * is to {@link CodingErrorAction#REPORT </code>report<code>} them. The
112.112 + * malformed-input error action may be changed via the {@link
112.113 + * #onMalformedInput(CodingErrorAction) onMalformedInput} method; the
112.114 + * unmappable-character action may be changed via the {@link
112.115 + * #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter} method.
112.116 + *
112.117 + * <p> This class is designed to handle many of the details of the encoding
112.118 + * process, including the implementation of error actions. An encoder for a
112.119 + * specific charset, which is a concrete subclass of this class, need only
112.120 + * implement the abstract {@link #encodeLoop encodeLoop} method, which
112.121 + * encapsulates the basic encoding loop. A subclass that maintains internal
112.122 + * state should, additionally, override the {@link #implFlush implFlush} and
112.123 + * {@link #implReset implReset} methods.
112.124 + *
112.125 + * <p> Instances of this class are not safe for use by multiple concurrent
112.126 + * threads. </p>
112.127 + *
112.128 + *
112.129 + * @author Mark Reinhold
112.130 + * @author JSR-51 Expert Group
112.131 + * @since 1.4
112.132 + *
112.133 + * @see ByteBuffer
112.134 + * @see CharBuffer
112.135 + * @see Charset
112.136 + * @see CharsetDecoder
112.137 + */
112.138 +
112.139 +public abstract class CharsetEncoder {
112.140 +
112.141 + private final Charset charset;
112.142 + private final float averageBytesPerChar;
112.143 + private final float maxBytesPerChar;
112.144 +
112.145 + private byte[] replacement;
112.146 +// private CodingErrorAction malformedInputAction
112.147 +// = CodingErrorAction.REPORT;
112.148 +// private CodingErrorAction unmappableCharacterAction
112.149 +// = CodingErrorAction.REPORT;
112.150 +
112.151 + // Internal states
112.152 + //
112.153 + private static final int ST_RESET = 0;
112.154 + private static final int ST_CODING = 1;
112.155 + private static final int ST_END = 2;
112.156 + private static final int ST_FLUSHED = 3;
112.157 +
112.158 + private int state = ST_RESET;
112.159 +
112.160 + private static String stateNames[]
112.161 + = { "RESET", "CODING", "CODING_END", "FLUSHED" };
112.162 +
112.163 +
112.164 + /**
112.165 + * Initializes a new encoder. The new encoder will have the given
112.166 + * bytes-per-char and replacement values. </p>
112.167 + *
112.168 + * @param averageBytesPerChar
112.169 + * A positive float value indicating the expected number of
112.170 + * bytes that will be produced for each input character
112.171 + *
112.172 + * @param maxBytesPerChar
112.173 + * A positive float value indicating the maximum number of
112.174 + * bytes that will be produced for each input character
112.175 + *
112.176 + * @param replacement
112.177 + * The initial replacement; must not be <tt>null</tt>, must have
112.178 + * non-zero length, must not be longer than maxBytesPerChar,
112.179 + * and must be {@link #isLegalReplacement </code>legal<code>}
112.180 + *
112.181 + * @throws IllegalArgumentException
112.182 + * If the preconditions on the parameters do not hold
112.183 + */
112.184 + protected
112.185 + CharsetEncoder(Charset cs,
112.186 + float averageBytesPerChar,
112.187 + float maxBytesPerChar,
112.188 + byte[] replacement)
112.189 + {
112.190 + this.charset = cs;
112.191 + if (averageBytesPerChar <= 0.0f)
112.192 + throw new IllegalArgumentException("Non-positive "
112.193 + + "averageBytesPerChar");
112.194 + if (maxBytesPerChar <= 0.0f)
112.195 + throw new IllegalArgumentException("Non-positive "
112.196 + + "maxBytesPerChar");
112.197 + if (averageBytesPerChar > maxBytesPerChar)
112.198 + throw new IllegalArgumentException("averageBytesPerChar"
112.199 + + " exceeds "
112.200 + + "maxBytesPerChar");
112.201 + this.replacement = replacement;
112.202 + this.averageBytesPerChar = averageBytesPerChar;
112.203 + this.maxBytesPerChar = maxBytesPerChar;
112.204 + replaceWith(replacement);
112.205 + }
112.206 +
112.207 + /**
112.208 + * Initializes a new encoder. The new encoder will have the given
112.209 + * bytes-per-char values and its replacement will be the
112.210 + * byte array <tt>{</tt> <tt>(byte)'?'</tt> <tt>}</tt>. </p>
112.211 + *
112.212 + * @param averageBytesPerChar
112.213 + * A positive float value indicating the expected number of
112.214 + * bytes that will be produced for each input character
112.215 + *
112.216 + * @param maxBytesPerChar
112.217 + * A positive float value indicating the maximum number of
112.218 + * bytes that will be produced for each input character
112.219 + *
112.220 + * @throws IllegalArgumentException
112.221 + * If the preconditions on the parameters do not hold
112.222 + */
112.223 + protected CharsetEncoder(Charset cs,
112.224 + float averageBytesPerChar,
112.225 + float maxBytesPerChar)
112.226 + {
112.227 + this(cs,
112.228 + averageBytesPerChar, maxBytesPerChar,
112.229 + new byte[] { (byte)'?' });
112.230 + }
112.231 +
112.232 + /**
112.233 + * Returns the charset that created this encoder. </p>
112.234 + *
112.235 + * @return This encoder's charset
112.236 + */
112.237 + public final Charset charset() {
112.238 + return charset;
112.239 + }
112.240 +
112.241 + /**
112.242 + * Returns this encoder's replacement value. </p>
112.243 + *
112.244 + * @return This encoder's current replacement,
112.245 + * which is never <tt>null</tt> and is never empty
112.246 + */
112.247 + public final byte[] replacement() {
112.248 + return replacement;
112.249 + }
112.250 +
112.251 + /**
112.252 + * Changes this encoder's replacement value.
112.253 + *
112.254 + * <p> This method invokes the {@link #implReplaceWith implReplaceWith}
112.255 + * method, passing the new replacement, after checking that the new
112.256 + * replacement is acceptable. </p>
112.257 + *
112.258 + * @param newReplacement
112.259 + *
112.260 +
112.261 +
112.262 +
112.263 +
112.264 +
112.265 + * The new replacement; must not be <tt>null</tt>, must have
112.266 + * non-zero length, must not be longer than the value returned by
112.267 + * the {@link #maxBytesPerChar() maxBytesPerChar} method, and
112.268 + * must be {@link #isLegalReplacement </code>legal<code>}
112.269 +
112.270 + *
112.271 + * @return This encoder
112.272 + *
112.273 + * @throws IllegalArgumentException
112.274 + * If the preconditions on the parameter do not hold
112.275 + */
112.276 + public final CharsetEncoder replaceWith(byte[] newReplacement) {
112.277 + if (newReplacement == null)
112.278 + throw new IllegalArgumentException("Null replacement");
112.279 + int len = newReplacement.length;
112.280 + if (len == 0)
112.281 + throw new IllegalArgumentException("Empty replacement");
112.282 + if (len > maxBytesPerChar)
112.283 + throw new IllegalArgumentException("Replacement too long");
112.284 +
112.285 +// if (!isLegalReplacement(newReplacement))
112.286 +// throw new IllegalArgumentException("Illegal replacement");
112.287 +
112.288 + this.replacement = newReplacement;
112.289 + implReplaceWith(newReplacement);
112.290 + return this;
112.291 + }
112.292 +
112.293 + /**
112.294 + * Reports a change to this encoder's replacement value.
112.295 + *
112.296 + * <p> The default implementation of this method does nothing. This method
112.297 + * should be overridden by encoders that require notification of changes to
112.298 + * the replacement. </p>
112.299 + *
112.300 + * @param newReplacement
112.301 + */
112.302 + protected void implReplaceWith(byte[] newReplacement) {
112.303 + }
112.304 +
112.305 +
112.306 +
112.307 + private WeakReference<CharsetDecoder> cachedDecoder = null;
112.308 +
112.309 + /**
112.310 + * Tells whether or not the given byte array is a legal replacement value
112.311 + * for this encoder.
112.312 + *
112.313 + * <p> A replacement is legal if, and only if, it is a legal sequence of
112.314 + * bytes in this encoder's charset; that is, it must be possible to decode
112.315 + * the replacement into one or more sixteen-bit Unicode characters.
112.316 + *
112.317 + * <p> The default implementation of this method is not very efficient; it
112.318 + * should generally be overridden to improve performance. </p>
112.319 + *
112.320 + * @param repl The byte array to be tested
112.321 + *
112.322 + * @return <tt>true</tt> if, and only if, the given byte array
112.323 + * is a legal replacement value for this encoder
112.324 + */
112.325 +// public boolean isLegalReplacement(byte[] repl) {
112.326 +// WeakReference<CharsetDecoder> wr = cachedDecoder;
112.327 +// CharsetDecoder dec = null;
112.328 +// if ((wr == null) || ((dec = wr.get()) == null)) {
112.329 +// dec = charset().newDecoder();
112.330 +// dec.onMalformedInput(CodingErrorAction.REPORT);
112.331 +// dec.onUnmappableCharacter(CodingErrorAction.REPORT);
112.332 +// cachedDecoder = new WeakReference<CharsetDecoder>(dec);
112.333 +// } else {
112.334 +// dec.reset();
112.335 +// }
112.336 +// ByteBuffer bb = ByteBuffer.wrap(repl);
112.337 +// CharBuffer cb = CharBuffer.allocate((int)(bb.remaining()
112.338 +// * dec.maxCharsPerByte()));
112.339 +// CoderResult cr = dec.decode(bb, cb, true);
112.340 +// return !cr.isError();
112.341 +// }
112.342 +
112.343 +
112.344 +
112.345 + /**
112.346 + * Returns this encoder's current action for malformed-input errors. </p>
112.347 + *
112.348 + * @return The current malformed-input action, which is never <tt>null</tt>
112.349 + */
112.350 +// public CodingErrorAction malformedInputAction() {
112.351 +// return malformedInputAction;
112.352 +// }
112.353 +
112.354 + /**
112.355 + * Changes this encoder's action for malformed-input errors. </p>
112.356 + *
112.357 + * <p> This method invokes the {@link #implOnMalformedInput
112.358 + * implOnMalformedInput} method, passing the new action. </p>
112.359 + *
112.360 + * @param newAction The new action; must not be <tt>null</tt>
112.361 + *
112.362 + * @return This encoder
112.363 + *
112.364 + * @throws IllegalArgumentException
112.365 + * If the precondition on the parameter does not hold
112.366 + */
112.367 +// public final CharsetEncoder onMalformedInput(CodingErrorAction newAction) {
112.368 +// if (newAction == null)
112.369 +// throw new IllegalArgumentException("Null action");
112.370 +// malformedInputAction = newAction;
112.371 +// implOnMalformedInput(newAction);
112.372 +// return this;
112.373 +// }
112.374 +
112.375 + /**
112.376 + * Reports a change to this encoder's malformed-input action.
112.377 + *
112.378 + * <p> The default implementation of this method does nothing. This method
112.379 + * should be overridden by encoders that require notification of changes to
112.380 + * the malformed-input action. </p>
112.381 + */
112.382 +// protected void implOnMalformedInput(CodingErrorAction newAction) { }
112.383 +
112.384 + /**
112.385 + * Returns this encoder's current action for unmappable-character errors.
112.386 + * </p>
112.387 + *
112.388 + * @return The current unmappable-character action, which is never
112.389 + * <tt>null</tt>
112.390 + */
112.391 +// public CodingErrorAction unmappableCharacterAction() {
112.392 +// return unmappableCharacterAction;
112.393 +// }
112.394 +
112.395 + /**
112.396 + * Changes this encoder's action for unmappable-character errors.
112.397 + *
112.398 + * <p> This method invokes the {@link #implOnUnmappableCharacter
112.399 + * implOnUnmappableCharacter} method, passing the new action. </p>
112.400 + *
112.401 + * @param newAction The new action; must not be <tt>null</tt>
112.402 + *
112.403 + * @return This encoder
112.404 + *
112.405 + * @throws IllegalArgumentException
112.406 + * If the precondition on the parameter does not hold
112.407 + */
112.408 +// public final CharsetEncoder onUnmappableCharacter(CodingErrorAction
112.409 +// newAction)
112.410 +// {
112.411 +// if (newAction == null)
112.412 +// throw new IllegalArgumentException("Null action");
112.413 +// unmappableCharacterAction = newAction;
112.414 +// implOnUnmappableCharacter(newAction);
112.415 +// return this;
112.416 +// }
112.417 +
112.418 + /**
112.419 + * Reports a change to this encoder's unmappable-character action.
112.420 + *
112.421 + * <p> The default implementation of this method does nothing. This method
112.422 + * should be overridden by encoders that require notification of changes to
112.423 + * the unmappable-character action. </p>
112.424 + */
112.425 +// protected void implOnUnmappableCharacter(CodingErrorAction newAction) { }
112.426 +
112.427 + /**
112.428 + * Returns the average number of bytes that will be produced for each
112.429 + * character of input. This heuristic value may be used to estimate the size
112.430 + * of the output buffer required for a given input sequence. </p>
112.431 + *
112.432 + * @return The average number of bytes produced
112.433 + * per character of input
112.434 + */
112.435 + public final float averageBytesPerChar() {
112.436 + return averageBytesPerChar;
112.437 + }
112.438 +
112.439 + /**
112.440 + * Returns the maximum number of bytes that will be produced for each
112.441 + * character of input. This value may be used to compute the worst-case size
112.442 + * of the output buffer required for a given input sequence. </p>
112.443 + *
112.444 + * @return The maximum number of bytes that will be produced per
112.445 + * character of input
112.446 + */
112.447 + public final float maxBytesPerChar() {
112.448 + return maxBytesPerChar;
112.449 + }
112.450 +
112.451 + /**
112.452 + * Encodes as many characters as possible from the given input buffer,
112.453 + * writing the results to the given output buffer.
112.454 + *
112.455 + * <p> The buffers are read from, and written to, starting at their current
112.456 + * positions. At most {@link Buffer#remaining in.remaining()} characters
112.457 + * will be read and at most {@link Buffer#remaining out.remaining()}
112.458 + * bytes will be written. The buffers' positions will be advanced to
112.459 + * reflect the characters read and the bytes written, but their marks and
112.460 + * limits will not be modified.
112.461 + *
112.462 + * <p> In addition to reading characters from the input buffer and writing
112.463 + * bytes to the output buffer, this method returns a {@link CoderResult}
112.464 + * object to describe its reason for termination:
112.465 + *
112.466 + * <ul>
112.467 + *
112.468 + * <li><p> {@link CoderResult#UNDERFLOW} indicates that as much of the
112.469 + * input buffer as possible has been encoded. If there is no further
112.470 + * input then the invoker can proceed to the next step of the
112.471 + * <a href="#steps">encoding operation</a>. Otherwise this method
112.472 + * should be invoked again with further input. </p></li>
112.473 + *
112.474 + * <li><p> {@link CoderResult#OVERFLOW} indicates that there is
112.475 + * insufficient space in the output buffer to encode any more characters.
112.476 + * This method should be invoked again with an output buffer that has
112.477 + * more {@linkplain Buffer#remaining remaining} bytes. This is
112.478 + * typically done by draining any encoded bytes from the output
112.479 + * buffer. </p></li>
112.480 + *
112.481 + * <li><p> A {@link CoderResult#malformedForLength
112.482 + * </code>malformed-input<code>} result indicates that a malformed-input
112.483 + * error has been detected. The malformed characters begin at the input
112.484 + * buffer's (possibly incremented) position; the number of malformed
112.485 + * characters may be determined by invoking the result object's {@link
112.486 + * CoderResult#length() length} method. This case applies only if the
112.487 + * {@link #onMalformedInput </code>malformed action<code>} of this encoder
112.488 + * is {@link CodingErrorAction#REPORT}; otherwise the malformed input
112.489 + * will be ignored or replaced, as requested. </p></li>
112.490 + *
112.491 + * <li><p> An {@link CoderResult#unmappableForLength
112.492 + * </code>unmappable-character<code>} result indicates that an
112.493 + * unmappable-character error has been detected. The characters that
112.494 + * encode the unmappable character begin at the input buffer's (possibly
112.495 + * incremented) position; the number of such characters may be determined
112.496 + * by invoking the result object's {@link CoderResult#length() length}
112.497 + * method. This case applies only if the {@link #onUnmappableCharacter
112.498 + * </code>unmappable action<code>} of this encoder is {@link
112.499 + * CodingErrorAction#REPORT}; otherwise the unmappable character will be
112.500 + * ignored or replaced, as requested. </p></li>
112.501 + *
112.502 + * </ul>
112.503 + *
112.504 + * In any case, if this method is to be reinvoked in the same encoding
112.505 + * operation then care should be taken to preserve any characters remaining
112.506 + * in the input buffer so that they are available to the next invocation.
112.507 + *
112.508 + * <p> The <tt>endOfInput</tt> parameter advises this method as to whether
112.509 + * the invoker can provide further input beyond that contained in the given
112.510 + * input buffer. If there is a possibility of providing additional input
112.511 + * then the invoker should pass <tt>false</tt> for this parameter; if there
112.512 + * is no possibility of providing further input then the invoker should
112.513 + * pass <tt>true</tt>. It is not erroneous, and in fact it is quite
112.514 + * common, to pass <tt>false</tt> in one invocation and later discover that
112.515 + * no further input was actually available. It is critical, however, that
112.516 + * the final invocation of this method in a sequence of invocations always
112.517 + * pass <tt>true</tt> so that any remaining unencoded input will be treated
112.518 + * as being malformed.
112.519 + *
112.520 + * <p> This method works by invoking the {@link #encodeLoop encodeLoop}
112.521 + * method, interpreting its results, handling error conditions, and
112.522 + * reinvoking it as necessary. </p>
112.523 + *
112.524 + *
112.525 + * @param in
112.526 + * The input character buffer
112.527 + *
112.528 + * @param out
112.529 + * The output byte buffer
112.530 + *
112.531 + * @param endOfInput
112.532 + * <tt>true</tt> if, and only if, the invoker can provide no
112.533 + * additional input characters beyond those in the given buffer
112.534 + *
112.535 + * @return A coder-result object describing the reason for termination
112.536 + *
112.537 + * @throws IllegalStateException
112.538 + * If an encoding operation is already in progress and the previous
112.539 + * step was an invocation neither of the {@link #reset reset}
112.540 + * method, nor of this method with a value of <tt>false</tt> for
112.541 + * the <tt>endOfInput</tt> parameter, nor of this method with a
112.542 + * value of <tt>true</tt> for the <tt>endOfInput</tt> parameter
112.543 + * but a return value indicating an incomplete encoding operation
112.544 + *
112.545 + * @throws CoderMalfunctionError
112.546 + * If an invocation of the encodeLoop method threw
112.547 + * an unexpected exception
112.548 + */
112.549 +// public final CoderResult encode(CharBuffer in, ByteBuffer out,
112.550 +// boolean endOfInput)
112.551 +// {
112.552 +// int newState = endOfInput ? ST_END : ST_CODING;
112.553 +// if ((state != ST_RESET) && (state != ST_CODING)
112.554 +// && !(endOfInput && (state == ST_END)))
112.555 +// throwIllegalStateException(state, newState);
112.556 +// state = newState;
112.557 +//
112.558 +// for (;;) {
112.559 +//
112.560 +// CoderResult cr;
112.561 +// try {
112.562 +// cr = encodeLoop(in, out);
112.563 +// } catch (BufferUnderflowException x) {
112.564 +// throw new CoderMalfunctionError(x);
112.565 +// } catch (BufferOverflowException x) {
112.566 +// throw new CoderMalfunctionError(x);
112.567 +// }
112.568 +//
112.569 +// if (cr.isOverflow())
112.570 +// return cr;
112.571 +//
112.572 +// if (cr.isUnderflow()) {
112.573 +// if (endOfInput && in.hasRemaining()) {
112.574 +// cr = CoderResult.malformedForLength(in.remaining());
112.575 +// // Fall through to malformed-input case
112.576 +// } else {
112.577 +// return cr;
112.578 +// }
112.579 +// }
112.580 +//
112.581 +// CodingErrorAction action = null;
112.582 +// if (cr.isMalformed())
112.583 +// action = malformedInputAction;
112.584 +// else if (cr.isUnmappable())
112.585 +// action = unmappableCharacterAction;
112.586 +// else
112.587 +// assert false : cr.toString();
112.588 +//
112.589 +// if (action == CodingErrorAction.REPORT)
112.590 +// return cr;
112.591 +//
112.592 +// if (action == CodingErrorAction.REPLACE) {
112.593 +// if (out.remaining() < replacement.length)
112.594 +// return CoderResult.OVERFLOW;
112.595 +// out.put(replacement);
112.596 +// }
112.597 +//
112.598 +// if ((action == CodingErrorAction.IGNORE)
112.599 +// || (action == CodingErrorAction.REPLACE)) {
112.600 +// // Skip erroneous input either way
112.601 +// in.position(in.position() + cr.length());
112.602 +// continue;
112.603 +// }
112.604 +//
112.605 +// assert false;
112.606 +// }
112.607 +//
112.608 +// }
112.609 +
112.610 + /**
112.611 + * Flushes this encoder.
112.612 + *
112.613 + * <p> Some encoders maintain internal state and may need to write some
112.614 + * final bytes to the output buffer once the overall input sequence has
112.615 + * been read.
112.616 + *
112.617 + * <p> Any additional output is written to the output buffer beginning at
112.618 + * its current position. At most {@link Buffer#remaining out.remaining()}
112.619 + * bytes will be written. The buffer's position will be advanced
112.620 + * appropriately, but its mark and limit will not be modified.
112.621 + *
112.622 + * <p> If this method completes successfully then it returns {@link
112.623 + * CoderResult#UNDERFLOW}. If there is insufficient room in the output
112.624 + * buffer then it returns {@link CoderResult#OVERFLOW}. If this happens
112.625 + * then this method must be invoked again, with an output buffer that has
112.626 + * more room, in order to complete the current <a href="#steps">encoding
112.627 + * operation</a>.
112.628 + *
112.629 + * <p> If this encoder has already been flushed then invoking this method
112.630 + * has no effect.
112.631 + *
112.632 + * <p> This method invokes the {@link #implFlush implFlush} method to
112.633 + * perform the actual flushing operation. </p>
112.634 + *
112.635 + * @param out
112.636 + * The output byte buffer
112.637 + *
112.638 + * @return A coder-result object, either {@link CoderResult#UNDERFLOW} or
112.639 + * {@link CoderResult#OVERFLOW}
112.640 + *
112.641 + * @throws IllegalStateException
112.642 + * If the previous step of the current encoding operation was an
112.643 + * invocation neither of the {@link #flush flush} method nor of
112.644 + * the three-argument {@link
112.645 + * #encode(CharBuffer,ByteBuffer,boolean) encode} method
112.646 + * with a value of <tt>true</tt> for the <tt>endOfInput</tt>
112.647 + * parameter
112.648 + */
112.649 +// public final CoderResult flush(ByteBuffer out) {
112.650 +// if (state == ST_END) {
112.651 +// CoderResult cr = implFlush(out);
112.652 +// if (cr.isUnderflow())
112.653 +// state = ST_FLUSHED;
112.654 +// return cr;
112.655 +// }
112.656 +//
112.657 +// if (state != ST_FLUSHED)
112.658 +// throwIllegalStateException(state, ST_FLUSHED);
112.659 +//
112.660 +// return CoderResult.UNDERFLOW; // Already flushed
112.661 +// }
112.662 +
112.663 + /**
112.664 + * Flushes this encoder.
112.665 + *
112.666 + * <p> The default implementation of this method does nothing, and always
112.667 + * returns {@link CoderResult#UNDERFLOW}. This method should be overridden
112.668 + * by encoders that may need to write final bytes to the output buffer
112.669 + * once the entire input sequence has been read. </p>
112.670 + *
112.671 + * @param out
112.672 + * The output byte buffer
112.673 + *
112.674 + * @return A coder-result object, either {@link CoderResult#UNDERFLOW} or
112.675 + * {@link CoderResult#OVERFLOW}
112.676 + */
112.677 +// protected CoderResult implFlush(ByteBuffer out) {
112.678 +// return CoderResult.UNDERFLOW;
112.679 +// }
112.680 +
112.681 + /**
112.682 + * Resets this encoder, clearing any internal state.
112.683 + *
112.684 + * <p> This method resets charset-independent state and also invokes the
112.685 + * {@link #implReset() implReset} method in order to perform any
112.686 + * charset-specific reset actions. </p>
112.687 + *
112.688 + * @return This encoder
112.689 + *
112.690 + */
112.691 + public final CharsetEncoder reset() {
112.692 + implReset();
112.693 + state = ST_RESET;
112.694 + return this;
112.695 + }
112.696 +
112.697 + /**
112.698 + * Resets this encoder, clearing any charset-specific internal state.
112.699 + *
112.700 + * <p> The default implementation of this method does nothing. This method
112.701 + * should be overridden by encoders that maintain internal state. </p>
112.702 + */
112.703 + protected void implReset() { }
112.704 +
112.705 + /**
112.706 + * Encodes one or more characters into one or more bytes.
112.707 + *
112.708 + * <p> This method encapsulates the basic encoding loop, encoding as many
112.709 + * characters as possible until it either runs out of input, runs out of room
112.710 + * in the output buffer, or encounters an encoding error. This method is
112.711 + * invoked by the {@link #encode encode} method, which handles result
112.712 + * interpretation and error recovery.
112.713 + *
112.714 + * <p> The buffers are read from, and written to, starting at their current
112.715 + * positions. At most {@link Buffer#remaining in.remaining()} characters
112.716 + * will be read, and at most {@link Buffer#remaining out.remaining()}
112.717 + * bytes will be written. The buffers' positions will be advanced to
112.718 + * reflect the characters read and the bytes written, but their marks and
112.719 + * limits will not be modified.
112.720 + *
112.721 + * <p> This method returns a {@link CoderResult} object to describe its
112.722 + * reason for termination, in the same manner as the {@link #encode encode}
112.723 + * method. Most implementations of this method will handle encoding errors
112.724 + * by returning an appropriate result object for interpretation by the
112.725 + * {@link #encode encode} method. An optimized implementation may instead
112.726 + * examine the relevant error action and implement that action itself.
112.727 + *
112.728 + * <p> An implementation of this method may perform arbitrary lookahead by
112.729 + * returning {@link CoderResult#UNDERFLOW} until it receives sufficient
112.730 + * input. </p>
112.731 + *
112.732 + * @param in
112.733 + * The input character buffer
112.734 + *
112.735 + * @param out
112.736 + * The output byte buffer
112.737 + *
112.738 + * @return A coder-result object describing the reason for termination
112.739 + */
112.740 +// protected abstract CoderResult encodeLoop(CharBuffer in,
112.741 +// ByteBuffer out);
112.742 +
112.743 + /**
112.744 + * Convenience method that encodes the remaining content of a single input
112.745 + * character buffer into a newly-allocated byte buffer.
112.746 + *
112.747 + * <p> This method implements an entire <a href="#steps">encoding
112.748 + * operation</a>; that is, it resets this encoder, then it encodes the
112.749 + * characters in the given character buffer, and finally it flushes this
112.750 + * encoder. This method should therefore not be invoked if an encoding
112.751 + * operation is already in progress. </p>
112.752 + *
112.753 + * @param in
112.754 + * The input character buffer
112.755 + *
112.756 + * @return A newly-allocated byte buffer containing the result of the
112.757 + * encoding operation. The buffer's position will be zero and its
112.758 + * limit will follow the last byte written.
112.759 + *
112.760 + * @throws IllegalStateException
112.761 + * If an encoding operation is already in progress
112.762 + *
112.763 + * @throws MalformedInputException
112.764 + * If the character sequence starting at the input buffer's current
112.765 + * position is not a legal sixteen-bit Unicode sequence and the current malformed-input action
112.766 + * is {@link CodingErrorAction#REPORT}
112.767 + *
112.768 + * @throws UnmappableCharacterException
112.769 + * If the character sequence starting at the input buffer's current
112.770 + * position cannot be mapped to an equivalent byte sequence and
112.771 + * the current unmappable-character action is {@link
112.772 + * CodingErrorAction#REPORT}
112.773 + */
112.774 +// public final ByteBuffer encode(CharBuffer in)
112.775 +// throws CharacterCodingException
112.776 +// {
112.777 +// int n = (int)(in.remaining() * averageBytesPerChar());
112.778 +// ByteBuffer out = ByteBuffer.allocate(n);
112.779 +//
112.780 +// if ((n == 0) && (in.remaining() == 0))
112.781 +// return out;
112.782 +// reset();
112.783 +// for (;;) {
112.784 +// CoderResult cr = in.hasRemaining() ?
112.785 +// encode(in, out, true) : CoderResult.UNDERFLOW;
112.786 +// if (cr.isUnderflow())
112.787 +// cr = flush(out);
112.788 +//
112.789 +// if (cr.isUnderflow())
112.790 +// break;
112.791 +// if (cr.isOverflow()) {
112.792 +// n = 2*n + 1; // Ensure progress; n might be 0!
112.793 +// ByteBuffer o = ByteBuffer.allocate(n);
112.794 +// out.flip();
112.795 +// o.put(out);
112.796 +// out = o;
112.797 +// continue;
112.798 +// }
112.799 +// cr.throwException();
112.800 +// }
112.801 +// out.flip();
112.802 +// return out;
112.803 +// }
112.804 +
112.805 +
112.806 +
112.807 +
112.808 +
112.809 +
112.810 +
112.811 +
112.812 +
112.813 +
112.814 +
112.815 +
112.816 +
112.817 +
112.818 +
112.819 +
112.820 +
112.821 +
112.822 +
112.823 +
112.824 +
112.825 +
112.826 +
112.827 +
112.828 +
112.829 +
112.830 +
112.831 +
112.832 +
112.833 +
112.834 +
112.835 +
112.836 +
112.837 +
112.838 +
112.839 +
112.840 +
112.841 +
112.842 +
112.843 +
112.844 +
112.845 +
112.846 +
112.847 +
112.848 +
112.849 +
112.850 +
112.851 +
112.852 +
112.853 +
112.854 +
112.855 +
112.856 +
112.857 +
112.858 +
112.859 +
112.860 +
112.861 +
112.862 +
112.863 +
112.864 +
112.865 +
112.866 +
112.867 +
112.868 +
112.869 +
112.870 +
112.871 +
112.872 +
112.873 +
112.874 +
112.875 +
112.876 +
112.877 +
112.878 +
112.879 +
112.880 +
112.881 +
112.882 +
112.883 +// private boolean canEncode(CharBuffer cb) {
112.884 +// if (state == ST_FLUSHED)
112.885 +// reset();
112.886 +// else if (state != ST_RESET)
112.887 +// throwIllegalStateException(state, ST_CODING);
112.888 +// CodingErrorAction ma = malformedInputAction();
112.889 +// CodingErrorAction ua = unmappableCharacterAction();
112.890 +// try {
112.891 +// onMalformedInput(CodingErrorAction.REPORT);
112.892 +// onUnmappableCharacter(CodingErrorAction.REPORT);
112.893 +// encode(cb);
112.894 +// } catch (CharacterCodingException x) {
112.895 +// return false;
112.896 +// } finally {
112.897 +// onMalformedInput(ma);
112.898 +// onUnmappableCharacter(ua);
112.899 +// reset();
112.900 +// }
112.901 +// return true;
112.902 +// }
112.903 +
112.904 + /**
112.905 + * Tells whether or not this encoder can encode the given character.
112.906 + *
112.907 + * <p> This method returns <tt>false</tt> if the given character is a
112.908 + * surrogate character; such characters can be interpreted only when they
112.909 + * are members of a pair consisting of a high surrogate followed by a low
112.910 + * surrogate. The {@link #canEncode(java.lang.CharSequence)
112.911 + * canEncode(CharSequence)} method may be used to test whether or not a
112.912 + * character sequence can be encoded.
112.913 + *
112.914 + * <p> This method may modify this encoder's state; it should therefore not
112.915 + * be invoked if an <a href="#steps">encoding operation</a> is already in
112.916 + * progress.
112.917 + *
112.918 + * <p> The default implementation of this method is not very efficient; it
112.919 + * should generally be overridden to improve performance. </p>
112.920 + *
112.921 + * @return <tt>true</tt> if, and only if, this encoder can encode
112.922 + * the given character
112.923 + *
112.924 + * @throws IllegalStateException
112.925 + * If an encoding operation is already in progress
112.926 + */
112.927 +// public boolean canEncode(char c) {
112.928 +// CharBuffer cb = CharBuffer.allocate(1);
112.929 +// cb.put(c);
112.930 +// cb.flip();
112.931 +// return canEncode(cb);
112.932 +// }
112.933 +
112.934 + /**
112.935 + * Tells whether or not this encoder can encode the given character
112.936 + * sequence.
112.937 + *
112.938 + * <p> If this method returns <tt>false</tt> for a particular character
112.939 + * sequence then more information about why the sequence cannot be encoded
112.940 + * may be obtained by performing a full <a href="#steps">encoding
112.941 + * operation</a>.
112.942 + *
112.943 + * <p> This method may modify this encoder's state; it should therefore not
112.944 + * be invoked if an encoding operation is already in progress.
112.945 + *
112.946 + * <p> The default implementation of this method is not very efficient; it
112.947 + * should generally be overridden to improve performance. </p>
112.948 + *
112.949 + * @return <tt>true</tt> if, and only if, this encoder can encode
112.950 + * the given character without throwing any exceptions and without
112.951 + * performing any replacements
112.952 + *
112.953 + * @throws IllegalStateException
112.954 + * If an encoding operation is already in progress
112.955 + */
112.956 +// public boolean canEncode(CharSequence cs) {
112.957 +// CharBuffer cb;
112.958 +// if (cs instanceof CharBuffer)
112.959 +// cb = ((CharBuffer)cs).duplicate();
112.960 +// else
112.961 +// cb = CharBuffer.wrap(cs.toString());
112.962 +// return canEncode(cb);
112.963 +// }
112.964 +
112.965 +
112.966 +
112.967 +
112.968 + private void throwIllegalStateException(int from, int to) {
112.969 + throw new IllegalStateException("Current state = " + stateNames[from]
112.970 + + ", new state = " + stateNames[to]);
112.971 + }
112.972 +
112.973 +}
113.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
113.2 +++ b/rt/emul/compact/src/main/java/java/nio/charset/IllegalCharsetNameException.java Tue Feb 11 13:31:42 2014 +0100
113.3 @@ -0,0 +1,68 @@
113.4 +/*
113.5 + * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
113.6 + *
113.7 + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
113.8 + *
113.9 + *
113.10 + *
113.11 + *
113.12 + *
113.13 + *
113.14 + *
113.15 + *
113.16 + *
113.17 + *
113.18 + *
113.19 + *
113.20 + *
113.21 + *
113.22 + *
113.23 + *
113.24 + *
113.25 + *
113.26 + *
113.27 + *
113.28 + *
113.29 + */
113.30 +
113.31 +// -- This file was mechanically generated: Do not edit! -- //
113.32 +
113.33 +package java.nio.charset;
113.34 +
113.35 +
113.36 +/**
113.37 + * Unchecked exception thrown when a string that is not a
113.38 + * <a href=Charset.html#names>legal charset name</a> is used as such.
113.39 + *
113.40 + * @since 1.4
113.41 + */
113.42 +
113.43 +public class IllegalCharsetNameException
113.44 + extends IllegalArgumentException
113.45 +{
113.46 +
113.47 + private static final long serialVersionUID = 1457525358470002989L;
113.48 +
113.49 + private String charsetName;
113.50 +
113.51 + /**
113.52 + * Constructs an instance of this class. </p>
113.53 + *
113.54 + * @param charsetName
113.55 + * The illegal charset name
113.56 + */
113.57 + public IllegalCharsetNameException(String charsetName) {
113.58 + super(String.valueOf(charsetName));
113.59 + this.charsetName = charsetName;
113.60 + }
113.61 +
113.62 + /**
113.63 + * Retrieves the illegal charset name. </p>
113.64 + *
113.65 + * @return The illegal charset name
113.66 + */
113.67 + public String getCharsetName() {
113.68 + return charsetName;
113.69 + }
113.70 +
113.71 +}
114.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
114.2 +++ b/rt/emul/compact/src/main/java/java/nio/charset/UnsupportedCharsetException.java Tue Feb 11 13:31:42 2014 +0100
114.3 @@ -0,0 +1,68 @@
114.4 +/*
114.5 + * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
114.6 + *
114.7 + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
114.8 + *
114.9 + *
114.10 + *
114.11 + *
114.12 + *
114.13 + *
114.14 + *
114.15 + *
114.16 + *
114.17 + *
114.18 + *
114.19 + *
114.20 + *
114.21 + *
114.22 + *
114.23 + *
114.24 + *
114.25 + *
114.26 + *
114.27 + *
114.28 + *
114.29 + */
114.30 +
114.31 +// -- This file was mechanically generated: Do not edit! -- //
114.32 +
114.33 +package java.nio.charset;
114.34 +
114.35 +
114.36 +/**
114.37 + * Unchecked exception thrown when no support is available
114.38 + * for a requested charset.
114.39 + *
114.40 + * @since 1.4
114.41 + */
114.42 +
114.43 +public class UnsupportedCharsetException
114.44 + extends IllegalArgumentException
114.45 +{
114.46 +
114.47 + private static final long serialVersionUID = 1490765524727386367L;
114.48 +
114.49 + private String charsetName;
114.50 +
114.51 + /**
114.52 + * Constructs an instance of this class. </p>
114.53 + *
114.54 + * @param charsetName
114.55 + * The name of the unsupported charset
114.56 + */
114.57 + public UnsupportedCharsetException(String charsetName) {
114.58 + super(String.valueOf(charsetName));
114.59 + this.charsetName = charsetName;
114.60 + }
114.61 +
114.62 + /**
114.63 + * Retrieves the name of the unsupported charset. </p>
114.64 + *
114.65 + * @return The name of the unsupported charset
114.66 + */
114.67 + public String getCharsetName() {
114.68 + return charsetName;
114.69 + }
114.70 +
114.71 +}
115.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
115.2 +++ b/rt/emul/compact/src/main/java/java/security/AccessController.java Tue Feb 11 13:31:42 2014 +0100
115.3 @@ -0,0 +1,504 @@
115.4 +/*
115.5 + * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
115.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
115.7 + *
115.8 + * This code is free software; you can redistribute it and/or modify it
115.9 + * under the terms of the GNU General Public License version 2 only, as
115.10 + * published by the Free Software Foundation. Oracle designates this
115.11 + * particular file as subject to the "Classpath" exception as provided
115.12 + * by Oracle in the LICENSE file that accompanied this code.
115.13 + *
115.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
115.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
115.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
115.17 + * version 2 for more details (a copy is included in the LICENSE file that
115.18 + * accompanied this code).
115.19 + *
115.20 + * You should have received a copy of the GNU General Public License version
115.21 + * 2 along with this work; if not, write to the Free Software Foundation,
115.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
115.23 + *
115.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
115.25 + * or visit www.oracle.com if you need additional information or have any
115.26 + * questions.
115.27 + */
115.28 +
115.29 +package java.security;
115.30 +
115.31 +/**
115.32 + * <p> The AccessController class is used for access control operations
115.33 + * and decisions.
115.34 + *
115.35 + * <p> More specifically, the AccessController class is used for
115.36 + * three purposes:
115.37 + *
115.38 + * <ul>
115.39 + * <li> to decide whether an access to a critical system
115.40 + * resource is to be allowed or denied, based on the security policy
115.41 + * currently in effect,<p>
115.42 + * <li>to mark code as being "privileged", thus affecting subsequent
115.43 + * access determinations, and<p>
115.44 + * <li>to obtain a "snapshot" of the current calling context so
115.45 + * access-control decisions from a different context can be made with
115.46 + * respect to the saved context. </ul>
115.47 + *
115.48 + * <p> The {@link #checkPermission(Permission) checkPermission} method
115.49 + * determines whether the access request indicated by a specified
115.50 + * permission should be granted or denied. A sample call appears
115.51 + * below. In this example, <code>checkPermission</code> will determine
115.52 + * whether or not to grant "read" access to the file named "testFile" in
115.53 + * the "/temp" directory.
115.54 + *
115.55 + * <pre>
115.56 + *
115.57 + * FilePermission perm = new FilePermission("/temp/testFile", "read");
115.58 + * AccessController.checkPermission(perm);
115.59 + *
115.60 + * </pre>
115.61 + *
115.62 + * <p> If a requested access is allowed,
115.63 + * <code>checkPermission</code> returns quietly. If denied, an
115.64 + * AccessControlException is
115.65 + * thrown. AccessControlException can also be thrown if the requested
115.66 + * permission is of an incorrect type or contains an invalid value.
115.67 + * Such information is given whenever possible.
115.68 + *
115.69 + * Suppose the current thread traversed m callers, in the order of caller 1
115.70 + * to caller 2 to caller m. Then caller m invoked the
115.71 + * <code>checkPermission</code> method.
115.72 + * The <code>checkPermission </code>method determines whether access
115.73 + * is granted or denied based on the following algorithm:
115.74 + *
115.75 + * <pre> {@code
115.76 + * for (int i = m; i > 0; i--) {
115.77 + *
115.78 + * if (caller i's domain does not have the permission)
115.79 + * throw AccessControlException
115.80 + *
115.81 + * else if (caller i is marked as privileged) {
115.82 + * if (a context was specified in the call to doPrivileged)
115.83 + * context.checkPermission(permission)
115.84 + * return;
115.85 + * }
115.86 + * };
115.87 + *
115.88 + * // Next, check the context inherited when the thread was created.
115.89 + * // Whenever a new thread is created, the AccessControlContext at
115.90 + * // that time is stored and associated with the new thread, as the
115.91 + * // "inherited" context.
115.92 + *
115.93 + * inheritedContext.checkPermission(permission);
115.94 + * }</pre>
115.95 + *
115.96 + * <p> A caller can be marked as being "privileged"
115.97 + * (see {@link #doPrivileged(PrivilegedAction) doPrivileged} and below).
115.98 + * When making access control decisions, the <code>checkPermission</code>
115.99 + * method stops checking if it reaches a caller that
115.100 + * was marked as "privileged" via a <code>doPrivileged</code>
115.101 + * call without a context argument (see below for information about a
115.102 + * context argument). If that caller's domain has the
115.103 + * specified permission, no further checking is done and
115.104 + * <code>checkPermission</code>
115.105 + * returns quietly, indicating that the requested access is allowed.
115.106 + * If that domain does not have the specified permission, an exception
115.107 + * is thrown, as usual.
115.108 + *
115.109 + * <p> The normal use of the "privileged" feature is as follows. If you
115.110 + * don't need to return a value from within the "privileged" block, do
115.111 + * the following:
115.112 + *
115.113 + * <pre> {@code
115.114 + * somemethod() {
115.115 + * ...normal code here...
115.116 + * AccessController.doPrivileged(new PrivilegedAction<Void>() {
115.117 + * public Void run() {
115.118 + * // privileged code goes here, for example:
115.119 + * System.loadLibrary("awt");
115.120 + * return null; // nothing to return
115.121 + * }
115.122 + * });
115.123 + * ...normal code here...
115.124 + * }}</pre>
115.125 + *
115.126 + * <p>
115.127 + * PrivilegedAction is an interface with a single method, named
115.128 + * <code>run</code>.
115.129 + * The above example shows creation of an implementation
115.130 + * of that interface; a concrete implementation of the
115.131 + * <code>run</code> method is supplied.
115.132 + * When the call to <code>doPrivileged</code> is made, an
115.133 + * instance of the PrivilegedAction implementation is passed
115.134 + * to it. The <code>doPrivileged</code> method calls the
115.135 + * <code>run</code> method from the PrivilegedAction
115.136 + * implementation after enabling privileges, and returns the
115.137 + * <code>run</code> method's return value as the
115.138 + * <code>doPrivileged</code> return value (which is
115.139 + * ignored in this example).
115.140 + *
115.141 + * <p> If you need to return a value, you can do something like the following:
115.142 + *
115.143 + * <pre> {@code
115.144 + * somemethod() {
115.145 + * ...normal code here...
115.146 + * String user = AccessController.doPrivileged(
115.147 + * new PrivilegedAction<String>() {
115.148 + * public String run() {
115.149 + * return System.getProperty("user.name");
115.150 + * }
115.151 + * });
115.152 + * ...normal code here...
115.153 + * }}</pre>
115.154 + *
115.155 + * <p>If the action performed in your <code>run</code> method could
115.156 + * throw a "checked" exception (those listed in the <code>throws</code> clause
115.157 + * of a method), then you need to use the
115.158 + * <code>PrivilegedExceptionAction</code> interface instead of the
115.159 + * <code>PrivilegedAction</code> interface:
115.160 + *
115.161 + * <pre> {@code
115.162 + * somemethod() throws FileNotFoundException {
115.163 + * ...normal code here...
115.164 + * try {
115.165 + * FileInputStream fis = AccessController.doPrivileged(
115.166 + * new PrivilegedExceptionAction<FileInputStream>() {
115.167 + * public FileInputStream run() throws FileNotFoundException {
115.168 + * return new FileInputStream("someFile");
115.169 + * }
115.170 + * });
115.171 + * } catch (PrivilegedActionException e) {
115.172 + * // e.getException() should be an instance of FileNotFoundException,
115.173 + * // as only "checked" exceptions will be "wrapped" in a
115.174 + * // PrivilegedActionException.
115.175 + * throw (FileNotFoundException) e.getException();
115.176 + * }
115.177 + * ...normal code here...
115.178 + * }}</pre>
115.179 + *
115.180 + * <p> Be *very* careful in your use of the "privileged" construct, and
115.181 + * always remember to make the privileged code section as small as possible.
115.182 + *
115.183 + * <p> Note that <code>checkPermission</code> always performs security checks
115.184 + * within the context of the currently executing thread.
115.185 + * Sometimes a security check that should be made within a given context
115.186 + * will actually need to be done from within a
115.187 + * <i>different</i> context (for example, from within a worker thread).
115.188 + * The {@link #getContext() getContext} method and
115.189 + * AccessControlContext class are provided
115.190 + * for this situation. The <code>getContext</code> method takes a "snapshot"
115.191 + * of the current calling context, and places
115.192 + * it in an AccessControlContext object, which it returns. A sample call is
115.193 + * the following:
115.194 + *
115.195 + * <pre>
115.196 + *
115.197 + * AccessControlContext acc = AccessController.getContext()
115.198 + *
115.199 + * </pre>
115.200 + *
115.201 + * <p>
115.202 + * AccessControlContext itself has a <code>checkPermission</code> method
115.203 + * that makes access decisions based on the context <i>it</i> encapsulates,
115.204 + * rather than that of the current execution thread.
115.205 + * Code within a different context can thus call that method on the
115.206 + * previously-saved AccessControlContext object. A sample call is the
115.207 + * following:
115.208 + *
115.209 + * <pre>
115.210 + *
115.211 + * acc.checkPermission(permission)
115.212 + *
115.213 + * </pre>
115.214 + *
115.215 + * <p> There are also times where you don't know a priori which permissions
115.216 + * to check the context against. In these cases you can use the
115.217 + * doPrivileged method that takes a context:
115.218 + *
115.219 + * <pre> {@code
115.220 + * somemethod() {
115.221 + * AccessController.doPrivileged(new PrivilegedAction<Object>() {
115.222 + * public Object run() {
115.223 + * // Code goes here. Any permission checks within this
115.224 + * // run method will require that the intersection of the
115.225 + * // callers protection domain and the snapshot's
115.226 + * // context have the desired permission.
115.227 + * }
115.228 + * }, acc);
115.229 + * ...normal code here...
115.230 + * }}</pre>
115.231 + *
115.232 + * @see AccessControlContext
115.233 + *
115.234 + * @author Li Gong
115.235 + * @author Roland Schemers
115.236 + */
115.237 +
115.238 +public final class AccessController {
115.239 +
115.240 + /**
115.241 + * Don't allow anyone to instantiate an AccessController
115.242 + */
115.243 + private AccessController() { }
115.244 +
115.245 + /**
115.246 + * Performs the specified <code>PrivilegedAction</code> with privileges
115.247 + * enabled. The action is performed with <i>all</i> of the permissions
115.248 + * possessed by the caller's protection domain.
115.249 + *
115.250 + * <p> If the action's <code>run</code> method throws an (unchecked)
115.251 + * exception, it will propagate through this method.
115.252 + *
115.253 + * <p> Note that any DomainCombiner associated with the current
115.254 + * AccessControlContext will be ignored while the action is performed.
115.255 + *
115.256 + * @param action the action to be performed.
115.257 + *
115.258 + * @return the value returned by the action's <code>run</code> method.
115.259 + *
115.260 + * @exception NullPointerException if the action is <code>null</code>
115.261 + *
115.262 + * @see #doPrivileged(PrivilegedAction,AccessControlContext)
115.263 + * @see #doPrivileged(PrivilegedExceptionAction)
115.264 + * @see #doPrivilegedWithCombiner(PrivilegedAction)
115.265 + * @see java.security.DomainCombiner
115.266 + */
115.267 +
115.268 + public static <T> T doPrivileged(PrivilegedAction<T> action) {
115.269 + return action.run();
115.270 + }
115.271 +
115.272 + /**
115.273 + * Performs the specified <code>PrivilegedAction</code> with privileges
115.274 + * enabled. The action is performed with <i>all</i> of the permissions
115.275 + * possessed by the caller's protection domain.
115.276 + *
115.277 + * <p> If the action's <code>run</code> method throws an (unchecked)
115.278 + * exception, it will propagate through this method.
115.279 + *
115.280 + * <p> This method preserves the current AccessControlContext's
115.281 + * DomainCombiner (which may be null) while the action is performed.
115.282 + *
115.283 + * @param action the action to be performed.
115.284 + *
115.285 + * @return the value returned by the action's <code>run</code> method.
115.286 + *
115.287 + * @exception NullPointerException if the action is <code>null</code>
115.288 + *
115.289 + * @see #doPrivileged(PrivilegedAction)
115.290 + * @see java.security.DomainCombiner
115.291 + *
115.292 + * @since 1.6
115.293 + */
115.294 + public static <T> T doPrivilegedWithCombiner(PrivilegedAction<T> action) {
115.295 + return action.run();
115.296 + }
115.297 +
115.298 +
115.299 + /**
115.300 + * Performs the specified <code>PrivilegedAction</code> with privileges
115.301 + * enabled and restricted by the specified
115.302 + * <code>AccessControlContext</code>.
115.303 + * The action is performed with the intersection of the permissions
115.304 + * possessed by the caller's protection domain, and those possessed
115.305 + * by the domains represented by the specified
115.306 + * <code>AccessControlContext</code>.
115.307 + * <p>
115.308 + * If the action's <code>run</code> method throws an (unchecked) exception,
115.309 + * it will propagate through this method.
115.310 + *
115.311 + * @param action the action to be performed.
115.312 + * @param context an <i>access control context</i>
115.313 + * representing the restriction to be applied to the
115.314 + * caller's domain's privileges before performing
115.315 + * the specified action. If the context is
115.316 + * <code>null</code>,
115.317 + * then no additional restriction is applied.
115.318 + *
115.319 + * @return the value returned by the action's <code>run</code> method.
115.320 + *
115.321 + * @exception NullPointerException if the action is <code>null</code>
115.322 + *
115.323 + * @see #doPrivileged(PrivilegedAction)
115.324 + * @see #doPrivileged(PrivilegedExceptionAction,AccessControlContext)
115.325 + */
115.326 +// public static native <T> T doPrivileged(PrivilegedAction<T> action,
115.327 +// AccessControlContext context);
115.328 +
115.329 + /**
115.330 + * Performs the specified <code>PrivilegedExceptionAction</code> with
115.331 + * privileges enabled. The action is performed with <i>all</i> of the
115.332 + * permissions possessed by the caller's protection domain.
115.333 + *
115.334 + * <p> If the action's <code>run</code> method throws an <i>unchecked</i>
115.335 + * exception, it will propagate through this method.
115.336 + *
115.337 + * <p> Note that any DomainCombiner associated with the current
115.338 + * AccessControlContext will be ignored while the action is performed.
115.339 + *
115.340 + * @param action the action to be performed
115.341 + *
115.342 + * @return the value returned by the action's <code>run</code> method
115.343 + *
115.344 + * @exception PrivilegedActionException if the specified action's
115.345 + * <code>run</code> method threw a <i>checked</i> exception
115.346 + * @exception NullPointerException if the action is <code>null</code>
115.347 + *
115.348 + * @see #doPrivileged(PrivilegedAction)
115.349 + * @see #doPrivileged(PrivilegedExceptionAction,AccessControlContext)
115.350 + * @see #doPrivilegedWithCombiner(PrivilegedExceptionAction)
115.351 + * @see java.security.DomainCombiner
115.352 + */
115.353 + public static <T> T
115.354 + doPrivileged(PrivilegedExceptionAction<T> action)
115.355 + throws PrivilegedActionException {
115.356 + try {
115.357 + return action.run();
115.358 + } catch (Exception ex) {
115.359 + throw new PrivilegedActionException(ex);
115.360 + }
115.361 + }
115.362 +
115.363 +
115.364 + /**
115.365 + * Performs the specified <code>PrivilegedExceptionAction</code> with
115.366 + * privileges enabled. The action is performed with <i>all</i> of the
115.367 + * permissions possessed by the caller's protection domain.
115.368 + *
115.369 + * <p> If the action's <code>run</code> method throws an <i>unchecked</i>
115.370 + * exception, it will propagate through this method.
115.371 + *
115.372 + * <p> This method preserves the current AccessControlContext's
115.373 + * DomainCombiner (which may be null) while the action is performed.
115.374 + *
115.375 + * @param action the action to be performed.
115.376 + *
115.377 + * @return the value returned by the action's <code>run</code> method
115.378 + *
115.379 + * @exception PrivilegedActionException if the specified action's
115.380 + * <code>run</code> method threw a <i>checked</i> exception
115.381 + * @exception NullPointerException if the action is <code>null</code>
115.382 + *
115.383 + * @see #doPrivileged(PrivilegedAction)
115.384 + * @see #doPrivileged(PrivilegedExceptionAction,AccessControlContext)
115.385 + * @see java.security.DomainCombiner
115.386 + *
115.387 + * @since 1.6
115.388 + */
115.389 + public static <T> T doPrivilegedWithCombiner
115.390 + (PrivilegedExceptionAction<T> action) throws PrivilegedActionException {
115.391 + return doPrivileged(action);
115.392 + }
115.393 +
115.394 + /**
115.395 + * Performs the specified <code>PrivilegedExceptionAction</code> with
115.396 + * privileges enabled and restricted by the specified
115.397 + * <code>AccessControlContext</code>. The action is performed with the
115.398 + * intersection of the permissions possessed by the caller's
115.399 + * protection domain, and those possessed by the domains represented by the
115.400 + * specified <code>AccessControlContext</code>.
115.401 + * <p>
115.402 + * If the action's <code>run</code> method throws an <i>unchecked</i>
115.403 + * exception, it will propagate through this method.
115.404 + *
115.405 + * @param action the action to be performed
115.406 + * @param context an <i>access control context</i>
115.407 + * representing the restriction to be applied to the
115.408 + * caller's domain's privileges before performing
115.409 + * the specified action. If the context is
115.410 + * <code>null</code>,
115.411 + * then no additional restriction is applied.
115.412 + *
115.413 + * @return the value returned by the action's <code>run</code> method
115.414 + *
115.415 + * @exception PrivilegedActionException if the specified action's
115.416 + * <code>run</code> method
115.417 + * threw a <i>checked</i> exception
115.418 + * @exception NullPointerException if the action is <code>null</code>
115.419 + *
115.420 + * @see #doPrivileged(PrivilegedAction)
115.421 + * @see #doPrivileged(PrivilegedExceptionAction,AccessControlContext)
115.422 + */
115.423 +// public static native <T> T
115.424 +// doPrivileged(PrivilegedExceptionAction<T> action,
115.425 +// AccessControlContext context)
115.426 +// throws PrivilegedActionException;
115.427 +
115.428 + /**
115.429 + * This method takes a "snapshot" of the current calling context, which
115.430 + * includes the current Thread's inherited AccessControlContext,
115.431 + * and places it in an AccessControlContext object. This context may then
115.432 + * be checked at a later point, possibly in another thread.
115.433 + *
115.434 + * @see AccessControlContext
115.435 + *
115.436 + * @return the AccessControlContext based on the current context.
115.437 + */
115.438 +
115.439 +// public static AccessControlContext getContext()
115.440 +// {
115.441 +// AccessControlContext acc = getStackAccessControlContext();
115.442 +// if (acc == null) {
115.443 +// // all we had was privileged system code. We don't want
115.444 +// // to return null though, so we construct a real ACC.
115.445 +// return new AccessControlContext(null, true);
115.446 +// } else {
115.447 +// return acc.optimize();
115.448 +// }
115.449 +// }
115.450 +
115.451 + /**
115.452 + * Determines whether the access request indicated by the
115.453 + * specified permission should be allowed or denied, based on
115.454 + * the current AccessControlContext and security policy.
115.455 + * This method quietly returns if the access request
115.456 + * is permitted, or throws an AccessControlException otherwise. The
115.457 + * getPermission method of the AccessControlException returns the
115.458 + * <code>perm</code> Permission object instance.
115.459 + *
115.460 + * @param perm the requested permission.
115.461 + *
115.462 + * @exception AccessControlException if the specified permission
115.463 + * is not permitted, based on the current security policy.
115.464 + * @exception NullPointerException if the specified permission
115.465 + * is <code>null</code> and is checked based on the
115.466 + * security policy currently in effect.
115.467 + */
115.468 +
115.469 +// public static void checkPermission(Permission perm)
115.470 +// throws AccessControlException
115.471 +// {
115.472 +// //System.err.println("checkPermission "+perm);
115.473 +// //Thread.currentThread().dumpStack();
115.474 +//
115.475 +// if (perm == null) {
115.476 +// throw new NullPointerException("permission can't be null");
115.477 +// }
115.478 +//
115.479 +// AccessControlContext stack = getStackAccessControlContext();
115.480 +// // if context is null, we had privileged system code on the stack.
115.481 +// if (stack == null) {
115.482 +// Debug debug = AccessControlContext.getDebug();
115.483 +// boolean dumpDebug = false;
115.484 +// if (debug != null) {
115.485 +// dumpDebug = !Debug.isOn("codebase=");
115.486 +// dumpDebug &= !Debug.isOn("permission=") ||
115.487 +// Debug.isOn("permission=" + perm.getClass().getCanonicalName());
115.488 +// }
115.489 +//
115.490 +// if (dumpDebug && Debug.isOn("stack")) {
115.491 +// Thread.currentThread().dumpStack();
115.492 +// }
115.493 +//
115.494 +// if (dumpDebug && Debug.isOn("domain")) {
115.495 +// debug.println("domain (context is null)");
115.496 +// }
115.497 +//
115.498 +// if (dumpDebug) {
115.499 +// debug.println("access allowed "+perm);
115.500 +// }
115.501 +// return;
115.502 +// }
115.503 +//
115.504 +// AccessControlContext acc = stack.optimize();
115.505 +// acc.checkPermission(perm);
115.506 +// }
115.507 +}
116.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
116.2 +++ b/rt/emul/compact/src/main/java/java/security/PrivilegedAction.java Tue Feb 11 13:31:42 2014 +0100
116.3 @@ -0,0 +1,56 @@
116.4 +/*
116.5 + * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
116.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
116.7 + *
116.8 + * This code is free software; you can redistribute it and/or modify it
116.9 + * under the terms of the GNU General Public License version 2 only, as
116.10 + * published by the Free Software Foundation. Oracle designates this
116.11 + * particular file as subject to the "Classpath" exception as provided
116.12 + * by Oracle in the LICENSE file that accompanied this code.
116.13 + *
116.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
116.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
116.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
116.17 + * version 2 for more details (a copy is included in the LICENSE file that
116.18 + * accompanied this code).
116.19 + *
116.20 + * You should have received a copy of the GNU General Public License version
116.21 + * 2 along with this work; if not, write to the Free Software Foundation,
116.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
116.23 + *
116.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
116.25 + * or visit www.oracle.com if you need additional information or have any
116.26 + * questions.
116.27 + */
116.28 +
116.29 +package java.security;
116.30 +
116.31 +
116.32 +/**
116.33 + * A computation to be performed with privileges enabled. The computation is
116.34 + * performed by invoking <code>AccessController.doPrivileged</code> on the
116.35 + * <code>PrivilegedAction</code> object. This interface is used only for
116.36 + * computations that do not throw checked exceptions; computations that
116.37 + * throw checked exceptions must use <code>PrivilegedExceptionAction</code>
116.38 + * instead.
116.39 + *
116.40 + * @see AccessController
116.41 + * @see AccessController#doPrivileged(PrivilegedAction)
116.42 + * @see PrivilegedExceptionAction
116.43 + */
116.44 +
116.45 +public interface PrivilegedAction<T> {
116.46 + /**
116.47 + * Performs the computation. This method will be called by
116.48 + * <code>AccessController.doPrivileged</code> after enabling privileges.
116.49 + *
116.50 + * @return a class-dependent value that may represent the results of the
116.51 + * computation. Each class that implements
116.52 + * <code>PrivilegedAction</code>
116.53 + * should document what (if anything) this value represents.
116.54 + * @see AccessController#doPrivileged(PrivilegedAction)
116.55 + * @see AccessController#doPrivileged(PrivilegedAction,
116.56 + * AccessControlContext)
116.57 + */
116.58 + T run();
116.59 +}
117.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
117.2 +++ b/rt/emul/compact/src/main/java/java/security/PrivilegedActionException.java Tue Feb 11 13:31:42 2014 +0100
117.3 @@ -0,0 +1,105 @@
117.4 +/*
117.5 + * Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
117.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
117.7 + *
117.8 + * This code is free software; you can redistribute it and/or modify it
117.9 + * under the terms of the GNU General Public License version 2 only, as
117.10 + * published by the Free Software Foundation. Oracle designates this
117.11 + * particular file as subject to the "Classpath" exception as provided
117.12 + * by Oracle in the LICENSE file that accompanied this code.
117.13 + *
117.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
117.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
117.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
117.17 + * version 2 for more details (a copy is included in the LICENSE file that
117.18 + * accompanied this code).
117.19 + *
117.20 + * You should have received a copy of the GNU General Public License version
117.21 + * 2 along with this work; if not, write to the Free Software Foundation,
117.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
117.23 + *
117.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
117.25 + * or visit www.oracle.com if you need additional information or have any
117.26 + * questions.
117.27 + */
117.28 +
117.29 +package java.security;
117.30 +
117.31 +/**
117.32 + * This exception is thrown by
117.33 + * <code>doPrivileged(PrivilegedExceptionAction)</code> and
117.34 + * <code>doPrivileged(PrivilegedExceptionAction,
117.35 + * AccessControlContext context)</code> to indicate
117.36 + * that the action being performed threw a checked exception. The exception
117.37 + * thrown by the action can be obtained by calling the
117.38 + * <code>getException</code> method. In effect, an
117.39 + * <code>PrivilegedActionException</code> is a "wrapper"
117.40 + * for an exception thrown by a privileged action.
117.41 + *
117.42 + * <p>As of release 1.4, this exception has been retrofitted to conform to
117.43 + * the general purpose exception-chaining mechanism. The "exception thrown
117.44 + * by the privileged computation" that is provided at construction time and
117.45 + * accessed via the {@link #getException()} method is now known as the
117.46 + * <i>cause</i>, and may be accessed via the {@link Throwable#getCause()}
117.47 + * method, as well as the aforementioned "legacy method."
117.48 + *
117.49 + * @see PrivilegedExceptionAction
117.50 + * @see AccessController#doPrivileged(PrivilegedExceptionAction)
117.51 + * @see AccessController#doPrivileged(PrivilegedExceptionAction,AccessControlContext)
117.52 + */
117.53 +public class PrivilegedActionException extends Exception {
117.54 + // use serialVersionUID from JDK 1.2.2 for interoperability
117.55 + private static final long serialVersionUID = 4724086851538908602L;
117.56 +
117.57 + /**
117.58 + * @serial
117.59 + */
117.60 + private Exception exception;
117.61 +
117.62 + /**
117.63 + * Constructs a new PrivilegedActionException "wrapping"
117.64 + * the specific Exception.
117.65 + *
117.66 + * @param exception The exception thrown
117.67 + */
117.68 + public PrivilegedActionException(Exception exception) {
117.69 + super((Throwable)null); // Disallow initCause
117.70 + this.exception = exception;
117.71 + }
117.72 +
117.73 + /**
117.74 + * Returns the exception thrown by the privileged computation that
117.75 + * resulted in this <code>PrivilegedActionException</code>.
117.76 + *
117.77 + * <p>This method predates the general-purpose exception chaining facility.
117.78 + * The {@link Throwable#getCause()} method is now the preferred means of
117.79 + * obtaining this information.
117.80 + *
117.81 + * @return the exception thrown by the privileged computation that
117.82 + * resulted in this <code>PrivilegedActionException</code>.
117.83 + * @see PrivilegedExceptionAction
117.84 + * @see AccessController#doPrivileged(PrivilegedExceptionAction)
117.85 + * @see AccessController#doPrivileged(PrivilegedExceptionAction,
117.86 + * AccessControlContext)
117.87 + */
117.88 + public Exception getException() {
117.89 + return exception;
117.90 + }
117.91 +
117.92 + /**
117.93 + * Returns the cause of this exception (the exception thrown by
117.94 + * the privileged computation that resulted in this
117.95 + * <code>PrivilegedActionException</code>).
117.96 + *
117.97 + * @return the cause of this exception.
117.98 + * @since 1.4
117.99 + */
117.100 + public Throwable getCause() {
117.101 + return exception;
117.102 + }
117.103 +
117.104 + public String toString() {
117.105 + String s = getClass().getName();
117.106 + return (exception != null) ? (s + ": " + exception.toString()) : s;
117.107 + }
117.108 +}
118.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
118.2 +++ b/rt/emul/compact/src/main/java/java/security/PrivilegedExceptionAction.java Tue Feb 11 13:31:42 2014 +0100
118.3 @@ -0,0 +1,62 @@
118.4 +/*
118.5 + * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
118.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
118.7 + *
118.8 + * This code is free software; you can redistribute it and/or modify it
118.9 + * under the terms of the GNU General Public License version 2 only, as
118.10 + * published by the Free Software Foundation. Oracle designates this
118.11 + * particular file as subject to the "Classpath" exception as provided
118.12 + * by Oracle in the LICENSE file that accompanied this code.
118.13 + *
118.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
118.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
118.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
118.17 + * version 2 for more details (a copy is included in the LICENSE file that
118.18 + * accompanied this code).
118.19 + *
118.20 + * You should have received a copy of the GNU General Public License version
118.21 + * 2 along with this work; if not, write to the Free Software Foundation,
118.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
118.23 + *
118.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
118.25 + * or visit www.oracle.com if you need additional information or have any
118.26 + * questions.
118.27 + */
118.28 +
118.29 +package java.security;
118.30 +
118.31 +
118.32 +/**
118.33 + * A computation to be performed with privileges enabled, that throws one or
118.34 + * more checked exceptions. The computation is performed by invoking
118.35 + * <code>AccessController.doPrivileged</code> on the
118.36 + * <code>PrivilegedExceptionAction</code> object. This interface is
118.37 + * used only for computations that throw checked exceptions;
118.38 + * computations that do not throw
118.39 + * checked exceptions should use <code>PrivilegedAction</code> instead.
118.40 + *
118.41 + * @see AccessController
118.42 + * @see AccessController#doPrivileged(PrivilegedExceptionAction)
118.43 + * @see AccessController#doPrivileged(PrivilegedExceptionAction,
118.44 + * AccessControlContext)
118.45 + * @see PrivilegedAction
118.46 + */
118.47 +
118.48 +public interface PrivilegedExceptionAction<T> {
118.49 + /**
118.50 + * Performs the computation. This method will be called by
118.51 + * <code>AccessController.doPrivileged</code> after enabling privileges.
118.52 + *
118.53 + * @return a class-dependent value that may represent the results of the
118.54 + * computation. Each class that implements
118.55 + * <code>PrivilegedExceptionAction</code> should document what
118.56 + * (if anything) this value represents.
118.57 + * @throws Exception an exceptional condition has occurred. Each class
118.58 + * that implements <code>PrivilegedExceptionAction</code> should
118.59 + * document the exceptions that its run method can throw.
118.60 + * @see AccessController#doPrivileged(PrivilegedExceptionAction)
118.61 + * @see AccessController#doPrivileged(PrivilegedExceptionAction,AccessControlContext)
118.62 + */
118.63 +
118.64 + T run() throws Exception;
118.65 +}
119.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
119.2 +++ b/rt/emul/compact/src/main/java/java/text/Annotation.java Tue Feb 11 13:31:42 2014 +0100
119.3 @@ -0,0 +1,84 @@
119.4 +/*
119.5 + * Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved.
119.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
119.7 + *
119.8 + * This code is free software; you can redistribute it and/or modify it
119.9 + * under the terms of the GNU General Public License version 2 only, as
119.10 + * published by the Free Software Foundation. Oracle designates this
119.11 + * particular file as subject to the "Classpath" exception as provided
119.12 + * by Oracle in the LICENSE file that accompanied this code.
119.13 + *
119.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
119.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
119.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
119.17 + * version 2 for more details (a copy is included in the LICENSE file that
119.18 + * accompanied this code).
119.19 + *
119.20 + * You should have received a copy of the GNU General Public License version
119.21 + * 2 along with this work; if not, write to the Free Software Foundation,
119.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
119.23 + *
119.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
119.25 + * or visit www.oracle.com if you need additional information or have any
119.26 + * questions.
119.27 + */
119.28 +
119.29 +package java.text;
119.30 +
119.31 +/**
119.32 +* An Annotation object is used as a wrapper for a text attribute value if
119.33 +* the attribute has annotation characteristics. These characteristics are:
119.34 +* <ul>
119.35 +* <li>The text range that the attribute is applied to is critical to the
119.36 +* semantics of the range. That means, the attribute cannot be applied to subranges
119.37 +* of the text range that it applies to, and, if two adjacent text ranges have
119.38 +* the same value for this attribute, the attribute still cannot be applied to
119.39 +* the combined range as a whole with this value.
119.40 +* <li>The attribute or its value usually do no longer apply if the underlying text is
119.41 +* changed.
119.42 +* </ul>
119.43 +*
119.44 +* An example is grammatical information attached to a sentence:
119.45 +* For the previous sentence, you can say that "an example"
119.46 +* is the subject, but you cannot say the same about "an", "example", or "exam".
119.47 +* When the text is changed, the grammatical information typically becomes invalid.
119.48 +* Another example is Japanese reading information (yomi).
119.49 +*
119.50 +* <p>
119.51 +* Wrapping the attribute value into an Annotation object guarantees that
119.52 +* adjacent text runs don't get merged even if the attribute values are equal,
119.53 +* and indicates to text containers that the attribute should be discarded if
119.54 +* the underlying text is modified.
119.55 +*
119.56 +* @see AttributedCharacterIterator
119.57 +* @since 1.2
119.58 +*/
119.59 +
119.60 +public class Annotation {
119.61 +
119.62 + /**
119.63 + * Constructs an annotation record with the given value, which
119.64 + * may be null.
119.65 + * @param value The value of the attribute
119.66 + */
119.67 + public Annotation(Object value) {
119.68 + this.value = value;
119.69 + }
119.70 +
119.71 + /**
119.72 + * Returns the value of the attribute, which may be null.
119.73 + */
119.74 + public Object getValue() {
119.75 + return value;
119.76 + }
119.77 +
119.78 + /**
119.79 + * Returns the String representation of this Annotation.
119.80 + */
119.81 + public String toString() {
119.82 + return getClass().getName() + "[value=" + value + "]";
119.83 + }
119.84 +
119.85 + private Object value;
119.86 +
119.87 +};
120.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
120.2 +++ b/rt/emul/compact/src/main/java/java/text/AttributedCharacterIterator.java Tue Feb 11 13:31:42 2014 +0100
120.3 @@ -0,0 +1,254 @@
120.4 +/*
120.5 + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
120.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
120.7 + *
120.8 + * This code is free software; you can redistribute it and/or modify it
120.9 + * under the terms of the GNU General Public License version 2 only, as
120.10 + * published by the Free Software Foundation. Oracle designates this
120.11 + * particular file as subject to the "Classpath" exception as provided
120.12 + * by Oracle in the LICENSE file that accompanied this code.
120.13 + *
120.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
120.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
120.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
120.17 + * version 2 for more details (a copy is included in the LICENSE file that
120.18 + * accompanied this code).
120.19 + *
120.20 + * You should have received a copy of the GNU General Public License version
120.21 + * 2 along with this work; if not, write to the Free Software Foundation,
120.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
120.23 + *
120.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
120.25 + * or visit www.oracle.com if you need additional information or have any
120.26 + * questions.
120.27 + */
120.28 +
120.29 +package java.text;
120.30 +
120.31 +import java.io.InvalidObjectException;
120.32 +import java.io.Serializable;
120.33 +import java.util.HashMap;
120.34 +import java.util.Map;
120.35 +import java.util.Set;
120.36 +
120.37 +/**
120.38 + * An {@code AttributedCharacterIterator} allows iteration through both text and
120.39 + * related attribute information.
120.40 + *
120.41 + * <p>
120.42 + * An attribute is a key/value pair, identified by the key. No two
120.43 + * attributes on a given character can have the same key.
120.44 + *
120.45 + * <p>The values for an attribute are immutable, or must not be mutated
120.46 + * by clients or storage. They are always passed by reference, and not
120.47 + * cloned.
120.48 + *
120.49 + * <p>A <em>run with respect to an attribute</em> is a maximum text range for
120.50 + * which:
120.51 + * <ul>
120.52 + * <li>the attribute is undefined or {@code null} for the entire range, or
120.53 + * <li>the attribute value is defined and has the same non-{@code null} value for the
120.54 + * entire range.
120.55 + * </ul>
120.56 + *
120.57 + * <p>A <em>run with respect to a set of attributes</em> is a maximum text range for
120.58 + * which this condition is met for each member attribute.
120.59 + *
120.60 + * <p>When getting a run with no explicit attributes specified (i.e.,
120.61 + * calling {@link #getRunStart()} and {@link #getRunLimit()}), any
120.62 + * contiguous text segments having the same attributes (the same set
120.63 + * of attribute/value pairs) are treated as separate runs if the
120.64 + * attributes have been given to those text segments separately.
120.65 + *
120.66 + * <p>The returned indexes are limited to the range of the iterator.
120.67 + *
120.68 + * <p>The returned attribute information is limited to runs that contain
120.69 + * the current character.
120.70 + *
120.71 + * <p>
120.72 + * Attribute keys are instances of {@link AttributedCharacterIterator.Attribute} and its
120.73 + * subclasses, such as {@link java.awt.font.TextAttribute}.
120.74 + *
120.75 + * @see AttributedCharacterIterator.Attribute
120.76 + * @see java.awt.font.TextAttribute
120.77 + * @see AttributedString
120.78 + * @see Annotation
120.79 + * @since 1.2
120.80 + */
120.81 +
120.82 +public interface AttributedCharacterIterator extends CharacterIterator {
120.83 +
120.84 + /**
120.85 + * Defines attribute keys that are used to identify text attributes. These
120.86 + * keys are used in {@code AttributedCharacterIterator} and {@code AttributedString}.
120.87 + * @see AttributedCharacterIterator
120.88 + * @see AttributedString
120.89 + * @since 1.2
120.90 + */
120.91 +
120.92 + public static class Attribute implements Serializable {
120.93 +
120.94 + /**
120.95 + * The name of this {@code Attribute}. The name is used primarily by {@code readResolve}
120.96 + * to look up the corresponding predefined instance when deserializing
120.97 + * an instance.
120.98 + * @serial
120.99 + */
120.100 + private String name;
120.101 +
120.102 + // table of all instances in this class, used by readResolve
120.103 + private static final Map instanceMap = new HashMap(7);
120.104 +
120.105 + /**
120.106 + * Constructs an {@code Attribute} with the given name.
120.107 + */
120.108 + protected Attribute(String name) {
120.109 + this.name = name;
120.110 + if (this.getClass() == Attribute.class) {
120.111 + instanceMap.put(name, this);
120.112 + }
120.113 + }
120.114 +
120.115 + /**
120.116 + * Compares two objects for equality. This version only returns true
120.117 + * for <code>x.equals(y)</code> if <code>x</code> and <code>y</code> refer
120.118 + * to the same object, and guarantees this for all subclasses.
120.119 + */
120.120 + public final boolean equals(Object obj) {
120.121 + return super.equals(obj);
120.122 + }
120.123 +
120.124 + /**
120.125 + * Returns a hash code value for the object. This version is identical to
120.126 + * the one in {@code Object}, but is also final.
120.127 + */
120.128 + public final int hashCode() {
120.129 + return super.hashCode();
120.130 + }
120.131 +
120.132 + /**
120.133 + * Returns a string representation of the object. This version returns the
120.134 + * concatenation of class name, {@code "("}, a name identifying the attribute
120.135 + * and {@code ")"}.
120.136 + */
120.137 + public String toString() {
120.138 + return getClass().getName() + "(" + name + ")";
120.139 + }
120.140 +
120.141 + /**
120.142 + * Returns the name of the attribute.
120.143 + */
120.144 + protected String getName() {
120.145 + return name;
120.146 + }
120.147 +
120.148 + /**
120.149 + * Resolves instances being deserialized to the predefined constants.
120.150 + */
120.151 + protected Object readResolve() throws InvalidObjectException {
120.152 + if (this.getClass() != Attribute.class) {
120.153 + throw new InvalidObjectException("subclass didn't correctly implement readResolve");
120.154 + }
120.155 +
120.156 + Attribute instance = (Attribute) instanceMap.get(getName());
120.157 + if (instance != null) {
120.158 + return instance;
120.159 + } else {
120.160 + throw new InvalidObjectException("unknown attribute name");
120.161 + }
120.162 + }
120.163 +
120.164 + /**
120.165 + * Attribute key for the language of some text.
120.166 + * <p> Values are instances of {@link java.util.Locale Locale}.
120.167 + * @see java.util.Locale
120.168 + */
120.169 + public static final Attribute LANGUAGE = new Attribute("language");
120.170 +
120.171 + /**
120.172 + * Attribute key for the reading of some text. In languages where the written form
120.173 + * and the pronunciation of a word are only loosely related (such as Japanese),
120.174 + * it is often necessary to store the reading (pronunciation) along with the
120.175 + * written form.
120.176 + * <p>Values are instances of {@link Annotation} holding instances of {@link String}.
120.177 + * @see Annotation
120.178 + * @see java.lang.String
120.179 + */
120.180 + public static final Attribute READING = new Attribute("reading");
120.181 +
120.182 + /**
120.183 + * Attribute key for input method segments. Input methods often break
120.184 + * up text into segments, which usually correspond to words.
120.185 + * <p>Values are instances of {@link Annotation} holding a {@code null} reference.
120.186 + * @see Annotation
120.187 + */
120.188 + public static final Attribute INPUT_METHOD_SEGMENT = new Attribute("input_method_segment");
120.189 +
120.190 + // make sure the serial version doesn't change between compiler versions
120.191 + private static final long serialVersionUID = -9142742483513960612L;
120.192 +
120.193 + };
120.194 +
120.195 + /**
120.196 + * Returns the index of the first character of the run
120.197 + * with respect to all attributes containing the current character.
120.198 + *
120.199 + * <p>Any contiguous text segments having the same attributes (the
120.200 + * same set of attribute/value pairs) are treated as separate runs
120.201 + * if the attributes have been given to those text segments separately.
120.202 + */
120.203 + public int getRunStart();
120.204 +
120.205 + /**
120.206 + * Returns the index of the first character of the run
120.207 + * with respect to the given {@code attribute} containing the current character.
120.208 + */
120.209 + public int getRunStart(Attribute attribute);
120.210 +
120.211 + /**
120.212 + * Returns the index of the first character of the run
120.213 + * with respect to the given {@code attributes} containing the current character.
120.214 + */
120.215 + public int getRunStart(Set<? extends Attribute> attributes);
120.216 +
120.217 + /**
120.218 + * Returns the index of the first character following the run
120.219 + * with respect to all attributes containing the current character.
120.220 + *
120.221 + * <p>Any contiguous text segments having the same attributes (the
120.222 + * same set of attribute/value pairs) are treated as separate runs
120.223 + * if the attributes have been given to those text segments separately.
120.224 + */
120.225 + public int getRunLimit();
120.226 +
120.227 + /**
120.228 + * Returns the index of the first character following the run
120.229 + * with respect to the given {@code attribute} containing the current character.
120.230 + */
120.231 + public int getRunLimit(Attribute attribute);
120.232 +
120.233 + /**
120.234 + * Returns the index of the first character following the run
120.235 + * with respect to the given {@code attributes} containing the current character.
120.236 + */
120.237 + public int getRunLimit(Set<? extends Attribute> attributes);
120.238 +
120.239 + /**
120.240 + * Returns a map with the attributes defined on the current
120.241 + * character.
120.242 + */
120.243 + public Map<Attribute,Object> getAttributes();
120.244 +
120.245 + /**
120.246 + * Returns the value of the named {@code attribute} for the current character.
120.247 + * Returns {@code null} if the {@code attribute} is not defined.
120.248 + */
120.249 + public Object getAttribute(Attribute attribute);
120.250 +
120.251 + /**
120.252 + * Returns the keys of all attributes defined on the
120.253 + * iterator's text range. The set is empty if no
120.254 + * attributes are defined.
120.255 + */
120.256 + public Set<Attribute> getAllAttributeKeys();
120.257 +};
121.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
121.2 +++ b/rt/emul/compact/src/main/java/java/text/AttributedString.java Tue Feb 11 13:31:42 2014 +0100
121.3 @@ -0,0 +1,1120 @@
121.4 +/*
121.5 + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
121.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
121.7 + *
121.8 + * This code is free software; you can redistribute it and/or modify it
121.9 + * under the terms of the GNU General Public License version 2 only, as
121.10 + * published by the Free Software Foundation. Oracle designates this
121.11 + * particular file as subject to the "Classpath" exception as provided
121.12 + * by Oracle in the LICENSE file that accompanied this code.
121.13 + *
121.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
121.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
121.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
121.17 + * version 2 for more details (a copy is included in the LICENSE file that
121.18 + * accompanied this code).
121.19 + *
121.20 + * You should have received a copy of the GNU General Public License version
121.21 + * 2 along with this work; if not, write to the Free Software Foundation,
121.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
121.23 + *
121.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
121.25 + * or visit www.oracle.com if you need additional information or have any
121.26 + * questions.
121.27 + */
121.28 +
121.29 +package java.text;
121.30 +
121.31 +import java.util.*;
121.32 +import java.text.AttributedCharacterIterator.Attribute;
121.33 +
121.34 +/**
121.35 + * An AttributedString holds text and related attribute information. It
121.36 + * may be used as the actual data storage in some cases where a text
121.37 + * reader wants to access attributed text through the AttributedCharacterIterator
121.38 + * interface.
121.39 + *
121.40 + * <p>
121.41 + * An attribute is a key/value pair, identified by the key. No two
121.42 + * attributes on a given character can have the same key.
121.43 + *
121.44 + * <p>The values for an attribute are immutable, or must not be mutated
121.45 + * by clients or storage. They are always passed by reference, and not
121.46 + * cloned.
121.47 + *
121.48 + * @see AttributedCharacterIterator
121.49 + * @see Annotation
121.50 + * @since 1.2
121.51 + */
121.52 +
121.53 +public class AttributedString {
121.54 +
121.55 + // since there are no vectors of int, we have to use arrays.
121.56 + // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
121.57 + private static final int ARRAY_SIZE_INCREMENT = 10;
121.58 +
121.59 + // field holding the text
121.60 + String text;
121.61 +
121.62 + // fields holding run attribute information
121.63 + // run attributes are organized by run
121.64 + int runArraySize; // current size of the arrays
121.65 + int runCount; // actual number of runs, <= runArraySize
121.66 + int runStarts[]; // start index for each run
121.67 + Vector runAttributes[]; // vector of attribute keys for each run
121.68 + Vector runAttributeValues[]; // parallel vector of attribute values for each run
121.69 +
121.70 + /**
121.71 + * Constructs an AttributedString instance with the given
121.72 + * AttributedCharacterIterators.
121.73 + *
121.74 + * @param iterators AttributedCharacterIterators to construct
121.75 + * AttributedString from.
121.76 + * @throws NullPointerException if iterators is null
121.77 + */
121.78 + AttributedString(AttributedCharacterIterator[] iterators) {
121.79 + if (iterators == null) {
121.80 + throw new NullPointerException("Iterators must not be null");
121.81 + }
121.82 + if (iterators.length == 0) {
121.83 + text = "";
121.84 + }
121.85 + else {
121.86 + // Build the String contents
121.87 + StringBuffer buffer = new StringBuffer();
121.88 + for (int counter = 0; counter < iterators.length; counter++) {
121.89 + appendContents(buffer, iterators[counter]);
121.90 + }
121.91 +
121.92 + text = buffer.toString();
121.93 +
121.94 + if (text.length() > 0) {
121.95 + // Determine the runs, creating a new run when the attributes
121.96 + // differ.
121.97 + int offset = 0;
121.98 + Map last = null;
121.99 +
121.100 + for (int counter = 0; counter < iterators.length; counter++) {
121.101 + AttributedCharacterIterator iterator = iterators[counter];
121.102 + int start = iterator.getBeginIndex();
121.103 + int end = iterator.getEndIndex();
121.104 + int index = start;
121.105 +
121.106 + while (index < end) {
121.107 + iterator.setIndex(index);
121.108 +
121.109 + Map attrs = iterator.getAttributes();
121.110 +
121.111 + if (mapsDiffer(last, attrs)) {
121.112 + setAttributes(attrs, index - start + offset);
121.113 + }
121.114 + last = attrs;
121.115 + index = iterator.getRunLimit();
121.116 + }
121.117 + offset += (end - start);
121.118 + }
121.119 + }
121.120 + }
121.121 + }
121.122 +
121.123 + /**
121.124 + * Constructs an AttributedString instance with the given text.
121.125 + * @param text The text for this attributed string.
121.126 + * @exception NullPointerException if <code>text</code> is null.
121.127 + */
121.128 + public AttributedString(String text) {
121.129 + if (text == null) {
121.130 + throw new NullPointerException();
121.131 + }
121.132 + this.text = text;
121.133 + }
121.134 +
121.135 + /**
121.136 + * Constructs an AttributedString instance with the given text and attributes.
121.137 + * @param text The text for this attributed string.
121.138 + * @param attributes The attributes that apply to the entire string.
121.139 + * @exception NullPointerException if <code>text</code> or
121.140 + * <code>attributes</code> is null.
121.141 + * @exception IllegalArgumentException if the text has length 0
121.142 + * and the attributes parameter is not an empty Map (attributes
121.143 + * cannot be applied to a 0-length range).
121.144 + */
121.145 + public AttributedString(String text,
121.146 + Map<? extends Attribute, ?> attributes)
121.147 + {
121.148 + if (text == null || attributes == null) {
121.149 + throw new NullPointerException();
121.150 + }
121.151 + this.text = text;
121.152 +
121.153 + if (text.length() == 0) {
121.154 + if (attributes.isEmpty())
121.155 + return;
121.156 + throw new IllegalArgumentException("Can't add attribute to 0-length text");
121.157 + }
121.158 +
121.159 + int attributeCount = attributes.size();
121.160 + if (attributeCount > 0) {
121.161 + createRunAttributeDataVectors();
121.162 + Vector newRunAttributes = new Vector(attributeCount);
121.163 + Vector newRunAttributeValues = new Vector(attributeCount);
121.164 + runAttributes[0] = newRunAttributes;
121.165 + runAttributeValues[0] = newRunAttributeValues;
121.166 + Iterator iterator = attributes.entrySet().iterator();
121.167 + while (iterator.hasNext()) {
121.168 + Map.Entry entry = (Map.Entry) iterator.next();
121.169 + newRunAttributes.addElement(entry.getKey());
121.170 + newRunAttributeValues.addElement(entry.getValue());
121.171 + }
121.172 + }
121.173 + }
121.174 +
121.175 + /**
121.176 + * Constructs an AttributedString instance with the given attributed
121.177 + * text represented by AttributedCharacterIterator.
121.178 + * @param text The text for this attributed string.
121.179 + * @exception NullPointerException if <code>text</code> is null.
121.180 + */
121.181 + public AttributedString(AttributedCharacterIterator text) {
121.182 + // If performance is critical, this constructor should be
121.183 + // implemented here rather than invoking the constructor for a
121.184 + // subrange. We can avoid some range checking in the loops.
121.185 + this(text, text.getBeginIndex(), text.getEndIndex(), null);
121.186 + }
121.187 +
121.188 + /**
121.189 + * Constructs an AttributedString instance with the subrange of
121.190 + * the given attributed text represented by
121.191 + * AttributedCharacterIterator. If the given range produces an
121.192 + * empty text, all attributes will be discarded. Note that any
121.193 + * attributes wrapped by an Annotation object are discarded for a
121.194 + * subrange of the original attribute range.
121.195 + *
121.196 + * @param text The text for this attributed string.
121.197 + * @param beginIndex Index of the first character of the range.
121.198 + * @param endIndex Index of the character following the last character
121.199 + * of the range.
121.200 + * @exception NullPointerException if <code>text</code> is null.
121.201 + * @exception IllegalArgumentException if the subrange given by
121.202 + * beginIndex and endIndex is out of the text range.
121.203 + * @see java.text.Annotation
121.204 + */
121.205 + public AttributedString(AttributedCharacterIterator text,
121.206 + int beginIndex,
121.207 + int endIndex) {
121.208 + this(text, beginIndex, endIndex, null);
121.209 + }
121.210 +
121.211 + /**
121.212 + * Constructs an AttributedString instance with the subrange of
121.213 + * the given attributed text represented by
121.214 + * AttributedCharacterIterator. Only attributes that match the
121.215 + * given attributes will be incorporated into the instance. If the
121.216 + * given range produces an empty text, all attributes will be
121.217 + * discarded. Note that any attributes wrapped by an Annotation
121.218 + * object are discarded for a subrange of the original attribute
121.219 + * range.
121.220 + *
121.221 + * @param text The text for this attributed string.
121.222 + * @param beginIndex Index of the first character of the range.
121.223 + * @param endIndex Index of the character following the last character
121.224 + * of the range.
121.225 + * @param attributes Specifies attributes to be extracted
121.226 + * from the text. If null is specified, all available attributes will
121.227 + * be used.
121.228 + * @exception NullPointerException if <code>text</code> is null.
121.229 + * @exception IllegalArgumentException if the subrange given by
121.230 + * beginIndex and endIndex is out of the text range.
121.231 + * @see java.text.Annotation
121.232 + */
121.233 + public AttributedString(AttributedCharacterIterator text,
121.234 + int beginIndex,
121.235 + int endIndex,
121.236 + Attribute[] attributes) {
121.237 + if (text == null) {
121.238 + throw new NullPointerException();
121.239 + }
121.240 +
121.241 + // Validate the given subrange
121.242 + int textBeginIndex = text.getBeginIndex();
121.243 + int textEndIndex = text.getEndIndex();
121.244 + if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex)
121.245 + throw new IllegalArgumentException("Invalid substring range");
121.246 +
121.247 + // Copy the given string
121.248 + StringBuffer textBuffer = new StringBuffer();
121.249 + text.setIndex(beginIndex);
121.250 + for (char c = text.current(); text.getIndex() < endIndex; c = text.next())
121.251 + textBuffer.append(c);
121.252 + this.text = textBuffer.toString();
121.253 +
121.254 + if (beginIndex == endIndex)
121.255 + return;
121.256 +
121.257 + // Select attribute keys to be taken care of
121.258 + HashSet keys = new HashSet();
121.259 + if (attributes == null) {
121.260 + keys.addAll(text.getAllAttributeKeys());
121.261 + } else {
121.262 + for (int i = 0; i < attributes.length; i++)
121.263 + keys.add(attributes[i]);
121.264 + keys.retainAll(text.getAllAttributeKeys());
121.265 + }
121.266 + if (keys.isEmpty())
121.267 + return;
121.268 +
121.269 + // Get and set attribute runs for each attribute name. Need to
121.270 + // scan from the top of the text so that we can discard any
121.271 + // Annotation that is no longer applied to a subset text segment.
121.272 + Iterator itr = keys.iterator();
121.273 + while (itr.hasNext()) {
121.274 + Attribute attributeKey = (Attribute)itr.next();
121.275 + text.setIndex(textBeginIndex);
121.276 + while (text.getIndex() < endIndex) {
121.277 + int start = text.getRunStart(attributeKey);
121.278 + int limit = text.getRunLimit(attributeKey);
121.279 + Object value = text.getAttribute(attributeKey);
121.280 +
121.281 + if (value != null) {
121.282 + if (value instanceof Annotation) {
121.283 + if (start >= beginIndex && limit <= endIndex) {
121.284 + addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
121.285 + } else {
121.286 + if (limit > endIndex)
121.287 + break;
121.288 + }
121.289 + } else {
121.290 + // if the run is beyond the given (subset) range, we
121.291 + // don't need to process further.
121.292 + if (start >= endIndex)
121.293 + break;
121.294 + if (limit > beginIndex) {
121.295 + // attribute is applied to any subrange
121.296 + if (start < beginIndex)
121.297 + start = beginIndex;
121.298 + if (limit > endIndex)
121.299 + limit = endIndex;
121.300 + if (start != limit) {
121.301 + addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
121.302 + }
121.303 + }
121.304 + }
121.305 + }
121.306 + text.setIndex(limit);
121.307 + }
121.308 + }
121.309 + }
121.310 +
121.311 + /**
121.312 + * Adds an attribute to the entire string.
121.313 + * @param attribute the attribute key
121.314 + * @param value the value of the attribute; may be null
121.315 + * @exception NullPointerException if <code>attribute</code> is null.
121.316 + * @exception IllegalArgumentException if the AttributedString has length 0
121.317 + * (attributes cannot be applied to a 0-length range).
121.318 + */
121.319 + public void addAttribute(Attribute attribute, Object value) {
121.320 +
121.321 + if (attribute == null) {
121.322 + throw new NullPointerException();
121.323 + }
121.324 +
121.325 + int len = length();
121.326 + if (len == 0) {
121.327 + throw new IllegalArgumentException("Can't add attribute to 0-length text");
121.328 + }
121.329 +
121.330 + addAttributeImpl(attribute, value, 0, len);
121.331 + }
121.332 +
121.333 + /**
121.334 + * Adds an attribute to a subrange of the string.
121.335 + * @param attribute the attribute key
121.336 + * @param value The value of the attribute. May be null.
121.337 + * @param beginIndex Index of the first character of the range.
121.338 + * @param endIndex Index of the character following the last character of the range.
121.339 + * @exception NullPointerException if <code>attribute</code> is null.
121.340 + * @exception IllegalArgumentException if beginIndex is less then 0, endIndex is
121.341 + * greater than the length of the string, or beginIndex and endIndex together don't
121.342 + * define a non-empty subrange of the string.
121.343 + */
121.344 + public void addAttribute(Attribute attribute, Object value,
121.345 + int beginIndex, int endIndex) {
121.346 +
121.347 + if (attribute == null) {
121.348 + throw new NullPointerException();
121.349 + }
121.350 +
121.351 + if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {
121.352 + throw new IllegalArgumentException("Invalid substring range");
121.353 + }
121.354 +
121.355 + addAttributeImpl(attribute, value, beginIndex, endIndex);
121.356 + }
121.357 +
121.358 + /**
121.359 + * Adds a set of attributes to a subrange of the string.
121.360 + * @param attributes The attributes to be added to the string.
121.361 + * @param beginIndex Index of the first character of the range.
121.362 + * @param endIndex Index of the character following the last
121.363 + * character of the range.
121.364 + * @exception NullPointerException if <code>attributes</code> is null.
121.365 + * @exception IllegalArgumentException if beginIndex is less then
121.366 + * 0, endIndex is greater than the length of the string, or
121.367 + * beginIndex and endIndex together don't define a non-empty
121.368 + * subrange of the string and the attributes parameter is not an
121.369 + * empty Map.
121.370 + */
121.371 + public void addAttributes(Map<? extends Attribute, ?> attributes,
121.372 + int beginIndex, int endIndex)
121.373 + {
121.374 + if (attributes == null) {
121.375 + throw new NullPointerException();
121.376 + }
121.377 +
121.378 + if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) {
121.379 + throw new IllegalArgumentException("Invalid substring range");
121.380 + }
121.381 + if (beginIndex == endIndex) {
121.382 + if (attributes.isEmpty())
121.383 + return;
121.384 + throw new IllegalArgumentException("Can't add attribute to 0-length text");
121.385 + }
121.386 +
121.387 + // make sure we have run attribute data vectors
121.388 + if (runCount == 0) {
121.389 + createRunAttributeDataVectors();
121.390 + }
121.391 +
121.392 + // break up runs if necessary
121.393 + int beginRunIndex = ensureRunBreak(beginIndex);
121.394 + int endRunIndex = ensureRunBreak(endIndex);
121.395 +
121.396 + Iterator iterator = attributes.entrySet().iterator();
121.397 + while (iterator.hasNext()) {
121.398 + Map.Entry entry = (Map.Entry) iterator.next();
121.399 + addAttributeRunData((Attribute) entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex);
121.400 + }
121.401 + }
121.402 +
121.403 + private synchronized void addAttributeImpl(Attribute attribute, Object value,
121.404 + int beginIndex, int endIndex) {
121.405 +
121.406 + // make sure we have run attribute data vectors
121.407 + if (runCount == 0) {
121.408 + createRunAttributeDataVectors();
121.409 + }
121.410 +
121.411 + // break up runs if necessary
121.412 + int beginRunIndex = ensureRunBreak(beginIndex);
121.413 + int endRunIndex = ensureRunBreak(endIndex);
121.414 +
121.415 + addAttributeRunData(attribute, value, beginRunIndex, endRunIndex);
121.416 + }
121.417 +
121.418 + private final void createRunAttributeDataVectors() {
121.419 + // use temporary variables so things remain consistent in case of an exception
121.420 + int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
121.421 + Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT];
121.422 + Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
121.423 + runStarts = newRunStarts;
121.424 + runAttributes = newRunAttributes;
121.425 + runAttributeValues = newRunAttributeValues;
121.426 + runArraySize = ARRAY_SIZE_INCREMENT;
121.427 + runCount = 1; // assume initial run starting at index 0
121.428 + }
121.429 +
121.430 + // ensure there's a run break at offset, return the index of the run
121.431 + private final int ensureRunBreak(int offset) {
121.432 + return ensureRunBreak(offset, true);
121.433 + }
121.434 +
121.435 + /**
121.436 + * Ensures there is a run break at offset, returning the index of
121.437 + * the run. If this results in splitting a run, two things can happen:
121.438 + * <ul>
121.439 + * <li>If copyAttrs is true, the attributes from the existing run
121.440 + * will be placed in both of the newly created runs.
121.441 + * <li>If copyAttrs is false, the attributes from the existing run
121.442 + * will NOT be copied to the run to the right (>= offset) of the break,
121.443 + * but will exist on the run to the left (< offset).
121.444 + * </ul>
121.445 + */
121.446 + private final int ensureRunBreak(int offset, boolean copyAttrs) {
121.447 + if (offset == length()) {
121.448 + return runCount;
121.449 + }
121.450 +
121.451 + // search for the run index where this offset should be
121.452 + int runIndex = 0;
121.453 + while (runIndex < runCount && runStarts[runIndex] < offset) {
121.454 + runIndex++;
121.455 + }
121.456 +
121.457 + // if the offset is at a run start already, we're done
121.458 + if (runIndex < runCount && runStarts[runIndex] == offset) {
121.459 + return runIndex;
121.460 + }
121.461 +
121.462 + // we'll have to break up a run
121.463 + // first, make sure we have enough space in our arrays
121.464 + if (runCount == runArraySize) {
121.465 + int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
121.466 + int newRunStarts[] = new int[newArraySize];
121.467 + Vector newRunAttributes[] = new Vector[newArraySize];
121.468 + Vector newRunAttributeValues[] = new Vector[newArraySize];
121.469 + for (int i = 0; i < runArraySize; i++) {
121.470 + newRunStarts[i] = runStarts[i];
121.471 + newRunAttributes[i] = runAttributes[i];
121.472 + newRunAttributeValues[i] = runAttributeValues[i];
121.473 + }
121.474 + runStarts = newRunStarts;
121.475 + runAttributes = newRunAttributes;
121.476 + runAttributeValues = newRunAttributeValues;
121.477 + runArraySize = newArraySize;
121.478 + }
121.479 +
121.480 + // make copies of the attribute information of the old run that the new one used to be part of
121.481 + // use temporary variables so things remain consistent in case of an exception
121.482 + Vector newRunAttributes = null;
121.483 + Vector newRunAttributeValues = null;
121.484 +
121.485 + if (copyAttrs) {
121.486 + Vector oldRunAttributes = runAttributes[runIndex - 1];
121.487 + Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
121.488 + if (oldRunAttributes != null) {
121.489 + newRunAttributes = (Vector) oldRunAttributes.clone();
121.490 + }
121.491 + if (oldRunAttributeValues != null) {
121.492 + newRunAttributeValues = (Vector) oldRunAttributeValues.clone();
121.493 + }
121.494 + }
121.495 +
121.496 + // now actually break up the run
121.497 + runCount++;
121.498 + for (int i = runCount - 1; i > runIndex; i--) {
121.499 + runStarts[i] = runStarts[i - 1];
121.500 + runAttributes[i] = runAttributes[i - 1];
121.501 + runAttributeValues[i] = runAttributeValues[i - 1];
121.502 + }
121.503 + runStarts[runIndex] = offset;
121.504 + runAttributes[runIndex] = newRunAttributes;
121.505 + runAttributeValues[runIndex] = newRunAttributeValues;
121.506 +
121.507 + return runIndex;
121.508 + }
121.509 +
121.510 + // add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex
121.511 + private void addAttributeRunData(Attribute attribute, Object value,
121.512 + int beginRunIndex, int endRunIndex) {
121.513 +
121.514 + for (int i = beginRunIndex; i < endRunIndex; i++) {
121.515 + int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet
121.516 + if (runAttributes[i] == null) {
121.517 + Vector newRunAttributes = new Vector();
121.518 + Vector newRunAttributeValues = new Vector();
121.519 + runAttributes[i] = newRunAttributes;
121.520 + runAttributeValues[i] = newRunAttributeValues;
121.521 + } else {
121.522 + // check whether we have an entry already
121.523 + keyValueIndex = runAttributes[i].indexOf(attribute);
121.524 + }
121.525 +
121.526 + if (keyValueIndex == -1) {
121.527 + // create new entry
121.528 + int oldSize = runAttributes[i].size();
121.529 + runAttributes[i].addElement(attribute);
121.530 + try {
121.531 + runAttributeValues[i].addElement(value);
121.532 + }
121.533 + catch (Exception e) {
121.534 + runAttributes[i].setSize(oldSize);
121.535 + runAttributeValues[i].setSize(oldSize);
121.536 + }
121.537 + } else {
121.538 + // update existing entry
121.539 + runAttributeValues[i].set(keyValueIndex, value);
121.540 + }
121.541 + }
121.542 + }
121.543 +
121.544 + /**
121.545 + * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
121.546 + * this string.
121.547 + *
121.548 + * @return An iterator providing access to the text and its attributes.
121.549 + */
121.550 + public AttributedCharacterIterator getIterator() {
121.551 + return getIterator(null, 0, length());
121.552 + }
121.553 +
121.554 + /**
121.555 + * Creates an AttributedCharacterIterator instance that provides access to
121.556 + * selected contents of this string.
121.557 + * Information about attributes not listed in attributes that the
121.558 + * implementor may have need not be made accessible through the iterator.
121.559 + * If the list is null, all available attribute information should be made
121.560 + * accessible.
121.561 + *
121.562 + * @param attributes a list of attributes that the client is interested in
121.563 + * @return an iterator providing access to the entire text and its selected attributes
121.564 + */
121.565 + public AttributedCharacterIterator getIterator(Attribute[] attributes) {
121.566 + return getIterator(attributes, 0, length());
121.567 + }
121.568 +
121.569 + /**
121.570 + * Creates an AttributedCharacterIterator instance that provides access to
121.571 + * selected contents of this string.
121.572 + * Information about attributes not listed in attributes that the
121.573 + * implementor may have need not be made accessible through the iterator.
121.574 + * If the list is null, all available attribute information should be made
121.575 + * accessible.
121.576 + *
121.577 + * @param attributes a list of attributes that the client is interested in
121.578 + * @param beginIndex the index of the first character
121.579 + * @param endIndex the index of the character following the last character
121.580 + * @return an iterator providing access to the text and its attributes
121.581 + * @exception IllegalArgumentException if beginIndex is less then 0,
121.582 + * endIndex is greater than the length of the string, or beginIndex is
121.583 + * greater than endIndex.
121.584 + */
121.585 + public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) {
121.586 + return new AttributedStringIterator(attributes, beginIndex, endIndex);
121.587 + }
121.588 +
121.589 + // all (with the exception of length) reading operations are private,
121.590 + // since AttributedString instances are accessed through iterators.
121.591 +
121.592 + // length is package private so that CharacterIteratorFieldDelegate can
121.593 + // access it without creating an AttributedCharacterIterator.
121.594 + int length() {
121.595 + return text.length();
121.596 + }
121.597 +
121.598 + private char charAt(int index) {
121.599 + return text.charAt(index);
121.600 + }
121.601 +
121.602 + private synchronized Object getAttribute(Attribute attribute, int runIndex) {
121.603 + Vector currentRunAttributes = runAttributes[runIndex];
121.604 + Vector currentRunAttributeValues = runAttributeValues[runIndex];
121.605 + if (currentRunAttributes == null) {
121.606 + return null;
121.607 + }
121.608 + int attributeIndex = currentRunAttributes.indexOf(attribute);
121.609 + if (attributeIndex != -1) {
121.610 + return currentRunAttributeValues.elementAt(attributeIndex);
121.611 + }
121.612 + else {
121.613 + return null;
121.614 + }
121.615 + }
121.616 +
121.617 + // gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex
121.618 + private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) {
121.619 + Object value = getAttribute(attribute, runIndex);
121.620 + if (value instanceof Annotation) {
121.621 + // need to check whether the annotation's range extends outside the iterator's range
121.622 + if (beginIndex > 0) {
121.623 + int currIndex = runIndex;
121.624 + int runStart = runStarts[currIndex];
121.625 + while (runStart >= beginIndex &&
121.626 + valuesMatch(value, getAttribute(attribute, currIndex - 1))) {
121.627 + currIndex--;
121.628 + runStart = runStarts[currIndex];
121.629 + }
121.630 + if (runStart < beginIndex) {
121.631 + // annotation's range starts before iterator's range
121.632 + return null;
121.633 + }
121.634 + }
121.635 + int textLength = length();
121.636 + if (endIndex < textLength) {
121.637 + int currIndex = runIndex;
121.638 + int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
121.639 + while (runLimit <= endIndex &&
121.640 + valuesMatch(value, getAttribute(attribute, currIndex + 1))) {
121.641 + currIndex++;
121.642 + runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
121.643 + }
121.644 + if (runLimit > endIndex) {
121.645 + // annotation's range ends after iterator's range
121.646 + return null;
121.647 + }
121.648 + }
121.649 + // annotation's range is subrange of iterator's range,
121.650 + // so we can return the value
121.651 + }
121.652 + return value;
121.653 + }
121.654 +
121.655 + // returns whether all specified attributes have equal values in the runs with the given indices
121.656 + private boolean attributeValuesMatch(Set attributes, int runIndex1, int runIndex2) {
121.657 + Iterator iterator = attributes.iterator();
121.658 + while (iterator.hasNext()) {
121.659 + Attribute key = (Attribute) iterator.next();
121.660 + if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) {
121.661 + return false;
121.662 + }
121.663 + }
121.664 + return true;
121.665 + }
121.666 +
121.667 + // returns whether the two objects are either both null or equal
121.668 + private final static boolean valuesMatch(Object value1, Object value2) {
121.669 + if (value1 == null) {
121.670 + return value2 == null;
121.671 + } else {
121.672 + return value1.equals(value2);
121.673 + }
121.674 + }
121.675 +
121.676 + /**
121.677 + * Appends the contents of the CharacterIterator iterator into the
121.678 + * StringBuffer buf.
121.679 + */
121.680 + private final void appendContents(StringBuffer buf,
121.681 + CharacterIterator iterator) {
121.682 + int index = iterator.getBeginIndex();
121.683 + int end = iterator.getEndIndex();
121.684 +
121.685 + while (index < end) {
121.686 + iterator.setIndex(index++);
121.687 + buf.append(iterator.current());
121.688 + }
121.689 + }
121.690 +
121.691 + /**
121.692 + * Sets the attributes for the range from offset to the next run break
121.693 + * (typically the end of the text) to the ones specified in attrs.
121.694 + * This is only meant to be called from the constructor!
121.695 + */
121.696 + private void setAttributes(Map attrs, int offset) {
121.697 + if (runCount == 0) {
121.698 + createRunAttributeDataVectors();
121.699 + }
121.700 +
121.701 + int index = ensureRunBreak(offset, false);
121.702 + int size;
121.703 +
121.704 + if (attrs != null && (size = attrs.size()) > 0) {
121.705 + Vector runAttrs = new Vector(size);
121.706 + Vector runValues = new Vector(size);
121.707 + Iterator iterator = attrs.entrySet().iterator();
121.708 +
121.709 + while (iterator.hasNext()) {
121.710 + Map.Entry entry = (Map.Entry)iterator.next();
121.711 +
121.712 + runAttrs.add(entry.getKey());
121.713 + runValues.add(entry.getValue());
121.714 + }
121.715 + runAttributes[index] = runAttrs;
121.716 + runAttributeValues[index] = runValues;
121.717 + }
121.718 + }
121.719 +
121.720 + /**
121.721 + * Returns true if the attributes specified in last and attrs differ.
121.722 + */
121.723 + private static boolean mapsDiffer(Map last, Map attrs) {
121.724 + if (last == null) {
121.725 + return (attrs != null && attrs.size() > 0);
121.726 + }
121.727 + return (!last.equals(attrs));
121.728 + }
121.729 +
121.730 +
121.731 + // the iterator class associated with this string class
121.732 +
121.733 + final private class AttributedStringIterator implements AttributedCharacterIterator {
121.734 +
121.735 + // note on synchronization:
121.736 + // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
121.737 + // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
121.738 +
121.739 + // start and end index for our iteration
121.740 + private int beginIndex;
121.741 + private int endIndex;
121.742 +
121.743 + // attributes that our client is interested in
121.744 + private Attribute[] relevantAttributes;
121.745 +
121.746 + // the current index for our iteration
121.747 + // invariant: beginIndex <= currentIndex <= endIndex
121.748 + private int currentIndex;
121.749 +
121.750 + // information about the run that includes currentIndex
121.751 + private int currentRunIndex;
121.752 + private int currentRunStart;
121.753 + private int currentRunLimit;
121.754 +
121.755 + // constructor
121.756 + AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) {
121.757 +
121.758 + if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {
121.759 + throw new IllegalArgumentException("Invalid substring range");
121.760 + }
121.761 +
121.762 + this.beginIndex = beginIndex;
121.763 + this.endIndex = endIndex;
121.764 + this.currentIndex = beginIndex;
121.765 + updateRunInfo();
121.766 + if (attributes != null) {
121.767 + relevantAttributes = (Attribute[]) attributes.clone();
121.768 + }
121.769 + }
121.770 +
121.771 + // Object methods. See documentation in that class.
121.772 +
121.773 + public boolean equals(Object obj) {
121.774 + if (this == obj) {
121.775 + return true;
121.776 + }
121.777 + if (!(obj instanceof AttributedStringIterator)) {
121.778 + return false;
121.779 + }
121.780 +
121.781 + AttributedStringIterator that = (AttributedStringIterator) obj;
121.782 +
121.783 + if (AttributedString.this != that.getString())
121.784 + return false;
121.785 + if (currentIndex != that.currentIndex || beginIndex != that.beginIndex || endIndex != that.endIndex)
121.786 + return false;
121.787 + return true;
121.788 + }
121.789 +
121.790 + public int hashCode() {
121.791 + return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex;
121.792 + }
121.793 +
121.794 + public Object clone() {
121.795 + try {
121.796 + AttributedStringIterator other = (AttributedStringIterator) super.clone();
121.797 + return other;
121.798 + }
121.799 + catch (CloneNotSupportedException e) {
121.800 + throw new InternalError();
121.801 + }
121.802 + }
121.803 +
121.804 + // CharacterIterator methods. See documentation in that interface.
121.805 +
121.806 + public char first() {
121.807 + return internalSetIndex(beginIndex);
121.808 + }
121.809 +
121.810 + public char last() {
121.811 + if (endIndex == beginIndex) {
121.812 + return internalSetIndex(endIndex);
121.813 + } else {
121.814 + return internalSetIndex(endIndex - 1);
121.815 + }
121.816 + }
121.817 +
121.818 + public char current() {
121.819 + if (currentIndex == endIndex) {
121.820 + return DONE;
121.821 + } else {
121.822 + return charAt(currentIndex);
121.823 + }
121.824 + }
121.825 +
121.826 + public char next() {
121.827 + if (currentIndex < endIndex) {
121.828 + return internalSetIndex(currentIndex + 1);
121.829 + }
121.830 + else {
121.831 + return DONE;
121.832 + }
121.833 + }
121.834 +
121.835 + public char previous() {
121.836 + if (currentIndex > beginIndex) {
121.837 + return internalSetIndex(currentIndex - 1);
121.838 + }
121.839 + else {
121.840 + return DONE;
121.841 + }
121.842 + }
121.843 +
121.844 + public char setIndex(int position) {
121.845 + if (position < beginIndex || position > endIndex)
121.846 + throw new IllegalArgumentException("Invalid index");
121.847 + return internalSetIndex(position);
121.848 + }
121.849 +
121.850 + public int getBeginIndex() {
121.851 + return beginIndex;
121.852 + }
121.853 +
121.854 + public int getEndIndex() {
121.855 + return endIndex;
121.856 + }
121.857 +
121.858 + public int getIndex() {
121.859 + return currentIndex;
121.860 + }
121.861 +
121.862 + // AttributedCharacterIterator methods. See documentation in that interface.
121.863 +
121.864 + public int getRunStart() {
121.865 + return currentRunStart;
121.866 + }
121.867 +
121.868 + public int getRunStart(Attribute attribute) {
121.869 + if (currentRunStart == beginIndex || currentRunIndex == -1) {
121.870 + return currentRunStart;
121.871 + } else {
121.872 + Object value = getAttribute(attribute);
121.873 + int runStart = currentRunStart;
121.874 + int runIndex = currentRunIndex;
121.875 + while (runStart > beginIndex &&
121.876 + valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex - 1))) {
121.877 + runIndex--;
121.878 + runStart = runStarts[runIndex];
121.879 + }
121.880 + if (runStart < beginIndex) {
121.881 + runStart = beginIndex;
121.882 + }
121.883 + return runStart;
121.884 + }
121.885 + }
121.886 +
121.887 + public int getRunStart(Set<? extends Attribute> attributes) {
121.888 + if (currentRunStart == beginIndex || currentRunIndex == -1) {
121.889 + return currentRunStart;
121.890 + } else {
121.891 + int runStart = currentRunStart;
121.892 + int runIndex = currentRunIndex;
121.893 + while (runStart > beginIndex &&
121.894 + AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex - 1)) {
121.895 + runIndex--;
121.896 + runStart = runStarts[runIndex];
121.897 + }
121.898 + if (runStart < beginIndex) {
121.899 + runStart = beginIndex;
121.900 + }
121.901 + return runStart;
121.902 + }
121.903 + }
121.904 +
121.905 + public int getRunLimit() {
121.906 + return currentRunLimit;
121.907 + }
121.908 +
121.909 + public int getRunLimit(Attribute attribute) {
121.910 + if (currentRunLimit == endIndex || currentRunIndex == -1) {
121.911 + return currentRunLimit;
121.912 + } else {
121.913 + Object value = getAttribute(attribute);
121.914 + int runLimit = currentRunLimit;
121.915 + int runIndex = currentRunIndex;
121.916 + while (runLimit < endIndex &&
121.917 + valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex + 1))) {
121.918 + runIndex++;
121.919 + runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;
121.920 + }
121.921 + if (runLimit > endIndex) {
121.922 + runLimit = endIndex;
121.923 + }
121.924 + return runLimit;
121.925 + }
121.926 + }
121.927 +
121.928 + public int getRunLimit(Set<? extends Attribute> attributes) {
121.929 + if (currentRunLimit == endIndex || currentRunIndex == -1) {
121.930 + return currentRunLimit;
121.931 + } else {
121.932 + int runLimit = currentRunLimit;
121.933 + int runIndex = currentRunIndex;
121.934 + while (runLimit < endIndex &&
121.935 + AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex + 1)) {
121.936 + runIndex++;
121.937 + runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;
121.938 + }
121.939 + if (runLimit > endIndex) {
121.940 + runLimit = endIndex;
121.941 + }
121.942 + return runLimit;
121.943 + }
121.944 + }
121.945 +
121.946 + public Map<Attribute,Object> getAttributes() {
121.947 + if (runAttributes == null || currentRunIndex == -1 || runAttributes[currentRunIndex] == null) {
121.948 + // ??? would be nice to return null, but current spec doesn't allow it
121.949 + // returning Hashtable saves AttributeMap from dealing with emptiness
121.950 + return new Hashtable();
121.951 + }
121.952 + return new AttributeMap(currentRunIndex, beginIndex, endIndex);
121.953 + }
121.954 +
121.955 + public Set<Attribute> getAllAttributeKeys() {
121.956 + // ??? This should screen out attribute keys that aren't relevant to the client
121.957 + if (runAttributes == null) {
121.958 + // ??? would be nice to return null, but current spec doesn't allow it
121.959 + // returning HashSet saves us from dealing with emptiness
121.960 + return new HashSet();
121.961 + }
121.962 + synchronized (AttributedString.this) {
121.963 + // ??? should try to create this only once, then update if necessary,
121.964 + // and give callers read-only view
121.965 + Set keys = new HashSet();
121.966 + int i = 0;
121.967 + while (i < runCount) {
121.968 + if (runStarts[i] < endIndex && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) {
121.969 + Vector currentRunAttributes = runAttributes[i];
121.970 + if (currentRunAttributes != null) {
121.971 + int j = currentRunAttributes.size();
121.972 + while (j-- > 0) {
121.973 + keys.add(currentRunAttributes.get(j));
121.974 + }
121.975 + }
121.976 + }
121.977 + i++;
121.978 + }
121.979 + return keys;
121.980 + }
121.981 + }
121.982 +
121.983 + public Object getAttribute(Attribute attribute) {
121.984 + int runIndex = currentRunIndex;
121.985 + if (runIndex < 0) {
121.986 + return null;
121.987 + }
121.988 + return AttributedString.this.getAttributeCheckRange(attribute, runIndex, beginIndex, endIndex);
121.989 + }
121.990 +
121.991 + // internally used methods
121.992 +
121.993 + private AttributedString getString() {
121.994 + return AttributedString.this;
121.995 + }
121.996 +
121.997 + // set the current index, update information about the current run if necessary,
121.998 + // return the character at the current index
121.999 + private char internalSetIndex(int position) {
121.1000 + currentIndex = position;
121.1001 + if (position < currentRunStart || position >= currentRunLimit) {
121.1002 + updateRunInfo();
121.1003 + }
121.1004 + if (currentIndex == endIndex) {
121.1005 + return DONE;
121.1006 + } else {
121.1007 + return charAt(position);
121.1008 + }
121.1009 + }
121.1010 +
121.1011 + // update the information about the current run
121.1012 + private void updateRunInfo() {
121.1013 + if (currentIndex == endIndex) {
121.1014 + currentRunStart = currentRunLimit = endIndex;
121.1015 + currentRunIndex = -1;
121.1016 + } else {
121.1017 + synchronized (AttributedString.this) {
121.1018 + int runIndex = -1;
121.1019 + while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex)
121.1020 + runIndex++;
121.1021 + currentRunIndex = runIndex;
121.1022 + if (runIndex >= 0) {
121.1023 + currentRunStart = runStarts[runIndex];
121.1024 + if (currentRunStart < beginIndex)
121.1025 + currentRunStart = beginIndex;
121.1026 + }
121.1027 + else {
121.1028 + currentRunStart = beginIndex;
121.1029 + }
121.1030 + if (runIndex < runCount - 1) {
121.1031 + currentRunLimit = runStarts[runIndex + 1];
121.1032 + if (currentRunLimit > endIndex)
121.1033 + currentRunLimit = endIndex;
121.1034 + }
121.1035 + else {
121.1036 + currentRunLimit = endIndex;
121.1037 + }
121.1038 + }
121.1039 + }
121.1040 + }
121.1041 +
121.1042 + }
121.1043 +
121.1044 + // the map class associated with this string class, giving access to the attributes of one run
121.1045 +
121.1046 + final private class AttributeMap extends AbstractMap<Attribute,Object> {
121.1047 +
121.1048 + int runIndex;
121.1049 + int beginIndex;
121.1050 + int endIndex;
121.1051 +
121.1052 + AttributeMap(int runIndex, int beginIndex, int endIndex) {
121.1053 + this.runIndex = runIndex;
121.1054 + this.beginIndex = beginIndex;
121.1055 + this.endIndex = endIndex;
121.1056 + }
121.1057 +
121.1058 + public Set entrySet() {
121.1059 + HashSet set = new HashSet();
121.1060 + synchronized (AttributedString.this) {
121.1061 + int size = runAttributes[runIndex].size();
121.1062 + for (int i = 0; i < size; i++) {
121.1063 + Attribute key = (Attribute) runAttributes[runIndex].get(i);
121.1064 + Object value = runAttributeValues[runIndex].get(i);
121.1065 + if (value instanceof Annotation) {
121.1066 + value = AttributedString.this.getAttributeCheckRange(key,
121.1067 + runIndex, beginIndex, endIndex);
121.1068 + if (value == null) {
121.1069 + continue;
121.1070 + }
121.1071 + }
121.1072 + Map.Entry entry = new AttributeEntry(key, value);
121.1073 + set.add(entry);
121.1074 + }
121.1075 + }
121.1076 + return set;
121.1077 + }
121.1078 +
121.1079 + public Object get(Object key) {
121.1080 + return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex);
121.1081 + }
121.1082 + }
121.1083 +}
121.1084 +
121.1085 +class AttributeEntry implements Map.Entry {
121.1086 +
121.1087 + private Attribute key;
121.1088 + private Object value;
121.1089 +
121.1090 + AttributeEntry(Attribute key, Object value) {
121.1091 + this.key = key;
121.1092 + this.value = value;
121.1093 + }
121.1094 +
121.1095 + public boolean equals(Object o) {
121.1096 + if (!(o instanceof AttributeEntry)) {
121.1097 + return false;
121.1098 + }
121.1099 + AttributeEntry other = (AttributeEntry) o;
121.1100 + return other.key.equals(key) &&
121.1101 + (value == null ? other.value == null : other.value.equals(value));
121.1102 + }
121.1103 +
121.1104 + public Object getKey() {
121.1105 + return key;
121.1106 + }
121.1107 +
121.1108 + public Object getValue() {
121.1109 + return value;
121.1110 + }
121.1111 +
121.1112 + public Object setValue(Object newValue) {
121.1113 + throw new UnsupportedOperationException();
121.1114 + }
121.1115 +
121.1116 + public int hashCode() {
121.1117 + return key.hashCode() ^ (value==null ? 0 : value.hashCode());
121.1118 + }
121.1119 +
121.1120 + public String toString() {
121.1121 + return key.toString()+"="+value.toString();
121.1122 + }
121.1123 +}
122.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
122.2 +++ b/rt/emul/compact/src/main/java/java/text/CalendarBuilder.java Tue Feb 11 13:31:42 2014 +0100
122.3 @@ -0,0 +1,170 @@
122.4 +/*
122.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
122.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
122.7 + *
122.8 + * This code is free software; you can redistribute it and/or modify it
122.9 + * under the terms of the GNU General Public License version 2 only, as
122.10 + * published by the Free Software Foundation. Oracle designates this
122.11 + * particular file as subject to the "Classpath" exception as provided
122.12 + * by Oracle in the LICENSE file that accompanied this code.
122.13 + *
122.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
122.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
122.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
122.17 + * version 2 for more details (a copy is included in the LICENSE file that
122.18 + * accompanied this code).
122.19 + *
122.20 + * You should have received a copy of the GNU General Public License version
122.21 + * 2 along with this work; if not, write to the Free Software Foundation,
122.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
122.23 + *
122.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
122.25 + * or visit www.oracle.com if you need additional information or have any
122.26 + * questions.
122.27 + */
122.28 +
122.29 +package java.text;
122.30 +
122.31 +import java.util.Calendar;
122.32 +import static java.util.Calendar.*;
122.33 +
122.34 +/**
122.35 + * {@code CalendarBuilder} keeps field-value pairs for setting
122.36 + * the calendar fields of the given {@code Calendar}. It has the
122.37 + * {@link Calendar#FIELD_COUNT FIELD_COUNT}-th field for the week year
122.38 + * support. Also {@code ISO_DAY_OF_WEEK} is used to specify
122.39 + * {@code DAY_OF_WEEK} in the ISO day of week numbering.
122.40 + *
122.41 + * <p>{@code CalendarBuilder} retains the semantic of the pseudo
122.42 + * timestamp for fields. {@code CalendarBuilder} uses a single
122.43 + * int array combining fields[] and stamp[] of {@code Calendar}.
122.44 + *
122.45 + * @author Masayoshi Okutsu
122.46 + */
122.47 +class CalendarBuilder {
122.48 + /*
122.49 + * Pseudo time stamp constants used in java.util.Calendar
122.50 + */
122.51 + private static final int UNSET = 0;
122.52 + private static final int COMPUTED = 1;
122.53 + private static final int MINIMUM_USER_STAMP = 2;
122.54 +
122.55 + private static final int MAX_FIELD = FIELD_COUNT + 1;
122.56 +
122.57 + public static final int WEEK_YEAR = FIELD_COUNT;
122.58 + public static final int ISO_DAY_OF_WEEK = 1000; // pseudo field index
122.59 +
122.60 + // stamp[] (lower half) and field[] (upper half) combined
122.61 + private final int[] field;
122.62 + private int nextStamp;
122.63 + private int maxFieldIndex;
122.64 +
122.65 + CalendarBuilder() {
122.66 + field = new int[MAX_FIELD * 2];
122.67 + nextStamp = MINIMUM_USER_STAMP;
122.68 + maxFieldIndex = -1;
122.69 + }
122.70 +
122.71 + CalendarBuilder set(int index, int value) {
122.72 + if (index == ISO_DAY_OF_WEEK) {
122.73 + index = DAY_OF_WEEK;
122.74 + value = toCalendarDayOfWeek(value);
122.75 + }
122.76 + field[index] = nextStamp++;
122.77 + field[MAX_FIELD + index] = value;
122.78 + if (index > maxFieldIndex && index < FIELD_COUNT) {
122.79 + maxFieldIndex = index;
122.80 + }
122.81 + return this;
122.82 + }
122.83 +
122.84 + CalendarBuilder addYear(int value) {
122.85 + field[MAX_FIELD + YEAR] += value;
122.86 + field[MAX_FIELD + WEEK_YEAR] += value;
122.87 + return this;
122.88 + }
122.89 +
122.90 + boolean isSet(int index) {
122.91 + if (index == ISO_DAY_OF_WEEK) {
122.92 + index = DAY_OF_WEEK;
122.93 + }
122.94 + return field[index] > UNSET;
122.95 + }
122.96 +
122.97 + Calendar establish(Calendar cal) {
122.98 + boolean weekDate = isSet(WEEK_YEAR)
122.99 + && field[WEEK_YEAR] > field[YEAR];
122.100 + if (weekDate && !cal.isWeekDateSupported()) {
122.101 + // Use YEAR instead
122.102 + if (!isSet(YEAR)) {
122.103 + set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
122.104 + }
122.105 + weekDate = false;
122.106 + }
122.107 +
122.108 + cal.clear();
122.109 + // Set the fields from the min stamp to the max stamp so that
122.110 + // the field resolution works in the Calendar.
122.111 + for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
122.112 + for (int index = 0; index <= maxFieldIndex; index++) {
122.113 + if (field[index] == stamp) {
122.114 + cal.set(index, field[MAX_FIELD + index]);
122.115 + break;
122.116 + }
122.117 + }
122.118 + }
122.119 +
122.120 + if (weekDate) {
122.121 + int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1;
122.122 + int dayOfWeek = isSet(DAY_OF_WEEK) ?
122.123 + field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
122.124 + if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) {
122.125 + if (dayOfWeek >= 8) {
122.126 + dayOfWeek--;
122.127 + weekOfYear += dayOfWeek / 7;
122.128 + dayOfWeek = (dayOfWeek % 7) + 1;
122.129 + } else {
122.130 + while (dayOfWeek <= 0) {
122.131 + dayOfWeek += 7;
122.132 + weekOfYear--;
122.133 + }
122.134 + }
122.135 + dayOfWeek = toCalendarDayOfWeek(dayOfWeek);
122.136 + }
122.137 + cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek);
122.138 + }
122.139 + return cal;
122.140 + }
122.141 +
122.142 + public String toString() {
122.143 + StringBuilder sb = new StringBuilder();
122.144 + sb.append("CalendarBuilder:[");
122.145 + for (int i = 0; i < field.length; i++) {
122.146 + if (isSet(i)) {
122.147 + sb.append(i).append('=').append(field[MAX_FIELD + i]).append(',');
122.148 + }
122.149 + }
122.150 + int lastIndex = sb.length() - 1;
122.151 + if (sb.charAt(lastIndex) == ',') {
122.152 + sb.setLength(lastIndex);
122.153 + }
122.154 + sb.append(']');
122.155 + return sb.toString();
122.156 + }
122.157 +
122.158 + static int toISODayOfWeek(int calendarDayOfWeek) {
122.159 + return calendarDayOfWeek == SUNDAY ? 7 : calendarDayOfWeek - 1;
122.160 + }
122.161 +
122.162 + static int toCalendarDayOfWeek(int isoDayOfWeek) {
122.163 + if (!isValidDayOfWeek(isoDayOfWeek)) {
122.164 + // adjust later for lenient mode
122.165 + return isoDayOfWeek;
122.166 + }
122.167 + return isoDayOfWeek == 7 ? SUNDAY : isoDayOfWeek + 1;
122.168 + }
122.169 +
122.170 + static boolean isValidDayOfWeek(int dayOfWeek) {
122.171 + return dayOfWeek > 0 && dayOfWeek <= 7;
122.172 + }
122.173 +}
123.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
123.2 +++ b/rt/emul/compact/src/main/java/java/text/CharacterIterator.java Tue Feb 11 13:31:42 2014 +0100
123.3 @@ -0,0 +1,193 @@
123.4 +/*
123.5 + * Copyright (c) 1996, 2000, Oracle and/or its affiliates. All rights reserved.
123.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
123.7 + *
123.8 + * This code is free software; you can redistribute it and/or modify it
123.9 + * under the terms of the GNU General Public License version 2 only, as
123.10 + * published by the Free Software Foundation. Oracle designates this
123.11 + * particular file as subject to the "Classpath" exception as provided
123.12 + * by Oracle in the LICENSE file that accompanied this code.
123.13 + *
123.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
123.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
123.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
123.17 + * version 2 for more details (a copy is included in the LICENSE file that
123.18 + * accompanied this code).
123.19 + *
123.20 + * You should have received a copy of the GNU General Public License version
123.21 + * 2 along with this work; if not, write to the Free Software Foundation,
123.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
123.23 + *
123.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
123.25 + * or visit www.oracle.com if you need additional information or have any
123.26 + * questions.
123.27 + */
123.28 +
123.29 +/*
123.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
123.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
123.32 + *
123.33 + * The original version of this source code and documentation
123.34 + * is copyrighted and owned by Taligent, Inc., a wholly-owned
123.35 + * subsidiary of IBM. These materials are provided under terms
123.36 + * of a License Agreement between Taligent and Sun. This technology
123.37 + * is protected by multiple US and International patents.
123.38 + *
123.39 + * This notice and attribution to Taligent may not be removed.
123.40 + * Taligent is a registered trademark of Taligent, Inc.
123.41 + *
123.42 + */
123.43 +
123.44 +package java.text;
123.45 +
123.46 +
123.47 +/**
123.48 + * This interface defines a protocol for bidirectional iteration over text.
123.49 + * The iterator iterates over a bounded sequence of characters. Characters
123.50 + * are indexed with values beginning with the value returned by getBeginIndex() and
123.51 + * continuing through the value returned by getEndIndex()-1.
123.52 + * <p>
123.53 + * Iterators maintain a current character index, whose valid range is from
123.54 + * getBeginIndex() to getEndIndex(); the value getEndIndex() is included to allow
123.55 + * handling of zero-length text ranges and for historical reasons.
123.56 + * The current index can be retrieved by calling getIndex() and set directly
123.57 + * by calling setIndex(), first(), and last().
123.58 + * <p>
123.59 + * The methods previous() and next() are used for iteration. They return DONE if
123.60 + * they would move outside the range from getBeginIndex() to getEndIndex() -1,
123.61 + * signaling that the iterator has reached the end of the sequence. DONE is
123.62 + * also returned by other methods to indicate that the current index is
123.63 + * outside this range.
123.64 + *
123.65 + * <P>Examples:<P>
123.66 + *
123.67 + * Traverse the text from start to finish
123.68 + * <pre>
123.69 + * public void traverseForward(CharacterIterator iter) {
123.70 + * for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
123.71 + * processChar(c);
123.72 + * }
123.73 + * }
123.74 + * </pre>
123.75 + *
123.76 + * Traverse the text backwards, from end to start
123.77 + * <pre>
123.78 + * public void traverseBackward(CharacterIterator iter) {
123.79 + * for(char c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
123.80 + * processChar(c);
123.81 + * }
123.82 + * }
123.83 + * </pre>
123.84 + *
123.85 + * Traverse both forward and backward from a given position in the text.
123.86 + * Calls to notBoundary() in this example represents some
123.87 + * additional stopping criteria.
123.88 + * <pre>
123.89 + * public void traverseOut(CharacterIterator iter, int pos) {
123.90 + * for (char c = iter.setIndex(pos);
123.91 + * c != CharacterIterator.DONE && notBoundary(c);
123.92 + * c = iter.next()) {
123.93 + * }
123.94 + * int end = iter.getIndex();
123.95 + * for (char c = iter.setIndex(pos);
123.96 + * c != CharacterIterator.DONE && notBoundary(c);
123.97 + * c = iter.previous()) {
123.98 + * }
123.99 + * int start = iter.getIndex();
123.100 + * processSection(start, end);
123.101 + * }
123.102 + * </pre>
123.103 + *
123.104 + * @see StringCharacterIterator
123.105 + * @see AttributedCharacterIterator
123.106 + */
123.107 +
123.108 +public interface CharacterIterator extends Cloneable
123.109 +{
123.110 +
123.111 + /**
123.112 + * Constant that is returned when the iterator has reached either the end
123.113 + * or the beginning of the text. The value is '\\uFFFF', the "not a
123.114 + * character" value which should not occur in any valid Unicode string.
123.115 + */
123.116 + public static final char DONE = '\uFFFF';
123.117 +
123.118 + /**
123.119 + * Sets the position to getBeginIndex() and returns the character at that
123.120 + * position.
123.121 + * @return the first character in the text, or DONE if the text is empty
123.122 + * @see #getBeginIndex()
123.123 + */
123.124 + public char first();
123.125 +
123.126 + /**
123.127 + * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
123.128 + * and returns the character at that position.
123.129 + * @return the last character in the text, or DONE if the text is empty
123.130 + * @see #getEndIndex()
123.131 + */
123.132 + public char last();
123.133 +
123.134 + /**
123.135 + * Gets the character at the current position (as returned by getIndex()).
123.136 + * @return the character at the current position or DONE if the current
123.137 + * position is off the end of the text.
123.138 + * @see #getIndex()
123.139 + */
123.140 + public char current();
123.141 +
123.142 + /**
123.143 + * Increments the iterator's index by one and returns the character
123.144 + * at the new index. If the resulting index is greater or equal
123.145 + * to getEndIndex(), the current index is reset to getEndIndex() and
123.146 + * a value of DONE is returned.
123.147 + * @return the character at the new position or DONE if the new
123.148 + * position is off the end of the text range.
123.149 + */
123.150 + public char next();
123.151 +
123.152 + /**
123.153 + * Decrements the iterator's index by one and returns the character
123.154 + * at the new index. If the current index is getBeginIndex(), the index
123.155 + * remains at getBeginIndex() and a value of DONE is returned.
123.156 + * @return the character at the new position or DONE if the current
123.157 + * position is equal to getBeginIndex().
123.158 + */
123.159 + public char previous();
123.160 +
123.161 + /**
123.162 + * Sets the position to the specified position in the text and returns that
123.163 + * character.
123.164 + * @param position the position within the text. Valid values range from
123.165 + * getBeginIndex() to getEndIndex(). An IllegalArgumentException is thrown
123.166 + * if an invalid value is supplied.
123.167 + * @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
123.168 + */
123.169 + public char setIndex(int position);
123.170 +
123.171 + /**
123.172 + * Returns the start index of the text.
123.173 + * @return the index at which the text begins.
123.174 + */
123.175 + public int getBeginIndex();
123.176 +
123.177 + /**
123.178 + * Returns the end index of the text. This index is the index of the first
123.179 + * character following the end of the text.
123.180 + * @return the index after the last character in the text
123.181 + */
123.182 + public int getEndIndex();
123.183 +
123.184 + /**
123.185 + * Returns the current index.
123.186 + * @return the current index.
123.187 + */
123.188 + public int getIndex();
123.189 +
123.190 + /**
123.191 + * Create a copy of this iterator
123.192 + * @return A copy of this
123.193 + */
123.194 + public Object clone();
123.195 +
123.196 +}
124.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
124.2 +++ b/rt/emul/compact/src/main/java/java/text/CharacterIteratorFieldDelegate.java Tue Feb 11 13:31:42 2014 +0100
124.3 @@ -0,0 +1,124 @@
124.4 +/*
124.5 + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
124.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
124.7 + *
124.8 + * This code is free software; you can redistribute it and/or modify it
124.9 + * under the terms of the GNU General Public License version 2 only, as
124.10 + * published by the Free Software Foundation. Oracle designates this
124.11 + * particular file as subject to the "Classpath" exception as provided
124.12 + * by Oracle in the LICENSE file that accompanied this code.
124.13 + *
124.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
124.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
124.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
124.17 + * version 2 for more details (a copy is included in the LICENSE file that
124.18 + * accompanied this code).
124.19 + *
124.20 + * You should have received a copy of the GNU General Public License version
124.21 + * 2 along with this work; if not, write to the Free Software Foundation,
124.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
124.23 + *
124.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
124.25 + * or visit www.oracle.com if you need additional information or have any
124.26 + * questions.
124.27 + */
124.28 +package java.text;
124.29 +
124.30 +import java.util.ArrayList;
124.31 +
124.32 +/**
124.33 + * CharacterIteratorFieldDelegate combines the notifications from a Format
124.34 + * into a resulting <code>AttributedCharacterIterator</code>. The resulting
124.35 + * <code>AttributedCharacterIterator</code> can be retrieved by way of
124.36 + * the <code>getIterator</code> method.
124.37 + *
124.38 + */
124.39 +class CharacterIteratorFieldDelegate implements Format.FieldDelegate {
124.40 + /**
124.41 + * Array of AttributeStrings. Whenever <code>formatted</code> is invoked
124.42 + * for a region > size, a new instance of AttributedString is added to
124.43 + * attributedStrings. Subsequent invocations of <code>formatted</code>
124.44 + * for existing regions result in invoking addAttribute on the existing
124.45 + * AttributedStrings.
124.46 + */
124.47 + private ArrayList attributedStrings;
124.48 + /**
124.49 + * Running count of the number of characters that have
124.50 + * been encountered.
124.51 + */
124.52 + private int size;
124.53 +
124.54 +
124.55 + CharacterIteratorFieldDelegate() {
124.56 + attributedStrings = new ArrayList();
124.57 + }
124.58 +
124.59 + public void formatted(Format.Field attr, Object value, int start, int end,
124.60 + StringBuffer buffer) {
124.61 + if (start != end) {
124.62 + if (start < size) {
124.63 + // Adjust attributes of existing runs
124.64 + int index = size;
124.65 + int asIndex = attributedStrings.size() - 1;
124.66 +
124.67 + while (start < index) {
124.68 + AttributedString as = (AttributedString)attributedStrings.
124.69 + get(asIndex--);
124.70 + int newIndex = index - as.length();
124.71 + int aStart = Math.max(0, start - newIndex);
124.72 +
124.73 + as.addAttribute(attr, value, aStart, Math.min(
124.74 + end - start, as.length() - aStart) +
124.75 + aStart);
124.76 + index = newIndex;
124.77 + }
124.78 + }
124.79 + if (size < start) {
124.80 + // Pad attributes
124.81 + attributedStrings.add(new AttributedString(
124.82 + buffer.substring(size, start)));
124.83 + size = start;
124.84 + }
124.85 + if (size < end) {
124.86 + // Add new string
124.87 + int aStart = Math.max(start, size);
124.88 + AttributedString string = new AttributedString(
124.89 + buffer.substring(aStart, end));
124.90 +
124.91 + string.addAttribute(attr, value);
124.92 + attributedStrings.add(string);
124.93 + size = end;
124.94 + }
124.95 + }
124.96 + }
124.97 +
124.98 + public void formatted(int fieldID, Format.Field attr, Object value,
124.99 + int start, int end, StringBuffer buffer) {
124.100 + formatted(attr, value, start, end, buffer);
124.101 + }
124.102 +
124.103 + /**
124.104 + * Returns an <code>AttributedCharacterIterator</code> that can be used
124.105 + * to iterate over the resulting formatted String.
124.106 + *
124.107 + * @pararm string Result of formatting.
124.108 + */
124.109 + public AttributedCharacterIterator getIterator(String string) {
124.110 + // Add the last AttributedCharacterIterator if necessary
124.111 + // assert(size <= string.length());
124.112 + if (string.length() > size) {
124.113 + attributedStrings.add(new AttributedString(
124.114 + string.substring(size)));
124.115 + size = string.length();
124.116 + }
124.117 + int iCount = attributedStrings.size();
124.118 + AttributedCharacterIterator iterators[] = new
124.119 + AttributedCharacterIterator[iCount];
124.120 +
124.121 + for (int counter = 0; counter < iCount; counter++) {
124.122 + iterators[counter] = ((AttributedString)attributedStrings.
124.123 + get(counter)).getIterator();
124.124 + }
124.125 + return new AttributedString(iterators).getIterator();
124.126 + }
124.127 +}
125.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125.2 +++ b/rt/emul/compact/src/main/java/java/text/ChoiceFormat.java Tue Feb 11 13:31:42 2014 +0100
125.3 @@ -0,0 +1,619 @@
125.4 +/*
125.5 + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
125.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
125.7 + *
125.8 + * This code is free software; you can redistribute it and/or modify it
125.9 + * under the terms of the GNU General Public License version 2 only, as
125.10 + * published by the Free Software Foundation. Oracle designates this
125.11 + * particular file as subject to the "Classpath" exception as provided
125.12 + * by Oracle in the LICENSE file that accompanied this code.
125.13 + *
125.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
125.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
125.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
125.17 + * version 2 for more details (a copy is included in the LICENSE file that
125.18 + * accompanied this code).
125.19 + *
125.20 + * You should have received a copy of the GNU General Public License version
125.21 + * 2 along with this work; if not, write to the Free Software Foundation,
125.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
125.23 + *
125.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
125.25 + * or visit www.oracle.com if you need additional information or have any
125.26 + * questions.
125.27 + */
125.28 +
125.29 +/*
125.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
125.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
125.32 + *
125.33 + * The original version of this source code and documentation is copyrighted
125.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
125.35 + * materials are provided under terms of a License Agreement between Taligent
125.36 + * and Sun. This technology is protected by multiple US and International
125.37 + * patents. This notice and attribution to Taligent may not be removed.
125.38 + * Taligent is a registered trademark of Taligent, Inc.
125.39 + *
125.40 + */
125.41 +
125.42 +package java.text;
125.43 +
125.44 +import java.io.InvalidObjectException;
125.45 +import java.io.IOException;
125.46 +import java.io.ObjectInputStream;
125.47 +import java.util.Arrays;
125.48 +
125.49 +/**
125.50 + * A <code>ChoiceFormat</code> allows you to attach a format to a range of numbers.
125.51 + * It is generally used in a <code>MessageFormat</code> for handling plurals.
125.52 + * The choice is specified with an ascending list of doubles, where each item
125.53 + * specifies a half-open interval up to the next item:
125.54 + * <blockquote>
125.55 + * <pre>
125.56 + * X matches j if and only if limit[j] <= X < limit[j+1]
125.57 + * </pre>
125.58 + * </blockquote>
125.59 + * If there is no match, then either the first or last index is used, depending
125.60 + * on whether the number (X) is too low or too high. If the limit array is not
125.61 + * in ascending order, the results of formatting will be incorrect. ChoiceFormat
125.62 + * also accepts <code>\u221E</code> as equivalent to infinity(INF).
125.63 + *
125.64 + * <p>
125.65 + * <strong>Note:</strong>
125.66 + * <code>ChoiceFormat</code> differs from the other <code>Format</code>
125.67 + * classes in that you create a <code>ChoiceFormat</code> object with a
125.68 + * constructor (not with a <code>getInstance</code> style factory
125.69 + * method). The factory methods aren't necessary because <code>ChoiceFormat</code>
125.70 + * doesn't require any complex setup for a given locale. In fact,
125.71 + * <code>ChoiceFormat</code> doesn't implement any locale specific behavior.
125.72 + *
125.73 + * <p>
125.74 + * When creating a <code>ChoiceFormat</code>, you must specify an array of formats
125.75 + * and an array of limits. The length of these arrays must be the same.
125.76 + * For example,
125.77 + * <ul>
125.78 + * <li>
125.79 + * <em>limits</em> = {1,2,3,4,5,6,7}<br>
125.80 + * <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
125.81 + * <li>
125.82 + * <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
125.83 + * <em>formats</em> = {"no files", "one file", "many files"}<br>
125.84 + * (<code>nextDouble</code> can be used to get the next higher double, to
125.85 + * make the half-open interval.)
125.86 + * </ul>
125.87 + *
125.88 + * <p>
125.89 + * Here is a simple example that shows formatting and parsing:
125.90 + * <blockquote>
125.91 + * <pre>
125.92 + * double[] limits = {1,2,3,4,5,6,7};
125.93 + * String[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
125.94 + * ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
125.95 + * ParsePosition status = new ParsePosition(0);
125.96 + * for (double i = 0.0; i <= 8.0; ++i) {
125.97 + * status.setIndex(0);
125.98 + * System.out.println(i + " -> " + form.format(i) + " -> "
125.99 + * + form.parse(form.format(i),status));
125.100 + * }
125.101 + * </pre>
125.102 + * </blockquote>
125.103 + * Here is a more complex example, with a pattern format:
125.104 + * <blockquote>
125.105 + * <pre>
125.106 + * double[] filelimits = {0,1,2};
125.107 + * String[] filepart = {"are no files","is one file","are {2} files"};
125.108 + * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
125.109 + * Format[] testFormats = {fileform, null, NumberFormat.getInstance()};
125.110 + * MessageFormat pattform = new MessageFormat("There {0} on {1}");
125.111 + * pattform.setFormats(testFormats);
125.112 + * Object[] testArgs = {null, "ADisk", null};
125.113 + * for (int i = 0; i < 4; ++i) {
125.114 + * testArgs[0] = new Integer(i);
125.115 + * testArgs[2] = testArgs[0];
125.116 + * System.out.println(pattform.format(testArgs));
125.117 + * }
125.118 + * </pre>
125.119 + * </blockquote>
125.120 + * <p>
125.121 + * Specifying a pattern for ChoiceFormat objects is fairly straightforward.
125.122 + * For example:
125.123 + * <blockquote>
125.124 + * <pre>
125.125 + * ChoiceFormat fmt = new ChoiceFormat(
125.126 + * "-1#is negative| 0#is zero or fraction | 1#is one |1.0<is 1+ |2#is two |2<is more than 2.");
125.127 + * System.out.println("Formatter Pattern : " + fmt.toPattern());
125.128 + *
125.129 + * System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
125.130 + * System.out.println("Format with -1.0 : " + fmt.format(-1.0));
125.131 + * System.out.println("Format with 0 : " + fmt.format(0));
125.132 + * System.out.println("Format with 0.9 : " + fmt.format(0.9));
125.133 + * System.out.println("Format with 1.0 : " + fmt.format(1));
125.134 + * System.out.println("Format with 1.5 : " + fmt.format(1.5));
125.135 + * System.out.println("Format with 2 : " + fmt.format(2));
125.136 + * System.out.println("Format with 2.1 : " + fmt.format(2.1));
125.137 + * System.out.println("Format with NaN : " + fmt.format(Double.NaN));
125.138 + * System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
125.139 + * </pre>
125.140 + * </blockquote>
125.141 + * And the output result would be like the following:
125.142 + * <blockquote>
125.143 + * <pre>
125.144 + * Format with -INF : is negative
125.145 + * Format with -1.0 : is negative
125.146 + * Format with 0 : is zero or fraction
125.147 + * Format with 0.9 : is zero or fraction
125.148 + * Format with 1.0 : is one
125.149 + * Format with 1.5 : is 1+
125.150 + * Format with 2 : is two
125.151 + * Format with 2.1 : is more than 2.
125.152 + * Format with NaN : is negative
125.153 + * Format with +INF : is more than 2.
125.154 + * </pre>
125.155 + * </blockquote>
125.156 + *
125.157 + * <h4><a name="synchronization">Synchronization</a></h4>
125.158 + *
125.159 + * <p>
125.160 + * Choice formats are not synchronized.
125.161 + * It is recommended to create separate format instances for each thread.
125.162 + * If multiple threads access a format concurrently, it must be synchronized
125.163 + * externally.
125.164 + *
125.165 + *
125.166 + * @see DecimalFormat
125.167 + * @see MessageFormat
125.168 + * @author Mark Davis
125.169 + */
125.170 +public class ChoiceFormat extends NumberFormat {
125.171 +
125.172 + // Proclaim serial compatibility with 1.1 FCS
125.173 + private static final long serialVersionUID = 1795184449645032964L;
125.174 +
125.175 + /**
125.176 + * Sets the pattern.
125.177 + * @param newPattern See the class description.
125.178 + */
125.179 + public void applyPattern(String newPattern) {
125.180 + StringBuffer[] segments = new StringBuffer[2];
125.181 + for (int i = 0; i < segments.length; ++i) {
125.182 + segments[i] = new StringBuffer();
125.183 + }
125.184 + double[] newChoiceLimits = new double[30];
125.185 + String[] newChoiceFormats = new String[30];
125.186 + int count = 0;
125.187 + int part = 0;
125.188 + double startValue = 0;
125.189 + double oldStartValue = Double.NaN;
125.190 + boolean inQuote = false;
125.191 + for (int i = 0; i < newPattern.length(); ++i) {
125.192 + char ch = newPattern.charAt(i);
125.193 + if (ch=='\'') {
125.194 + // Check for "''" indicating a literal quote
125.195 + if ((i+1)<newPattern.length() && newPattern.charAt(i+1)==ch) {
125.196 + segments[part].append(ch);
125.197 + ++i;
125.198 + } else {
125.199 + inQuote = !inQuote;
125.200 + }
125.201 + } else if (inQuote) {
125.202 + segments[part].append(ch);
125.203 + } else if (ch == '<' || ch == '#' || ch == '\u2264') {
125.204 + if (segments[0].length() == 0) {
125.205 + throw new IllegalArgumentException();
125.206 + }
125.207 + try {
125.208 + String tempBuffer = segments[0].toString();
125.209 + if (tempBuffer.equals("\u221E")) {
125.210 + startValue = Double.POSITIVE_INFINITY;
125.211 + } else if (tempBuffer.equals("-\u221E")) {
125.212 + startValue = Double.NEGATIVE_INFINITY;
125.213 + } else {
125.214 + startValue = Double.valueOf(segments[0].toString()).doubleValue();
125.215 + }
125.216 + } catch (Exception e) {
125.217 + throw new IllegalArgumentException();
125.218 + }
125.219 + if (ch == '<' && startValue != Double.POSITIVE_INFINITY &&
125.220 + startValue != Double.NEGATIVE_INFINITY) {
125.221 + startValue = nextDouble(startValue);
125.222 + }
125.223 + if (startValue <= oldStartValue) {
125.224 + throw new IllegalArgumentException();
125.225 + }
125.226 + segments[0].setLength(0);
125.227 + part = 1;
125.228 + } else if (ch == '|') {
125.229 + if (count == newChoiceLimits.length) {
125.230 + newChoiceLimits = doubleArraySize(newChoiceLimits);
125.231 + newChoiceFormats = doubleArraySize(newChoiceFormats);
125.232 + }
125.233 + newChoiceLimits[count] = startValue;
125.234 + newChoiceFormats[count] = segments[1].toString();
125.235 + ++count;
125.236 + oldStartValue = startValue;
125.237 + segments[1].setLength(0);
125.238 + part = 0;
125.239 + } else {
125.240 + segments[part].append(ch);
125.241 + }
125.242 + }
125.243 + // clean up last one
125.244 + if (part == 1) {
125.245 + if (count == newChoiceLimits.length) {
125.246 + newChoiceLimits = doubleArraySize(newChoiceLimits);
125.247 + newChoiceFormats = doubleArraySize(newChoiceFormats);
125.248 + }
125.249 + newChoiceLimits[count] = startValue;
125.250 + newChoiceFormats[count] = segments[1].toString();
125.251 + ++count;
125.252 + }
125.253 + choiceLimits = new double[count];
125.254 + System.arraycopy(newChoiceLimits, 0, choiceLimits, 0, count);
125.255 + choiceFormats = new String[count];
125.256 + System.arraycopy(newChoiceFormats, 0, choiceFormats, 0, count);
125.257 + }
125.258 +
125.259 + /**
125.260 + * Gets the pattern.
125.261 + */
125.262 + public String toPattern() {
125.263 + StringBuffer result = new StringBuffer();
125.264 + for (int i = 0; i < choiceLimits.length; ++i) {
125.265 + if (i != 0) {
125.266 + result.append('|');
125.267 + }
125.268 + // choose based upon which has less precision
125.269 + // approximate that by choosing the closest one to an integer.
125.270 + // could do better, but it's not worth it.
125.271 + double less = previousDouble(choiceLimits[i]);
125.272 + double tryLessOrEqual = Math.abs(Math.IEEEremainder(choiceLimits[i], 1.0d));
125.273 + double tryLess = Math.abs(Math.IEEEremainder(less, 1.0d));
125.274 +
125.275 + if (tryLessOrEqual < tryLess) {
125.276 + result.append(""+choiceLimits[i]);
125.277 + result.append('#');
125.278 + } else {
125.279 + if (choiceLimits[i] == Double.POSITIVE_INFINITY) {
125.280 + result.append("\u221E");
125.281 + } else if (choiceLimits[i] == Double.NEGATIVE_INFINITY) {
125.282 + result.append("-\u221E");
125.283 + } else {
125.284 + result.append(""+less);
125.285 + }
125.286 + result.append('<');
125.287 + }
125.288 + // Append choiceFormats[i], using quotes if there are special characters.
125.289 + // Single quotes themselves must be escaped in either case.
125.290 + String text = choiceFormats[i];
125.291 + boolean needQuote = text.indexOf('<') >= 0
125.292 + || text.indexOf('#') >= 0
125.293 + || text.indexOf('\u2264') >= 0
125.294 + || text.indexOf('|') >= 0;
125.295 + if (needQuote) result.append('\'');
125.296 + if (text.indexOf('\'') < 0) result.append(text);
125.297 + else {
125.298 + for (int j=0; j<text.length(); ++j) {
125.299 + char c = text.charAt(j);
125.300 + result.append(c);
125.301 + if (c == '\'') result.append(c);
125.302 + }
125.303 + }
125.304 + if (needQuote) result.append('\'');
125.305 + }
125.306 + return result.toString();
125.307 + }
125.308 +
125.309 + /**
125.310 + * Constructs with limits and corresponding formats based on the pattern.
125.311 + * @see #applyPattern
125.312 + */
125.313 + public ChoiceFormat(String newPattern) {
125.314 + applyPattern(newPattern);
125.315 + }
125.316 +
125.317 + /**
125.318 + * Constructs with the limits and the corresponding formats.
125.319 + * @see #setChoices
125.320 + */
125.321 + public ChoiceFormat(double[] limits, String[] formats) {
125.322 + setChoices(limits, formats);
125.323 + }
125.324 +
125.325 + /**
125.326 + * Set the choices to be used in formatting.
125.327 + * @param limits contains the top value that you want
125.328 + * parsed with that format,and should be in ascending sorted order. When
125.329 + * formatting X, the choice will be the i, where
125.330 + * limit[i] <= X < limit[i+1].
125.331 + * If the limit array is not in ascending order, the results of formatting
125.332 + * will be incorrect.
125.333 + * @param formats are the formats you want to use for each limit.
125.334 + * They can be either Format objects or Strings.
125.335 + * When formatting with object Y,
125.336 + * if the object is a NumberFormat, then ((NumberFormat) Y).format(X)
125.337 + * is called. Otherwise Y.toString() is called.
125.338 + */
125.339 + public void setChoices(double[] limits, String formats[]) {
125.340 + if (limits.length != formats.length) {
125.341 + throw new IllegalArgumentException(
125.342 + "Array and limit arrays must be of the same length.");
125.343 + }
125.344 + choiceLimits = limits;
125.345 + choiceFormats = formats;
125.346 + }
125.347 +
125.348 + /**
125.349 + * Get the limits passed in the constructor.
125.350 + * @return the limits.
125.351 + */
125.352 + public double[] getLimits() {
125.353 + return choiceLimits;
125.354 + }
125.355 +
125.356 + /**
125.357 + * Get the formats passed in the constructor.
125.358 + * @return the formats.
125.359 + */
125.360 + public Object[] getFormats() {
125.361 + return choiceFormats;
125.362 + }
125.363 +
125.364 + // Overrides
125.365 +
125.366 + /**
125.367 + * Specialization of format. This method really calls
125.368 + * <code>format(double, StringBuffer, FieldPosition)</code>
125.369 + * thus the range of longs that are supported is only equal to
125.370 + * the range that can be stored by double. This will never be
125.371 + * a practical limitation.
125.372 + */
125.373 + public StringBuffer format(long number, StringBuffer toAppendTo,
125.374 + FieldPosition status) {
125.375 + return format((double)number, toAppendTo, status);
125.376 + }
125.377 +
125.378 + /**
125.379 + * Returns pattern with formatted double.
125.380 + * @param number number to be formatted & substituted.
125.381 + * @param toAppendTo where text is appended.
125.382 + * @param status ignore no useful status is returned.
125.383 + */
125.384 + public StringBuffer format(double number, StringBuffer toAppendTo,
125.385 + FieldPosition status) {
125.386 + // find the number
125.387 + int i;
125.388 + for (i = 0; i < choiceLimits.length; ++i) {
125.389 + if (!(number >= choiceLimits[i])) {
125.390 + // same as number < choiceLimits, except catchs NaN
125.391 + break;
125.392 + }
125.393 + }
125.394 + --i;
125.395 + if (i < 0) i = 0;
125.396 + // return either a formatted number, or a string
125.397 + return toAppendTo.append(choiceFormats[i]);
125.398 + }
125.399 +
125.400 + /**
125.401 + * Parses a Number from the input text.
125.402 + * @param text the source text.
125.403 + * @param status an input-output parameter. On input, the
125.404 + * status.index field indicates the first character of the
125.405 + * source text that should be parsed. On exit, if no error
125.406 + * occured, status.index is set to the first unparsed character
125.407 + * in the source text. On exit, if an error did occur,
125.408 + * status.index is unchanged and status.errorIndex is set to the
125.409 + * first index of the character that caused the parse to fail.
125.410 + * @return A Number representing the value of the number parsed.
125.411 + */
125.412 + public Number parse(String text, ParsePosition status) {
125.413 + // find the best number (defined as the one with the longest parse)
125.414 + int start = status.index;
125.415 + int furthest = start;
125.416 + double bestNumber = Double.NaN;
125.417 + double tempNumber = 0.0;
125.418 + for (int i = 0; i < choiceFormats.length; ++i) {
125.419 + String tempString = choiceFormats[i];
125.420 + if (text.regionMatches(start, tempString, 0, tempString.length())) {
125.421 + status.index = start + tempString.length();
125.422 + tempNumber = choiceLimits[i];
125.423 + if (status.index > furthest) {
125.424 + furthest = status.index;
125.425 + bestNumber = tempNumber;
125.426 + if (furthest == text.length()) break;
125.427 + }
125.428 + }
125.429 + }
125.430 + status.index = furthest;
125.431 + if (status.index == start) {
125.432 + status.errorIndex = furthest;
125.433 + }
125.434 + return new Double(bestNumber);
125.435 + }
125.436 +
125.437 + /**
125.438 + * Finds the least double greater than d.
125.439 + * If NaN, returns same value.
125.440 + * <p>Used to make half-open intervals.
125.441 + * @see #previousDouble
125.442 + */
125.443 + public static final double nextDouble (double d) {
125.444 + return nextDouble(d,true);
125.445 + }
125.446 +
125.447 + /**
125.448 + * Finds the greatest double less than d.
125.449 + * If NaN, returns same value.
125.450 + * @see #nextDouble
125.451 + */
125.452 + public static final double previousDouble (double d) {
125.453 + return nextDouble(d,false);
125.454 + }
125.455 +
125.456 + /**
125.457 + * Overrides Cloneable
125.458 + */
125.459 + public Object clone()
125.460 + {
125.461 + ChoiceFormat other = (ChoiceFormat) super.clone();
125.462 + // for primitives or immutables, shallow clone is enough
125.463 + other.choiceLimits = (double[]) choiceLimits.clone();
125.464 + other.choiceFormats = (String[]) choiceFormats.clone();
125.465 + return other;
125.466 + }
125.467 +
125.468 + /**
125.469 + * Generates a hash code for the message format object.
125.470 + */
125.471 + public int hashCode() {
125.472 + int result = choiceLimits.length;
125.473 + if (choiceFormats.length > 0) {
125.474 + // enough for reasonable distribution
125.475 + result ^= choiceFormats[choiceFormats.length-1].hashCode();
125.476 + }
125.477 + return result;
125.478 + }
125.479 +
125.480 + /**
125.481 + * Equality comparision between two
125.482 + */
125.483 + public boolean equals(Object obj) {
125.484 + if (obj == null) return false;
125.485 + if (this == obj) // quick check
125.486 + return true;
125.487 + if (getClass() != obj.getClass())
125.488 + return false;
125.489 + ChoiceFormat other = (ChoiceFormat) obj;
125.490 + return (Arrays.equals(choiceLimits, other.choiceLimits)
125.491 + && Arrays.equals(choiceFormats, other.choiceFormats));
125.492 + }
125.493 +
125.494 + /**
125.495 + * After reading an object from the input stream, do a simple verification
125.496 + * to maintain class invariants.
125.497 + * @throws InvalidObjectException if the objects read from the stream is invalid.
125.498 + */
125.499 + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
125.500 + in.defaultReadObject();
125.501 + if (choiceLimits.length != choiceFormats.length) {
125.502 + throw new InvalidObjectException(
125.503 + "limits and format arrays of different length.");
125.504 + }
125.505 + }
125.506 +
125.507 + // ===============privates===========================
125.508 +
125.509 + /**
125.510 + * A list of lower bounds for the choices. The formatter will return
125.511 + * <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
125.512 + * <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
125.513 + * @serial
125.514 + */
125.515 + private double[] choiceLimits;
125.516 +
125.517 + /**
125.518 + * A list of choice strings. The formatter will return
125.519 + * <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
125.520 + * <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
125.521 + * @serial
125.522 + */
125.523 + private String[] choiceFormats;
125.524 +
125.525 + /*
125.526 + static final long SIGN = 0x8000000000000000L;
125.527 + static final long EXPONENT = 0x7FF0000000000000L;
125.528 + static final long SIGNIFICAND = 0x000FFFFFFFFFFFFFL;
125.529 +
125.530 + private static double nextDouble (double d, boolean positive) {
125.531 + if (Double.isNaN(d) || Double.isInfinite(d)) {
125.532 + return d;
125.533 + }
125.534 + long bits = Double.doubleToLongBits(d);
125.535 + long significand = bits & SIGNIFICAND;
125.536 + if (bits < 0) {
125.537 + significand |= (SIGN | EXPONENT);
125.538 + }
125.539 + long exponent = bits & EXPONENT;
125.540 + if (positive) {
125.541 + significand += 1;
125.542 + // FIXME fix overflow & underflow
125.543 + } else {
125.544 + significand -= 1;
125.545 + // FIXME fix overflow & underflow
125.546 + }
125.547 + bits = exponent | (significand & ~EXPONENT);
125.548 + return Double.longBitsToDouble(bits);
125.549 + }
125.550 + */
125.551 +
125.552 + static final long SIGN = 0x8000000000000000L;
125.553 + static final long EXPONENT = 0x7FF0000000000000L;
125.554 + static final long POSITIVEINFINITY = 0x7FF0000000000000L;
125.555 +
125.556 + /**
125.557 + * Finds the least double greater than d (if positive == true),
125.558 + * or the greatest double less than d (if positive == false).
125.559 + * If NaN, returns same value.
125.560 + *
125.561 + * Does not affect floating-point flags,
125.562 + * provided these member functions do not:
125.563 + * Double.longBitsToDouble(long)
125.564 + * Double.doubleToLongBits(double)
125.565 + * Double.isNaN(double)
125.566 + */
125.567 + public static double nextDouble (double d, boolean positive) {
125.568 +
125.569 + /* filter out NaN's */
125.570 + if (Double.isNaN(d)) {
125.571 + return d;
125.572 + }
125.573 +
125.574 + /* zero's are also a special case */
125.575 + if (d == 0.0) {
125.576 + double smallestPositiveDouble = Double.longBitsToDouble(1L);
125.577 + if (positive) {
125.578 + return smallestPositiveDouble;
125.579 + } else {
125.580 + return -smallestPositiveDouble;
125.581 + }
125.582 + }
125.583 +
125.584 + /* if entering here, d is a nonzero value */
125.585 +
125.586 + /* hold all bits in a long for later use */
125.587 + long bits = Double.doubleToLongBits(d);
125.588 +
125.589 + /* strip off the sign bit */
125.590 + long magnitude = bits & ~SIGN;
125.591 +
125.592 + /* if next double away from zero, increase magnitude */
125.593 + if ((bits > 0) == positive) {
125.594 + if (magnitude != POSITIVEINFINITY) {
125.595 + magnitude += 1;
125.596 + }
125.597 + }
125.598 + /* else decrease magnitude */
125.599 + else {
125.600 + magnitude -= 1;
125.601 + }
125.602 +
125.603 + /* restore sign bit and return */
125.604 + long signbit = bits & SIGN;
125.605 + return Double.longBitsToDouble (magnitude | signbit);
125.606 + }
125.607 +
125.608 + private static double[] doubleArraySize(double[] array) {
125.609 + int oldSize = array.length;
125.610 + double[] newArray = new double[oldSize * 2];
125.611 + System.arraycopy(array, 0, newArray, 0, oldSize);
125.612 + return newArray;
125.613 + }
125.614 +
125.615 + private String[] doubleArraySize(String[] array) {
125.616 + int oldSize = array.length;
125.617 + String[] newArray = new String[oldSize * 2];
125.618 + System.arraycopy(array, 0, newArray, 0, oldSize);
125.619 + return newArray;
125.620 + }
125.621 +
125.622 +}
126.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
126.2 +++ b/rt/emul/compact/src/main/java/java/text/DateFormat.java Tue Feb 11 13:31:42 2014 +0100
126.3 @@ -0,0 +1,1025 @@
126.4 +/*
126.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
126.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
126.7 + *
126.8 + * This code is free software; you can redistribute it and/or modify it
126.9 + * under the terms of the GNU General Public License version 2 only, as
126.10 + * published by the Free Software Foundation. Oracle designates this
126.11 + * particular file as subject to the "Classpath" exception as provided
126.12 + * by Oracle in the LICENSE file that accompanied this code.
126.13 + *
126.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
126.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
126.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
126.17 + * version 2 for more details (a copy is included in the LICENSE file that
126.18 + * accompanied this code).
126.19 + *
126.20 + * You should have received a copy of the GNU General Public License version
126.21 + * 2 along with this work; if not, write to the Free Software Foundation,
126.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
126.23 + *
126.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
126.25 + * or visit www.oracle.com if you need additional information or have any
126.26 + * questions.
126.27 + */
126.28 +
126.29 +/*
126.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
126.31 + * (C) Copyright IBM Corp. 1996 - All Rights Reserved
126.32 + *
126.33 + * The original version of this source code and documentation is copyrighted
126.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
126.35 + * materials are provided under terms of a License Agreement between Taligent
126.36 + * and Sun. This technology is protected by multiple US and International
126.37 + * patents. This notice and attribution to Taligent may not be removed.
126.38 + * Taligent is a registered trademark of Taligent, Inc.
126.39 + *
126.40 + */
126.41 +
126.42 +package java.text;
126.43 +
126.44 +import java.io.InvalidObjectException;
126.45 +import java.util.Calendar;
126.46 +import java.util.Date;
126.47 +import java.util.HashMap;
126.48 +import java.util.Locale;
126.49 +import java.util.Map;
126.50 +import java.util.MissingResourceException;
126.51 +import java.util.TimeZone;
126.52 +
126.53 +/**
126.54 + * {@code DateFormat} is an abstract class for date/time formatting subclasses which
126.55 + * formats and parses dates or time in a language-independent manner.
126.56 + * The date/time formatting subclass, such as {@link SimpleDateFormat}, allows for
126.57 + * formatting (i.e., date -> text), parsing (text -> date), and
126.58 + * normalization. The date is represented as a <code>Date</code> object or
126.59 + * as the milliseconds since January 1, 1970, 00:00:00 GMT.
126.60 + *
126.61 + * <p>{@code DateFormat} provides many class methods for obtaining default date/time
126.62 + * formatters based on the default or a given locale and a number of formatting
126.63 + * styles. The formatting styles include {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, and {@link #SHORT}. More
126.64 + * detail and examples of using these styles are provided in the method
126.65 + * descriptions.
126.66 + *
126.67 + * <p>{@code DateFormat} helps you to format and parse dates for any locale.
126.68 + * Your code can be completely independent of the locale conventions for
126.69 + * months, days of the week, or even the calendar format: lunar vs. solar.
126.70 + *
126.71 + * <p>To format a date for the current Locale, use one of the
126.72 + * static factory methods:
126.73 + * <pre>
126.74 + * myString = DateFormat.getDateInstance().format(myDate);
126.75 + * </pre>
126.76 + * <p>If you are formatting multiple dates, it is
126.77 + * more efficient to get the format and use it multiple times so that
126.78 + * the system doesn't have to fetch the information about the local
126.79 + * language and country conventions multiple times.
126.80 + * <pre>
126.81 + * DateFormat df = DateFormat.getDateInstance();
126.82 + * for (int i = 0; i < myDate.length; ++i) {
126.83 + * output.println(df.format(myDate[i]) + "; ");
126.84 + * }
126.85 + * </pre>
126.86 + * <p>To format a date for a different Locale, specify it in the
126.87 + * call to {@link #getDateInstance(int, Locale) getDateInstance()}.
126.88 + * <pre>
126.89 + * DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
126.90 + * </pre>
126.91 + * <p>You can use a DateFormat to parse also.
126.92 + * <pre>
126.93 + * myDate = df.parse(myString);
126.94 + * </pre>
126.95 + * <p>Use {@code getDateInstance} to get the normal date format for that country.
126.96 + * There are other static factory methods available.
126.97 + * Use {@code getTimeInstance} to get the time format for that country.
126.98 + * Use {@code getDateTimeInstance} to get a date and time format. You can pass in
126.99 + * different options to these factory methods to control the length of the
126.100 + * result; from {@link #SHORT} to {@link #MEDIUM} to {@link #LONG} to {@link #FULL}. The exact result depends
126.101 + * on the locale, but generally:
126.102 + * <ul><li>{@link #SHORT} is completely numeric, such as {@code 12.13.52} or {@code 3:30pm}
126.103 + * <li>{@link #MEDIUM} is longer, such as {@code Jan 12, 1952}
126.104 + * <li>{@link #LONG} is longer, such as {@code January 12, 1952} or {@code 3:30:32pm}
126.105 + * <li>{@link #FULL} is pretty completely specified, such as
126.106 + * {@code Tuesday, April 12, 1952 AD or 3:30:42pm PST}.
126.107 + * </ul>
126.108 + *
126.109 + * <p>You can also set the time zone on the format if you wish.
126.110 + * If you want even more control over the format or parsing,
126.111 + * (or want to give your users more control),
126.112 + * you can try casting the {@code DateFormat} you get from the factory methods
126.113 + * to a {@link SimpleDateFormat}. This will work for the majority
126.114 + * of countries; just remember to put it in a {@code try} block in case you
126.115 + * encounter an unusual one.
126.116 + *
126.117 + * <p>You can also use forms of the parse and format methods with
126.118 + * {@link ParsePosition} and {@link FieldPosition} to
126.119 + * allow you to
126.120 + * <ul><li>progressively parse through pieces of a string.
126.121 + * <li>align any particular field, or find out where it is for selection
126.122 + * on the screen.
126.123 + * </ul>
126.124 + *
126.125 + * <h4><a name="synchronization">Synchronization</a></h4>
126.126 + *
126.127 + * <p>
126.128 + * Date formats are not synchronized.
126.129 + * It is recommended to create separate format instances for each thread.
126.130 + * If multiple threads access a format concurrently, it must be synchronized
126.131 + * externally.
126.132 + *
126.133 + * @see Format
126.134 + * @see NumberFormat
126.135 + * @see SimpleDateFormat
126.136 + * @see java.util.Calendar
126.137 + * @see java.util.GregorianCalendar
126.138 + * @see java.util.TimeZone
126.139 + * @author Mark Davis, Chen-Lieh Huang, Alan Liu
126.140 + */
126.141 +public abstract class DateFormat extends Format {
126.142 +
126.143 + /**
126.144 + * The {@link Calendar} instance used for calculating the date-time fields
126.145 + * and the instant of time. This field is used for both formatting and
126.146 + * parsing.
126.147 + *
126.148 + * <p>Subclasses should initialize this field to a {@link Calendar}
126.149 + * appropriate for the {@link Locale} associated with this
126.150 + * <code>DateFormat</code>.
126.151 + * @serial
126.152 + */
126.153 + protected Calendar calendar;
126.154 +
126.155 + /**
126.156 + * The number formatter that <code>DateFormat</code> uses to format numbers
126.157 + * in dates and times. Subclasses should initialize this to a number format
126.158 + * appropriate for the locale associated with this <code>DateFormat</code>.
126.159 + * @serial
126.160 + */
126.161 + protected NumberFormat numberFormat;
126.162 +
126.163 + /**
126.164 + * Useful constant for ERA field alignment.
126.165 + * Used in FieldPosition of date/time formatting.
126.166 + */
126.167 + public final static int ERA_FIELD = 0;
126.168 + /**
126.169 + * Useful constant for YEAR field alignment.
126.170 + * Used in FieldPosition of date/time formatting.
126.171 + */
126.172 + public final static int YEAR_FIELD = 1;
126.173 + /**
126.174 + * Useful constant for MONTH field alignment.
126.175 + * Used in FieldPosition of date/time formatting.
126.176 + */
126.177 + public final static int MONTH_FIELD = 2;
126.178 + /**
126.179 + * Useful constant for DATE field alignment.
126.180 + * Used in FieldPosition of date/time formatting.
126.181 + */
126.182 + public final static int DATE_FIELD = 3;
126.183 + /**
126.184 + * Useful constant for one-based HOUR_OF_DAY field alignment.
126.185 + * Used in FieldPosition of date/time formatting.
126.186 + * HOUR_OF_DAY1_FIELD is used for the one-based 24-hour clock.
126.187 + * For example, 23:59 + 01:00 results in 24:59.
126.188 + */
126.189 + public final static int HOUR_OF_DAY1_FIELD = 4;
126.190 + /**
126.191 + * Useful constant for zero-based HOUR_OF_DAY field alignment.
126.192 + * Used in FieldPosition of date/time formatting.
126.193 + * HOUR_OF_DAY0_FIELD is used for the zero-based 24-hour clock.
126.194 + * For example, 23:59 + 01:00 results in 00:59.
126.195 + */
126.196 + public final static int HOUR_OF_DAY0_FIELD = 5;
126.197 + /**
126.198 + * Useful constant for MINUTE field alignment.
126.199 + * Used in FieldPosition of date/time formatting.
126.200 + */
126.201 + public final static int MINUTE_FIELD = 6;
126.202 + /**
126.203 + * Useful constant for SECOND field alignment.
126.204 + * Used in FieldPosition of date/time formatting.
126.205 + */
126.206 + public final static int SECOND_FIELD = 7;
126.207 + /**
126.208 + * Useful constant for MILLISECOND field alignment.
126.209 + * Used in FieldPosition of date/time formatting.
126.210 + */
126.211 + public final static int MILLISECOND_FIELD = 8;
126.212 + /**
126.213 + * Useful constant for DAY_OF_WEEK field alignment.
126.214 + * Used in FieldPosition of date/time formatting.
126.215 + */
126.216 + public final static int DAY_OF_WEEK_FIELD = 9;
126.217 + /**
126.218 + * Useful constant for DAY_OF_YEAR field alignment.
126.219 + * Used in FieldPosition of date/time formatting.
126.220 + */
126.221 + public final static int DAY_OF_YEAR_FIELD = 10;
126.222 + /**
126.223 + * Useful constant for DAY_OF_WEEK_IN_MONTH field alignment.
126.224 + * Used in FieldPosition of date/time formatting.
126.225 + */
126.226 + public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
126.227 + /**
126.228 + * Useful constant for WEEK_OF_YEAR field alignment.
126.229 + * Used in FieldPosition of date/time formatting.
126.230 + */
126.231 + public final static int WEEK_OF_YEAR_FIELD = 12;
126.232 + /**
126.233 + * Useful constant for WEEK_OF_MONTH field alignment.
126.234 + * Used in FieldPosition of date/time formatting.
126.235 + */
126.236 + public final static int WEEK_OF_MONTH_FIELD = 13;
126.237 + /**
126.238 + * Useful constant for AM_PM field alignment.
126.239 + * Used in FieldPosition of date/time formatting.
126.240 + */
126.241 + public final static int AM_PM_FIELD = 14;
126.242 + /**
126.243 + * Useful constant for one-based HOUR field alignment.
126.244 + * Used in FieldPosition of date/time formatting.
126.245 + * HOUR1_FIELD is used for the one-based 12-hour clock.
126.246 + * For example, 11:30 PM + 1 hour results in 12:30 AM.
126.247 + */
126.248 + public final static int HOUR1_FIELD = 15;
126.249 + /**
126.250 + * Useful constant for zero-based HOUR field alignment.
126.251 + * Used in FieldPosition of date/time formatting.
126.252 + * HOUR0_FIELD is used for the zero-based 12-hour clock.
126.253 + * For example, 11:30 PM + 1 hour results in 00:30 AM.
126.254 + */
126.255 + public final static int HOUR0_FIELD = 16;
126.256 + /**
126.257 + * Useful constant for TIMEZONE field alignment.
126.258 + * Used in FieldPosition of date/time formatting.
126.259 + */
126.260 + public final static int TIMEZONE_FIELD = 17;
126.261 +
126.262 + // Proclaim serial compatibility with 1.1 FCS
126.263 + private static final long serialVersionUID = 7218322306649953788L;
126.264 +
126.265 + /**
126.266 + * Overrides Format.
126.267 + * Formats a time object into a time string. Examples of time objects
126.268 + * are a time value expressed in milliseconds and a Date object.
126.269 + * @param obj must be a Number or a Date.
126.270 + * @param toAppendTo the string buffer for the returning time string.
126.271 + * @return the string buffer passed in as toAppendTo, with formatted text appended.
126.272 + * @param fieldPosition keeps track of the position of the field
126.273 + * within the returned string.
126.274 + * On input: an alignment field,
126.275 + * if desired. On output: the offsets of the alignment field. For
126.276 + * example, given a time text "1996.07.10 AD at 15:08:56 PDT",
126.277 + * if the given fieldPosition is DateFormat.YEAR_FIELD, the
126.278 + * begin index and end index of fieldPosition will be set to
126.279 + * 0 and 4, respectively.
126.280 + * Notice that if the same time field appears
126.281 + * more than once in a pattern, the fieldPosition will be set for the first
126.282 + * occurrence of that time field. For instance, formatting a Date to
126.283 + * the time string "1 PM PDT (Pacific Daylight Time)" using the pattern
126.284 + * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
126.285 + * the begin index and end index of fieldPosition will be set to
126.286 + * 5 and 8, respectively, for the first occurrence of the timezone
126.287 + * pattern character 'z'.
126.288 + * @see java.text.Format
126.289 + */
126.290 + public final StringBuffer format(Object obj, StringBuffer toAppendTo,
126.291 + FieldPosition fieldPosition)
126.292 + {
126.293 + if (obj instanceof Date)
126.294 + return format( (Date)obj, toAppendTo, fieldPosition );
126.295 + else if (obj instanceof Number)
126.296 + return format( new Date(((Number)obj).longValue()),
126.297 + toAppendTo, fieldPosition );
126.298 + else
126.299 + throw new IllegalArgumentException("Cannot format given Object as a Date");
126.300 + }
126.301 +
126.302 + /**
126.303 + * Formats a Date into a date/time string.
126.304 + * @param date a Date to be formatted into a date/time string.
126.305 + * @param toAppendTo the string buffer for the returning date/time string.
126.306 + * @param fieldPosition keeps track of the position of the field
126.307 + * within the returned string.
126.308 + * On input: an alignment field,
126.309 + * if desired. On output: the offsets of the alignment field. For
126.310 + * example, given a time text "1996.07.10 AD at 15:08:56 PDT",
126.311 + * if the given fieldPosition is DateFormat.YEAR_FIELD, the
126.312 + * begin index and end index of fieldPosition will be set to
126.313 + * 0 and 4, respectively.
126.314 + * Notice that if the same time field appears
126.315 + * more than once in a pattern, the fieldPosition will be set for the first
126.316 + * occurrence of that time field. For instance, formatting a Date to
126.317 + * the time string "1 PM PDT (Pacific Daylight Time)" using the pattern
126.318 + * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
126.319 + * the begin index and end index of fieldPosition will be set to
126.320 + * 5 and 8, respectively, for the first occurrence of the timezone
126.321 + * pattern character 'z'.
126.322 + * @return the string buffer passed in as toAppendTo, with formatted text appended.
126.323 + */
126.324 + public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
126.325 + FieldPosition fieldPosition);
126.326 +
126.327 + /**
126.328 + * Formats a Date into a date/time string.
126.329 + * @param date the time value to be formatted into a time string.
126.330 + * @return the formatted time string.
126.331 + */
126.332 + public final String format(Date date)
126.333 + {
126.334 + return format(date, new StringBuffer(),
126.335 + DontCareFieldPosition.INSTANCE).toString();
126.336 + }
126.337 +
126.338 + /**
126.339 + * Parses text from the beginning of the given string to produce a date.
126.340 + * The method may not use the entire text of the given string.
126.341 + * <p>
126.342 + * See the {@link #parse(String, ParsePosition)} method for more information
126.343 + * on date parsing.
126.344 + *
126.345 + * @param source A <code>String</code> whose beginning should be parsed.
126.346 + * @return A <code>Date</code> parsed from the string.
126.347 + * @exception ParseException if the beginning of the specified string
126.348 + * cannot be parsed.
126.349 + */
126.350 + public Date parse(String source) throws ParseException
126.351 + {
126.352 + ParsePosition pos = new ParsePosition(0);
126.353 + Date result = parse(source, pos);
126.354 + if (pos.index == 0)
126.355 + throw new ParseException("Unparseable date: \"" + source + "\"" ,
126.356 + pos.errorIndex);
126.357 + return result;
126.358 + }
126.359 +
126.360 + /**
126.361 + * Parse a date/time string according to the given parse position. For
126.362 + * example, a time text {@code "07/10/96 4:5 PM, PDT"} will be parsed into a {@code Date}
126.363 + * that is equivalent to {@code Date(837039900000L)}.
126.364 + *
126.365 + * <p> By default, parsing is lenient: If the input is not in the form used
126.366 + * by this object's format method but can still be parsed as a date, then
126.367 + * the parse succeeds. Clients may insist on strict adherence to the
126.368 + * format by calling {@link #setLenient(boolean) setLenient(false)}.
126.369 + *
126.370 + * <p>This parsing operation uses the {@link #calendar} to produce
126.371 + * a {@code Date}. As a result, the {@code calendar}'s date-time
126.372 + * fields and the {@code TimeZone} value may have been
126.373 + * overwritten, depending on subclass implementations. Any {@code
126.374 + * TimeZone} value that has previously been set by a call to
126.375 + * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
126.376 + * to be restored for further operations.
126.377 + *
126.378 + * @param source The date/time string to be parsed
126.379 + *
126.380 + * @param pos On input, the position at which to start parsing; on
126.381 + * output, the position at which parsing terminated, or the
126.382 + * start position if the parse failed.
126.383 + *
126.384 + * @return A {@code Date}, or {@code null} if the input could not be parsed
126.385 + */
126.386 + public abstract Date parse(String source, ParsePosition pos);
126.387 +
126.388 + /**
126.389 + * Parses text from a string to produce a <code>Date</code>.
126.390 + * <p>
126.391 + * The method attempts to parse text starting at the index given by
126.392 + * <code>pos</code>.
126.393 + * If parsing succeeds, then the index of <code>pos</code> is updated
126.394 + * to the index after the last character used (parsing does not necessarily
126.395 + * use all characters up to the end of the string), and the parsed
126.396 + * date is returned. The updated <code>pos</code> can be used to
126.397 + * indicate the starting point for the next call to this method.
126.398 + * If an error occurs, then the index of <code>pos</code> is not
126.399 + * changed, the error index of <code>pos</code> is set to the index of
126.400 + * the character where the error occurred, and null is returned.
126.401 + * <p>
126.402 + * See the {@link #parse(String, ParsePosition)} method for more information
126.403 + * on date parsing.
126.404 + *
126.405 + * @param source A <code>String</code>, part of which should be parsed.
126.406 + * @param pos A <code>ParsePosition</code> object with index and error
126.407 + * index information as described above.
126.408 + * @return A <code>Date</code> parsed from the string. In case of
126.409 + * error, returns null.
126.410 + * @exception NullPointerException if <code>pos</code> is null.
126.411 + */
126.412 + public Object parseObject(String source, ParsePosition pos) {
126.413 + return parse(source, pos);
126.414 + }
126.415 +
126.416 + /**
126.417 + * Constant for full style pattern.
126.418 + */
126.419 + public static final int FULL = 0;
126.420 + /**
126.421 + * Constant for long style pattern.
126.422 + */
126.423 + public static final int LONG = 1;
126.424 + /**
126.425 + * Constant for medium style pattern.
126.426 + */
126.427 + public static final int MEDIUM = 2;
126.428 + /**
126.429 + * Constant for short style pattern.
126.430 + */
126.431 + public static final int SHORT = 3;
126.432 + /**
126.433 + * Constant for default style pattern. Its value is MEDIUM.
126.434 + */
126.435 + public static final int DEFAULT = MEDIUM;
126.436 +
126.437 + /**
126.438 + * Gets the time formatter with the default formatting style
126.439 + * for the default locale.
126.440 + * @return a time formatter.
126.441 + */
126.442 + public final static DateFormat getTimeInstance()
126.443 + {
126.444 + return get(DEFAULT, 0, 1, Locale.getDefault(Locale.Category.FORMAT));
126.445 + }
126.446 +
126.447 + /**
126.448 + * Gets the time formatter with the given formatting style
126.449 + * for the default locale.
126.450 + * @param style the given formatting style. For example,
126.451 + * SHORT for "h:mm a" in the US locale.
126.452 + * @return a time formatter.
126.453 + */
126.454 + public final static DateFormat getTimeInstance(int style)
126.455 + {
126.456 + return get(style, 0, 1, Locale.getDefault(Locale.Category.FORMAT));
126.457 + }
126.458 +
126.459 + /**
126.460 + * Gets the time formatter with the given formatting style
126.461 + * for the given locale.
126.462 + * @param style the given formatting style. For example,
126.463 + * SHORT for "h:mm a" in the US locale.
126.464 + * @param aLocale the given locale.
126.465 + * @return a time formatter.
126.466 + */
126.467 + public final static DateFormat getTimeInstance(int style,
126.468 + Locale aLocale)
126.469 + {
126.470 + return get(style, 0, 1, aLocale);
126.471 + }
126.472 +
126.473 + /**
126.474 + * Gets the date formatter with the default formatting style
126.475 + * for the default locale.
126.476 + * @return a date formatter.
126.477 + */
126.478 + public final static DateFormat getDateInstance()
126.479 + {
126.480 + return get(0, DEFAULT, 2, Locale.getDefault(Locale.Category.FORMAT));
126.481 + }
126.482 +
126.483 + /**
126.484 + * Gets the date formatter with the given formatting style
126.485 + * for the default locale.
126.486 + * @param style the given formatting style. For example,
126.487 + * SHORT for "M/d/yy" in the US locale.
126.488 + * @return a date formatter.
126.489 + */
126.490 + public final static DateFormat getDateInstance(int style)
126.491 + {
126.492 + return get(0, style, 2, Locale.getDefault(Locale.Category.FORMAT));
126.493 + }
126.494 +
126.495 + /**
126.496 + * Gets the date formatter with the given formatting style
126.497 + * for the given locale.
126.498 + * @param style the given formatting style. For example,
126.499 + * SHORT for "M/d/yy" in the US locale.
126.500 + * @param aLocale the given locale.
126.501 + * @return a date formatter.
126.502 + */
126.503 + public final static DateFormat getDateInstance(int style,
126.504 + Locale aLocale)
126.505 + {
126.506 + return get(0, style, 2, aLocale);
126.507 + }
126.508 +
126.509 + /**
126.510 + * Gets the date/time formatter with the default formatting style
126.511 + * for the default locale.
126.512 + * @return a date/time formatter.
126.513 + */
126.514 + public final static DateFormat getDateTimeInstance()
126.515 + {
126.516 + return get(DEFAULT, DEFAULT, 3, Locale.getDefault(Locale.Category.FORMAT));
126.517 + }
126.518 +
126.519 + /**
126.520 + * Gets the date/time formatter with the given date and time
126.521 + * formatting styles for the default locale.
126.522 + * @param dateStyle the given date formatting style. For example,
126.523 + * SHORT for "M/d/yy" in the US locale.
126.524 + * @param timeStyle the given time formatting style. For example,
126.525 + * SHORT for "h:mm a" in the US locale.
126.526 + * @return a date/time formatter.
126.527 + */
126.528 + public final static DateFormat getDateTimeInstance(int dateStyle,
126.529 + int timeStyle)
126.530 + {
126.531 + return get(timeStyle, dateStyle, 3, Locale.getDefault(Locale.Category.FORMAT));
126.532 + }
126.533 +
126.534 + /**
126.535 + * Gets the date/time formatter with the given formatting styles
126.536 + * for the given locale.
126.537 + * @param dateStyle the given date formatting style.
126.538 + * @param timeStyle the given time formatting style.
126.539 + * @param aLocale the given locale.
126.540 + * @return a date/time formatter.
126.541 + */
126.542 + public final static DateFormat
126.543 + getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
126.544 + {
126.545 + return get(timeStyle, dateStyle, 3, aLocale);
126.546 + }
126.547 +
126.548 + /**
126.549 + * Get a default date/time formatter that uses the SHORT style for both the
126.550 + * date and the time.
126.551 + */
126.552 + public final static DateFormat getInstance() {
126.553 + return getDateTimeInstance(SHORT, SHORT);
126.554 + }
126.555 +
126.556 + /**
126.557 + * Returns an array of all locales for which the
126.558 + * <code>get*Instance</code> methods of this class can return
126.559 + * localized instances.
126.560 + * The returned array represents the union of locales supported by the Java
126.561 + * runtime and by installed
126.562 + * {@link java.text.spi.DateFormatProvider DateFormatProvider} implementations.
126.563 + * It must contain at least a <code>Locale</code> instance equal to
126.564 + * {@link java.util.Locale#US Locale.US}.
126.565 + *
126.566 + * @return An array of locales for which localized
126.567 + * <code>DateFormat</code> instances are available.
126.568 + */
126.569 + public static Locale[] getAvailableLocales()
126.570 + {
126.571 + return new Locale[] { Locale.US };
126.572 + }
126.573 +
126.574 + /**
126.575 + * Set the calendar to be used by this date format. Initially, the default
126.576 + * calendar for the specified or default locale is used.
126.577 + *
126.578 + * <p>Any {@link java.util.TimeZone TimeZone} and {@linkplain
126.579 + * #isLenient() leniency} values that have previously been set are
126.580 + * overwritten by {@code newCalendar}'s values.
126.581 + *
126.582 + * @param newCalendar the new {@code Calendar} to be used by the date format
126.583 + */
126.584 + public void setCalendar(Calendar newCalendar)
126.585 + {
126.586 + this.calendar = newCalendar;
126.587 + }
126.588 +
126.589 + /**
126.590 + * Gets the calendar associated with this date/time formatter.
126.591 + *
126.592 + * @return the calendar associated with this date/time formatter.
126.593 + */
126.594 + public Calendar getCalendar()
126.595 + {
126.596 + return calendar;
126.597 + }
126.598 +
126.599 + /**
126.600 + * Allows you to set the number formatter.
126.601 + * @param newNumberFormat the given new NumberFormat.
126.602 + */
126.603 + public void setNumberFormat(NumberFormat newNumberFormat)
126.604 + {
126.605 + this.numberFormat = newNumberFormat;
126.606 + }
126.607 +
126.608 + /**
126.609 + * Gets the number formatter which this date/time formatter uses to
126.610 + * format and parse a time.
126.611 + * @return the number formatter which this date/time formatter uses.
126.612 + */
126.613 + public NumberFormat getNumberFormat()
126.614 + {
126.615 + return numberFormat;
126.616 + }
126.617 +
126.618 + /**
126.619 + * Sets the time zone for the calendar of this {@code DateFormat} object.
126.620 + * This method is equivalent to the following call.
126.621 + * <blockquote><pre>
126.622 + * getCalendar().setTimeZone(zone)
126.623 + * </pre></blockquote>
126.624 + *
126.625 + * <p>The {@code TimeZone} set by this method is overwritten by a
126.626 + * {@link #setCalendar(java.util.Calendar) setCalendar} call.
126.627 + *
126.628 + * <p>The {@code TimeZone} set by this method may be overwritten as
126.629 + * a result of a call to the parse method.
126.630 + *
126.631 + * @param zone the given new time zone.
126.632 + */
126.633 + public void setTimeZone(TimeZone zone)
126.634 + {
126.635 + calendar.setTimeZone(zone);
126.636 + }
126.637 +
126.638 + /**
126.639 + * Gets the time zone.
126.640 + * This method is equivalent to the following call.
126.641 + * <blockquote><pre>
126.642 + * getCalendar().getTimeZone()
126.643 + * </pre></blockquote>
126.644 + *
126.645 + * @return the time zone associated with the calendar of DateFormat.
126.646 + */
126.647 + public TimeZone getTimeZone()
126.648 + {
126.649 + return calendar.getTimeZone();
126.650 + }
126.651 +
126.652 + /**
126.653 + * Specify whether or not date/time parsing is to be lenient. With
126.654 + * lenient parsing, the parser may use heuristics to interpret inputs that
126.655 + * do not precisely match this object's format. With strict parsing,
126.656 + * inputs must match this object's format.
126.657 + *
126.658 + * <p>This method is equivalent to the following call.
126.659 + * <blockquote><pre>
126.660 + * getCalendar().setLenient(lenient)
126.661 + * </pre></blockquote>
126.662 + *
126.663 + * <p>This leniency value is overwritten by a call to {@link
126.664 + * #setCalendar(java.util.Calendar) setCalendar()}.
126.665 + *
126.666 + * @param lenient when {@code true}, parsing is lenient
126.667 + * @see java.util.Calendar#setLenient(boolean)
126.668 + */
126.669 + public void setLenient(boolean lenient)
126.670 + {
126.671 + calendar.setLenient(lenient);
126.672 + }
126.673 +
126.674 + /**
126.675 + * Tell whether date/time parsing is to be lenient.
126.676 + * This method is equivalent to the following call.
126.677 + * <blockquote><pre>
126.678 + * getCalendar().isLenient()
126.679 + * </pre></blockquote>
126.680 + *
126.681 + * @return {@code true} if the {@link #calendar} is lenient;
126.682 + * {@code false} otherwise.
126.683 + * @see java.util.Calendar#isLenient()
126.684 + */
126.685 + public boolean isLenient()
126.686 + {
126.687 + return calendar.isLenient();
126.688 + }
126.689 +
126.690 + /**
126.691 + * Overrides hashCode
126.692 + */
126.693 + public int hashCode() {
126.694 + return numberFormat.hashCode();
126.695 + // just enough fields for a reasonable distribution
126.696 + }
126.697 +
126.698 + /**
126.699 + * Overrides equals
126.700 + */
126.701 + public boolean equals(Object obj) {
126.702 + if (this == obj) return true;
126.703 + if (obj == null || getClass() != obj.getClass()) return false;
126.704 + DateFormat other = (DateFormat) obj;
126.705 + return (// calendar.equivalentTo(other.calendar) // THIS API DOESN'T EXIST YET!
126.706 + calendar.getFirstDayOfWeek() == other.calendar.getFirstDayOfWeek() &&
126.707 + calendar.getMinimalDaysInFirstWeek() == other.calendar.getMinimalDaysInFirstWeek() &&
126.708 + calendar.isLenient() == other.calendar.isLenient() &&
126.709 + calendar.getTimeZone().equals(other.calendar.getTimeZone()) &&
126.710 + numberFormat.equals(other.numberFormat));
126.711 + }
126.712 +
126.713 + /**
126.714 + * Overrides Cloneable
126.715 + */
126.716 + public Object clone()
126.717 + {
126.718 + DateFormat other = (DateFormat) super.clone();
126.719 + other.calendar = (Calendar) calendar.clone();
126.720 + other.numberFormat = (NumberFormat) numberFormat.clone();
126.721 + return other;
126.722 + }
126.723 +
126.724 + /**
126.725 + * Creates a DateFormat with the given time and/or date style in the given
126.726 + * locale.
126.727 + * @param timeStyle a value from 0 to 3 indicating the time format,
126.728 + * ignored if flags is 2
126.729 + * @param dateStyle a value from 0 to 3 indicating the time format,
126.730 + * ignored if flags is 1
126.731 + * @param flags either 1 for a time format, 2 for a date format,
126.732 + * or 3 for a date/time format
126.733 + * @param loc the locale for the format
126.734 + */
126.735 + private static DateFormat get(int timeStyle, int dateStyle,
126.736 + int flags, Locale loc) {
126.737 + if ((flags & 1) != 0) {
126.738 + if (timeStyle < 0 || timeStyle > 3) {
126.739 + throw new IllegalArgumentException("Illegal time style " + timeStyle);
126.740 + }
126.741 + } else {
126.742 + timeStyle = -1;
126.743 + }
126.744 + if ((flags & 2) != 0) {
126.745 + if (dateStyle < 0 || dateStyle > 3) {
126.746 + throw new IllegalArgumentException("Illegal date style " + dateStyle);
126.747 + }
126.748 + } else {
126.749 + dateStyle = -1;
126.750 + }
126.751 + try {
126.752 + // Check whether a provider can provide an implementation that's closer
126.753 + // to the requested locale than what the Java runtime itself can provide.
126.754 + /*
126.755 + LocaleServiceProviderPool pool =
126.756 + LocaleServiceProviderPool.getPool(DateFormatProvider.class);
126.757 + if (pool.hasProviders()) {
126.758 + DateFormat providersInstance = pool.getLocalizedObject(
126.759 + DateFormatGetter.INSTANCE,
126.760 + loc,
126.761 + timeStyle,
126.762 + dateStyle,
126.763 + flags);
126.764 + if (providersInstance != null) {
126.765 + return providersInstance;
126.766 + }
126.767 + }
126.768 + */
126.769 +
126.770 + return new SimpleDateFormat(timeStyle, dateStyle, loc);
126.771 + } catch (MissingResourceException e) {
126.772 + return new SimpleDateFormat("M/d/yy h:mm a");
126.773 + }
126.774 + }
126.775 +
126.776 + /**
126.777 + * Create a new date format.
126.778 + */
126.779 + protected DateFormat() {}
126.780 +
126.781 + /**
126.782 + * Defines constants that are used as attribute keys in the
126.783 + * <code>AttributedCharacterIterator</code> returned
126.784 + * from <code>DateFormat.formatToCharacterIterator</code> and as
126.785 + * field identifiers in <code>FieldPosition</code>.
126.786 + * <p>
126.787 + * The class also provides two methods to map
126.788 + * between its constants and the corresponding Calendar constants.
126.789 + *
126.790 + * @since 1.4
126.791 + * @see java.util.Calendar
126.792 + */
126.793 + public static class Field extends Format.Field {
126.794 +
126.795 + // Proclaim serial compatibility with 1.4 FCS
126.796 + private static final long serialVersionUID = 7441350119349544720L;
126.797 +
126.798 + // table of all instances in this class, used by readResolve
126.799 + private static final Map instanceMap = new HashMap(18);
126.800 + // Maps from Calendar constant (such as Calendar.ERA) to Field
126.801 + // constant (such as Field.ERA).
126.802 + private static final Field[] calendarToFieldMapping =
126.803 + new Field[Calendar.FIELD_COUNT];
126.804 +
126.805 + /** Calendar field. */
126.806 + private int calendarField;
126.807 +
126.808 + /**
126.809 + * Returns the <code>Field</code> constant that corresponds to
126.810 + * the <code>Calendar</code> constant <code>calendarField</code>.
126.811 + * If there is no direct mapping between the <code>Calendar</code>
126.812 + * constant and a <code>Field</code>, null is returned.
126.813 + *
126.814 + * @throws IllegalArgumentException if <code>calendarField</code> is
126.815 + * not the value of a <code>Calendar</code> field constant.
126.816 + * @param calendarField Calendar field constant
126.817 + * @return Field instance representing calendarField.
126.818 + * @see java.util.Calendar
126.819 + */
126.820 + public static Field ofCalendarField(int calendarField) {
126.821 + if (calendarField < 0 || calendarField >=
126.822 + calendarToFieldMapping.length) {
126.823 + throw new IllegalArgumentException("Unknown Calendar constant "
126.824 + + calendarField);
126.825 + }
126.826 + return calendarToFieldMapping[calendarField];
126.827 + }
126.828 +
126.829 + /**
126.830 + * Creates a <code>Field</code>.
126.831 + *
126.832 + * @param name the name of the <code>Field</code>
126.833 + * @param calendarField the <code>Calendar</code> constant this
126.834 + * <code>Field</code> corresponds to; any value, even one
126.835 + * outside the range of legal <code>Calendar</code> values may
126.836 + * be used, but <code>-1</code> should be used for values
126.837 + * that don't correspond to legal <code>Calendar</code> values
126.838 + */
126.839 + protected Field(String name, int calendarField) {
126.840 + super(name);
126.841 + this.calendarField = calendarField;
126.842 + if (this.getClass() == DateFormat.Field.class) {
126.843 + instanceMap.put(name, this);
126.844 + if (calendarField >= 0) {
126.845 + // assert(calendarField < Calendar.FIELD_COUNT);
126.846 + calendarToFieldMapping[calendarField] = this;
126.847 + }
126.848 + }
126.849 + }
126.850 +
126.851 + /**
126.852 + * Returns the <code>Calendar</code> field associated with this
126.853 + * attribute. For example, if this represents the hours field of
126.854 + * a <code>Calendar</code>, this would return
126.855 + * <code>Calendar.HOUR</code>. If there is no corresponding
126.856 + * <code>Calendar</code> constant, this will return -1.
126.857 + *
126.858 + * @return Calendar constant for this field
126.859 + * @see java.util.Calendar
126.860 + */
126.861 + public int getCalendarField() {
126.862 + return calendarField;
126.863 + }
126.864 +
126.865 + /**
126.866 + * Resolves instances being deserialized to the predefined constants.
126.867 + *
126.868 + * @throws InvalidObjectException if the constant could not be
126.869 + * resolved.
126.870 + * @return resolved DateFormat.Field constant
126.871 + */
126.872 + protected Object readResolve() throws InvalidObjectException {
126.873 + if (this.getClass() != DateFormat.Field.class) {
126.874 + throw new InvalidObjectException("subclass didn't correctly implement readResolve");
126.875 + }
126.876 +
126.877 + Object instance = instanceMap.get(getName());
126.878 + if (instance != null) {
126.879 + return instance;
126.880 + } else {
126.881 + throw new InvalidObjectException("unknown attribute name");
126.882 + }
126.883 + }
126.884 +
126.885 + //
126.886 + // The constants
126.887 + //
126.888 +
126.889 + /**
126.890 + * Constant identifying the era field.
126.891 + */
126.892 + public final static Field ERA = new Field("era", Calendar.ERA);
126.893 +
126.894 + /**
126.895 + * Constant identifying the year field.
126.896 + */
126.897 + public final static Field YEAR = new Field("year", Calendar.YEAR);
126.898 +
126.899 + /**
126.900 + * Constant identifying the month field.
126.901 + */
126.902 + public final static Field MONTH = new Field("month", Calendar.MONTH);
126.903 +
126.904 + /**
126.905 + * Constant identifying the day of month field.
126.906 + */
126.907 + public final static Field DAY_OF_MONTH = new
126.908 + Field("day of month", Calendar.DAY_OF_MONTH);
126.909 +
126.910 + /**
126.911 + * Constant identifying the hour of day field, where the legal values
126.912 + * are 1 to 24.
126.913 + */
126.914 + public final static Field HOUR_OF_DAY1 = new Field("hour of day 1",-1);
126.915 +
126.916 + /**
126.917 + * Constant identifying the hour of day field, where the legal values
126.918 + * are 0 to 23.
126.919 + */
126.920 + public final static Field HOUR_OF_DAY0 = new
126.921 + Field("hour of day", Calendar.HOUR_OF_DAY);
126.922 +
126.923 + /**
126.924 + * Constant identifying the minute field.
126.925 + */
126.926 + public final static Field MINUTE =new Field("minute", Calendar.MINUTE);
126.927 +
126.928 + /**
126.929 + * Constant identifying the second field.
126.930 + */
126.931 + public final static Field SECOND =new Field("second", Calendar.SECOND);
126.932 +
126.933 + /**
126.934 + * Constant identifying the millisecond field.
126.935 + */
126.936 + public final static Field MILLISECOND = new
126.937 + Field("millisecond", Calendar.MILLISECOND);
126.938 +
126.939 + /**
126.940 + * Constant identifying the day of week field.
126.941 + */
126.942 + public final static Field DAY_OF_WEEK = new
126.943 + Field("day of week", Calendar.DAY_OF_WEEK);
126.944 +
126.945 + /**
126.946 + * Constant identifying the day of year field.
126.947 + */
126.948 + public final static Field DAY_OF_YEAR = new
126.949 + Field("day of year", Calendar.DAY_OF_YEAR);
126.950 +
126.951 + /**
126.952 + * Constant identifying the day of week field.
126.953 + */
126.954 + public final static Field DAY_OF_WEEK_IN_MONTH =
126.955 + new Field("day of week in month",
126.956 + Calendar.DAY_OF_WEEK_IN_MONTH);
126.957 +
126.958 + /**
126.959 + * Constant identifying the week of year field.
126.960 + */
126.961 + public final static Field WEEK_OF_YEAR = new
126.962 + Field("week of year", Calendar.WEEK_OF_YEAR);
126.963 +
126.964 + /**
126.965 + * Constant identifying the week of month field.
126.966 + */
126.967 + public final static Field WEEK_OF_MONTH = new
126.968 + Field("week of month", Calendar.WEEK_OF_MONTH);
126.969 +
126.970 + /**
126.971 + * Constant identifying the time of day indicator
126.972 + * (e.g. "a.m." or "p.m.") field.
126.973 + */
126.974 + public final static Field AM_PM = new
126.975 + Field("am pm", Calendar.AM_PM);
126.976 +
126.977 + /**
126.978 + * Constant identifying the hour field, where the legal values are
126.979 + * 1 to 12.
126.980 + */
126.981 + public final static Field HOUR1 = new Field("hour 1", -1);
126.982 +
126.983 + /**
126.984 + * Constant identifying the hour field, where the legal values are
126.985 + * 0 to 11.
126.986 + */
126.987 + public final static Field HOUR0 = new
126.988 + Field("hour", Calendar.HOUR);
126.989 +
126.990 + /**
126.991 + * Constant identifying the time zone field.
126.992 + */
126.993 + public final static Field TIME_ZONE = new Field("time zone", -1);
126.994 + }
126.995 +
126.996 + /**
126.997 + * Obtains a DateFormat instance from a DateFormatProvider
126.998 + * implementation.
126.999 + private static class DateFormatGetter
126.1000 + implements LocaleServiceProviderPool.LocalizedObjectGetter<DateFormatProvider, DateFormat> {
126.1001 + private static final DateFormatGetter INSTANCE = new DateFormatGetter();
126.1002 +
126.1003 + public DateFormat getObject(DateFormatProvider dateFormatProvider,
126.1004 + Locale locale,
126.1005 + String key,
126.1006 + Object... params) {
126.1007 + assert params.length == 3;
126.1008 +
126.1009 + int timeStyle = (Integer)params[0];
126.1010 + int dateStyle = (Integer)params[1];
126.1011 + int flags = (Integer)params[2];
126.1012 +
126.1013 + switch (flags) {
126.1014 + case 1:
126.1015 + return dateFormatProvider.getTimeInstance(timeStyle, locale);
126.1016 + case 2:
126.1017 + return dateFormatProvider.getDateInstance(dateStyle, locale);
126.1018 + case 3:
126.1019 + return dateFormatProvider.getDateTimeInstance(dateStyle, timeStyle, locale);
126.1020 + default:
126.1021 + assert false : "should not happen";
126.1022 + }
126.1023 +
126.1024 + return null;
126.1025 + }
126.1026 + }
126.1027 + */
126.1028 +}
127.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
127.2 +++ b/rt/emul/compact/src/main/java/java/text/DateFormatSymbols.java Tue Feb 11 13:31:42 2014 +0100
127.3 @@ -0,0 +1,786 @@
127.4 +/*
127.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
127.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
127.7 + *
127.8 + * This code is free software; you can redistribute it and/or modify it
127.9 + * under the terms of the GNU General Public License version 2 only, as
127.10 + * published by the Free Software Foundation. Oracle designates this
127.11 + * particular file as subject to the "Classpath" exception as provided
127.12 + * by Oracle in the LICENSE file that accompanied this code.
127.13 + *
127.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
127.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
127.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
127.17 + * version 2 for more details (a copy is included in the LICENSE file that
127.18 + * accompanied this code).
127.19 + *
127.20 + * You should have received a copy of the GNU General Public License version
127.21 + * 2 along with this work; if not, write to the Free Software Foundation,
127.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
127.23 + *
127.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
127.25 + * or visit www.oracle.com if you need additional information or have any
127.26 + * questions.
127.27 + */
127.28 +
127.29 +/*
127.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
127.31 + * (C) Copyright IBM Corp. 1996 - All Rights Reserved
127.32 + *
127.33 + * The original version of this source code and documentation is copyrighted
127.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
127.35 + * materials are provided under terms of a License Agreement between Taligent
127.36 + * and Sun. This technology is protected by multiple US and International
127.37 + * patents. This notice and attribution to Taligent may not be removed.
127.38 + * Taligent is a registered trademark of Taligent, Inc.
127.39 + *
127.40 + */
127.41 +
127.42 +package java.text;
127.43 +
127.44 +import java.io.IOException;
127.45 +import java.io.ObjectOutputStream;
127.46 +import java.io.Serializable;
127.47 +import java.lang.ref.SoftReference;
127.48 +import java.util.Arrays;
127.49 +import java.util.Locale;
127.50 +import java.util.ResourceBundle;
127.51 +import java.util.concurrent.ConcurrentHashMap;
127.52 +import java.util.concurrent.ConcurrentMap;
127.53 +
127.54 +/**
127.55 + * <code>DateFormatSymbols</code> is a public class for encapsulating
127.56 + * localizable date-time formatting data, such as the names of the
127.57 + * months, the names of the days of the week, and the time zone data.
127.58 + * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
127.59 + * <code>DateFormatSymbols</code> to encapsulate this information.
127.60 + *
127.61 + * <p>
127.62 + * Typically you shouldn't use <code>DateFormatSymbols</code> directly.
127.63 + * Rather, you are encouraged to create a date-time formatter with the
127.64 + * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
127.65 + * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
127.66 + * These methods automatically create a <code>DateFormatSymbols</code> for
127.67 + * the formatter so that you don't have to. After the
127.68 + * formatter is created, you may modify its format pattern using the
127.69 + * <code>setPattern</code> method. For more information about
127.70 + * creating formatters using <code>DateFormat</code>'s factory methods,
127.71 + * see {@link DateFormat}.
127.72 + *
127.73 + * <p>
127.74 + * If you decide to create a date-time formatter with a specific
127.75 + * format pattern for a specific locale, you can do so with:
127.76 + * <blockquote>
127.77 + * <pre>
127.78 + * new SimpleDateFormat(aPattern, DateFormatSymbols.getInstance(aLocale)).
127.79 + * </pre>
127.80 + * </blockquote>
127.81 + *
127.82 + * <p>
127.83 + * <code>DateFormatSymbols</code> objects are cloneable. When you obtain
127.84 + * a <code>DateFormatSymbols</code> object, feel free to modify the
127.85 + * date-time formatting data. For instance, you can replace the localized
127.86 + * date-time format pattern characters with the ones that you feel easy
127.87 + * to remember. Or you can change the representative cities
127.88 + * to your favorite ones.
127.89 + *
127.90 + * <p>
127.91 + * New <code>DateFormatSymbols</code> subclasses may be added to support
127.92 + * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
127.93 +
127.94 + * @see DateFormat
127.95 + * @see SimpleDateFormat
127.96 + * @see java.util.SimpleTimeZone
127.97 + * @author Chen-Lieh Huang
127.98 + */
127.99 +public class DateFormatSymbols implements Serializable, Cloneable {
127.100 +
127.101 + /**
127.102 + * Construct a DateFormatSymbols object by loading format data from
127.103 + * resources for the default locale. This constructor can only
127.104 + * construct instances for the locales supported by the Java
127.105 + * runtime environment, not for those supported by installed
127.106 + * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
127.107 + * implementations. For full locale coverage, use the
127.108 + * {@link #getInstance(Locale) getInstance} method.
127.109 + *
127.110 + * @see #getInstance()
127.111 + * @exception java.util.MissingResourceException
127.112 + * if the resources for the default locale cannot be
127.113 + * found or cannot be loaded.
127.114 + */
127.115 + public DateFormatSymbols()
127.116 + {
127.117 + initializeData(Locale.getDefault(Locale.Category.FORMAT));
127.118 + }
127.119 +
127.120 + /**
127.121 + * Construct a DateFormatSymbols object by loading format data from
127.122 + * resources for the given locale. This constructor can only
127.123 + * construct instances for the locales supported by the Java
127.124 + * runtime environment, not for those supported by installed
127.125 + * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
127.126 + * implementations. For full locale coverage, use the
127.127 + * {@link #getInstance(Locale) getInstance} method.
127.128 + *
127.129 + * @see #getInstance(Locale)
127.130 + * @exception java.util.MissingResourceException
127.131 + * if the resources for the specified locale cannot be
127.132 + * found or cannot be loaded.
127.133 + */
127.134 + public DateFormatSymbols(Locale locale)
127.135 + {
127.136 + initializeData(locale);
127.137 + }
127.138 +
127.139 + /**
127.140 + * Era strings. For example: "AD" and "BC". An array of 2 strings,
127.141 + * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
127.142 + * @serial
127.143 + */
127.144 + String eras[] = null;
127.145 +
127.146 + /**
127.147 + * Month strings. For example: "January", "February", etc. An array
127.148 + * of 13 strings (some calendars have 13 months), indexed by
127.149 + * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
127.150 + * @serial
127.151 + */
127.152 + String months[] = null;
127.153 +
127.154 + /**
127.155 + * Short month strings. For example: "Jan", "Feb", etc. An array of
127.156 + * 13 strings (some calendars have 13 months), indexed by
127.157 + * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
127.158 +
127.159 + * @serial
127.160 + */
127.161 + String shortMonths[] = null;
127.162 +
127.163 + /**
127.164 + * Weekday strings. For example: "Sunday", "Monday", etc. An array
127.165 + * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
127.166 + * <code>Calendar.MONDAY</code>, etc.
127.167 + * The element <code>weekdays[0]</code> is ignored.
127.168 + * @serial
127.169 + */
127.170 + String weekdays[] = null;
127.171 +
127.172 + /**
127.173 + * Short weekday strings. For example: "Sun", "Mon", etc. An array
127.174 + * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
127.175 + * <code>Calendar.MONDAY</code>, etc.
127.176 + * The element <code>shortWeekdays[0]</code> is ignored.
127.177 + * @serial
127.178 + */
127.179 + String shortWeekdays[] = null;
127.180 +
127.181 + /**
127.182 + * AM and PM strings. For example: "AM" and "PM". An array of
127.183 + * 2 strings, indexed by <code>Calendar.AM</code> and
127.184 + * <code>Calendar.PM</code>.
127.185 + * @serial
127.186 + */
127.187 + String ampms[] = null;
127.188 +
127.189 + /**
127.190 + * Localized names of time zones in this locale. This is a
127.191 + * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
127.192 + * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
127.193 + * entry containing the localized names for a single <code>TimeZone</code>.
127.194 + * Each such row contains (with <code>i</code> ranging from
127.195 + * 0..<em>n</em>-1):
127.196 + * <ul>
127.197 + * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
127.198 + * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
127.199 + * time</li>
127.200 + * <li><code>zoneStrings[i][2]</code> - short name of zone in
127.201 + * standard time</li>
127.202 + * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
127.203 + * saving time</li>
127.204 + * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
127.205 + * saving time</li>
127.206 + * </ul>
127.207 + * The zone ID is <em>not</em> localized; it's one of the valid IDs of
127.208 + * the {@link java.util.TimeZone TimeZone} class that are not
127.209 + * <a href="../java/util/TimeZone.html#CustomID">custom IDs</a>.
127.210 + * All other entries are localized names.
127.211 + * @see java.util.TimeZone
127.212 + * @serial
127.213 + */
127.214 + String zoneStrings[][] = null;
127.215 +
127.216 + /**
127.217 + * Indicates that zoneStrings is set externally with setZoneStrings() method.
127.218 + */
127.219 + transient boolean isZoneStringsSet = false;
127.220 +
127.221 + /**
127.222 + * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
127.223 + * All locales use the same these unlocalized pattern characters.
127.224 + */
127.225 + static final String patternChars = "GyMdkHmsSEDFwWahKzZYuX";
127.226 +
127.227 + static final int PATTERN_ERA = 0; // G
127.228 + static final int PATTERN_YEAR = 1; // y
127.229 + static final int PATTERN_MONTH = 2; // M
127.230 + static final int PATTERN_DAY_OF_MONTH = 3; // d
127.231 + static final int PATTERN_HOUR_OF_DAY1 = 4; // k
127.232 + static final int PATTERN_HOUR_OF_DAY0 = 5; // H
127.233 + static final int PATTERN_MINUTE = 6; // m
127.234 + static final int PATTERN_SECOND = 7; // s
127.235 + static final int PATTERN_MILLISECOND = 8; // S
127.236 + static final int PATTERN_DAY_OF_WEEK = 9; // E
127.237 + static final int PATTERN_DAY_OF_YEAR = 10; // D
127.238 + static final int PATTERN_DAY_OF_WEEK_IN_MONTH = 11; // F
127.239 + static final int PATTERN_WEEK_OF_YEAR = 12; // w
127.240 + static final int PATTERN_WEEK_OF_MONTH = 13; // W
127.241 + static final int PATTERN_AM_PM = 14; // a
127.242 + static final int PATTERN_HOUR1 = 15; // h
127.243 + static final int PATTERN_HOUR0 = 16; // K
127.244 + static final int PATTERN_ZONE_NAME = 17; // z
127.245 + static final int PATTERN_ZONE_VALUE = 18; // Z
127.246 + static final int PATTERN_WEEK_YEAR = 19; // Y
127.247 + static final int PATTERN_ISO_DAY_OF_WEEK = 20; // u
127.248 + static final int PATTERN_ISO_ZONE = 21; // X
127.249 +
127.250 + /**
127.251 + * Localized date-time pattern characters. For example, a locale may
127.252 + * wish to use 'u' rather than 'y' to represent years in its date format
127.253 + * pattern strings.
127.254 + * This string must be exactly 18 characters long, with the index of
127.255 + * the characters described by <code>DateFormat.ERA_FIELD</code>,
127.256 + * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were
127.257 + * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
127.258 + * @serial
127.259 + */
127.260 + String localPatternChars = null;
127.261 +
127.262 + /**
127.263 + * The locale which is used for initializing this DateFormatSymbols object.
127.264 + *
127.265 + * @since 1.6
127.266 + * @serial
127.267 + */
127.268 + Locale locale = null;
127.269 +
127.270 + /* use serialVersionUID from JDK 1.1.4 for interoperability */
127.271 + static final long serialVersionUID = -5987973545549424702L;
127.272 +
127.273 + /**
127.274 + * Returns an array of all locales for which the
127.275 + * <code>getInstance</code> methods of this class can return
127.276 + * localized instances.
127.277 + * The returned array represents the union of locales supported by the
127.278 + * Java runtime and by installed
127.279 + * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
127.280 + * implementations. It must contain at least a <code>Locale</code>
127.281 + * instance equal to {@link java.util.Locale#US Locale.US}.
127.282 + *
127.283 + * @return An array of locales for which localized
127.284 + * <code>DateFormatSymbols</code> instances are available.
127.285 + * @since 1.6
127.286 + */
127.287 + public static Locale[] getAvailableLocales() {
127.288 + return new Locale[] { Locale.US };
127.289 +// LocaleServiceProviderPool pool=
127.290 +// LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
127.291 +// return pool.getAvailableLocales();
127.292 + }
127.293 +
127.294 + /**
127.295 + * Gets the <code>DateFormatSymbols</code> instance for the default
127.296 + * locale. This method provides access to <code>DateFormatSymbols</code>
127.297 + * instances for locales supported by the Java runtime itself as well
127.298 + * as for those supported by installed
127.299 + * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
127.300 + * implementations.
127.301 + * @return a <code>DateFormatSymbols</code> instance.
127.302 + * @since 1.6
127.303 + */
127.304 + public static final DateFormatSymbols getInstance() {
127.305 + return getInstance(Locale.getDefault(Locale.Category.FORMAT));
127.306 + }
127.307 +
127.308 + /**
127.309 + * Gets the <code>DateFormatSymbols</code> instance for the specified
127.310 + * locale. This method provides access to <code>DateFormatSymbols</code>
127.311 + * instances for locales supported by the Java runtime itself as well
127.312 + * as for those supported by installed
127.313 + * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
127.314 + * implementations.
127.315 + * @param locale the given locale.
127.316 + * @return a <code>DateFormatSymbols</code> instance.
127.317 + * @exception NullPointerException if <code>locale</code> is null
127.318 + * @since 1.6
127.319 + */
127.320 + public static final DateFormatSymbols getInstance(Locale locale) {
127.321 + DateFormatSymbols dfs = getProviderInstance(locale);
127.322 + if (dfs != null) {
127.323 + return dfs;
127.324 + }
127.325 + return (DateFormatSymbols) getCachedInstance(locale).clone();
127.326 + }
127.327 +
127.328 + /**
127.329 + * Returns a DateFormatSymbols provided by a provider or found in
127.330 + * the cache. Note that this method returns a cached instance,
127.331 + * not its clone. Therefore, the instance should never be given to
127.332 + * an application.
127.333 + */
127.334 + static final DateFormatSymbols getInstanceRef(Locale locale) {
127.335 + DateFormatSymbols dfs = getProviderInstance(locale);
127.336 + if (dfs != null) {
127.337 + return dfs;
127.338 + }
127.339 + return getCachedInstance(locale);
127.340 + }
127.341 +
127.342 + private static DateFormatSymbols getProviderInstance(Locale locale) {
127.343 + DateFormatSymbols providersInstance = null;
127.344 +
127.345 + // Check whether a provider can provide an implementation that's closer
127.346 + // to the requested locale than what the Java runtime itself can provide.
127.347 +// LocaleServiceProviderPool pool =
127.348 +// LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
127.349 +// if (pool.hasProviders()) {
127.350 +// providersInstance = pool.getLocalizedObject(
127.351 +// DateFormatSymbolsGetter.INSTANCE, locale);
127.352 +// }
127.353 + return providersInstance;
127.354 + }
127.355 +
127.356 + /**
127.357 + * Returns a cached DateFormatSymbols if it's found in the
127.358 + * cache. Otherwise, this method returns a newly cached instance
127.359 + * for the given locale.
127.360 + */
127.361 + private static DateFormatSymbols getCachedInstance(Locale locale) {
127.362 + SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
127.363 + DateFormatSymbols dfs = null;
127.364 + if (ref == null || (dfs = ref.get()) == null) {
127.365 + dfs = new DateFormatSymbols(locale);
127.366 + ref = new SoftReference<DateFormatSymbols>(dfs);
127.367 + SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref);
127.368 + if (x != null) {
127.369 + DateFormatSymbols y = x.get();
127.370 + if (y != null) {
127.371 + dfs = y;
127.372 + } else {
127.373 + // Replace the empty SoftReference with ref.
127.374 + cachedInstances.put(locale, ref);
127.375 + }
127.376 + }
127.377 + }
127.378 + return dfs;
127.379 + }
127.380 +
127.381 + /**
127.382 + * Gets era strings. For example: "AD" and "BC".
127.383 + * @return the era strings.
127.384 + */
127.385 + public String[] getEras() {
127.386 + return Arrays.copyOf(eras, eras.length);
127.387 + }
127.388 +
127.389 + /**
127.390 + * Sets era strings. For example: "AD" and "BC".
127.391 + * @param newEras the new era strings.
127.392 + */
127.393 + public void setEras(String[] newEras) {
127.394 + eras = Arrays.copyOf(newEras, newEras.length);
127.395 + }
127.396 +
127.397 + /**
127.398 + * Gets month strings. For example: "January", "February", etc.
127.399 + * @return the month strings.
127.400 + */
127.401 + public String[] getMonths() {
127.402 + return Arrays.copyOf(months, months.length);
127.403 + }
127.404 +
127.405 + /**
127.406 + * Sets month strings. For example: "January", "February", etc.
127.407 + * @param newMonths the new month strings.
127.408 + */
127.409 + public void setMonths(String[] newMonths) {
127.410 + months = Arrays.copyOf(newMonths, newMonths.length);
127.411 + }
127.412 +
127.413 + /**
127.414 + * Gets short month strings. For example: "Jan", "Feb", etc.
127.415 + * @return the short month strings.
127.416 + */
127.417 + public String[] getShortMonths() {
127.418 + return Arrays.copyOf(shortMonths, shortMonths.length);
127.419 + }
127.420 +
127.421 + /**
127.422 + * Sets short month strings. For example: "Jan", "Feb", etc.
127.423 + * @param newShortMonths the new short month strings.
127.424 + */
127.425 + public void setShortMonths(String[] newShortMonths) {
127.426 + shortMonths = Arrays.copyOf(newShortMonths, newShortMonths.length);
127.427 + }
127.428 +
127.429 + /**
127.430 + * Gets weekday strings. For example: "Sunday", "Monday", etc.
127.431 + * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
127.432 + * <code>Calendar.MONDAY</code>, etc. to index the result array.
127.433 + */
127.434 + public String[] getWeekdays() {
127.435 + return Arrays.copyOf(weekdays, weekdays.length);
127.436 + }
127.437 +
127.438 + /**
127.439 + * Sets weekday strings. For example: "Sunday", "Monday", etc.
127.440 + * @param newWeekdays the new weekday strings. The array should
127.441 + * be indexed by <code>Calendar.SUNDAY</code>,
127.442 + * <code>Calendar.MONDAY</code>, etc.
127.443 + */
127.444 + public void setWeekdays(String[] newWeekdays) {
127.445 + weekdays = Arrays.copyOf(newWeekdays, newWeekdays.length);
127.446 + }
127.447 +
127.448 + /**
127.449 + * Gets short weekday strings. For example: "Sun", "Mon", etc.
127.450 + * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
127.451 + * <code>Calendar.MONDAY</code>, etc. to index the result array.
127.452 + */
127.453 + public String[] getShortWeekdays() {
127.454 + return Arrays.copyOf(shortWeekdays, shortWeekdays.length);
127.455 + }
127.456 +
127.457 + /**
127.458 + * Sets short weekday strings. For example: "Sun", "Mon", etc.
127.459 + * @param newShortWeekdays the new short weekday strings. The array should
127.460 + * be indexed by <code>Calendar.SUNDAY</code>,
127.461 + * <code>Calendar.MONDAY</code>, etc.
127.462 + */
127.463 + public void setShortWeekdays(String[] newShortWeekdays) {
127.464 + shortWeekdays = Arrays.copyOf(newShortWeekdays, newShortWeekdays.length);
127.465 + }
127.466 +
127.467 + /**
127.468 + * Gets ampm strings. For example: "AM" and "PM".
127.469 + * @return the ampm strings.
127.470 + */
127.471 + public String[] getAmPmStrings() {
127.472 + return Arrays.copyOf(ampms, ampms.length);
127.473 + }
127.474 +
127.475 + /**
127.476 + * Sets ampm strings. For example: "AM" and "PM".
127.477 + * @param newAmpms the new ampm strings.
127.478 + */
127.479 + public void setAmPmStrings(String[] newAmpms) {
127.480 + ampms = Arrays.copyOf(newAmpms, newAmpms.length);
127.481 + }
127.482 +
127.483 + /**
127.484 + * Gets time zone strings. Use of this method is discouraged; use
127.485 + * {@link java.util.TimeZone#getDisplayName() TimeZone.getDisplayName()}
127.486 + * instead.
127.487 + * <p>
127.488 + * The value returned is a
127.489 + * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
127.490 + * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
127.491 + * entry containing the localized names for a single <code>TimeZone</code>.
127.492 + * Each such row contains (with <code>i</code> ranging from
127.493 + * 0..<em>n</em>-1):
127.494 + * <ul>
127.495 + * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
127.496 + * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
127.497 + * time</li>
127.498 + * <li><code>zoneStrings[i][2]</code> - short name of zone in
127.499 + * standard time</li>
127.500 + * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
127.501 + * saving time</li>
127.502 + * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
127.503 + * saving time</li>
127.504 + * </ul>
127.505 + * The zone ID is <em>not</em> localized; it's one of the valid IDs of
127.506 + * the {@link java.util.TimeZone TimeZone} class that are not
127.507 + * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
127.508 + * All other entries are localized names. If a zone does not implement
127.509 + * daylight saving time, the daylight saving time names should not be used.
127.510 + * <p>
127.511 + * If {@link #setZoneStrings(String[][]) setZoneStrings} has been called
127.512 + * on this <code>DateFormatSymbols</code> instance, then the strings
127.513 + * provided by that call are returned. Otherwise, the returned array
127.514 + * contains names provided by the Java runtime and by installed
127.515 + * {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider}
127.516 + * implementations.
127.517 + *
127.518 + * @return the time zone strings.
127.519 + * @see #setZoneStrings(String[][])
127.520 + */
127.521 + public String[][] getZoneStrings() {
127.522 + return getZoneStringsImpl(true);
127.523 + }
127.524 +
127.525 + /**
127.526 + * Sets time zone strings. The argument must be a
127.527 + * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
127.528 + * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
127.529 + * entry containing the localized names for a single <code>TimeZone</code>.
127.530 + * Each such row contains (with <code>i</code> ranging from
127.531 + * 0..<em>n</em>-1):
127.532 + * <ul>
127.533 + * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
127.534 + * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
127.535 + * time</li>
127.536 + * <li><code>zoneStrings[i][2]</code> - short name of zone in
127.537 + * standard time</li>
127.538 + * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
127.539 + * saving time</li>
127.540 + * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
127.541 + * saving time</li>
127.542 + * </ul>
127.543 + * The zone ID is <em>not</em> localized; it's one of the valid IDs of
127.544 + * the {@link java.util.TimeZone TimeZone} class that are not
127.545 + * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
127.546 + * All other entries are localized names.
127.547 + *
127.548 + * @param newZoneStrings the new time zone strings.
127.549 + * @exception IllegalArgumentException if the length of any row in
127.550 + * <code>newZoneStrings</code> is less than 5
127.551 + * @exception NullPointerException if <code>newZoneStrings</code> is null
127.552 + * @see #getZoneStrings()
127.553 + */
127.554 + public void setZoneStrings(String[][] newZoneStrings) {
127.555 + String[][] aCopy = new String[newZoneStrings.length][];
127.556 + for (int i = 0; i < newZoneStrings.length; ++i) {
127.557 + int len = newZoneStrings[i].length;
127.558 + if (len < 5) {
127.559 + throw new IllegalArgumentException();
127.560 + }
127.561 + aCopy[i] = Arrays.copyOf(newZoneStrings[i], len);
127.562 + }
127.563 + zoneStrings = aCopy;
127.564 + isZoneStringsSet = true;
127.565 + }
127.566 +
127.567 + /**
127.568 + * Gets localized date-time pattern characters. For example: 'u', 't', etc.
127.569 + * @return the localized date-time pattern characters.
127.570 + */
127.571 + public String getLocalPatternChars() {
127.572 + return localPatternChars;
127.573 + }
127.574 +
127.575 + /**
127.576 + * Sets localized date-time pattern characters. For example: 'u', 't', etc.
127.577 + * @param newLocalPatternChars the new localized date-time
127.578 + * pattern characters.
127.579 + */
127.580 + public void setLocalPatternChars(String newLocalPatternChars) {
127.581 + // Call toString() to throw an NPE in case the argument is null
127.582 + localPatternChars = newLocalPatternChars.toString();
127.583 + }
127.584 +
127.585 + /**
127.586 + * Overrides Cloneable
127.587 + */
127.588 + public Object clone()
127.589 + {
127.590 + try
127.591 + {
127.592 + DateFormatSymbols other = (DateFormatSymbols)super.clone();
127.593 + copyMembers(this, other);
127.594 + return other;
127.595 + } catch (CloneNotSupportedException e) {
127.596 + throw new InternalError();
127.597 + }
127.598 + }
127.599 +
127.600 + /**
127.601 + * Override hashCode.
127.602 + * Generates a hash code for the DateFormatSymbols object.
127.603 + */
127.604 + public int hashCode() {
127.605 + int hashcode = 0;
127.606 + String[][] zoneStrings = getZoneStringsWrapper();
127.607 + for (int index = 0; index < zoneStrings[0].length; ++index)
127.608 + hashcode ^= zoneStrings[0][index].hashCode();
127.609 + return hashcode;
127.610 + }
127.611 +
127.612 + /**
127.613 + * Override equals
127.614 + */
127.615 + public boolean equals(Object obj)
127.616 + {
127.617 + if (this == obj) return true;
127.618 + if (obj == null || getClass() != obj.getClass()) return false;
127.619 + DateFormatSymbols that = (DateFormatSymbols) obj;
127.620 + return (Arrays.equals(eras, that.eras)
127.621 + && Arrays.equals(months, that.months)
127.622 + && Arrays.equals(shortMonths, that.shortMonths)
127.623 + && Arrays.equals(weekdays, that.weekdays)
127.624 + && Arrays.equals(shortWeekdays, that.shortWeekdays)
127.625 + && Arrays.equals(ampms, that.ampms)
127.626 + && Arrays.deepEquals(getZoneStringsWrapper(), that.getZoneStringsWrapper())
127.627 + && ((localPatternChars != null
127.628 + && localPatternChars.equals(that.localPatternChars))
127.629 + || (localPatternChars == null
127.630 + && that.localPatternChars == null)));
127.631 + }
127.632 +
127.633 + // =======================privates===============================
127.634 +
127.635 + /**
127.636 + * Useful constant for defining time zone offsets.
127.637 + */
127.638 + static final int millisPerHour = 60*60*1000;
127.639 +
127.640 + /**
127.641 + * Cache to hold DateFormatSymbols instances per Locale.
127.642 + */
127.643 + private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
127.644 + = new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3);
127.645 +
127.646 + private void initializeData(Locale desiredLocale) {
127.647 + locale = desiredLocale;
127.648 +
127.649 + // Copy values of a cached instance if any.
127.650 + SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
127.651 + DateFormatSymbols dfs;
127.652 + if (ref != null && (dfs = ref.get()) != null) {
127.653 + copyMembers(dfs, this);
127.654 + return;
127.655 + }
127.656 +
127.657 + // Initialize the fields from the ResourceBundle for locale.
127.658 +// ResourceBundle resource = LocaleData.getDateFormatData(locale);
127.659 +//
127.660 +// eras = resource.getStringArray("Eras");
127.661 +// months = resource.getStringArray("MonthNames");
127.662 +// shortMonths = resource.getStringArray("MonthAbbreviations");
127.663 +// ampms = resource.getStringArray("AmPmMarkers");
127.664 +// localPatternChars = resource.getString("DateTimePatternChars");
127.665 +//
127.666 +// // Day of week names are stored in a 1-based array.
127.667 +// weekdays = toOneBasedArray(resource.getStringArray("DayNames"));
127.668 +// shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations"));
127.669 + }
127.670 +
127.671 + private static String[] toOneBasedArray(String[] src) {
127.672 + int len = src.length;
127.673 + String[] dst = new String[len + 1];
127.674 + dst[0] = "";
127.675 + for (int i = 0; i < len; i++) {
127.676 + dst[i + 1] = src[i];
127.677 + }
127.678 + return dst;
127.679 + }
127.680 +
127.681 + /**
127.682 + * Package private: used by SimpleDateFormat
127.683 + * Gets the index for the given time zone ID to obtain the time zone
127.684 + * strings for formatting. The time zone ID is just for programmatic
127.685 + * lookup. NOT LOCALIZED!!!
127.686 + * @param ID the given time zone ID.
127.687 + * @return the index of the given time zone ID. Returns -1 if
127.688 + * the given time zone ID can't be located in the DateFormatSymbols object.
127.689 + * @see java.util.SimpleTimeZone
127.690 + */
127.691 + final int getZoneIndex(String ID)
127.692 + {
127.693 + String[][] zoneStrings = getZoneStringsWrapper();
127.694 + for (int index=0; index<zoneStrings.length; index++)
127.695 + {
127.696 + if (ID.equals(zoneStrings[index][0])) return index;
127.697 + }
127.698 +
127.699 + return -1;
127.700 + }
127.701 +
127.702 + /**
127.703 + * Wrapper method to the getZoneStrings(), which is called from inside
127.704 + * the java.text package and not to mutate the returned arrays, so that
127.705 + * it does not need to create a defensive copy.
127.706 + */
127.707 + final String[][] getZoneStringsWrapper() {
127.708 + if (isSubclassObject()) {
127.709 + return getZoneStrings();
127.710 + } else {
127.711 + return getZoneStringsImpl(false);
127.712 + }
127.713 + }
127.714 +
127.715 + private final String[][] getZoneStringsImpl(boolean needsCopy) {
127.716 + if (zoneStrings == null) {
127.717 +// zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
127.718 + }
127.719 +
127.720 + if (!needsCopy) {
127.721 + return zoneStrings;
127.722 + }
127.723 +
127.724 + int len = zoneStrings.length;
127.725 + String[][] aCopy = new String[len][];
127.726 + for (int i = 0; i < len; i++) {
127.727 + aCopy[i] = Arrays.copyOf(zoneStrings[i], zoneStrings[i].length);
127.728 + }
127.729 + return aCopy;
127.730 + }
127.731 +
127.732 + private final boolean isSubclassObject() {
127.733 + return !getClass().getName().equals("java.text.DateFormatSymbols");
127.734 + }
127.735 +
127.736 + /**
127.737 + * Clones all the data members from the source DateFormatSymbols to
127.738 + * the target DateFormatSymbols. This is only for subclasses.
127.739 + * @param src the source DateFormatSymbols.
127.740 + * @param dst the target DateFormatSymbols.
127.741 + */
127.742 + private final void copyMembers(DateFormatSymbols src, DateFormatSymbols dst)
127.743 + {
127.744 + dst.eras = Arrays.copyOf(src.eras, src.eras.length);
127.745 + dst.months = Arrays.copyOf(src.months, src.months.length);
127.746 + dst.shortMonths = Arrays.copyOf(src.shortMonths, src.shortMonths.length);
127.747 + dst.weekdays = Arrays.copyOf(src.weekdays, src.weekdays.length);
127.748 + dst.shortWeekdays = Arrays.copyOf(src.shortWeekdays, src.shortWeekdays.length);
127.749 + dst.ampms = Arrays.copyOf(src.ampms, src.ampms.length);
127.750 + if (src.zoneStrings != null) {
127.751 + dst.zoneStrings = src.getZoneStringsImpl(true);
127.752 + } else {
127.753 + dst.zoneStrings = null;
127.754 + }
127.755 + dst.localPatternChars = src.localPatternChars;
127.756 + }
127.757 +
127.758 + /**
127.759 + * Write out the default serializable data, after ensuring the
127.760 + * <code>zoneStrings</code> field is initialized in order to make
127.761 + * sure the backward compatibility.
127.762 + *
127.763 + * @since 1.6
127.764 + private void writeObject(ObjectOutputStream stream) throws IOException {
127.765 + if (zoneStrings == null) {
127.766 + zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
127.767 + }
127.768 + stream.defaultWriteObject();
127.769 + }
127.770 +
127.771 + /**
127.772 + * Obtains a DateFormatSymbols instance from a DateFormatSymbolsProvider
127.773 + * implementation.
127.774 + private static class DateFormatSymbolsGetter
127.775 + implements LocaleServiceProviderPool.LocalizedObjectGetter<DateFormatSymbolsProvider,
127.776 + DateFormatSymbols> {
127.777 + private static final DateFormatSymbolsGetter INSTANCE =
127.778 + new DateFormatSymbolsGetter();
127.779 +
127.780 + public DateFormatSymbols getObject(DateFormatSymbolsProvider dateFormatSymbolsProvider,
127.781 + Locale locale,
127.782 + String key,
127.783 + Object... params) {
127.784 + assert params.length == 0;
127.785 + return dateFormatSymbolsProvider.getInstance(locale);
127.786 + }
127.787 + }
127.788 + */
127.789 +}
128.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
128.2 +++ b/rt/emul/compact/src/main/java/java/text/DecimalFormat.java Tue Feb 11 13:31:42 2014 +0100
128.3 @@ -0,0 +1,3277 @@
128.4 +/*
128.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
128.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
128.7 + *
128.8 + * This code is free software; you can redistribute it and/or modify it
128.9 + * under the terms of the GNU General Public License version 2 only, as
128.10 + * published by the Free Software Foundation. Oracle designates this
128.11 + * particular file as subject to the "Classpath" exception as provided
128.12 + * by Oracle in the LICENSE file that accompanied this code.
128.13 + *
128.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
128.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
128.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
128.17 + * version 2 for more details (a copy is included in the LICENSE file that
128.18 + * accompanied this code).
128.19 + *
128.20 + * You should have received a copy of the GNU General Public License version
128.21 + * 2 along with this work; if not, write to the Free Software Foundation,
128.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
128.23 + *
128.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
128.25 + * or visit www.oracle.com if you need additional information or have any
128.26 + * questions.
128.27 + */
128.28 +
128.29 +/*
128.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
128.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
128.32 + *
128.33 + * The original version of this source code and documentation is copyrighted
128.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
128.35 + * materials are provided under terms of a License Agreement between Taligent
128.36 + * and Sun. This technology is protected by multiple US and International
128.37 + * patents. This notice and attribution to Taligent may not be removed.
128.38 + * Taligent is a registered trademark of Taligent, Inc.
128.39 + *
128.40 + */
128.41 +
128.42 +package java.text;
128.43 +
128.44 +import java.io.InvalidObjectException;
128.45 +import java.io.IOException;
128.46 +import java.io.ObjectInputStream;
128.47 +import java.math.BigDecimal;
128.48 +import java.math.BigInteger;
128.49 +import java.math.RoundingMode;
128.50 +import java.util.ArrayList;
128.51 +import java.util.Currency;
128.52 +import java.util.Locale;
128.53 +import java.util.ResourceBundle;
128.54 +import java.util.concurrent.ConcurrentHashMap;
128.55 +import java.util.concurrent.ConcurrentMap;
128.56 +import java.util.concurrent.atomic.AtomicInteger;
128.57 +import java.util.concurrent.atomic.AtomicLong;
128.58 +
128.59 +/**
128.60 + * <code>DecimalFormat</code> is a concrete subclass of
128.61 + * <code>NumberFormat</code> that formats decimal numbers. It has a variety of
128.62 + * features designed to make it possible to parse and format numbers in any
128.63 + * locale, including support for Western, Arabic, and Indic digits. It also
128.64 + * supports different kinds of numbers, including integers (123), fixed-point
128.65 + * numbers (123.4), scientific notation (1.23E4), percentages (12%), and
128.66 + * currency amounts ($123). All of these can be localized.
128.67 + *
128.68 + * <p>To obtain a <code>NumberFormat</code> for a specific locale, including the
128.69 + * default locale, call one of <code>NumberFormat</code>'s factory methods, such
128.70 + * as <code>getInstance()</code>. In general, do not call the
128.71 + * <code>DecimalFormat</code> constructors directly, since the
128.72 + * <code>NumberFormat</code> factory methods may return subclasses other than
128.73 + * <code>DecimalFormat</code>. If you need to customize the format object, do
128.74 + * something like this:
128.75 + *
128.76 + * <blockquote><pre>
128.77 + * NumberFormat f = NumberFormat.getInstance(loc);
128.78 + * if (f instanceof DecimalFormat) {
128.79 + * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true);
128.80 + * }
128.81 + * </pre></blockquote>
128.82 + *
128.83 + * <p>A <code>DecimalFormat</code> comprises a <em>pattern</em> and a set of
128.84 + * <em>symbols</em>. The pattern may be set directly using
128.85 + * <code>applyPattern()</code>, or indirectly using the API methods. The
128.86 + * symbols are stored in a <code>DecimalFormatSymbols</code> object. When using
128.87 + * the <code>NumberFormat</code> factory methods, the pattern and symbols are
128.88 + * read from localized <code>ResourceBundle</code>s.
128.89 + *
128.90 + * <h4>Patterns</h4>
128.91 + *
128.92 + * <code>DecimalFormat</code> patterns have the following syntax:
128.93 + * <blockquote><pre>
128.94 + * <i>Pattern:</i>
128.95 + * <i>PositivePattern</i>
128.96 + * <i>PositivePattern</i> ; <i>NegativePattern</i>
128.97 + * <i>PositivePattern:</i>
128.98 + * <i>Prefix<sub>opt</sub></i> <i>Number</i> <i>Suffix<sub>opt</sub></i>
128.99 + * <i>NegativePattern:</i>
128.100 + * <i>Prefix<sub>opt</sub></i> <i>Number</i> <i>Suffix<sub>opt</sub></i>
128.101 + * <i>Prefix:</i>
128.102 + * any Unicode characters except \uFFFE, \uFFFF, and special characters
128.103 + * <i>Suffix:</i>
128.104 + * any Unicode characters except \uFFFE, \uFFFF, and special characters
128.105 + * <i>Number:</i>
128.106 + * <i>Integer</i> <i>Exponent<sub>opt</sub></i>
128.107 + * <i>Integer</i> . <i>Fraction</i> <i>Exponent<sub>opt</sub></i>
128.108 + * <i>Integer:</i>
128.109 + * <i>MinimumInteger</i>
128.110 + * #
128.111 + * # <i>Integer</i>
128.112 + * # , <i>Integer</i>
128.113 + * <i>MinimumInteger:</i>
128.114 + * 0
128.115 + * 0 <i>MinimumInteger</i>
128.116 + * 0 , <i>MinimumInteger</i>
128.117 + * <i>Fraction:</i>
128.118 + * <i>MinimumFraction<sub>opt</sub></i> <i>OptionalFraction<sub>opt</sub></i>
128.119 + * <i>MinimumFraction:</i>
128.120 + * 0 <i>MinimumFraction<sub>opt</sub></i>
128.121 + * <i>OptionalFraction:</i>
128.122 + * # <i>OptionalFraction<sub>opt</sub></i>
128.123 + * <i>Exponent:</i>
128.124 + * E <i>MinimumExponent</i>
128.125 + * <i>MinimumExponent:</i>
128.126 + * 0 <i>MinimumExponent<sub>opt</sub></i>
128.127 + * </pre></blockquote>
128.128 + *
128.129 + * <p>A <code>DecimalFormat</code> pattern contains a positive and negative
128.130 + * subpattern, for example, <code>"#,##0.00;(#,##0.00)"</code>. Each
128.131 + * subpattern has a prefix, numeric part, and suffix. The negative subpattern
128.132 + * is optional; if absent, then the positive subpattern prefixed with the
128.133 + * localized minus sign (<code>'-'</code> in most locales) is used as the
128.134 + * negative subpattern. That is, <code>"0.00"</code> alone is equivalent to
128.135 + * <code>"0.00;-0.00"</code>. If there is an explicit negative subpattern, it
128.136 + * serves only to specify the negative prefix and suffix; the number of digits,
128.137 + * minimal digits, and other characteristics are all the same as the positive
128.138 + * pattern. That means that <code>"#,##0.0#;(#)"</code> produces precisely
128.139 + * the same behavior as <code>"#,##0.0#;(#,##0.0#)"</code>.
128.140 + *
128.141 + * <p>The prefixes, suffixes, and various symbols used for infinity, digits,
128.142 + * thousands separators, decimal separators, etc. may be set to arbitrary
128.143 + * values, and they will appear properly during formatting. However, care must
128.144 + * be taken that the symbols and strings do not conflict, or parsing will be
128.145 + * unreliable. For example, either the positive and negative prefixes or the
128.146 + * suffixes must be distinct for <code>DecimalFormat.parse()</code> to be able
128.147 + * to distinguish positive from negative values. (If they are identical, then
128.148 + * <code>DecimalFormat</code> will behave as if no negative subpattern was
128.149 + * specified.) Another example is that the decimal separator and thousands
128.150 + * separator should be distinct characters, or parsing will be impossible.
128.151 + *
128.152 + * <p>The grouping separator is commonly used for thousands, but in some
128.153 + * countries it separates ten-thousands. The grouping size is a constant number
128.154 + * of digits between the grouping characters, such as 3 for 100,000,000 or 4 for
128.155 + * 1,0000,0000. If you supply a pattern with multiple grouping characters, the
128.156 + * interval between the last one and the end of the integer is the one that is
128.157 + * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> ==
128.158 + * <code>"##,####,####"</code>.
128.159 + *
128.160 + * <h4>Special Pattern Characters</h4>
128.161 + *
128.162 + * <p>Many characters in a pattern are taken literally; they are matched during
128.163 + * parsing and output unchanged during formatting. Special characters, on the
128.164 + * other hand, stand for other characters, strings, or classes of characters.
128.165 + * They must be quoted, unless noted otherwise, if they are to appear in the
128.166 + * prefix or suffix as literals.
128.167 + *
128.168 + * <p>The characters listed here are used in non-localized patterns. Localized
128.169 + * patterns use the corresponding characters taken from this formatter's
128.170 + * <code>DecimalFormatSymbols</code> object instead, and these characters lose
128.171 + * their special status. Two exceptions are the currency sign and quote, which
128.172 + * are not localized.
128.173 + *
128.174 + * <blockquote>
128.175 + * <table border=0 cellspacing=3 cellpadding=0 summary="Chart showing symbol,
128.176 + * location, localized, and meaning.">
128.177 + * <tr bgcolor="#ccccff">
128.178 + * <th align=left>Symbol
128.179 + * <th align=left>Location
128.180 + * <th align=left>Localized?
128.181 + * <th align=left>Meaning
128.182 + * <tr valign=top>
128.183 + * <td><code>0</code>
128.184 + * <td>Number
128.185 + * <td>Yes
128.186 + * <td>Digit
128.187 + * <tr valign=top bgcolor="#eeeeff">
128.188 + * <td><code>#</code>
128.189 + * <td>Number
128.190 + * <td>Yes
128.191 + * <td>Digit, zero shows as absent
128.192 + * <tr valign=top>
128.193 + * <td><code>.</code>
128.194 + * <td>Number
128.195 + * <td>Yes
128.196 + * <td>Decimal separator or monetary decimal separator
128.197 + * <tr valign=top bgcolor="#eeeeff">
128.198 + * <td><code>-</code>
128.199 + * <td>Number
128.200 + * <td>Yes
128.201 + * <td>Minus sign
128.202 + * <tr valign=top>
128.203 + * <td><code>,</code>
128.204 + * <td>Number
128.205 + * <td>Yes
128.206 + * <td>Grouping separator
128.207 + * <tr valign=top bgcolor="#eeeeff">
128.208 + * <td><code>E</code>
128.209 + * <td>Number
128.210 + * <td>Yes
128.211 + * <td>Separates mantissa and exponent in scientific notation.
128.212 + * <em>Need not be quoted in prefix or suffix.</em>
128.213 + * <tr valign=top>
128.214 + * <td><code>;</code>
128.215 + * <td>Subpattern boundary
128.216 + * <td>Yes
128.217 + * <td>Separates positive and negative subpatterns
128.218 + * <tr valign=top bgcolor="#eeeeff">
128.219 + * <td><code>%</code>
128.220 + * <td>Prefix or suffix
128.221 + * <td>Yes
128.222 + * <td>Multiply by 100 and show as percentage
128.223 + * <tr valign=top>
128.224 + * <td><code>\u2030</code>
128.225 + * <td>Prefix or suffix
128.226 + * <td>Yes
128.227 + * <td>Multiply by 1000 and show as per mille value
128.228 + * <tr valign=top bgcolor="#eeeeff">
128.229 + * <td><code>¤</code> (<code>\u00A4</code>)
128.230 + * <td>Prefix or suffix
128.231 + * <td>No
128.232 + * <td>Currency sign, replaced by currency symbol. If
128.233 + * doubled, replaced by international currency symbol.
128.234 + * If present in a pattern, the monetary decimal separator
128.235 + * is used instead of the decimal separator.
128.236 + * <tr valign=top>
128.237 + * <td><code>'</code>
128.238 + * <td>Prefix or suffix
128.239 + * <td>No
128.240 + * <td>Used to quote special characters in a prefix or suffix,
128.241 + * for example, <code>"'#'#"</code> formats 123 to
128.242 + * <code>"#123"</code>. To create a single quote
128.243 + * itself, use two in a row: <code>"# o''clock"</code>.
128.244 + * </table>
128.245 + * </blockquote>
128.246 + *
128.247 + * <h4>Scientific Notation</h4>
128.248 + *
128.249 + * <p>Numbers in scientific notation are expressed as the product of a mantissa
128.250 + * and a power of ten, for example, 1234 can be expressed as 1.234 x 10^3. The
128.251 + * mantissa is often in the range 1.0 <= x < 10.0, but it need not be.
128.252 + * <code>DecimalFormat</code> can be instructed to format and parse scientific
128.253 + * notation <em>only via a pattern</em>; there is currently no factory method
128.254 + * that creates a scientific notation format. In a pattern, the exponent
128.255 + * character immediately followed by one or more digit characters indicates
128.256 + * scientific notation. Example: <code>"0.###E0"</code> formats the number
128.257 + * 1234 as <code>"1.234E3"</code>.
128.258 + *
128.259 + * <ul>
128.260 + * <li>The number of digit characters after the exponent character gives the
128.261 + * minimum exponent digit count. There is no maximum. Negative exponents are
128.262 + * formatted using the localized minus sign, <em>not</em> the prefix and suffix
128.263 + * from the pattern. This allows patterns such as <code>"0.###E0 m/s"</code>.
128.264 + *
128.265 + * <li>The minimum and maximum number of integer digits are interpreted
128.266 + * together:
128.267 + *
128.268 + * <ul>
128.269 + * <li>If the maximum number of integer digits is greater than their minimum number
128.270 + * and greater than 1, it forces the exponent to be a multiple of the maximum
128.271 + * number of integer digits, and the minimum number of integer digits to be
128.272 + * interpreted as 1. The most common use of this is to generate
128.273 + * <em>engineering notation</em>, in which the exponent is a multiple of three,
128.274 + * e.g., <code>"##0.#####E0"</code>. Using this pattern, the number 12345
128.275 + * formats to <code>"12.345E3"</code>, and 123456 formats to
128.276 + * <code>"123.456E3"</code>.
128.277 + *
128.278 + * <li>Otherwise, the minimum number of integer digits is achieved by adjusting the
128.279 + * exponent. Example: 0.00123 formatted with <code>"00.###E0"</code> yields
128.280 + * <code>"12.3E-4"</code>.
128.281 + * </ul>
128.282 + *
128.283 + * <li>The number of significant digits in the mantissa is the sum of the
128.284 + * <em>minimum integer</em> and <em>maximum fraction</em> digits, and is
128.285 + * unaffected by the maximum integer digits. For example, 12345 formatted with
128.286 + * <code>"##0.##E0"</code> is <code>"12.3E3"</code>. To show all digits, set
128.287 + * the significant digits count to zero. The number of significant digits
128.288 + * does not affect parsing.
128.289 + *
128.290 + * <li>Exponential patterns may not contain grouping separators.
128.291 + * </ul>
128.292 + *
128.293 + * <h4>Rounding</h4>
128.294 + *
128.295 + * <code>DecimalFormat</code> provides rounding modes defined in
128.296 + * {@link java.math.RoundingMode} for formatting. By default, it uses
128.297 + * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}.
128.298 + *
128.299 + * <h4>Digits</h4>
128.300 + *
128.301 + * For formatting, <code>DecimalFormat</code> uses the ten consecutive
128.302 + * characters starting with the localized zero digit defined in the
128.303 + * <code>DecimalFormatSymbols</code> object as digits. For parsing, these
128.304 + * digits as well as all Unicode decimal digits, as defined by
128.305 + * {@link Character#digit Character.digit}, are recognized.
128.306 + *
128.307 + * <h4>Special Values</h4>
128.308 + *
128.309 + * <p><code>NaN</code> is formatted as a string, which typically has a single character
128.310 + * <code>\uFFFD</code>. This string is determined by the
128.311 + * <code>DecimalFormatSymbols</code> object. This is the only value for which
128.312 + * the prefixes and suffixes are not used.
128.313 + *
128.314 + * <p>Infinity is formatted as a string, which typically has a single character
128.315 + * <code>\u221E</code>, with the positive or negative prefixes and suffixes
128.316 + * applied. The infinity string is determined by the
128.317 + * <code>DecimalFormatSymbols</code> object.
128.318 + *
128.319 + * <p>Negative zero (<code>"-0"</code>) parses to
128.320 + * <ul>
128.321 + * <li><code>BigDecimal(0)</code> if <code>isParseBigDecimal()</code> is
128.322 + * true,
128.323 + * <li><code>Long(0)</code> if <code>isParseBigDecimal()</code> is false
128.324 + * and <code>isParseIntegerOnly()</code> is true,
128.325 + * <li><code>Double(-0.0)</code> if both <code>isParseBigDecimal()</code>
128.326 + * and <code>isParseIntegerOnly()</code> are false.
128.327 + * </ul>
128.328 + *
128.329 + * <h4><a name="synchronization">Synchronization</a></h4>
128.330 + *
128.331 + * <p>
128.332 + * Decimal formats are generally not synchronized.
128.333 + * It is recommended to create separate format instances for each thread.
128.334 + * If multiple threads access a format concurrently, it must be synchronized
128.335 + * externally.
128.336 + *
128.337 + * <h4>Example</h4>
128.338 + *
128.339 + * <blockquote><pre>
128.340 + * <strong>// Print out a number using the localized number, integer, currency,
128.341 + * // and percent format for each locale</strong>
128.342 + * Locale[] locales = NumberFormat.getAvailableLocales();
128.343 + * double myNumber = -1234.56;
128.344 + * NumberFormat form;
128.345 + * for (int j=0; j<4; ++j) {
128.346 + * System.out.println("FORMAT");
128.347 + * for (int i = 0; i < locales.length; ++i) {
128.348 + * if (locales[i].getCountry().length() == 0) {
128.349 + * continue; // Skip language-only locales
128.350 + * }
128.351 + * System.out.print(locales[i].getDisplayName());
128.352 + * switch (j) {
128.353 + * case 0:
128.354 + * form = NumberFormat.getInstance(locales[i]); break;
128.355 + * case 1:
128.356 + * form = NumberFormat.getIntegerInstance(locales[i]); break;
128.357 + * case 2:
128.358 + * form = NumberFormat.getCurrencyInstance(locales[i]); break;
128.359 + * default:
128.360 + * form = NumberFormat.getPercentInstance(locales[i]); break;
128.361 + * }
128.362 + * if (form instanceof DecimalFormat) {
128.363 + * System.out.print(": " + ((DecimalFormat) form).toPattern());
128.364 + * }
128.365 + * System.out.print(" -> " + form.format(myNumber));
128.366 + * try {
128.367 + * System.out.println(" -> " + form.parse(form.format(myNumber)));
128.368 + * } catch (ParseException e) {}
128.369 + * }
128.370 + * }
128.371 + * </pre></blockquote>
128.372 + *
128.373 + * @see <a href="http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html">Java Tutorial</a>
128.374 + * @see NumberFormat
128.375 + * @see DecimalFormatSymbols
128.376 + * @see ParsePosition
128.377 + * @author Mark Davis
128.378 + * @author Alan Liu
128.379 + */
128.380 +public class DecimalFormat extends NumberFormat {
128.381 +
128.382 + /**
128.383 + * Creates a DecimalFormat using the default pattern and symbols
128.384 + * for the default locale. This is a convenient way to obtain a
128.385 + * DecimalFormat when internationalization is not the main concern.
128.386 + * <p>
128.387 + * To obtain standard formats for a given locale, use the factory methods
128.388 + * on NumberFormat such as getNumberInstance. These factories will
128.389 + * return the most appropriate sub-class of NumberFormat for a given
128.390 + * locale.
128.391 + *
128.392 + * @see java.text.NumberFormat#getInstance
128.393 + * @see java.text.NumberFormat#getNumberInstance
128.394 + * @see java.text.NumberFormat#getCurrencyInstance
128.395 + * @see java.text.NumberFormat#getPercentInstance
128.396 + */
128.397 + public DecimalFormat() {
128.398 + Locale def = Locale.getDefault(Locale.Category.FORMAT);
128.399 + // try to get the pattern from the cache
128.400 + String pattern = cachedLocaleData.get(def);
128.401 + if (pattern == null) { /* cache miss */
128.402 + // Get the pattern for the default locale.
128.403 +// ResourceBundle rb = LocaleData.getNumberFormatData(def);
128.404 +// String[] all = rb.getStringArray("NumberPatterns");
128.405 +// pattern = all[0];
128.406 +// /* update cache */
128.407 +// cachedLocaleData.putIfAbsent(def, pattern);
128.408 + }
128.409 +
128.410 + // Always applyPattern after the symbols are set
128.411 + this.symbols = new DecimalFormatSymbols(def);
128.412 + applyPattern(pattern, false);
128.413 + }
128.414 +
128.415 +
128.416 + /**
128.417 + * Creates a DecimalFormat using the given pattern and the symbols
128.418 + * for the default locale. This is a convenient way to obtain a
128.419 + * DecimalFormat when internationalization is not the main concern.
128.420 + * <p>
128.421 + * To obtain standard formats for a given locale, use the factory methods
128.422 + * on NumberFormat such as getNumberInstance. These factories will
128.423 + * return the most appropriate sub-class of NumberFormat for a given
128.424 + * locale.
128.425 + *
128.426 + * @param pattern A non-localized pattern string.
128.427 + * @exception NullPointerException if <code>pattern</code> is null
128.428 + * @exception IllegalArgumentException if the given pattern is invalid.
128.429 + * @see java.text.NumberFormat#getInstance
128.430 + * @see java.text.NumberFormat#getNumberInstance
128.431 + * @see java.text.NumberFormat#getCurrencyInstance
128.432 + * @see java.text.NumberFormat#getPercentInstance
128.433 + */
128.434 + public DecimalFormat(String pattern) {
128.435 + // Always applyPattern after the symbols are set
128.436 + this.symbols = new DecimalFormatSymbols(Locale.getDefault(Locale.Category.FORMAT));
128.437 + applyPattern(pattern, false);
128.438 + }
128.439 +
128.440 +
128.441 + /**
128.442 + * Creates a DecimalFormat using the given pattern and symbols.
128.443 + * Use this constructor when you need to completely customize the
128.444 + * behavior of the format.
128.445 + * <p>
128.446 + * To obtain standard formats for a given
128.447 + * locale, use the factory methods on NumberFormat such as
128.448 + * getInstance or getCurrencyInstance. If you need only minor adjustments
128.449 + * to a standard format, you can modify the format returned by
128.450 + * a NumberFormat factory method.
128.451 + *
128.452 + * @param pattern a non-localized pattern string
128.453 + * @param symbols the set of symbols to be used
128.454 + * @exception NullPointerException if any of the given arguments is null
128.455 + * @exception IllegalArgumentException if the given pattern is invalid
128.456 + * @see java.text.NumberFormat#getInstance
128.457 + * @see java.text.NumberFormat#getNumberInstance
128.458 + * @see java.text.NumberFormat#getCurrencyInstance
128.459 + * @see java.text.NumberFormat#getPercentInstance
128.460 + * @see java.text.DecimalFormatSymbols
128.461 + */
128.462 + public DecimalFormat (String pattern, DecimalFormatSymbols symbols) {
128.463 + // Always applyPattern after the symbols are set
128.464 + this.symbols = (DecimalFormatSymbols)symbols.clone();
128.465 + applyPattern(pattern, false);
128.466 + }
128.467 +
128.468 +
128.469 + // Overrides
128.470 + /**
128.471 + * Formats a number and appends the resulting text to the given string
128.472 + * buffer.
128.473 + * The number can be of any subclass of {@link java.lang.Number}.
128.474 + * <p>
128.475 + * This implementation uses the maximum precision permitted.
128.476 + * @param number the number to format
128.477 + * @param toAppendTo the <code>StringBuffer</code> to which the formatted
128.478 + * text is to be appended
128.479 + * @param pos On input: an alignment field, if desired.
128.480 + * On output: the offsets of the alignment field.
128.481 + * @return the value passed in as <code>toAppendTo</code>
128.482 + * @exception IllegalArgumentException if <code>number</code> is
128.483 + * null or not an instance of <code>Number</code>.
128.484 + * @exception NullPointerException if <code>toAppendTo</code> or
128.485 + * <code>pos</code> is null
128.486 + * @exception ArithmeticException if rounding is needed with rounding
128.487 + * mode being set to RoundingMode.UNNECESSARY
128.488 + * @see java.text.FieldPosition
128.489 + */
128.490 + public final StringBuffer format(Object number,
128.491 + StringBuffer toAppendTo,
128.492 + FieldPosition pos) {
128.493 + if (number instanceof Long || number instanceof Integer ||
128.494 + number instanceof Short || number instanceof Byte ||
128.495 + number instanceof AtomicInteger ||
128.496 + number instanceof AtomicLong ||
128.497 + (number instanceof BigInteger &&
128.498 + ((BigInteger)number).bitLength () < 64)) {
128.499 + return format(((Number)number).longValue(), toAppendTo, pos);
128.500 + } else if (number instanceof BigDecimal) {
128.501 + return format((BigDecimal)number, toAppendTo, pos);
128.502 + } else if (number instanceof BigInteger) {
128.503 + return format((BigInteger)number, toAppendTo, pos);
128.504 + } else if (number instanceof Number) {
128.505 + return format(((Number)number).doubleValue(), toAppendTo, pos);
128.506 + } else {
128.507 + throw new IllegalArgumentException("Cannot format given Object as a Number");
128.508 + }
128.509 + }
128.510 +
128.511 + /**
128.512 + * Formats a double to produce a string.
128.513 + * @param number The double to format
128.514 + * @param result where the text is to be appended
128.515 + * @param fieldPosition On input: an alignment field, if desired.
128.516 + * On output: the offsets of the alignment field.
128.517 + * @exception ArithmeticException if rounding is needed with rounding
128.518 + * mode being set to RoundingMode.UNNECESSARY
128.519 + * @return The formatted number string
128.520 + * @see java.text.FieldPosition
128.521 + */
128.522 + public StringBuffer format(double number, StringBuffer result,
128.523 + FieldPosition fieldPosition) {
128.524 + fieldPosition.setBeginIndex(0);
128.525 + fieldPosition.setEndIndex(0);
128.526 +
128.527 + return format(number, result, fieldPosition.getFieldDelegate());
128.528 + }
128.529 +
128.530 + /**
128.531 + * Formats a double to produce a string.
128.532 + * @param number The double to format
128.533 + * @param result where the text is to be appended
128.534 + * @param delegate notified of locations of sub fields
128.535 + * @exception ArithmeticException if rounding is needed with rounding
128.536 + * mode being set to RoundingMode.UNNECESSARY
128.537 + * @return The formatted number string
128.538 + */
128.539 + private StringBuffer format(double number, StringBuffer result,
128.540 + FieldDelegate delegate) {
128.541 + if (Double.isNaN(number) ||
128.542 + (Double.isInfinite(number) && multiplier == 0)) {
128.543 + int iFieldStart = result.length();
128.544 + result.append(symbols.getNaN());
128.545 + delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
128.546 + iFieldStart, result.length(), result);
128.547 + return result;
128.548 + }
128.549 +
128.550 + /* Detecting whether a double is negative is easy with the exception of
128.551 + * the value -0.0. This is a double which has a zero mantissa (and
128.552 + * exponent), but a negative sign bit. It is semantically distinct from
128.553 + * a zero with a positive sign bit, and this distinction is important
128.554 + * to certain kinds of computations. However, it's a little tricky to
128.555 + * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may
128.556 + * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
128.557 + * -Infinity. Proper detection of -0.0 is needed to deal with the
128.558 + * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
128.559 + */
128.560 + boolean isNegative = ((number < 0.0) || (number == 0.0 && 1/number < 0.0)) ^ (multiplier < 0);
128.561 +
128.562 + if (multiplier != 1) {
128.563 + number *= multiplier;
128.564 + }
128.565 +
128.566 + if (Double.isInfinite(number)) {
128.567 + if (isNegative) {
128.568 + append(result, negativePrefix, delegate,
128.569 + getNegativePrefixFieldPositions(), Field.SIGN);
128.570 + } else {
128.571 + append(result, positivePrefix, delegate,
128.572 + getPositivePrefixFieldPositions(), Field.SIGN);
128.573 + }
128.574 +
128.575 + int iFieldStart = result.length();
128.576 + result.append(symbols.getInfinity());
128.577 + delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
128.578 + iFieldStart, result.length(), result);
128.579 +
128.580 + if (isNegative) {
128.581 + append(result, negativeSuffix, delegate,
128.582 + getNegativeSuffixFieldPositions(), Field.SIGN);
128.583 + } else {
128.584 + append(result, positiveSuffix, delegate,
128.585 + getPositiveSuffixFieldPositions(), Field.SIGN);
128.586 + }
128.587 +
128.588 + return result;
128.589 + }
128.590 +
128.591 + if (isNegative) {
128.592 + number = -number;
128.593 + }
128.594 +
128.595 + // at this point we are guaranteed a nonnegative finite number.
128.596 + assert(number >= 0 && !Double.isInfinite(number));
128.597 +
128.598 + synchronized(digitList) {
128.599 + int maxIntDigits = super.getMaximumIntegerDigits();
128.600 + int minIntDigits = super.getMinimumIntegerDigits();
128.601 + int maxFraDigits = super.getMaximumFractionDigits();
128.602 + int minFraDigits = super.getMinimumFractionDigits();
128.603 +
128.604 + digitList.set(isNegative, number, useExponentialNotation ?
128.605 + maxIntDigits + maxFraDigits : maxFraDigits,
128.606 + !useExponentialNotation);
128.607 + return subformat(result, delegate, isNegative, false,
128.608 + maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
128.609 + }
128.610 + }
128.611 +
128.612 + /**
128.613 + * Format a long to produce a string.
128.614 + * @param number The long to format
128.615 + * @param result where the text is to be appended
128.616 + * @param fieldPosition On input: an alignment field, if desired.
128.617 + * On output: the offsets of the alignment field.
128.618 + * @exception ArithmeticException if rounding is needed with rounding
128.619 + * mode being set to RoundingMode.UNNECESSARY
128.620 + * @return The formatted number string
128.621 + * @see java.text.FieldPosition
128.622 + */
128.623 + public StringBuffer format(long number, StringBuffer result,
128.624 + FieldPosition fieldPosition) {
128.625 + fieldPosition.setBeginIndex(0);
128.626 + fieldPosition.setEndIndex(0);
128.627 +
128.628 + return format(number, result, fieldPosition.getFieldDelegate());
128.629 + }
128.630 +
128.631 + /**
128.632 + * Format a long to produce a string.
128.633 + * @param number The long to format
128.634 + * @param result where the text is to be appended
128.635 + * @param delegate notified of locations of sub fields
128.636 + * @return The formatted number string
128.637 + * @exception ArithmeticException if rounding is needed with rounding
128.638 + * mode being set to RoundingMode.UNNECESSARY
128.639 + * @see java.text.FieldPosition
128.640 + */
128.641 + private StringBuffer format(long number, StringBuffer result,
128.642 + FieldDelegate delegate) {
128.643 + boolean isNegative = (number < 0);
128.644 + if (isNegative) {
128.645 + number = -number;
128.646 + }
128.647 +
128.648 + // In general, long values always represent real finite numbers, so
128.649 + // we don't have to check for +/- Infinity or NaN. However, there
128.650 + // is one case we have to be careful of: The multiplier can push
128.651 + // a number near MIN_VALUE or MAX_VALUE outside the legal range. We
128.652 + // check for this before multiplying, and if it happens we use
128.653 + // BigInteger instead.
128.654 + boolean useBigInteger = false;
128.655 + if (number < 0) { // This can only happen if number == Long.MIN_VALUE.
128.656 + if (multiplier != 0) {
128.657 + useBigInteger = true;
128.658 + }
128.659 + } else if (multiplier != 1 && multiplier != 0) {
128.660 + long cutoff = Long.MAX_VALUE / multiplier;
128.661 + if (cutoff < 0) {
128.662 + cutoff = -cutoff;
128.663 + }
128.664 + useBigInteger = (number > cutoff);
128.665 + }
128.666 +
128.667 + if (useBigInteger) {
128.668 + if (isNegative) {
128.669 + number = -number;
128.670 + }
128.671 + BigInteger bigIntegerValue = BigInteger.valueOf(number);
128.672 + return format(bigIntegerValue, result, delegate, true);
128.673 + }
128.674 +
128.675 + number *= multiplier;
128.676 + if (number == 0) {
128.677 + isNegative = false;
128.678 + } else {
128.679 + if (multiplier < 0) {
128.680 + number = -number;
128.681 + isNegative = !isNegative;
128.682 + }
128.683 + }
128.684 +
128.685 + synchronized(digitList) {
128.686 + int maxIntDigits = super.getMaximumIntegerDigits();
128.687 + int minIntDigits = super.getMinimumIntegerDigits();
128.688 + int maxFraDigits = super.getMaximumFractionDigits();
128.689 + int minFraDigits = super.getMinimumFractionDigits();
128.690 +
128.691 + digitList.set(isNegative, number,
128.692 + useExponentialNotation ? maxIntDigits + maxFraDigits : 0);
128.693 +
128.694 + return subformat(result, delegate, isNegative, true,
128.695 + maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
128.696 + }
128.697 + }
128.698 +
128.699 + /**
128.700 + * Formats a BigDecimal to produce a string.
128.701 + * @param number The BigDecimal to format
128.702 + * @param result where the text is to be appended
128.703 + * @param fieldPosition On input: an alignment field, if desired.
128.704 + * On output: the offsets of the alignment field.
128.705 + * @return The formatted number string
128.706 + * @exception ArithmeticException if rounding is needed with rounding
128.707 + * mode being set to RoundingMode.UNNECESSARY
128.708 + * @see java.text.FieldPosition
128.709 + */
128.710 + private StringBuffer format(BigDecimal number, StringBuffer result,
128.711 + FieldPosition fieldPosition) {
128.712 + fieldPosition.setBeginIndex(0);
128.713 + fieldPosition.setEndIndex(0);
128.714 + return format(number, result, fieldPosition.getFieldDelegate());
128.715 + }
128.716 +
128.717 + /**
128.718 + * Formats a BigDecimal to produce a string.
128.719 + * @param number The BigDecimal to format
128.720 + * @param result where the text is to be appended
128.721 + * @param delegate notified of locations of sub fields
128.722 + * @exception ArithmeticException if rounding is needed with rounding
128.723 + * mode being set to RoundingMode.UNNECESSARY
128.724 + * @return The formatted number string
128.725 + */
128.726 + private StringBuffer format(BigDecimal number, StringBuffer result,
128.727 + FieldDelegate delegate) {
128.728 + if (multiplier != 1) {
128.729 + number = number.multiply(getBigDecimalMultiplier());
128.730 + }
128.731 + boolean isNegative = number.signum() == -1;
128.732 + if (isNegative) {
128.733 + number = number.negate();
128.734 + }
128.735 +
128.736 + synchronized(digitList) {
128.737 + int maxIntDigits = getMaximumIntegerDigits();
128.738 + int minIntDigits = getMinimumIntegerDigits();
128.739 + int maxFraDigits = getMaximumFractionDigits();
128.740 + int minFraDigits = getMinimumFractionDigits();
128.741 + int maximumDigits = maxIntDigits + maxFraDigits;
128.742 +
128.743 + digitList.set(isNegative, number, useExponentialNotation ?
128.744 + ((maximumDigits < 0) ? Integer.MAX_VALUE : maximumDigits) :
128.745 + maxFraDigits, !useExponentialNotation);
128.746 +
128.747 + return subformat(result, delegate, isNegative, false,
128.748 + maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
128.749 + }
128.750 + }
128.751 +
128.752 + /**
128.753 + * Format a BigInteger to produce a string.
128.754 + * @param number The BigInteger to format
128.755 + * @param result where the text is to be appended
128.756 + * @param fieldPosition On input: an alignment field, if desired.
128.757 + * On output: the offsets of the alignment field.
128.758 + * @return The formatted number string
128.759 + * @exception ArithmeticException if rounding is needed with rounding
128.760 + * mode being set to RoundingMode.UNNECESSARY
128.761 + * @see java.text.FieldPosition
128.762 + */
128.763 + private StringBuffer format(BigInteger number, StringBuffer result,
128.764 + FieldPosition fieldPosition) {
128.765 + fieldPosition.setBeginIndex(0);
128.766 + fieldPosition.setEndIndex(0);
128.767 +
128.768 + return format(number, result, fieldPosition.getFieldDelegate(), false);
128.769 + }
128.770 +
128.771 + /**
128.772 + * Format a BigInteger to produce a string.
128.773 + * @param number The BigInteger to format
128.774 + * @param result where the text is to be appended
128.775 + * @param delegate notified of locations of sub fields
128.776 + * @return The formatted number string
128.777 + * @exception ArithmeticException if rounding is needed with rounding
128.778 + * mode being set to RoundingMode.UNNECESSARY
128.779 + * @see java.text.FieldPosition
128.780 + */
128.781 + private StringBuffer format(BigInteger number, StringBuffer result,
128.782 + FieldDelegate delegate, boolean formatLong) {
128.783 + if (multiplier != 1) {
128.784 + number = number.multiply(getBigIntegerMultiplier());
128.785 + }
128.786 + boolean isNegative = number.signum() == -1;
128.787 + if (isNegative) {
128.788 + number = number.negate();
128.789 + }
128.790 +
128.791 + synchronized(digitList) {
128.792 + int maxIntDigits, minIntDigits, maxFraDigits, minFraDigits, maximumDigits;
128.793 + if (formatLong) {
128.794 + maxIntDigits = super.getMaximumIntegerDigits();
128.795 + minIntDigits = super.getMinimumIntegerDigits();
128.796 + maxFraDigits = super.getMaximumFractionDigits();
128.797 + minFraDigits = super.getMinimumFractionDigits();
128.798 + maximumDigits = maxIntDigits + maxFraDigits;
128.799 + } else {
128.800 + maxIntDigits = getMaximumIntegerDigits();
128.801 + minIntDigits = getMinimumIntegerDigits();
128.802 + maxFraDigits = getMaximumFractionDigits();
128.803 + minFraDigits = getMinimumFractionDigits();
128.804 + maximumDigits = maxIntDigits + maxFraDigits;
128.805 + if (maximumDigits < 0) {
128.806 + maximumDigits = Integer.MAX_VALUE;
128.807 + }
128.808 + }
128.809 +
128.810 + digitList.set(isNegative, number,
128.811 + useExponentialNotation ? maximumDigits : 0);
128.812 +
128.813 + return subformat(result, delegate, isNegative, true,
128.814 + maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
128.815 + }
128.816 + }
128.817 +
128.818 + /**
128.819 + * Formats an Object producing an <code>AttributedCharacterIterator</code>.
128.820 + * You can use the returned <code>AttributedCharacterIterator</code>
128.821 + * to build the resulting String, as well as to determine information
128.822 + * about the resulting String.
128.823 + * <p>
128.824 + * Each attribute key of the AttributedCharacterIterator will be of type
128.825 + * <code>NumberFormat.Field</code>, with the attribute value being the
128.826 + * same as the attribute key.
128.827 + *
128.828 + * @exception NullPointerException if obj is null.
128.829 + * @exception IllegalArgumentException when the Format cannot format the
128.830 + * given object.
128.831 + * @exception ArithmeticException if rounding is needed with rounding
128.832 + * mode being set to RoundingMode.UNNECESSARY
128.833 + * @param obj The object to format
128.834 + * @return AttributedCharacterIterator describing the formatted value.
128.835 + * @since 1.4
128.836 + */
128.837 + public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
128.838 + CharacterIteratorFieldDelegate delegate =
128.839 + new CharacterIteratorFieldDelegate();
128.840 + StringBuffer sb = new StringBuffer();
128.841 +
128.842 + if (obj instanceof Double || obj instanceof Float) {
128.843 + format(((Number)obj).doubleValue(), sb, delegate);
128.844 + } else if (obj instanceof Long || obj instanceof Integer ||
128.845 + obj instanceof Short || obj instanceof Byte ||
128.846 + obj instanceof AtomicInteger || obj instanceof AtomicLong) {
128.847 + format(((Number)obj).longValue(), sb, delegate);
128.848 + } else if (obj instanceof BigDecimal) {
128.849 + format((BigDecimal)obj, sb, delegate);
128.850 + } else if (obj instanceof BigInteger) {
128.851 + format((BigInteger)obj, sb, delegate, false);
128.852 + } else if (obj == null) {
128.853 + throw new NullPointerException(
128.854 + "formatToCharacterIterator must be passed non-null object");
128.855 + } else {
128.856 + throw new IllegalArgumentException(
128.857 + "Cannot format given Object as a Number");
128.858 + }
128.859 + return delegate.getIterator(sb.toString());
128.860 + }
128.861 +
128.862 + /**
128.863 + * Complete the formatting of a finite number. On entry, the digitList must
128.864 + * be filled in with the correct digits.
128.865 + */
128.866 + private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
128.867 + boolean isNegative, boolean isInteger,
128.868 + int maxIntDigits, int minIntDigits,
128.869 + int maxFraDigits, int minFraDigits) {
128.870 + // NOTE: This isn't required anymore because DigitList takes care of this.
128.871 + //
128.872 + // // The negative of the exponent represents the number of leading
128.873 + // // zeros between the decimal and the first non-zero digit, for
128.874 + // // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this
128.875 + // // is more than the maximum fraction digits, then we have an underflow
128.876 + // // for the printed representation. We recognize this here and set
128.877 + // // the DigitList representation to zero in this situation.
128.878 + //
128.879 + // if (-digitList.decimalAt >= getMaximumFractionDigits())
128.880 + // {
128.881 + // digitList.count = 0;
128.882 + // }
128.883 +
128.884 + char zero = symbols.getZeroDigit();
128.885 + int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
128.886 + char grouping = symbols.getGroupingSeparator();
128.887 + char decimal = isCurrencyFormat ?
128.888 + symbols.getMonetaryDecimalSeparator() :
128.889 + symbols.getDecimalSeparator();
128.890 +
128.891 + /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
128.892 + * format as zero. This allows sensible computations and preserves
128.893 + * relations such as signum(1/x) = signum(x), where x is +Infinity or
128.894 + * -Infinity. Prior to this fix, we always formatted zero values as if
128.895 + * they were positive. Liu 7/6/98.
128.896 + */
128.897 + if (digitList.isZero()) {
128.898 + digitList.decimalAt = 0; // Normalize
128.899 + }
128.900 +
128.901 + if (isNegative) {
128.902 + append(result, negativePrefix, delegate,
128.903 + getNegativePrefixFieldPositions(), Field.SIGN);
128.904 + } else {
128.905 + append(result, positivePrefix, delegate,
128.906 + getPositivePrefixFieldPositions(), Field.SIGN);
128.907 + }
128.908 +
128.909 + if (useExponentialNotation) {
128.910 + int iFieldStart = result.length();
128.911 + int iFieldEnd = -1;
128.912 + int fFieldStart = -1;
128.913 +
128.914 + // Minimum integer digits are handled in exponential format by
128.915 + // adjusting the exponent. For example, 0.01234 with 3 minimum
128.916 + // integer digits is "123.4E-4".
128.917 +
128.918 + // Maximum integer digits are interpreted as indicating the
128.919 + // repeating range. This is useful for engineering notation, in
128.920 + // which the exponent is restricted to a multiple of 3. For
128.921 + // example, 0.01234 with 3 maximum integer digits is "12.34e-3".
128.922 + // If maximum integer digits are > 1 and are larger than
128.923 + // minimum integer digits, then minimum integer digits are
128.924 + // ignored.
128.925 + int exponent = digitList.decimalAt;
128.926 + int repeat = maxIntDigits;
128.927 + int minimumIntegerDigits = minIntDigits;
128.928 + if (repeat > 1 && repeat > minIntDigits) {
128.929 + // A repeating range is defined; adjust to it as follows.
128.930 + // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
128.931 + // -3,-4,-5=>-6, etc. This takes into account that the
128.932 + // exponent we have here is off by one from what we expect;
128.933 + // it is for the format 0.MMMMMx10^n.
128.934 + if (exponent >= 1) {
128.935 + exponent = ((exponent - 1) / repeat) * repeat;
128.936 + } else {
128.937 + // integer division rounds towards 0
128.938 + exponent = ((exponent - repeat) / repeat) * repeat;
128.939 + }
128.940 + minimumIntegerDigits = 1;
128.941 + } else {
128.942 + // No repeating range is defined; use minimum integer digits.
128.943 + exponent -= minimumIntegerDigits;
128.944 + }
128.945 +
128.946 + // We now output a minimum number of digits, and more if there
128.947 + // are more digits, up to the maximum number of digits. We
128.948 + // place the decimal point after the "integer" digits, which
128.949 + // are the first (decimalAt - exponent) digits.
128.950 + int minimumDigits = minIntDigits + minFraDigits;
128.951 + if (minimumDigits < 0) { // overflow?
128.952 + minimumDigits = Integer.MAX_VALUE;
128.953 + }
128.954 +
128.955 + // The number of integer digits is handled specially if the number
128.956 + // is zero, since then there may be no digits.
128.957 + int integerDigits = digitList.isZero() ? minimumIntegerDigits :
128.958 + digitList.decimalAt - exponent;
128.959 + if (minimumDigits < integerDigits) {
128.960 + minimumDigits = integerDigits;
128.961 + }
128.962 + int totalDigits = digitList.count;
128.963 + if (minimumDigits > totalDigits) {
128.964 + totalDigits = minimumDigits;
128.965 + }
128.966 + boolean addedDecimalSeparator = false;
128.967 +
128.968 + for (int i=0; i<totalDigits; ++i) {
128.969 + if (i == integerDigits) {
128.970 + // Record field information for caller.
128.971 + iFieldEnd = result.length();
128.972 +
128.973 + result.append(decimal);
128.974 + addedDecimalSeparator = true;
128.975 +
128.976 + // Record field information for caller.
128.977 + fFieldStart = result.length();
128.978 + }
128.979 + result.append((i < digitList.count) ?
128.980 + (char)(digitList.digits[i] + zeroDelta) :
128.981 + zero);
128.982 + }
128.983 +
128.984 + if (decimalSeparatorAlwaysShown && totalDigits == integerDigits) {
128.985 + // Record field information for caller.
128.986 + iFieldEnd = result.length();
128.987 +
128.988 + result.append(decimal);
128.989 + addedDecimalSeparator = true;
128.990 +
128.991 + // Record field information for caller.
128.992 + fFieldStart = result.length();
128.993 + }
128.994 +
128.995 + // Record field information
128.996 + if (iFieldEnd == -1) {
128.997 + iFieldEnd = result.length();
128.998 + }
128.999 + delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
128.1000 + iFieldStart, iFieldEnd, result);
128.1001 + if (addedDecimalSeparator) {
128.1002 + delegate.formatted(Field.DECIMAL_SEPARATOR,
128.1003 + Field.DECIMAL_SEPARATOR,
128.1004 + iFieldEnd, fFieldStart, result);
128.1005 + }
128.1006 + if (fFieldStart == -1) {
128.1007 + fFieldStart = result.length();
128.1008 + }
128.1009 + delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
128.1010 + fFieldStart, result.length(), result);
128.1011 +
128.1012 + // The exponent is output using the pattern-specified minimum
128.1013 + // exponent digits. There is no maximum limit to the exponent
128.1014 + // digits, since truncating the exponent would result in an
128.1015 + // unacceptable inaccuracy.
128.1016 + int fieldStart = result.length();
128.1017 +
128.1018 + result.append(symbols.getExponentSeparator());
128.1019 +
128.1020 + delegate.formatted(Field.EXPONENT_SYMBOL, Field.EXPONENT_SYMBOL,
128.1021 + fieldStart, result.length(), result);
128.1022 +
128.1023 + // For zero values, we force the exponent to zero. We
128.1024 + // must do this here, and not earlier, because the value
128.1025 + // is used to determine integer digit count above.
128.1026 + if (digitList.isZero()) {
128.1027 + exponent = 0;
128.1028 + }
128.1029 +
128.1030 + boolean negativeExponent = exponent < 0;
128.1031 + if (negativeExponent) {
128.1032 + exponent = -exponent;
128.1033 + fieldStart = result.length();
128.1034 + result.append(symbols.getMinusSign());
128.1035 + delegate.formatted(Field.EXPONENT_SIGN, Field.EXPONENT_SIGN,
128.1036 + fieldStart, result.length(), result);
128.1037 + }
128.1038 + digitList.set(negativeExponent, exponent);
128.1039 +
128.1040 + int eFieldStart = result.length();
128.1041 +
128.1042 + for (int i=digitList.decimalAt; i<minExponentDigits; ++i) {
128.1043 + result.append(zero);
128.1044 + }
128.1045 + for (int i=0; i<digitList.decimalAt; ++i) {
128.1046 + result.append((i < digitList.count) ?
128.1047 + (char)(digitList.digits[i] + zeroDelta) : zero);
128.1048 + }
128.1049 + delegate.formatted(Field.EXPONENT, Field.EXPONENT, eFieldStart,
128.1050 + result.length(), result);
128.1051 + } else {
128.1052 + int iFieldStart = result.length();
128.1053 +
128.1054 + // Output the integer portion. Here 'count' is the total
128.1055 + // number of integer digits we will display, including both
128.1056 + // leading zeros required to satisfy getMinimumIntegerDigits,
128.1057 + // and actual digits present in the number.
128.1058 + int count = minIntDigits;
128.1059 + int digitIndex = 0; // Index into digitList.fDigits[]
128.1060 + if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
128.1061 + count = digitList.decimalAt;
128.1062 + }
128.1063 +
128.1064 + // Handle the case where getMaximumIntegerDigits() is smaller
128.1065 + // than the real number of integer digits. If this is so, we
128.1066 + // output the least significant max integer digits. For example,
128.1067 + // the value 1997 printed with 2 max integer digits is just "97".
128.1068 + if (count > maxIntDigits) {
128.1069 + count = maxIntDigits;
128.1070 + digitIndex = digitList.decimalAt - count;
128.1071 + }
128.1072 +
128.1073 + int sizeBeforeIntegerPart = result.length();
128.1074 + for (int i=count-1; i>=0; --i) {
128.1075 + if (i < digitList.decimalAt && digitIndex < digitList.count) {
128.1076 + // Output a real digit
128.1077 + result.append((char)(digitList.digits[digitIndex++] + zeroDelta));
128.1078 + } else {
128.1079 + // Output a leading zero
128.1080 + result.append(zero);
128.1081 + }
128.1082 +
128.1083 + // Output grouping separator if necessary. Don't output a
128.1084 + // grouping separator if i==0 though; that's at the end of
128.1085 + // the integer part.
128.1086 + if (isGroupingUsed() && i>0 && (groupingSize != 0) &&
128.1087 + (i % groupingSize == 0)) {
128.1088 + int gStart = result.length();
128.1089 + result.append(grouping);
128.1090 + delegate.formatted(Field.GROUPING_SEPARATOR,
128.1091 + Field.GROUPING_SEPARATOR, gStart,
128.1092 + result.length(), result);
128.1093 + }
128.1094 + }
128.1095 +
128.1096 + // Determine whether or not there are any printable fractional
128.1097 + // digits. If we've used up the digits we know there aren't.
128.1098 + boolean fractionPresent = (minFraDigits > 0) ||
128.1099 + (!isInteger && digitIndex < digitList.count);
128.1100 +
128.1101 + // If there is no fraction present, and we haven't printed any
128.1102 + // integer digits, then print a zero. Otherwise we won't print
128.1103 + // _any_ digits, and we won't be able to parse this string.
128.1104 + if (!fractionPresent && result.length() == sizeBeforeIntegerPart) {
128.1105 + result.append(zero);
128.1106 + }
128.1107 +
128.1108 + delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
128.1109 + iFieldStart, result.length(), result);
128.1110 +
128.1111 + // Output the decimal separator if we always do so.
128.1112 + int sStart = result.length();
128.1113 + if (decimalSeparatorAlwaysShown || fractionPresent) {
128.1114 + result.append(decimal);
128.1115 + }
128.1116 +
128.1117 + if (sStart != result.length()) {
128.1118 + delegate.formatted(Field.DECIMAL_SEPARATOR,
128.1119 + Field.DECIMAL_SEPARATOR,
128.1120 + sStart, result.length(), result);
128.1121 + }
128.1122 + int fFieldStart = result.length();
128.1123 +
128.1124 + for (int i=0; i < maxFraDigits; ++i) {
128.1125 + // Here is where we escape from the loop. We escape if we've
128.1126 + // output the maximum fraction digits (specified in the for
128.1127 + // expression above).
128.1128 + // We also stop when we've output the minimum digits and either:
128.1129 + // we have an integer, so there is no fractional stuff to
128.1130 + // display, or we're out of significant digits.
128.1131 + if (i >= minFraDigits &&
128.1132 + (isInteger || digitIndex >= digitList.count)) {
128.1133 + break;
128.1134 + }
128.1135 +
128.1136 + // Output leading fractional zeros. These are zeros that come
128.1137 + // after the decimal but before any significant digits. These
128.1138 + // are only output if abs(number being formatted) < 1.0.
128.1139 + if (-1-i > (digitList.decimalAt-1)) {
128.1140 + result.append(zero);
128.1141 + continue;
128.1142 + }
128.1143 +
128.1144 + // Output a digit, if we have any precision left, or a
128.1145 + // zero if we don't. We don't want to output noise digits.
128.1146 + if (!isInteger && digitIndex < digitList.count) {
128.1147 + result.append((char)(digitList.digits[digitIndex++] + zeroDelta));
128.1148 + } else {
128.1149 + result.append(zero);
128.1150 + }
128.1151 + }
128.1152 +
128.1153 + // Record field information for caller.
128.1154 + delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
128.1155 + fFieldStart, result.length(), result);
128.1156 + }
128.1157 +
128.1158 + if (isNegative) {
128.1159 + append(result, negativeSuffix, delegate,
128.1160 + getNegativeSuffixFieldPositions(), Field.SIGN);
128.1161 + }
128.1162 + else {
128.1163 + append(result, positiveSuffix, delegate,
128.1164 + getPositiveSuffixFieldPositions(), Field.SIGN);
128.1165 + }
128.1166 +
128.1167 + return result;
128.1168 + }
128.1169 +
128.1170 + /**
128.1171 + * Appends the String <code>string</code> to <code>result</code>.
128.1172 + * <code>delegate</code> is notified of all the
128.1173 + * <code>FieldPosition</code>s in <code>positions</code>.
128.1174 + * <p>
128.1175 + * If one of the <code>FieldPosition</code>s in <code>positions</code>
128.1176 + * identifies a <code>SIGN</code> attribute, it is mapped to
128.1177 + * <code>signAttribute</code>. This is used
128.1178 + * to map the <code>SIGN</code> attribute to the <code>EXPONENT</code>
128.1179 + * attribute as necessary.
128.1180 + * <p>
128.1181 + * This is used by <code>subformat</code> to add the prefix/suffix.
128.1182 + */
128.1183 + private void append(StringBuffer result, String string,
128.1184 + FieldDelegate delegate,
128.1185 + FieldPosition[] positions,
128.1186 + Format.Field signAttribute) {
128.1187 + int start = result.length();
128.1188 +
128.1189 + if (string.length() > 0) {
128.1190 + result.append(string);
128.1191 + for (int counter = 0, max = positions.length; counter < max;
128.1192 + counter++) {
128.1193 + FieldPosition fp = positions[counter];
128.1194 + Format.Field attribute = fp.getFieldAttribute();
128.1195 +
128.1196 + if (attribute == Field.SIGN) {
128.1197 + attribute = signAttribute;
128.1198 + }
128.1199 + delegate.formatted(attribute, attribute,
128.1200 + start + fp.getBeginIndex(),
128.1201 + start + fp.getEndIndex(), result);
128.1202 + }
128.1203 + }
128.1204 + }
128.1205 +
128.1206 + /**
128.1207 + * Parses text from a string to produce a <code>Number</code>.
128.1208 + * <p>
128.1209 + * The method attempts to parse text starting at the index given by
128.1210 + * <code>pos</code>.
128.1211 + * If parsing succeeds, then the index of <code>pos</code> is updated
128.1212 + * to the index after the last character used (parsing does not necessarily
128.1213 + * use all characters up to the end of the string), and the parsed
128.1214 + * number is returned. The updated <code>pos</code> can be used to
128.1215 + * indicate the starting point for the next call to this method.
128.1216 + * If an error occurs, then the index of <code>pos</code> is not
128.1217 + * changed, the error index of <code>pos</code> is set to the index of
128.1218 + * the character where the error occurred, and null is returned.
128.1219 + * <p>
128.1220 + * The subclass returned depends on the value of {@link #isParseBigDecimal}
128.1221 + * as well as on the string being parsed.
128.1222 + * <ul>
128.1223 + * <li>If <code>isParseBigDecimal()</code> is false (the default),
128.1224 + * most integer values are returned as <code>Long</code>
128.1225 + * objects, no matter how they are written: <code>"17"</code> and
128.1226 + * <code>"17.000"</code> both parse to <code>Long(17)</code>.
128.1227 + * Values that cannot fit into a <code>Long</code> are returned as
128.1228 + * <code>Double</code>s. This includes values with a fractional part,
128.1229 + * infinite values, <code>NaN</code>, and the value -0.0.
128.1230 + * <code>DecimalFormat</code> does <em>not</em> decide whether to
128.1231 + * return a <code>Double</code> or a <code>Long</code> based on the
128.1232 + * presence of a decimal separator in the source string. Doing so
128.1233 + * would prevent integers that overflow the mantissa of a double,
128.1234 + * such as <code>"-9,223,372,036,854,775,808.00"</code>, from being
128.1235 + * parsed accurately.
128.1236 + * <p>
128.1237 + * Callers may use the <code>Number</code> methods
128.1238 + * <code>doubleValue</code>, <code>longValue</code>, etc., to obtain
128.1239 + * the type they want.
128.1240 + * <li>If <code>isParseBigDecimal()</code> is true, values are returned
128.1241 + * as <code>BigDecimal</code> objects. The values are the ones
128.1242 + * constructed by {@link java.math.BigDecimal#BigDecimal(String)}
128.1243 + * for corresponding strings in locale-independent format. The
128.1244 + * special cases negative and positive infinity and NaN are returned
128.1245 + * as <code>Double</code> instances holding the values of the
128.1246 + * corresponding <code>Double</code> constants.
128.1247 + * </ul>
128.1248 + * <p>
128.1249 + * <code>DecimalFormat</code> parses all Unicode characters that represent
128.1250 + * decimal digits, as defined by <code>Character.digit()</code>. In
128.1251 + * addition, <code>DecimalFormat</code> also recognizes as digits the ten
128.1252 + * consecutive characters starting with the localized zero digit defined in
128.1253 + * the <code>DecimalFormatSymbols</code> object.
128.1254 + *
128.1255 + * @param text the string to be parsed
128.1256 + * @param pos A <code>ParsePosition</code> object with index and error
128.1257 + * index information as described above.
128.1258 + * @return the parsed value, or <code>null</code> if the parse fails
128.1259 + * @exception NullPointerException if <code>text</code> or
128.1260 + * <code>pos</code> is null.
128.1261 + */
128.1262 + public Number parse(String text, ParsePosition pos) {
128.1263 + // special case NaN
128.1264 + if (text.regionMatches(pos.index, symbols.getNaN(), 0, symbols.getNaN().length())) {
128.1265 + pos.index = pos.index + symbols.getNaN().length();
128.1266 + return new Double(Double.NaN);
128.1267 + }
128.1268 +
128.1269 + boolean[] status = new boolean[STATUS_LENGTH];
128.1270 + if (!subparse(text, pos, positivePrefix, negativePrefix, digitList, false, status)) {
128.1271 + return null;
128.1272 + }
128.1273 +
128.1274 + // special case INFINITY
128.1275 + if (status[STATUS_INFINITE]) {
128.1276 + if (status[STATUS_POSITIVE] == (multiplier >= 0)) {
128.1277 + return new Double(Double.POSITIVE_INFINITY);
128.1278 + } else {
128.1279 + return new Double(Double.NEGATIVE_INFINITY);
128.1280 + }
128.1281 + }
128.1282 +
128.1283 + if (multiplier == 0) {
128.1284 + if (digitList.isZero()) {
128.1285 + return new Double(Double.NaN);
128.1286 + } else if (status[STATUS_POSITIVE]) {
128.1287 + return new Double(Double.POSITIVE_INFINITY);
128.1288 + } else {
128.1289 + return new Double(Double.NEGATIVE_INFINITY);
128.1290 + }
128.1291 + }
128.1292 +
128.1293 + if (isParseBigDecimal()) {
128.1294 + BigDecimal bigDecimalResult = digitList.getBigDecimal();
128.1295 +
128.1296 + if (multiplier != 1) {
128.1297 + try {
128.1298 + bigDecimalResult = bigDecimalResult.divide(getBigDecimalMultiplier());
128.1299 + }
128.1300 + catch (ArithmeticException e) { // non-terminating decimal expansion
128.1301 + bigDecimalResult = bigDecimalResult.divide(getBigDecimalMultiplier(), roundingMode);
128.1302 + }
128.1303 + }
128.1304 +
128.1305 + if (!status[STATUS_POSITIVE]) {
128.1306 + bigDecimalResult = bigDecimalResult.negate();
128.1307 + }
128.1308 + return bigDecimalResult;
128.1309 + } else {
128.1310 + boolean gotDouble = true;
128.1311 + boolean gotLongMinimum = false;
128.1312 + double doubleResult = 0.0;
128.1313 + long longResult = 0;
128.1314 +
128.1315 + // Finally, have DigitList parse the digits into a value.
128.1316 + if (digitList.fitsIntoLong(status[STATUS_POSITIVE], isParseIntegerOnly())) {
128.1317 + gotDouble = false;
128.1318 + longResult = digitList.getLong();
128.1319 + if (longResult < 0) { // got Long.MIN_VALUE
128.1320 + gotLongMinimum = true;
128.1321 + }
128.1322 + } else {
128.1323 + doubleResult = digitList.getDouble();
128.1324 + }
128.1325 +
128.1326 + // Divide by multiplier. We have to be careful here not to do
128.1327 + // unneeded conversions between double and long.
128.1328 + if (multiplier != 1) {
128.1329 + if (gotDouble) {
128.1330 + doubleResult /= multiplier;
128.1331 + } else {
128.1332 + // Avoid converting to double if we can
128.1333 + if (longResult % multiplier == 0) {
128.1334 + longResult /= multiplier;
128.1335 + } else {
128.1336 + doubleResult = ((double)longResult) / multiplier;
128.1337 + gotDouble = true;
128.1338 + }
128.1339 + }
128.1340 + }
128.1341 +
128.1342 + if (!status[STATUS_POSITIVE] && !gotLongMinimum) {
128.1343 + doubleResult = -doubleResult;
128.1344 + longResult = -longResult;
128.1345 + }
128.1346 +
128.1347 + // At this point, if we divided the result by the multiplier, the
128.1348 + // result may fit into a long. We check for this case and return
128.1349 + // a long if possible.
128.1350 + // We must do this AFTER applying the negative (if appropriate)
128.1351 + // in order to handle the case of LONG_MIN; otherwise, if we do
128.1352 + // this with a positive value -LONG_MIN, the double is > 0, but
128.1353 + // the long is < 0. We also must retain a double in the case of
128.1354 + // -0.0, which will compare as == to a long 0 cast to a double
128.1355 + // (bug 4162852).
128.1356 + if (multiplier != 1 && gotDouble) {
128.1357 + longResult = (long)doubleResult;
128.1358 + gotDouble = ((doubleResult != (double)longResult) ||
128.1359 + (doubleResult == 0.0 && 1/doubleResult < 0.0)) &&
128.1360 + !isParseIntegerOnly();
128.1361 + }
128.1362 +
128.1363 + return gotDouble ?
128.1364 + (Number)new Double(doubleResult) : (Number)new Long(longResult);
128.1365 + }
128.1366 + }
128.1367 +
128.1368 + /**
128.1369 + * Return a BigInteger multiplier.
128.1370 + */
128.1371 + private BigInteger getBigIntegerMultiplier() {
128.1372 + if (bigIntegerMultiplier == null) {
128.1373 + bigIntegerMultiplier = BigInteger.valueOf(multiplier);
128.1374 + }
128.1375 + return bigIntegerMultiplier;
128.1376 + }
128.1377 + private transient BigInteger bigIntegerMultiplier;
128.1378 +
128.1379 + /**
128.1380 + * Return a BigDecimal multiplier.
128.1381 + */
128.1382 + private BigDecimal getBigDecimalMultiplier() {
128.1383 + if (bigDecimalMultiplier == null) {
128.1384 + bigDecimalMultiplier = new BigDecimal(multiplier);
128.1385 + }
128.1386 + return bigDecimalMultiplier;
128.1387 + }
128.1388 + private transient BigDecimal bigDecimalMultiplier;
128.1389 +
128.1390 + private static final int STATUS_INFINITE = 0;
128.1391 + private static final int STATUS_POSITIVE = 1;
128.1392 + private static final int STATUS_LENGTH = 2;
128.1393 +
128.1394 + /**
128.1395 + * Parse the given text into a number. The text is parsed beginning at
128.1396 + * parsePosition, until an unparseable character is seen.
128.1397 + * @param text The string to parse.
128.1398 + * @param parsePosition The position at which to being parsing. Upon
128.1399 + * return, the first unparseable character.
128.1400 + * @param digits The DigitList to set to the parsed value.
128.1401 + * @param isExponent If true, parse an exponent. This means no
128.1402 + * infinite values and integer only.
128.1403 + * @param status Upon return contains boolean status flags indicating
128.1404 + * whether the value was infinite and whether it was positive.
128.1405 + */
128.1406 + private final boolean subparse(String text, ParsePosition parsePosition,
128.1407 + String positivePrefix, String negativePrefix,
128.1408 + DigitList digits, boolean isExponent,
128.1409 + boolean status[]) {
128.1410 + int position = parsePosition.index;
128.1411 + int oldStart = parsePosition.index;
128.1412 + int backup;
128.1413 + boolean gotPositive, gotNegative;
128.1414 +
128.1415 + // check for positivePrefix; take longest
128.1416 + gotPositive = text.regionMatches(position, positivePrefix, 0,
128.1417 + positivePrefix.length());
128.1418 + gotNegative = text.regionMatches(position, negativePrefix, 0,
128.1419 + negativePrefix.length());
128.1420 +
128.1421 + if (gotPositive && gotNegative) {
128.1422 + if (positivePrefix.length() > negativePrefix.length()) {
128.1423 + gotNegative = false;
128.1424 + } else if (positivePrefix.length() < negativePrefix.length()) {
128.1425 + gotPositive = false;
128.1426 + }
128.1427 + }
128.1428 +
128.1429 + if (gotPositive) {
128.1430 + position += positivePrefix.length();
128.1431 + } else if (gotNegative) {
128.1432 + position += negativePrefix.length();
128.1433 + } else {
128.1434 + parsePosition.errorIndex = position;
128.1435 + return false;
128.1436 + }
128.1437 +
128.1438 + // process digits or Inf, find decimal position
128.1439 + status[STATUS_INFINITE] = false;
128.1440 + if (!isExponent && text.regionMatches(position,symbols.getInfinity(),0,
128.1441 + symbols.getInfinity().length())) {
128.1442 + position += symbols.getInfinity().length();
128.1443 + status[STATUS_INFINITE] = true;
128.1444 + } else {
128.1445 + // We now have a string of digits, possibly with grouping symbols,
128.1446 + // and decimal points. We want to process these into a DigitList.
128.1447 + // We don't want to put a bunch of leading zeros into the DigitList
128.1448 + // though, so we keep track of the location of the decimal point,
128.1449 + // put only significant digits into the DigitList, and adjust the
128.1450 + // exponent as needed.
128.1451 +
128.1452 + digits.decimalAt = digits.count = 0;
128.1453 + char zero = symbols.getZeroDigit();
128.1454 + char decimal = isCurrencyFormat ?
128.1455 + symbols.getMonetaryDecimalSeparator() :
128.1456 + symbols.getDecimalSeparator();
128.1457 + char grouping = symbols.getGroupingSeparator();
128.1458 + String exponentString = symbols.getExponentSeparator();
128.1459 + boolean sawDecimal = false;
128.1460 + boolean sawExponent = false;
128.1461 + boolean sawDigit = false;
128.1462 + int exponent = 0; // Set to the exponent value, if any
128.1463 +
128.1464 + // We have to track digitCount ourselves, because digits.count will
128.1465 + // pin when the maximum allowable digits is reached.
128.1466 + int digitCount = 0;
128.1467 +
128.1468 + backup = -1;
128.1469 + for (; position < text.length(); ++position) {
128.1470 + char ch = text.charAt(position);
128.1471 +
128.1472 + /* We recognize all digit ranges, not only the Latin digit range
128.1473 + * '0'..'9'. We do so by using the Character.digit() method,
128.1474 + * which converts a valid Unicode digit to the range 0..9.
128.1475 + *
128.1476 + * The character 'ch' may be a digit. If so, place its value
128.1477 + * from 0 to 9 in 'digit'. First try using the locale digit,
128.1478 + * which may or MAY NOT be a standard Unicode digit range. If
128.1479 + * this fails, try using the standard Unicode digit ranges by
128.1480 + * calling Character.digit(). If this also fails, digit will
128.1481 + * have a value outside the range 0..9.
128.1482 + */
128.1483 + int digit = ch - zero;
128.1484 + if (digit < 0 || digit > 9) {
128.1485 + digit = Character.digit(ch, 10);
128.1486 + }
128.1487 +
128.1488 + if (digit == 0) {
128.1489 + // Cancel out backup setting (see grouping handler below)
128.1490 + backup = -1; // Do this BEFORE continue statement below!!!
128.1491 + sawDigit = true;
128.1492 +
128.1493 + // Handle leading zeros
128.1494 + if (digits.count == 0) {
128.1495 + // Ignore leading zeros in integer part of number.
128.1496 + if (!sawDecimal) {
128.1497 + continue;
128.1498 + }
128.1499 +
128.1500 + // If we have seen the decimal, but no significant
128.1501 + // digits yet, then we account for leading zeros by
128.1502 + // decrementing the digits.decimalAt into negative
128.1503 + // values.
128.1504 + --digits.decimalAt;
128.1505 + } else {
128.1506 + ++digitCount;
128.1507 + digits.append((char)(digit + '0'));
128.1508 + }
128.1509 + } else if (digit > 0 && digit <= 9) { // [sic] digit==0 handled above
128.1510 + sawDigit = true;
128.1511 + ++digitCount;
128.1512 + digits.append((char)(digit + '0'));
128.1513 +
128.1514 + // Cancel out backup setting (see grouping handler below)
128.1515 + backup = -1;
128.1516 + } else if (!isExponent && ch == decimal) {
128.1517 + // If we're only parsing integers, or if we ALREADY saw the
128.1518 + // decimal, then don't parse this one.
128.1519 + if (isParseIntegerOnly() || sawDecimal) {
128.1520 + break;
128.1521 + }
128.1522 + digits.decimalAt = digitCount; // Not digits.count!
128.1523 + sawDecimal = true;
128.1524 + } else if (!isExponent && ch == grouping && isGroupingUsed()) {
128.1525 + if (sawDecimal) {
128.1526 + break;
128.1527 + }
128.1528 + // Ignore grouping characters, if we are using them, but
128.1529 + // require that they be followed by a digit. Otherwise
128.1530 + // we backup and reprocess them.
128.1531 + backup = position;
128.1532 + } else if (!isExponent && text.regionMatches(position, exponentString, 0, exponentString.length())
128.1533 + && !sawExponent) {
128.1534 + // Process the exponent by recursively calling this method.
128.1535 + ParsePosition pos = new ParsePosition(position + exponentString.length());
128.1536 + boolean[] stat = new boolean[STATUS_LENGTH];
128.1537 + DigitList exponentDigits = new DigitList();
128.1538 +
128.1539 + if (subparse(text, pos, "", Character.toString(symbols.getMinusSign()), exponentDigits, true, stat) &&
128.1540 + exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
128.1541 + position = pos.index; // Advance past the exponent
128.1542 + exponent = (int)exponentDigits.getLong();
128.1543 + if (!stat[STATUS_POSITIVE]) {
128.1544 + exponent = -exponent;
128.1545 + }
128.1546 + sawExponent = true;
128.1547 + }
128.1548 + break; // Whether we fail or succeed, we exit this loop
128.1549 + }
128.1550 + else {
128.1551 + break;
128.1552 + }
128.1553 + }
128.1554 +
128.1555 + if (backup != -1) {
128.1556 + position = backup;
128.1557 + }
128.1558 +
128.1559 + // If there was no decimal point we have an integer
128.1560 + if (!sawDecimal) {
128.1561 + digits.decimalAt = digitCount; // Not digits.count!
128.1562 + }
128.1563 +
128.1564 + // Adjust for exponent, if any
128.1565 + digits.decimalAt += exponent;
128.1566 +
128.1567 + // If none of the text string was recognized. For example, parse
128.1568 + // "x" with pattern "#0.00" (return index and error index both 0)
128.1569 + // parse "$" with pattern "$#0.00". (return index 0 and error
128.1570 + // index 1).
128.1571 + if (!sawDigit && digitCount == 0) {
128.1572 + parsePosition.index = oldStart;
128.1573 + parsePosition.errorIndex = oldStart;
128.1574 + return false;
128.1575 + }
128.1576 + }
128.1577 +
128.1578 + // check for suffix
128.1579 + if (!isExponent) {
128.1580 + if (gotPositive) {
128.1581 + gotPositive = text.regionMatches(position,positiveSuffix,0,
128.1582 + positiveSuffix.length());
128.1583 + }
128.1584 + if (gotNegative) {
128.1585 + gotNegative = text.regionMatches(position,negativeSuffix,0,
128.1586 + negativeSuffix.length());
128.1587 + }
128.1588 +
128.1589 + // if both match, take longest
128.1590 + if (gotPositive && gotNegative) {
128.1591 + if (positiveSuffix.length() > negativeSuffix.length()) {
128.1592 + gotNegative = false;
128.1593 + } else if (positiveSuffix.length() < negativeSuffix.length()) {
128.1594 + gotPositive = false;
128.1595 + }
128.1596 + }
128.1597 +
128.1598 + // fail if neither or both
128.1599 + if (gotPositive == gotNegative) {
128.1600 + parsePosition.errorIndex = position;
128.1601 + return false;
128.1602 + }
128.1603 +
128.1604 + parsePosition.index = position +
128.1605 + (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success!
128.1606 + } else {
128.1607 + parsePosition.index = position;
128.1608 + }
128.1609 +
128.1610 + status[STATUS_POSITIVE] = gotPositive;
128.1611 + if (parsePosition.index == oldStart) {
128.1612 + parsePosition.errorIndex = position;
128.1613 + return false;
128.1614 + }
128.1615 + return true;
128.1616 + }
128.1617 +
128.1618 + /**
128.1619 + * Returns a copy of the decimal format symbols, which is generally not
128.1620 + * changed by the programmer or user.
128.1621 + * @return a copy of the desired DecimalFormatSymbols
128.1622 + * @see java.text.DecimalFormatSymbols
128.1623 + */
128.1624 + public DecimalFormatSymbols getDecimalFormatSymbols() {
128.1625 + try {
128.1626 + // don't allow multiple references
128.1627 + return (DecimalFormatSymbols) symbols.clone();
128.1628 + } catch (Exception foo) {
128.1629 + return null; // should never happen
128.1630 + }
128.1631 + }
128.1632 +
128.1633 +
128.1634 + /**
128.1635 + * Sets the decimal format symbols, which is generally not changed
128.1636 + * by the programmer or user.
128.1637 + * @param newSymbols desired DecimalFormatSymbols
128.1638 + * @see java.text.DecimalFormatSymbols
128.1639 + */
128.1640 + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
128.1641 + try {
128.1642 + // don't allow multiple references
128.1643 + symbols = (DecimalFormatSymbols) newSymbols.clone();
128.1644 + expandAffixes();
128.1645 + } catch (Exception foo) {
128.1646 + // should never happen
128.1647 + }
128.1648 + }
128.1649 +
128.1650 + /**
128.1651 + * Get the positive prefix.
128.1652 + * <P>Examples: +123, $123, sFr123
128.1653 + */
128.1654 + public String getPositivePrefix () {
128.1655 + return positivePrefix;
128.1656 + }
128.1657 +
128.1658 + /**
128.1659 + * Set the positive prefix.
128.1660 + * <P>Examples: +123, $123, sFr123
128.1661 + */
128.1662 + public void setPositivePrefix (String newValue) {
128.1663 + positivePrefix = newValue;
128.1664 + posPrefixPattern = null;
128.1665 + positivePrefixFieldPositions = null;
128.1666 + }
128.1667 +
128.1668 + /**
128.1669 + * Returns the FieldPositions of the fields in the prefix used for
128.1670 + * positive numbers. This is not used if the user has explicitly set
128.1671 + * a positive prefix via <code>setPositivePrefix</code>. This is
128.1672 + * lazily created.
128.1673 + *
128.1674 + * @return FieldPositions in positive prefix
128.1675 + */
128.1676 + private FieldPosition[] getPositivePrefixFieldPositions() {
128.1677 + if (positivePrefixFieldPositions == null) {
128.1678 + if (posPrefixPattern != null) {
128.1679 + positivePrefixFieldPositions = expandAffix(posPrefixPattern);
128.1680 + }
128.1681 + else {
128.1682 + positivePrefixFieldPositions = EmptyFieldPositionArray;
128.1683 + }
128.1684 + }
128.1685 + return positivePrefixFieldPositions;
128.1686 + }
128.1687 +
128.1688 + /**
128.1689 + * Get the negative prefix.
128.1690 + * <P>Examples: -123, ($123) (with negative suffix), sFr-123
128.1691 + */
128.1692 + public String getNegativePrefix () {
128.1693 + return negativePrefix;
128.1694 + }
128.1695 +
128.1696 + /**
128.1697 + * Set the negative prefix.
128.1698 + * <P>Examples: -123, ($123) (with negative suffix), sFr-123
128.1699 + */
128.1700 + public void setNegativePrefix (String newValue) {
128.1701 + negativePrefix = newValue;
128.1702 + negPrefixPattern = null;
128.1703 + }
128.1704 +
128.1705 + /**
128.1706 + * Returns the FieldPositions of the fields in the prefix used for
128.1707 + * negative numbers. This is not used if the user has explicitly set
128.1708 + * a negative prefix via <code>setNegativePrefix</code>. This is
128.1709 + * lazily created.
128.1710 + *
128.1711 + * @return FieldPositions in positive prefix
128.1712 + */
128.1713 + private FieldPosition[] getNegativePrefixFieldPositions() {
128.1714 + if (negativePrefixFieldPositions == null) {
128.1715 + if (negPrefixPattern != null) {
128.1716 + negativePrefixFieldPositions = expandAffix(negPrefixPattern);
128.1717 + }
128.1718 + else {
128.1719 + negativePrefixFieldPositions = EmptyFieldPositionArray;
128.1720 + }
128.1721 + }
128.1722 + return negativePrefixFieldPositions;
128.1723 + }
128.1724 +
128.1725 + /**
128.1726 + * Get the positive suffix.
128.1727 + * <P>Example: 123%
128.1728 + */
128.1729 + public String getPositiveSuffix () {
128.1730 + return positiveSuffix;
128.1731 + }
128.1732 +
128.1733 + /**
128.1734 + * Set the positive suffix.
128.1735 + * <P>Example: 123%
128.1736 + */
128.1737 + public void setPositiveSuffix (String newValue) {
128.1738 + positiveSuffix = newValue;
128.1739 + posSuffixPattern = null;
128.1740 + }
128.1741 +
128.1742 + /**
128.1743 + * Returns the FieldPositions of the fields in the suffix used for
128.1744 + * positive numbers. This is not used if the user has explicitly set
128.1745 + * a positive suffix via <code>setPositiveSuffix</code>. This is
128.1746 + * lazily created.
128.1747 + *
128.1748 + * @return FieldPositions in positive prefix
128.1749 + */
128.1750 + private FieldPosition[] getPositiveSuffixFieldPositions() {
128.1751 + if (positiveSuffixFieldPositions == null) {
128.1752 + if (posSuffixPattern != null) {
128.1753 + positiveSuffixFieldPositions = expandAffix(posSuffixPattern);
128.1754 + }
128.1755 + else {
128.1756 + positiveSuffixFieldPositions = EmptyFieldPositionArray;
128.1757 + }
128.1758 + }
128.1759 + return positiveSuffixFieldPositions;
128.1760 + }
128.1761 +
128.1762 + /**
128.1763 + * Get the negative suffix.
128.1764 + * <P>Examples: -123%, ($123) (with positive suffixes)
128.1765 + */
128.1766 + public String getNegativeSuffix () {
128.1767 + return negativeSuffix;
128.1768 + }
128.1769 +
128.1770 + /**
128.1771 + * Set the negative suffix.
128.1772 + * <P>Examples: 123%
128.1773 + */
128.1774 + public void setNegativeSuffix (String newValue) {
128.1775 + negativeSuffix = newValue;
128.1776 + negSuffixPattern = null;
128.1777 + }
128.1778 +
128.1779 + /**
128.1780 + * Returns the FieldPositions of the fields in the suffix used for
128.1781 + * negative numbers. This is not used if the user has explicitly set
128.1782 + * a negative suffix via <code>setNegativeSuffix</code>. This is
128.1783 + * lazily created.
128.1784 + *
128.1785 + * @return FieldPositions in positive prefix
128.1786 + */
128.1787 + private FieldPosition[] getNegativeSuffixFieldPositions() {
128.1788 + if (negativeSuffixFieldPositions == null) {
128.1789 + if (negSuffixPattern != null) {
128.1790 + negativeSuffixFieldPositions = expandAffix(negSuffixPattern);
128.1791 + }
128.1792 + else {
128.1793 + negativeSuffixFieldPositions = EmptyFieldPositionArray;
128.1794 + }
128.1795 + }
128.1796 + return negativeSuffixFieldPositions;
128.1797 + }
128.1798 +
128.1799 + /**
128.1800 + * Gets the multiplier for use in percent, per mille, and similar
128.1801 + * formats.
128.1802 + *
128.1803 + * @see #setMultiplier(int)
128.1804 + */
128.1805 + public int getMultiplier () {
128.1806 + return multiplier;
128.1807 + }
128.1808 +
128.1809 + /**
128.1810 + * Sets the multiplier for use in percent, per mille, and similar
128.1811 + * formats.
128.1812 + * For a percent format, set the multiplier to 100 and the suffixes to
128.1813 + * have '%' (for Arabic, use the Arabic percent sign).
128.1814 + * For a per mille format, set the multiplier to 1000 and the suffixes to
128.1815 + * have '\u2030'.
128.1816 + *
128.1817 + * <P>Example: with multiplier 100, 1.23 is formatted as "123", and
128.1818 + * "123" is parsed into 1.23.
128.1819 + *
128.1820 + * @see #getMultiplier
128.1821 + */
128.1822 + public void setMultiplier (int newValue) {
128.1823 + multiplier = newValue;
128.1824 + bigDecimalMultiplier = null;
128.1825 + bigIntegerMultiplier = null;
128.1826 + }
128.1827 +
128.1828 + /**
128.1829 + * Return the grouping size. Grouping size is the number of digits between
128.1830 + * grouping separators in the integer portion of a number. For example,
128.1831 + * in the number "123,456.78", the grouping size is 3.
128.1832 + * @see #setGroupingSize
128.1833 + * @see java.text.NumberFormat#isGroupingUsed
128.1834 + * @see java.text.DecimalFormatSymbols#getGroupingSeparator
128.1835 + */
128.1836 + public int getGroupingSize () {
128.1837 + return groupingSize;
128.1838 + }
128.1839 +
128.1840 + /**
128.1841 + * Set the grouping size. Grouping size is the number of digits between
128.1842 + * grouping separators in the integer portion of a number. For example,
128.1843 + * in the number "123,456.78", the grouping size is 3.
128.1844 + * <br>
128.1845 + * The value passed in is converted to a byte, which may lose information.
128.1846 + * @see #getGroupingSize
128.1847 + * @see java.text.NumberFormat#setGroupingUsed
128.1848 + * @see java.text.DecimalFormatSymbols#setGroupingSeparator
128.1849 + */
128.1850 + public void setGroupingSize (int newValue) {
128.1851 + groupingSize = (byte)newValue;
128.1852 + }
128.1853 +
128.1854 + /**
128.1855 + * Allows you to get the behavior of the decimal separator with integers.
128.1856 + * (The decimal separator will always appear with decimals.)
128.1857 + * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
128.1858 + */
128.1859 + public boolean isDecimalSeparatorAlwaysShown() {
128.1860 + return decimalSeparatorAlwaysShown;
128.1861 + }
128.1862 +
128.1863 + /**
128.1864 + * Allows you to set the behavior of the decimal separator with integers.
128.1865 + * (The decimal separator will always appear with decimals.)
128.1866 + * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
128.1867 + */
128.1868 + public void setDecimalSeparatorAlwaysShown(boolean newValue) {
128.1869 + decimalSeparatorAlwaysShown = newValue;
128.1870 + }
128.1871 +
128.1872 + /**
128.1873 + * Returns whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
128.1874 + * method returns <code>BigDecimal</code>. The default value is false.
128.1875 + * @see #setParseBigDecimal
128.1876 + * @since 1.5
128.1877 + */
128.1878 + public boolean isParseBigDecimal() {
128.1879 + return parseBigDecimal;
128.1880 + }
128.1881 +
128.1882 + /**
128.1883 + * Sets whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
128.1884 + * method returns <code>BigDecimal</code>.
128.1885 + * @see #isParseBigDecimal
128.1886 + * @since 1.5
128.1887 + */
128.1888 + public void setParseBigDecimal(boolean newValue) {
128.1889 + parseBigDecimal = newValue;
128.1890 + }
128.1891 +
128.1892 + /**
128.1893 + * Standard override; no change in semantics.
128.1894 + */
128.1895 + public Object clone() {
128.1896 + try {
128.1897 + DecimalFormat other = (DecimalFormat) super.clone();
128.1898 + other.symbols = (DecimalFormatSymbols) symbols.clone();
128.1899 + other.digitList = (DigitList) digitList.clone();
128.1900 + return other;
128.1901 + } catch (Exception e) {
128.1902 + throw new InternalError();
128.1903 + }
128.1904 + }
128.1905 +
128.1906 + /**
128.1907 + * Overrides equals
128.1908 + */
128.1909 + public boolean equals(Object obj)
128.1910 + {
128.1911 + if (obj == null) return false;
128.1912 + if (!super.equals(obj)) return false; // super does class check
128.1913 + DecimalFormat other = (DecimalFormat) obj;
128.1914 + return ((posPrefixPattern == other.posPrefixPattern &&
128.1915 + positivePrefix.equals(other.positivePrefix))
128.1916 + || (posPrefixPattern != null &&
128.1917 + posPrefixPattern.equals(other.posPrefixPattern)))
128.1918 + && ((posSuffixPattern == other.posSuffixPattern &&
128.1919 + positiveSuffix.equals(other.positiveSuffix))
128.1920 + || (posSuffixPattern != null &&
128.1921 + posSuffixPattern.equals(other.posSuffixPattern)))
128.1922 + && ((negPrefixPattern == other.negPrefixPattern &&
128.1923 + negativePrefix.equals(other.negativePrefix))
128.1924 + || (negPrefixPattern != null &&
128.1925 + negPrefixPattern.equals(other.negPrefixPattern)))
128.1926 + && ((negSuffixPattern == other.negSuffixPattern &&
128.1927 + negativeSuffix.equals(other.negativeSuffix))
128.1928 + || (negSuffixPattern != null &&
128.1929 + negSuffixPattern.equals(other.negSuffixPattern)))
128.1930 + && multiplier == other.multiplier
128.1931 + && groupingSize == other.groupingSize
128.1932 + && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown
128.1933 + && parseBigDecimal == other.parseBigDecimal
128.1934 + && useExponentialNotation == other.useExponentialNotation
128.1935 + && (!useExponentialNotation ||
128.1936 + minExponentDigits == other.minExponentDigits)
128.1937 + && maximumIntegerDigits == other.maximumIntegerDigits
128.1938 + && minimumIntegerDigits == other.minimumIntegerDigits
128.1939 + && maximumFractionDigits == other.maximumFractionDigits
128.1940 + && minimumFractionDigits == other.minimumFractionDigits
128.1941 + && roundingMode == other.roundingMode
128.1942 + && symbols.equals(other.symbols);
128.1943 + }
128.1944 +
128.1945 + /**
128.1946 + * Overrides hashCode
128.1947 + */
128.1948 + public int hashCode() {
128.1949 + return super.hashCode() * 37 + positivePrefix.hashCode();
128.1950 + // just enough fields for a reasonable distribution
128.1951 + }
128.1952 +
128.1953 + /**
128.1954 + * Synthesizes a pattern string that represents the current state
128.1955 + * of this Format object.
128.1956 + * @see #applyPattern
128.1957 + */
128.1958 + public String toPattern() {
128.1959 + return toPattern( false );
128.1960 + }
128.1961 +
128.1962 + /**
128.1963 + * Synthesizes a localized pattern string that represents the current
128.1964 + * state of this Format object.
128.1965 + * @see #applyPattern
128.1966 + */
128.1967 + public String toLocalizedPattern() {
128.1968 + return toPattern( true );
128.1969 + }
128.1970 +
128.1971 + /**
128.1972 + * Expand the affix pattern strings into the expanded affix strings. If any
128.1973 + * affix pattern string is null, do not expand it. This method should be
128.1974 + * called any time the symbols or the affix patterns change in order to keep
128.1975 + * the expanded affix strings up to date.
128.1976 + */
128.1977 + private void expandAffixes() {
128.1978 + // Reuse one StringBuffer for better performance
128.1979 + StringBuffer buffer = new StringBuffer();
128.1980 + if (posPrefixPattern != null) {
128.1981 + positivePrefix = expandAffix(posPrefixPattern, buffer);
128.1982 + positivePrefixFieldPositions = null;
128.1983 + }
128.1984 + if (posSuffixPattern != null) {
128.1985 + positiveSuffix = expandAffix(posSuffixPattern, buffer);
128.1986 + positiveSuffixFieldPositions = null;
128.1987 + }
128.1988 + if (negPrefixPattern != null) {
128.1989 + negativePrefix = expandAffix(negPrefixPattern, buffer);
128.1990 + negativePrefixFieldPositions = null;
128.1991 + }
128.1992 + if (negSuffixPattern != null) {
128.1993 + negativeSuffix = expandAffix(negSuffixPattern, buffer);
128.1994 + negativeSuffixFieldPositions = null;
128.1995 + }
128.1996 + }
128.1997 +
128.1998 + /**
128.1999 + * Expand an affix pattern into an affix string. All characters in the
128.2000 + * pattern are literal unless prefixed by QUOTE. The following characters
128.2001 + * after QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE,
128.2002 + * PATTERN_MINUS, and CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE +
128.2003 + * CURRENCY_SIGN + CURRENCY_SIGN), it is interpreted as an ISO 4217
128.2004 + * currency code. Any other character after a QUOTE represents itself.
128.2005 + * QUOTE must be followed by another character; QUOTE may not occur by
128.2006 + * itself at the end of the pattern.
128.2007 + *
128.2008 + * @param pattern the non-null, possibly empty pattern
128.2009 + * @param buffer a scratch StringBuffer; its contents will be lost
128.2010 + * @return the expanded equivalent of pattern
128.2011 + */
128.2012 + private String expandAffix(String pattern, StringBuffer buffer) {
128.2013 + buffer.setLength(0);
128.2014 + for (int i=0; i<pattern.length(); ) {
128.2015 + char c = pattern.charAt(i++);
128.2016 + if (c == QUOTE) {
128.2017 + c = pattern.charAt(i++);
128.2018 + switch (c) {
128.2019 + case CURRENCY_SIGN:
128.2020 + if (i<pattern.length() &&
128.2021 + pattern.charAt(i) == CURRENCY_SIGN) {
128.2022 + ++i;
128.2023 + buffer.append(symbols.getInternationalCurrencySymbol());
128.2024 + } else {
128.2025 + buffer.append(symbols.getCurrencySymbol());
128.2026 + }
128.2027 + continue;
128.2028 + case PATTERN_PERCENT:
128.2029 + c = symbols.getPercent();
128.2030 + break;
128.2031 + case PATTERN_PER_MILLE:
128.2032 + c = symbols.getPerMill();
128.2033 + break;
128.2034 + case PATTERN_MINUS:
128.2035 + c = symbols.getMinusSign();
128.2036 + break;
128.2037 + }
128.2038 + }
128.2039 + buffer.append(c);
128.2040 + }
128.2041 + return buffer.toString();
128.2042 + }
128.2043 +
128.2044 + /**
128.2045 + * Expand an affix pattern into an array of FieldPositions describing
128.2046 + * how the pattern would be expanded.
128.2047 + * All characters in the
128.2048 + * pattern are literal unless prefixed by QUOTE. The following characters
128.2049 + * after QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE,
128.2050 + * PATTERN_MINUS, and CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE +
128.2051 + * CURRENCY_SIGN + CURRENCY_SIGN), it is interpreted as an ISO 4217
128.2052 + * currency code. Any other character after a QUOTE represents itself.
128.2053 + * QUOTE must be followed by another character; QUOTE may not occur by
128.2054 + * itself at the end of the pattern.
128.2055 + *
128.2056 + * @param pattern the non-null, possibly empty pattern
128.2057 + * @return FieldPosition array of the resulting fields.
128.2058 + */
128.2059 + private FieldPosition[] expandAffix(String pattern) {
128.2060 + ArrayList positions = null;
128.2061 + int stringIndex = 0;
128.2062 + for (int i=0; i<pattern.length(); ) {
128.2063 + char c = pattern.charAt(i++);
128.2064 + if (c == QUOTE) {
128.2065 + int field = -1;
128.2066 + Format.Field fieldID = null;
128.2067 + c = pattern.charAt(i++);
128.2068 + switch (c) {
128.2069 + case CURRENCY_SIGN:
128.2070 + String string;
128.2071 + if (i<pattern.length() &&
128.2072 + pattern.charAt(i) == CURRENCY_SIGN) {
128.2073 + ++i;
128.2074 + string = symbols.getInternationalCurrencySymbol();
128.2075 + } else {
128.2076 + string = symbols.getCurrencySymbol();
128.2077 + }
128.2078 + if (string.length() > 0) {
128.2079 + if (positions == null) {
128.2080 + positions = new ArrayList(2);
128.2081 + }
128.2082 + FieldPosition fp = new FieldPosition(Field.CURRENCY);
128.2083 + fp.setBeginIndex(stringIndex);
128.2084 + fp.setEndIndex(stringIndex + string.length());
128.2085 + positions.add(fp);
128.2086 + stringIndex += string.length();
128.2087 + }
128.2088 + continue;
128.2089 + case PATTERN_PERCENT:
128.2090 + c = symbols.getPercent();
128.2091 + field = -1;
128.2092 + fieldID = Field.PERCENT;
128.2093 + break;
128.2094 + case PATTERN_PER_MILLE:
128.2095 + c = symbols.getPerMill();
128.2096 + field = -1;
128.2097 + fieldID = Field.PERMILLE;
128.2098 + break;
128.2099 + case PATTERN_MINUS:
128.2100 + c = symbols.getMinusSign();
128.2101 + field = -1;
128.2102 + fieldID = Field.SIGN;
128.2103 + break;
128.2104 + }
128.2105 + if (fieldID != null) {
128.2106 + if (positions == null) {
128.2107 + positions = new ArrayList(2);
128.2108 + }
128.2109 + FieldPosition fp = new FieldPosition(fieldID, field);
128.2110 + fp.setBeginIndex(stringIndex);
128.2111 + fp.setEndIndex(stringIndex + 1);
128.2112 + positions.add(fp);
128.2113 + }
128.2114 + }
128.2115 + stringIndex++;
128.2116 + }
128.2117 + if (positions != null) {
128.2118 + return (FieldPosition[])positions.toArray(EmptyFieldPositionArray);
128.2119 + }
128.2120 + return EmptyFieldPositionArray;
128.2121 + }
128.2122 +
128.2123 + /**
128.2124 + * Appends an affix pattern to the given StringBuffer, quoting special
128.2125 + * characters as needed. Uses the internal affix pattern, if that exists,
128.2126 + * or the literal affix, if the internal affix pattern is null. The
128.2127 + * appended string will generate the same affix pattern (or literal affix)
128.2128 + * when passed to toPattern().
128.2129 + *
128.2130 + * @param buffer the affix string is appended to this
128.2131 + * @param affixPattern a pattern such as posPrefixPattern; may be null
128.2132 + * @param expAffix a corresponding expanded affix, such as positivePrefix.
128.2133 + * Ignored unless affixPattern is null. If affixPattern is null, then
128.2134 + * expAffix is appended as a literal affix.
128.2135 + * @param localized true if the appended pattern should contain localized
128.2136 + * pattern characters; otherwise, non-localized pattern chars are appended
128.2137 + */
128.2138 + private void appendAffix(StringBuffer buffer, String affixPattern,
128.2139 + String expAffix, boolean localized) {
128.2140 + if (affixPattern == null) {
128.2141 + appendAffix(buffer, expAffix, localized);
128.2142 + } else {
128.2143 + int i;
128.2144 + for (int pos=0; pos<affixPattern.length(); pos=i) {
128.2145 + i = affixPattern.indexOf(QUOTE, pos);
128.2146 + if (i < 0) {
128.2147 + appendAffix(buffer, affixPattern.substring(pos), localized);
128.2148 + break;
128.2149 + }
128.2150 + if (i > pos) {
128.2151 + appendAffix(buffer, affixPattern.substring(pos, i), localized);
128.2152 + }
128.2153 + char c = affixPattern.charAt(++i);
128.2154 + ++i;
128.2155 + if (c == QUOTE) {
128.2156 + buffer.append(c);
128.2157 + // Fall through and append another QUOTE below
128.2158 + } else if (c == CURRENCY_SIGN &&
128.2159 + i<affixPattern.length() &&
128.2160 + affixPattern.charAt(i) == CURRENCY_SIGN) {
128.2161 + ++i;
128.2162 + buffer.append(c);
128.2163 + // Fall through and append another CURRENCY_SIGN below
128.2164 + } else if (localized) {
128.2165 + switch (c) {
128.2166 + case PATTERN_PERCENT:
128.2167 + c = symbols.getPercent();
128.2168 + break;
128.2169 + case PATTERN_PER_MILLE:
128.2170 + c = symbols.getPerMill();
128.2171 + break;
128.2172 + case PATTERN_MINUS:
128.2173 + c = symbols.getMinusSign();
128.2174 + break;
128.2175 + }
128.2176 + }
128.2177 + buffer.append(c);
128.2178 + }
128.2179 + }
128.2180 + }
128.2181 +
128.2182 + /**
128.2183 + * Append an affix to the given StringBuffer, using quotes if
128.2184 + * there are special characters. Single quotes themselves must be
128.2185 + * escaped in either case.
128.2186 + */
128.2187 + private void appendAffix(StringBuffer buffer, String affix, boolean localized) {
128.2188 + boolean needQuote;
128.2189 + if (localized) {
128.2190 + needQuote = affix.indexOf(symbols.getZeroDigit()) >= 0
128.2191 + || affix.indexOf(symbols.getGroupingSeparator()) >= 0
128.2192 + || affix.indexOf(symbols.getDecimalSeparator()) >= 0
128.2193 + || affix.indexOf(symbols.getPercent()) >= 0
128.2194 + || affix.indexOf(symbols.getPerMill()) >= 0
128.2195 + || affix.indexOf(symbols.getDigit()) >= 0
128.2196 + || affix.indexOf(symbols.getPatternSeparator()) >= 0
128.2197 + || affix.indexOf(symbols.getMinusSign()) >= 0
128.2198 + || affix.indexOf(CURRENCY_SIGN) >= 0;
128.2199 + }
128.2200 + else {
128.2201 + needQuote = affix.indexOf(PATTERN_ZERO_DIGIT) >= 0
128.2202 + || affix.indexOf(PATTERN_GROUPING_SEPARATOR) >= 0
128.2203 + || affix.indexOf(PATTERN_DECIMAL_SEPARATOR) >= 0
128.2204 + || affix.indexOf(PATTERN_PERCENT) >= 0
128.2205 + || affix.indexOf(PATTERN_PER_MILLE) >= 0
128.2206 + || affix.indexOf(PATTERN_DIGIT) >= 0
128.2207 + || affix.indexOf(PATTERN_SEPARATOR) >= 0
128.2208 + || affix.indexOf(PATTERN_MINUS) >= 0
128.2209 + || affix.indexOf(CURRENCY_SIGN) >= 0;
128.2210 + }
128.2211 + if (needQuote) buffer.append('\'');
128.2212 + if (affix.indexOf('\'') < 0) buffer.append(affix);
128.2213 + else {
128.2214 + for (int j=0; j<affix.length(); ++j) {
128.2215 + char c = affix.charAt(j);
128.2216 + buffer.append(c);
128.2217 + if (c == '\'') buffer.append(c);
128.2218 + }
128.2219 + }
128.2220 + if (needQuote) buffer.append('\'');
128.2221 + }
128.2222 +
128.2223 + /**
128.2224 + * Does the real work of generating a pattern. */
128.2225 + private String toPattern(boolean localized) {
128.2226 + StringBuffer result = new StringBuffer();
128.2227 + for (int j = 1; j >= 0; --j) {
128.2228 + if (j == 1)
128.2229 + appendAffix(result, posPrefixPattern, positivePrefix, localized);
128.2230 + else appendAffix(result, negPrefixPattern, negativePrefix, localized);
128.2231 + int i;
128.2232 + int digitCount = useExponentialNotation
128.2233 + ? getMaximumIntegerDigits()
128.2234 + : Math.max(groupingSize, getMinimumIntegerDigits())+1;
128.2235 + for (i = digitCount; i > 0; --i) {
128.2236 + if (i != digitCount && isGroupingUsed() && groupingSize != 0 &&
128.2237 + i % groupingSize == 0) {
128.2238 + result.append(localized ? symbols.getGroupingSeparator() :
128.2239 + PATTERN_GROUPING_SEPARATOR);
128.2240 + }
128.2241 + result.append(i <= getMinimumIntegerDigits()
128.2242 + ? (localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT)
128.2243 + : (localized ? symbols.getDigit() : PATTERN_DIGIT));
128.2244 + }
128.2245 + if (getMaximumFractionDigits() > 0 || decimalSeparatorAlwaysShown)
128.2246 + result.append(localized ? symbols.getDecimalSeparator() :
128.2247 + PATTERN_DECIMAL_SEPARATOR);
128.2248 + for (i = 0; i < getMaximumFractionDigits(); ++i) {
128.2249 + if (i < getMinimumFractionDigits()) {
128.2250 + result.append(localized ? symbols.getZeroDigit() :
128.2251 + PATTERN_ZERO_DIGIT);
128.2252 + } else {
128.2253 + result.append(localized ? symbols.getDigit() :
128.2254 + PATTERN_DIGIT);
128.2255 + }
128.2256 + }
128.2257 + if (useExponentialNotation)
128.2258 + {
128.2259 + result.append(localized ? symbols.getExponentSeparator() :
128.2260 + PATTERN_EXPONENT);
128.2261 + for (i=0; i<minExponentDigits; ++i)
128.2262 + result.append(localized ? symbols.getZeroDigit() :
128.2263 + PATTERN_ZERO_DIGIT);
128.2264 + }
128.2265 + if (j == 1) {
128.2266 + appendAffix(result, posSuffixPattern, positiveSuffix, localized);
128.2267 + if ((negSuffixPattern == posSuffixPattern && // n == p == null
128.2268 + negativeSuffix.equals(positiveSuffix))
128.2269 + || (negSuffixPattern != null &&
128.2270 + negSuffixPattern.equals(posSuffixPattern))) {
128.2271 + if ((negPrefixPattern != null && posPrefixPattern != null &&
128.2272 + negPrefixPattern.equals("'-" + posPrefixPattern)) ||
128.2273 + (negPrefixPattern == posPrefixPattern && // n == p == null
128.2274 + negativePrefix.equals(symbols.getMinusSign() + positivePrefix)))
128.2275 + break;
128.2276 + }
128.2277 + result.append(localized ? symbols.getPatternSeparator() :
128.2278 + PATTERN_SEPARATOR);
128.2279 + } else appendAffix(result, negSuffixPattern, negativeSuffix, localized);
128.2280 + }
128.2281 + return result.toString();
128.2282 + }
128.2283 +
128.2284 + /**
128.2285 + * Apply the given pattern to this Format object. A pattern is a
128.2286 + * short-hand specification for the various formatting properties.
128.2287 + * These properties can also be changed individually through the
128.2288 + * various setter methods.
128.2289 + * <p>
128.2290 + * There is no limit to integer digits set
128.2291 + * by this routine, since that is the typical end-user desire;
128.2292 + * use setMaximumInteger if you want to set a real value.
128.2293 + * For negative numbers, use a second pattern, separated by a semicolon
128.2294 + * <P>Example <code>"#,#00.0#"</code> -> 1,234.56
128.2295 + * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
128.2296 + * a maximum of 2 fraction digits.
128.2297 + * <p>Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in
128.2298 + * parentheses.
128.2299 + * <p>In negative patterns, the minimum and maximum counts are ignored;
128.2300 + * these are presumed to be set in the positive pattern.
128.2301 + *
128.2302 + * @exception NullPointerException if <code>pattern</code> is null
128.2303 + * @exception IllegalArgumentException if the given pattern is invalid.
128.2304 + */
128.2305 + public void applyPattern(String pattern) {
128.2306 + applyPattern(pattern, false);
128.2307 + }
128.2308 +
128.2309 + /**
128.2310 + * Apply the given pattern to this Format object. The pattern
128.2311 + * is assumed to be in a localized notation. A pattern is a
128.2312 + * short-hand specification for the various formatting properties.
128.2313 + * These properties can also be changed individually through the
128.2314 + * various setter methods.
128.2315 + * <p>
128.2316 + * There is no limit to integer digits set
128.2317 + * by this routine, since that is the typical end-user desire;
128.2318 + * use setMaximumInteger if you want to set a real value.
128.2319 + * For negative numbers, use a second pattern, separated by a semicolon
128.2320 + * <P>Example <code>"#,#00.0#"</code> -> 1,234.56
128.2321 + * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
128.2322 + * a maximum of 2 fraction digits.
128.2323 + * <p>Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in
128.2324 + * parentheses.
128.2325 + * <p>In negative patterns, the minimum and maximum counts are ignored;
128.2326 + * these are presumed to be set in the positive pattern.
128.2327 + *
128.2328 + * @exception NullPointerException if <code>pattern</code> is null
128.2329 + * @exception IllegalArgumentException if the given pattern is invalid.
128.2330 + */
128.2331 + public void applyLocalizedPattern(String pattern) {
128.2332 + applyPattern(pattern, true);
128.2333 + }
128.2334 +
128.2335 + /**
128.2336 + * Does the real work of applying a pattern.
128.2337 + */
128.2338 + private void applyPattern(String pattern, boolean localized) {
128.2339 + char zeroDigit = PATTERN_ZERO_DIGIT;
128.2340 + char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
128.2341 + char decimalSeparator = PATTERN_DECIMAL_SEPARATOR;
128.2342 + char percent = PATTERN_PERCENT;
128.2343 + char perMill = PATTERN_PER_MILLE;
128.2344 + char digit = PATTERN_DIGIT;
128.2345 + char separator = PATTERN_SEPARATOR;
128.2346 + String exponent = PATTERN_EXPONENT;
128.2347 + char minus = PATTERN_MINUS;
128.2348 + if (localized) {
128.2349 + zeroDigit = symbols.getZeroDigit();
128.2350 + groupingSeparator = symbols.getGroupingSeparator();
128.2351 + decimalSeparator = symbols.getDecimalSeparator();
128.2352 + percent = symbols.getPercent();
128.2353 + perMill = symbols.getPerMill();
128.2354 + digit = symbols.getDigit();
128.2355 + separator = symbols.getPatternSeparator();
128.2356 + exponent = symbols.getExponentSeparator();
128.2357 + minus = symbols.getMinusSign();
128.2358 + }
128.2359 + boolean gotNegative = false;
128.2360 + decimalSeparatorAlwaysShown = false;
128.2361 + isCurrencyFormat = false;
128.2362 + useExponentialNotation = false;
128.2363 +
128.2364 + // Two variables are used to record the subrange of the pattern
128.2365 + // occupied by phase 1. This is used during the processing of the
128.2366 + // second pattern (the one representing negative numbers) to ensure
128.2367 + // that no deviation exists in phase 1 between the two patterns.
128.2368 + int phaseOneStart = 0;
128.2369 + int phaseOneLength = 0;
128.2370 +
128.2371 + int start = 0;
128.2372 + for (int j = 1; j >= 0 && start < pattern.length(); --j) {
128.2373 + boolean inQuote = false;
128.2374 + StringBuffer prefix = new StringBuffer();
128.2375 + StringBuffer suffix = new StringBuffer();
128.2376 + int decimalPos = -1;
128.2377 + int multiplier = 1;
128.2378 + int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0;
128.2379 + byte groupingCount = -1;
128.2380 +
128.2381 + // The phase ranges from 0 to 2. Phase 0 is the prefix. Phase 1 is
128.2382 + // the section of the pattern with digits, decimal separator,
128.2383 + // grouping characters. Phase 2 is the suffix. In phases 0 and 2,
128.2384 + // percent, per mille, and currency symbols are recognized and
128.2385 + // translated. The separation of the characters into phases is
128.2386 + // strictly enforced; if phase 1 characters are to appear in the
128.2387 + // suffix, for example, they must be quoted.
128.2388 + int phase = 0;
128.2389 +
128.2390 + // The affix is either the prefix or the suffix.
128.2391 + StringBuffer affix = prefix;
128.2392 +
128.2393 + for (int pos = start; pos < pattern.length(); ++pos) {
128.2394 + char ch = pattern.charAt(pos);
128.2395 + switch (phase) {
128.2396 + case 0:
128.2397 + case 2:
128.2398 + // Process the prefix / suffix characters
128.2399 + if (inQuote) {
128.2400 + // A quote within quotes indicates either the closing
128.2401 + // quote or two quotes, which is a quote literal. That
128.2402 + // is, we have the second quote in 'do' or 'don''t'.
128.2403 + if (ch == QUOTE) {
128.2404 + if ((pos+1) < pattern.length() &&
128.2405 + pattern.charAt(pos+1) == QUOTE) {
128.2406 + ++pos;
128.2407 + affix.append("''"); // 'don''t'
128.2408 + } else {
128.2409 + inQuote = false; // 'do'
128.2410 + }
128.2411 + continue;
128.2412 + }
128.2413 + } else {
128.2414 + // Process unquoted characters seen in prefix or suffix
128.2415 + // phase.
128.2416 + if (ch == digit ||
128.2417 + ch == zeroDigit ||
128.2418 + ch == groupingSeparator ||
128.2419 + ch == decimalSeparator) {
128.2420 + phase = 1;
128.2421 + if (j == 1) {
128.2422 + phaseOneStart = pos;
128.2423 + }
128.2424 + --pos; // Reprocess this character
128.2425 + continue;
128.2426 + } else if (ch == CURRENCY_SIGN) {
128.2427 + // Use lookahead to determine if the currency sign
128.2428 + // is doubled or not.
128.2429 + boolean doubled = (pos + 1) < pattern.length() &&
128.2430 + pattern.charAt(pos + 1) == CURRENCY_SIGN;
128.2431 + if (doubled) { // Skip over the doubled character
128.2432 + ++pos;
128.2433 + }
128.2434 + isCurrencyFormat = true;
128.2435 + affix.append(doubled ? "'\u00A4\u00A4" : "'\u00A4");
128.2436 + continue;
128.2437 + } else if (ch == QUOTE) {
128.2438 + // A quote outside quotes indicates either the
128.2439 + // opening quote or two quotes, which is a quote
128.2440 + // literal. That is, we have the first quote in 'do'
128.2441 + // or o''clock.
128.2442 + if (ch == QUOTE) {
128.2443 + if ((pos+1) < pattern.length() &&
128.2444 + pattern.charAt(pos+1) == QUOTE) {
128.2445 + ++pos;
128.2446 + affix.append("''"); // o''clock
128.2447 + } else {
128.2448 + inQuote = true; // 'do'
128.2449 + }
128.2450 + continue;
128.2451 + }
128.2452 + } else if (ch == separator) {
128.2453 + // Don't allow separators before we see digit
128.2454 + // characters of phase 1, and don't allow separators
128.2455 + // in the second pattern (j == 0).
128.2456 + if (phase == 0 || j == 0) {
128.2457 + throw new IllegalArgumentException("Unquoted special character '" +
128.2458 + ch + "' in pattern \"" + pattern + '"');
128.2459 + }
128.2460 + start = pos + 1;
128.2461 + pos = pattern.length();
128.2462 + continue;
128.2463 + }
128.2464 +
128.2465 + // Next handle characters which are appended directly.
128.2466 + else if (ch == percent) {
128.2467 + if (multiplier != 1) {
128.2468 + throw new IllegalArgumentException("Too many percent/per mille characters in pattern \"" +
128.2469 + pattern + '"');
128.2470 + }
128.2471 + multiplier = 100;
128.2472 + affix.append("'%");
128.2473 + continue;
128.2474 + } else if (ch == perMill) {
128.2475 + if (multiplier != 1) {
128.2476 + throw new IllegalArgumentException("Too many percent/per mille characters in pattern \"" +
128.2477 + pattern + '"');
128.2478 + }
128.2479 + multiplier = 1000;
128.2480 + affix.append("'\u2030");
128.2481 + continue;
128.2482 + } else if (ch == minus) {
128.2483 + affix.append("'-");
128.2484 + continue;
128.2485 + }
128.2486 + }
128.2487 + // Note that if we are within quotes, or if this is an
128.2488 + // unquoted, non-special character, then we usually fall
128.2489 + // through to here.
128.2490 + affix.append(ch);
128.2491 + break;
128.2492 +
128.2493 + case 1:
128.2494 + // Phase one must be identical in the two sub-patterns. We
128.2495 + // enforce this by doing a direct comparison. While
128.2496 + // processing the first sub-pattern, we just record its
128.2497 + // length. While processing the second, we compare
128.2498 + // characters.
128.2499 + if (j == 1) {
128.2500 + ++phaseOneLength;
128.2501 + } else {
128.2502 + if (--phaseOneLength == 0) {
128.2503 + phase = 2;
128.2504 + affix = suffix;
128.2505 + }
128.2506 + continue;
128.2507 + }
128.2508 +
128.2509 + // Process the digits, decimal, and grouping characters. We
128.2510 + // record five pieces of information. We expect the digits
128.2511 + // to occur in the pattern ####0000.####, and we record the
128.2512 + // number of left digits, zero (central) digits, and right
128.2513 + // digits. The position of the last grouping character is
128.2514 + // recorded (should be somewhere within the first two blocks
128.2515 + // of characters), as is the position of the decimal point,
128.2516 + // if any (should be in the zero digits). If there is no
128.2517 + // decimal point, then there should be no right digits.
128.2518 + if (ch == digit) {
128.2519 + if (zeroDigitCount > 0) {
128.2520 + ++digitRightCount;
128.2521 + } else {
128.2522 + ++digitLeftCount;
128.2523 + }
128.2524 + if (groupingCount >= 0 && decimalPos < 0) {
128.2525 + ++groupingCount;
128.2526 + }
128.2527 + } else if (ch == zeroDigit) {
128.2528 + if (digitRightCount > 0) {
128.2529 + throw new IllegalArgumentException("Unexpected '0' in pattern \"" +
128.2530 + pattern + '"');
128.2531 + }
128.2532 + ++zeroDigitCount;
128.2533 + if (groupingCount >= 0 && decimalPos < 0) {
128.2534 + ++groupingCount;
128.2535 + }
128.2536 + } else if (ch == groupingSeparator) {
128.2537 + groupingCount = 0;
128.2538 + } else if (ch == decimalSeparator) {
128.2539 + if (decimalPos >= 0) {
128.2540 + throw new IllegalArgumentException("Multiple decimal separators in pattern \"" +
128.2541 + pattern + '"');
128.2542 + }
128.2543 + decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
128.2544 + } else if (pattern.regionMatches(pos, exponent, 0, exponent.length())){
128.2545 + if (useExponentialNotation) {
128.2546 + throw new IllegalArgumentException("Multiple exponential " +
128.2547 + "symbols in pattern \"" + pattern + '"');
128.2548 + }
128.2549 + useExponentialNotation = true;
128.2550 + minExponentDigits = 0;
128.2551 +
128.2552 + // Use lookahead to parse out the exponential part
128.2553 + // of the pattern, then jump into phase 2.
128.2554 + pos = pos+exponent.length();
128.2555 + while (pos < pattern.length() &&
128.2556 + pattern.charAt(pos) == zeroDigit) {
128.2557 + ++minExponentDigits;
128.2558 + ++phaseOneLength;
128.2559 + ++pos;
128.2560 + }
128.2561 +
128.2562 + if ((digitLeftCount + zeroDigitCount) < 1 ||
128.2563 + minExponentDigits < 1) {
128.2564 + throw new IllegalArgumentException("Malformed exponential " +
128.2565 + "pattern \"" + pattern + '"');
128.2566 + }
128.2567 +
128.2568 + // Transition to phase 2
128.2569 + phase = 2;
128.2570 + affix = suffix;
128.2571 + --pos;
128.2572 + continue;
128.2573 + } else {
128.2574 + phase = 2;
128.2575 + affix = suffix;
128.2576 + --pos;
128.2577 + --phaseOneLength;
128.2578 + continue;
128.2579 + }
128.2580 + break;
128.2581 + }
128.2582 + }
128.2583 +
128.2584 + // Handle patterns with no '0' pattern character. These patterns
128.2585 + // are legal, but must be interpreted. "##.###" -> "#0.###".
128.2586 + // ".###" -> ".0##".
128.2587 + /* We allow patterns of the form "####" to produce a zeroDigitCount
128.2588 + * of zero (got that?); although this seems like it might make it
128.2589 + * possible for format() to produce empty strings, format() checks
128.2590 + * for this condition and outputs a zero digit in this situation.
128.2591 + * Having a zeroDigitCount of zero yields a minimum integer digits
128.2592 + * of zero, which allows proper round-trip patterns. That is, we
128.2593 + * don't want "#" to become "#0" when toPattern() is called (even
128.2594 + * though that's what it really is, semantically).
128.2595 + */
128.2596 + if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) {
128.2597 + // Handle "###.###" and "###." and ".###"
128.2598 + int n = decimalPos;
128.2599 + if (n == 0) { // Handle ".###"
128.2600 + ++n;
128.2601 + }
128.2602 + digitRightCount = digitLeftCount - n;
128.2603 + digitLeftCount = n - 1;
128.2604 + zeroDigitCount = 1;
128.2605 + }
128.2606 +
128.2607 + // Do syntax checking on the digits.
128.2608 + if ((decimalPos < 0 && digitRightCount > 0) ||
128.2609 + (decimalPos >= 0 && (decimalPos < digitLeftCount ||
128.2610 + decimalPos > (digitLeftCount + zeroDigitCount))) ||
128.2611 + groupingCount == 0 || inQuote) {
128.2612 + throw new IllegalArgumentException("Malformed pattern \"" +
128.2613 + pattern + '"');
128.2614 + }
128.2615 +
128.2616 + if (j == 1) {
128.2617 + posPrefixPattern = prefix.toString();
128.2618 + posSuffixPattern = suffix.toString();
128.2619 + negPrefixPattern = posPrefixPattern; // assume these for now
128.2620 + negSuffixPattern = posSuffixPattern;
128.2621 + int digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount;
128.2622 + /* The effectiveDecimalPos is the position the decimal is at or
128.2623 + * would be at if there is no decimal. Note that if decimalPos<0,
128.2624 + * then digitTotalCount == digitLeftCount + zeroDigitCount.
128.2625 + */
128.2626 + int effectiveDecimalPos = decimalPos >= 0 ?
128.2627 + decimalPos : digitTotalCount;
128.2628 + setMinimumIntegerDigits(effectiveDecimalPos - digitLeftCount);
128.2629 + setMaximumIntegerDigits(useExponentialNotation ?
128.2630 + digitLeftCount + getMinimumIntegerDigits() :
128.2631 + MAXIMUM_INTEGER_DIGITS);
128.2632 + setMaximumFractionDigits(decimalPos >= 0 ?
128.2633 + (digitTotalCount - decimalPos) : 0);
128.2634 + setMinimumFractionDigits(decimalPos >= 0 ?
128.2635 + (digitLeftCount + zeroDigitCount - decimalPos) : 0);
128.2636 + setGroupingUsed(groupingCount > 0);
128.2637 + this.groupingSize = (groupingCount > 0) ? groupingCount : 0;
128.2638 + this.multiplier = multiplier;
128.2639 + setDecimalSeparatorAlwaysShown(decimalPos == 0 ||
128.2640 + decimalPos == digitTotalCount);
128.2641 + } else {
128.2642 + negPrefixPattern = prefix.toString();
128.2643 + negSuffixPattern = suffix.toString();
128.2644 + gotNegative = true;
128.2645 + }
128.2646 + }
128.2647 +
128.2648 + if (pattern.length() == 0) {
128.2649 + posPrefixPattern = posSuffixPattern = "";
128.2650 + setMinimumIntegerDigits(0);
128.2651 + setMaximumIntegerDigits(MAXIMUM_INTEGER_DIGITS);
128.2652 + setMinimumFractionDigits(0);
128.2653 + setMaximumFractionDigits(MAXIMUM_FRACTION_DIGITS);
128.2654 + }
128.2655 +
128.2656 + // If there was no negative pattern, or if the negative pattern is
128.2657 + // identical to the positive pattern, then prepend the minus sign to
128.2658 + // the positive pattern to form the negative pattern.
128.2659 + if (!gotNegative ||
128.2660 + (negPrefixPattern.equals(posPrefixPattern)
128.2661 + && negSuffixPattern.equals(posSuffixPattern))) {
128.2662 + negSuffixPattern = posSuffixPattern;
128.2663 + negPrefixPattern = "'-" + posPrefixPattern;
128.2664 + }
128.2665 +
128.2666 + expandAffixes();
128.2667 + }
128.2668 +
128.2669 + /**
128.2670 + * Sets the maximum number of digits allowed in the integer portion of a
128.2671 + * number.
128.2672 + * For formatting numbers other than <code>BigInteger</code> and
128.2673 + * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
128.2674 + * 309 is used. Negative input values are replaced with 0.
128.2675 + * @see NumberFormat#setMaximumIntegerDigits
128.2676 + */
128.2677 + public void setMaximumIntegerDigits(int newValue) {
128.2678 + maximumIntegerDigits = Math.min(Math.max(0, newValue), MAXIMUM_INTEGER_DIGITS);
128.2679 + super.setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
128.2680 + DOUBLE_INTEGER_DIGITS : maximumIntegerDigits);
128.2681 + if (minimumIntegerDigits > maximumIntegerDigits) {
128.2682 + minimumIntegerDigits = maximumIntegerDigits;
128.2683 + super.setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
128.2684 + DOUBLE_INTEGER_DIGITS : minimumIntegerDigits);
128.2685 + }
128.2686 + }
128.2687 +
128.2688 + /**
128.2689 + * Sets the minimum number of digits allowed in the integer portion of a
128.2690 + * number.
128.2691 + * For formatting numbers other than <code>BigInteger</code> and
128.2692 + * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
128.2693 + * 309 is used. Negative input values are replaced with 0.
128.2694 + * @see NumberFormat#setMinimumIntegerDigits
128.2695 + */
128.2696 + public void setMinimumIntegerDigits(int newValue) {
128.2697 + minimumIntegerDigits = Math.min(Math.max(0, newValue), MAXIMUM_INTEGER_DIGITS);
128.2698 + super.setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
128.2699 + DOUBLE_INTEGER_DIGITS : minimumIntegerDigits);
128.2700 + if (minimumIntegerDigits > maximumIntegerDigits) {
128.2701 + maximumIntegerDigits = minimumIntegerDigits;
128.2702 + super.setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
128.2703 + DOUBLE_INTEGER_DIGITS : maximumIntegerDigits);
128.2704 + }
128.2705 + }
128.2706 +
128.2707 + /**
128.2708 + * Sets the maximum number of digits allowed in the fraction portion of a
128.2709 + * number.
128.2710 + * For formatting numbers other than <code>BigInteger</code> and
128.2711 + * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
128.2712 + * 340 is used. Negative input values are replaced with 0.
128.2713 + * @see NumberFormat#setMaximumFractionDigits
128.2714 + */
128.2715 + public void setMaximumFractionDigits(int newValue) {
128.2716 + maximumFractionDigits = Math.min(Math.max(0, newValue), MAXIMUM_FRACTION_DIGITS);
128.2717 + super.setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
128.2718 + DOUBLE_FRACTION_DIGITS : maximumFractionDigits);
128.2719 + if (minimumFractionDigits > maximumFractionDigits) {
128.2720 + minimumFractionDigits = maximumFractionDigits;
128.2721 + super.setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
128.2722 + DOUBLE_FRACTION_DIGITS : minimumFractionDigits);
128.2723 + }
128.2724 + }
128.2725 +
128.2726 + /**
128.2727 + * Sets the minimum number of digits allowed in the fraction portion of a
128.2728 + * number.
128.2729 + * For formatting numbers other than <code>BigInteger</code> and
128.2730 + * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
128.2731 + * 340 is used. Negative input values are replaced with 0.
128.2732 + * @see NumberFormat#setMinimumFractionDigits
128.2733 + */
128.2734 + public void setMinimumFractionDigits(int newValue) {
128.2735 + minimumFractionDigits = Math.min(Math.max(0, newValue), MAXIMUM_FRACTION_DIGITS);
128.2736 + super.setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
128.2737 + DOUBLE_FRACTION_DIGITS : minimumFractionDigits);
128.2738 + if (minimumFractionDigits > maximumFractionDigits) {
128.2739 + maximumFractionDigits = minimumFractionDigits;
128.2740 + super.setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
128.2741 + DOUBLE_FRACTION_DIGITS : maximumFractionDigits);
128.2742 + }
128.2743 + }
128.2744 +
128.2745 + /**
128.2746 + * Gets the maximum number of digits allowed in the integer portion of a
128.2747 + * number.
128.2748 + * For formatting numbers other than <code>BigInteger</code> and
128.2749 + * <code>BigDecimal</code> objects, the lower of the return value and
128.2750 + * 309 is used.
128.2751 + * @see #setMaximumIntegerDigits
128.2752 + */
128.2753 + public int getMaximumIntegerDigits() {
128.2754 + return maximumIntegerDigits;
128.2755 + }
128.2756 +
128.2757 + /**
128.2758 + * Gets the minimum number of digits allowed in the integer portion of a
128.2759 + * number.
128.2760 + * For formatting numbers other than <code>BigInteger</code> and
128.2761 + * <code>BigDecimal</code> objects, the lower of the return value and
128.2762 + * 309 is used.
128.2763 + * @see #setMinimumIntegerDigits
128.2764 + */
128.2765 + public int getMinimumIntegerDigits() {
128.2766 + return minimumIntegerDigits;
128.2767 + }
128.2768 +
128.2769 + /**
128.2770 + * Gets the maximum number of digits allowed in the fraction portion of a
128.2771 + * number.
128.2772 + * For formatting numbers other than <code>BigInteger</code> and
128.2773 + * <code>BigDecimal</code> objects, the lower of the return value and
128.2774 + * 340 is used.
128.2775 + * @see #setMaximumFractionDigits
128.2776 + */
128.2777 + public int getMaximumFractionDigits() {
128.2778 + return maximumFractionDigits;
128.2779 + }
128.2780 +
128.2781 + /**
128.2782 + * Gets the minimum number of digits allowed in the fraction portion of a
128.2783 + * number.
128.2784 + * For formatting numbers other than <code>BigInteger</code> and
128.2785 + * <code>BigDecimal</code> objects, the lower of the return value and
128.2786 + * 340 is used.
128.2787 + * @see #setMinimumFractionDigits
128.2788 + */
128.2789 + public int getMinimumFractionDigits() {
128.2790 + return minimumFractionDigits;
128.2791 + }
128.2792 +
128.2793 + /**
128.2794 + * Gets the currency used by this decimal format when formatting
128.2795 + * currency values.
128.2796 + * The currency is obtained by calling
128.2797 + * {@link DecimalFormatSymbols#getCurrency DecimalFormatSymbols.getCurrency}
128.2798 + * on this number format's symbols.
128.2799 + *
128.2800 + * @return the currency used by this decimal format, or <code>null</code>
128.2801 + * @since 1.4
128.2802 + */
128.2803 + public Currency getCurrency() {
128.2804 + return symbols.getCurrency();
128.2805 + }
128.2806 +
128.2807 + /**
128.2808 + * Sets the currency used by this number format when formatting
128.2809 + * currency values. This does not update the minimum or maximum
128.2810 + * number of fraction digits used by the number format.
128.2811 + * The currency is set by calling
128.2812 + * {@link DecimalFormatSymbols#setCurrency DecimalFormatSymbols.setCurrency}
128.2813 + * on this number format's symbols.
128.2814 + *
128.2815 + * @param currency the new currency to be used by this decimal format
128.2816 + * @exception NullPointerException if <code>currency</code> is null
128.2817 + * @since 1.4
128.2818 + */
128.2819 + public void setCurrency(Currency currency) {
128.2820 + if (currency != symbols.getCurrency()) {
128.2821 + symbols.setCurrency(currency);
128.2822 + if (isCurrencyFormat) {
128.2823 + expandAffixes();
128.2824 + }
128.2825 + }
128.2826 + }
128.2827 +
128.2828 + /**
128.2829 + * Gets the {@link java.math.RoundingMode} used in this DecimalFormat.
128.2830 + *
128.2831 + * @return The <code>RoundingMode</code> used for this DecimalFormat.
128.2832 + * @see #setRoundingMode(RoundingMode)
128.2833 + * @since 1.6
128.2834 + */
128.2835 + public RoundingMode getRoundingMode() {
128.2836 + return roundingMode;
128.2837 + }
128.2838 +
128.2839 + /**
128.2840 + * Sets the {@link java.math.RoundingMode} used in this DecimalFormat.
128.2841 + *
128.2842 + * @param roundingMode The <code>RoundingMode</code> to be used
128.2843 + * @see #getRoundingMode()
128.2844 + * @exception NullPointerException if <code>roundingMode</code> is null.
128.2845 + * @since 1.6
128.2846 + */
128.2847 + public void setRoundingMode(RoundingMode roundingMode) {
128.2848 + if (roundingMode == null) {
128.2849 + throw new NullPointerException();
128.2850 + }
128.2851 +
128.2852 + this.roundingMode = roundingMode;
128.2853 + digitList.setRoundingMode(roundingMode);
128.2854 + }
128.2855 +
128.2856 + /**
128.2857 + * Adjusts the minimum and maximum fraction digits to values that
128.2858 + * are reasonable for the currency's default fraction digits.
128.2859 + */
128.2860 + void adjustForCurrencyDefaultFractionDigits() {
128.2861 + Currency currency = symbols.getCurrency();
128.2862 + if (currency == null) {
128.2863 + try {
128.2864 + currency = Currency.getInstance(symbols.getInternationalCurrencySymbol());
128.2865 + } catch (IllegalArgumentException e) {
128.2866 + }
128.2867 + }
128.2868 + if (currency != null) {
128.2869 + int digits = currency.getDefaultFractionDigits();
128.2870 + if (digits != -1) {
128.2871 + int oldMinDigits = getMinimumFractionDigits();
128.2872 + // Common patterns are "#.##", "#.00", "#".
128.2873 + // Try to adjust all of them in a reasonable way.
128.2874 + if (oldMinDigits == getMaximumFractionDigits()) {
128.2875 + setMinimumFractionDigits(digits);
128.2876 + setMaximumFractionDigits(digits);
128.2877 + } else {
128.2878 + setMinimumFractionDigits(Math.min(digits, oldMinDigits));
128.2879 + setMaximumFractionDigits(digits);
128.2880 + }
128.2881 + }
128.2882 + }
128.2883 + }
128.2884 +
128.2885 + /**
128.2886 + * Reads the default serializable fields from the stream and performs
128.2887 + * validations and adjustments for older serialized versions. The
128.2888 + * validations and adjustments are:
128.2889 + * <ol>
128.2890 + * <li>
128.2891 + * Verify that the superclass's digit count fields correctly reflect
128.2892 + * the limits imposed on formatting numbers other than
128.2893 + * <code>BigInteger</code> and <code>BigDecimal</code> objects. These
128.2894 + * limits are stored in the superclass for serialization compatibility
128.2895 + * with older versions, while the limits for <code>BigInteger</code> and
128.2896 + * <code>BigDecimal</code> objects are kept in this class.
128.2897 + * If, in the superclass, the minimum or maximum integer digit count is
128.2898 + * larger than <code>DOUBLE_INTEGER_DIGITS</code> or if the minimum or
128.2899 + * maximum fraction digit count is larger than
128.2900 + * <code>DOUBLE_FRACTION_DIGITS</code>, then the stream data is invalid
128.2901 + * and this method throws an <code>InvalidObjectException</code>.
128.2902 + * <li>
128.2903 + * If <code>serialVersionOnStream</code> is less than 4, initialize
128.2904 + * <code>roundingMode</code> to {@link java.math.RoundingMode#HALF_EVEN
128.2905 + * RoundingMode.HALF_EVEN}. This field is new with version 4.
128.2906 + * <li>
128.2907 + * If <code>serialVersionOnStream</code> is less than 3, then call
128.2908 + * the setters for the minimum and maximum integer and fraction digits with
128.2909 + * the values of the corresponding superclass getters to initialize the
128.2910 + * fields in this class. The fields in this class are new with version 3.
128.2911 + * <li>
128.2912 + * If <code>serialVersionOnStream</code> is less than 1, indicating that
128.2913 + * the stream was written by JDK 1.1, initialize
128.2914 + * <code>useExponentialNotation</code>
128.2915 + * to false, since it was not present in JDK 1.1.
128.2916 + * <li>
128.2917 + * Set <code>serialVersionOnStream</code> to the maximum allowed value so
128.2918 + * that default serialization will work properly if this object is streamed
128.2919 + * out again.
128.2920 + * </ol>
128.2921 + *
128.2922 + * <p>Stream versions older than 2 will not have the affix pattern variables
128.2923 + * <code>posPrefixPattern</code> etc. As a result, they will be initialized
128.2924 + * to <code>null</code>, which means the affix strings will be taken as
128.2925 + * literal values. This is exactly what we want, since that corresponds to
128.2926 + * the pre-version-2 behavior.
128.2927 + */
128.2928 + private void readObject(ObjectInputStream stream)
128.2929 + throws IOException, ClassNotFoundException
128.2930 + {
128.2931 + stream.defaultReadObject();
128.2932 + digitList = new DigitList();
128.2933 +
128.2934 + if (serialVersionOnStream < 4) {
128.2935 + setRoundingMode(RoundingMode.HALF_EVEN);
128.2936 + }
128.2937 + // We only need to check the maximum counts because NumberFormat
128.2938 + // .readObject has already ensured that the maximum is greater than the
128.2939 + // minimum count.
128.2940 + if (super.getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS ||
128.2941 + super.getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
128.2942 + throw new InvalidObjectException("Digit count out of range");
128.2943 + }
128.2944 + if (serialVersionOnStream < 3) {
128.2945 + setMaximumIntegerDigits(super.getMaximumIntegerDigits());
128.2946 + setMinimumIntegerDigits(super.getMinimumIntegerDigits());
128.2947 + setMaximumFractionDigits(super.getMaximumFractionDigits());
128.2948 + setMinimumFractionDigits(super.getMinimumFractionDigits());
128.2949 + }
128.2950 + if (serialVersionOnStream < 1) {
128.2951 + // Didn't have exponential fields
128.2952 + useExponentialNotation = false;
128.2953 + }
128.2954 + serialVersionOnStream = currentSerialVersion;
128.2955 + }
128.2956 +
128.2957 + //----------------------------------------------------------------------
128.2958 + // INSTANCE VARIABLES
128.2959 + //----------------------------------------------------------------------
128.2960 +
128.2961 + private transient DigitList digitList = new DigitList();
128.2962 +
128.2963 + /**
128.2964 + * The symbol used as a prefix when formatting positive numbers, e.g. "+".
128.2965 + *
128.2966 + * @serial
128.2967 + * @see #getPositivePrefix
128.2968 + */
128.2969 + private String positivePrefix = "";
128.2970 +
128.2971 + /**
128.2972 + * The symbol used as a suffix when formatting positive numbers.
128.2973 + * This is often an empty string.
128.2974 + *
128.2975 + * @serial
128.2976 + * @see #getPositiveSuffix
128.2977 + */
128.2978 + private String positiveSuffix = "";
128.2979 +
128.2980 + /**
128.2981 + * The symbol used as a prefix when formatting negative numbers, e.g. "-".
128.2982 + *
128.2983 + * @serial
128.2984 + * @see #getNegativePrefix
128.2985 + */
128.2986 + private String negativePrefix = "-";
128.2987 +
128.2988 + /**
128.2989 + * The symbol used as a suffix when formatting negative numbers.
128.2990 + * This is often an empty string.
128.2991 + *
128.2992 + * @serial
128.2993 + * @see #getNegativeSuffix
128.2994 + */
128.2995 + private String negativeSuffix = "";
128.2996 +
128.2997 + /**
128.2998 + * The prefix pattern for non-negative numbers. This variable corresponds
128.2999 + * to <code>positivePrefix</code>.
128.3000 + *
128.3001 + * <p>This pattern is expanded by the method <code>expandAffix()</code> to
128.3002 + * <code>positivePrefix</code> to update the latter to reflect changes in
128.3003 + * <code>symbols</code>. If this variable is <code>null</code> then
128.3004 + * <code>positivePrefix</code> is taken as a literal value that does not
128.3005 + * change when <code>symbols</code> changes. This variable is always
128.3006 + * <code>null</code> for <code>DecimalFormat</code> objects older than
128.3007 + * stream version 2 restored from stream.
128.3008 + *
128.3009 + * @serial
128.3010 + * @since 1.3
128.3011 + */
128.3012 + private String posPrefixPattern;
128.3013 +
128.3014 + /**
128.3015 + * The suffix pattern for non-negative numbers. This variable corresponds
128.3016 + * to <code>positiveSuffix</code>. This variable is analogous to
128.3017 + * <code>posPrefixPattern</code>; see that variable for further
128.3018 + * documentation.
128.3019 + *
128.3020 + * @serial
128.3021 + * @since 1.3
128.3022 + */
128.3023 + private String posSuffixPattern;
128.3024 +
128.3025 + /**
128.3026 + * The prefix pattern for negative numbers. This variable corresponds
128.3027 + * to <code>negativePrefix</code>. This variable is analogous to
128.3028 + * <code>posPrefixPattern</code>; see that variable for further
128.3029 + * documentation.
128.3030 + *
128.3031 + * @serial
128.3032 + * @since 1.3
128.3033 + */
128.3034 + private String negPrefixPattern;
128.3035 +
128.3036 + /**
128.3037 + * The suffix pattern for negative numbers. This variable corresponds
128.3038 + * to <code>negativeSuffix</code>. This variable is analogous to
128.3039 + * <code>posPrefixPattern</code>; see that variable for further
128.3040 + * documentation.
128.3041 + *
128.3042 + * @serial
128.3043 + * @since 1.3
128.3044 + */
128.3045 + private String negSuffixPattern;
128.3046 +
128.3047 + /**
128.3048 + * The multiplier for use in percent, per mille, etc.
128.3049 + *
128.3050 + * @serial
128.3051 + * @see #getMultiplier
128.3052 + */
128.3053 + private int multiplier = 1;
128.3054 +
128.3055 + /**
128.3056 + * The number of digits between grouping separators in the integer
128.3057 + * portion of a number. Must be greater than 0 if
128.3058 + * <code>NumberFormat.groupingUsed</code> is true.
128.3059 + *
128.3060 + * @serial
128.3061 + * @see #getGroupingSize
128.3062 + * @see java.text.NumberFormat#isGroupingUsed
128.3063 + */
128.3064 + private byte groupingSize = 3; // invariant, > 0 if useThousands
128.3065 +
128.3066 + /**
128.3067 + * If true, forces the decimal separator to always appear in a formatted
128.3068 + * number, even if the fractional part of the number is zero.
128.3069 + *
128.3070 + * @serial
128.3071 + * @see #isDecimalSeparatorAlwaysShown
128.3072 + */
128.3073 + private boolean decimalSeparatorAlwaysShown = false;
128.3074 +
128.3075 + /**
128.3076 + * If true, parse returns BigDecimal wherever possible.
128.3077 + *
128.3078 + * @serial
128.3079 + * @see #isParseBigDecimal
128.3080 + * @since 1.5
128.3081 + */
128.3082 + private boolean parseBigDecimal = false;
128.3083 +
128.3084 +
128.3085 + /**
128.3086 + * True if this object represents a currency format. This determines
128.3087 + * whether the monetary decimal separator is used instead of the normal one.
128.3088 + */
128.3089 + private transient boolean isCurrencyFormat = false;
128.3090 +
128.3091 + /**
128.3092 + * The <code>DecimalFormatSymbols</code> object used by this format.
128.3093 + * It contains the symbols used to format numbers, e.g. the grouping separator,
128.3094 + * decimal separator, and so on.
128.3095 + *
128.3096 + * @serial
128.3097 + * @see #setDecimalFormatSymbols
128.3098 + * @see java.text.DecimalFormatSymbols
128.3099 + */
128.3100 + private DecimalFormatSymbols symbols = null; // LIU new DecimalFormatSymbols();
128.3101 +
128.3102 + /**
128.3103 + * True to force the use of exponential (i.e. scientific) notation when formatting
128.3104 + * numbers.
128.3105 + *
128.3106 + * @serial
128.3107 + * @since 1.2
128.3108 + */
128.3109 + private boolean useExponentialNotation; // Newly persistent in the Java 2 platform v.1.2
128.3110 +
128.3111 + /**
128.3112 + * FieldPositions describing the positive prefix String. This is
128.3113 + * lazily created. Use <code>getPositivePrefixFieldPositions</code>
128.3114 + * when needed.
128.3115 + */
128.3116 + private transient FieldPosition[] positivePrefixFieldPositions;
128.3117 +
128.3118 + /**
128.3119 + * FieldPositions describing the positive suffix String. This is
128.3120 + * lazily created. Use <code>getPositiveSuffixFieldPositions</code>
128.3121 + * when needed.
128.3122 + */
128.3123 + private transient FieldPosition[] positiveSuffixFieldPositions;
128.3124 +
128.3125 + /**
128.3126 + * FieldPositions describing the negative prefix String. This is
128.3127 + * lazily created. Use <code>getNegativePrefixFieldPositions</code>
128.3128 + * when needed.
128.3129 + */
128.3130 + private transient FieldPosition[] negativePrefixFieldPositions;
128.3131 +
128.3132 + /**
128.3133 + * FieldPositions describing the negative suffix String. This is
128.3134 + * lazily created. Use <code>getNegativeSuffixFieldPositions</code>
128.3135 + * when needed.
128.3136 + */
128.3137 + private transient FieldPosition[] negativeSuffixFieldPositions;
128.3138 +
128.3139 + /**
128.3140 + * The minimum number of digits used to display the exponent when a number is
128.3141 + * formatted in exponential notation. This field is ignored if
128.3142 + * <code>useExponentialNotation</code> is not true.
128.3143 + *
128.3144 + * @serial
128.3145 + * @since 1.2
128.3146 + */
128.3147 + private byte minExponentDigits; // Newly persistent in the Java 2 platform v.1.2
128.3148 +
128.3149 + /**
128.3150 + * The maximum number of digits allowed in the integer portion of a
128.3151 + * <code>BigInteger</code> or <code>BigDecimal</code> number.
128.3152 + * <code>maximumIntegerDigits</code> must be greater than or equal to
128.3153 + * <code>minimumIntegerDigits</code>.
128.3154 + *
128.3155 + * @serial
128.3156 + * @see #getMaximumIntegerDigits
128.3157 + * @since 1.5
128.3158 + */
128.3159 + private int maximumIntegerDigits = super.getMaximumIntegerDigits();
128.3160 +
128.3161 + /**
128.3162 + * The minimum number of digits allowed in the integer portion of a
128.3163 + * <code>BigInteger</code> or <code>BigDecimal</code> number.
128.3164 + * <code>minimumIntegerDigits</code> must be less than or equal to
128.3165 + * <code>maximumIntegerDigits</code>.
128.3166 + *
128.3167 + * @serial
128.3168 + * @see #getMinimumIntegerDigits
128.3169 + * @since 1.5
128.3170 + */
128.3171 + private int minimumIntegerDigits = super.getMinimumIntegerDigits();
128.3172 +
128.3173 + /**
128.3174 + * The maximum number of digits allowed in the fractional portion of a
128.3175 + * <code>BigInteger</code> or <code>BigDecimal</code> number.
128.3176 + * <code>maximumFractionDigits</code> must be greater than or equal to
128.3177 + * <code>minimumFractionDigits</code>.
128.3178 + *
128.3179 + * @serial
128.3180 + * @see #getMaximumFractionDigits
128.3181 + * @since 1.5
128.3182 + */
128.3183 + private int maximumFractionDigits = super.getMaximumFractionDigits();
128.3184 +
128.3185 + /**
128.3186 + * The minimum number of digits allowed in the fractional portion of a
128.3187 + * <code>BigInteger</code> or <code>BigDecimal</code> number.
128.3188 + * <code>minimumFractionDigits</code> must be less than or equal to
128.3189 + * <code>maximumFractionDigits</code>.
128.3190 + *
128.3191 + * @serial
128.3192 + * @see #getMinimumFractionDigits
128.3193 + * @since 1.5
128.3194 + */
128.3195 + private int minimumFractionDigits = super.getMinimumFractionDigits();
128.3196 +
128.3197 + /**
128.3198 + * The {@link java.math.RoundingMode} used in this DecimalFormat.
128.3199 + *
128.3200 + * @serial
128.3201 + * @since 1.6
128.3202 + */
128.3203 + private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
128.3204 +
128.3205 + //----------------------------------------------------------------------
128.3206 +
128.3207 + static final int currentSerialVersion = 4;
128.3208 +
128.3209 + /**
128.3210 + * The internal serial version which says which version was written.
128.3211 + * Possible values are:
128.3212 + * <ul>
128.3213 + * <li><b>0</b> (default): versions before the Java 2 platform v1.2
128.3214 + * <li><b>1</b>: version for 1.2, which includes the two new fields
128.3215 + * <code>useExponentialNotation</code> and
128.3216 + * <code>minExponentDigits</code>.
128.3217 + * <li><b>2</b>: version for 1.3 and later, which adds four new fields:
128.3218 + * <code>posPrefixPattern</code>, <code>posSuffixPattern</code>,
128.3219 + * <code>negPrefixPattern</code>, and <code>negSuffixPattern</code>.
128.3220 + * <li><b>3</b>: version for 1.5 and later, which adds five new fields:
128.3221 + * <code>maximumIntegerDigits</code>,
128.3222 + * <code>minimumIntegerDigits</code>,
128.3223 + * <code>maximumFractionDigits</code>,
128.3224 + * <code>minimumFractionDigits</code>, and
128.3225 + * <code>parseBigDecimal</code>.
128.3226 + * <li><b>4</b>: version for 1.6 and later, which adds one new field:
128.3227 + * <code>roundingMode</code>.
128.3228 + * </ul>
128.3229 + * @since 1.2
128.3230 + * @serial
128.3231 + */
128.3232 + private int serialVersionOnStream = currentSerialVersion;
128.3233 +
128.3234 + //----------------------------------------------------------------------
128.3235 + // CONSTANTS
128.3236 + //----------------------------------------------------------------------
128.3237 +
128.3238 + // Constants for characters used in programmatic (unlocalized) patterns.
128.3239 + private static final char PATTERN_ZERO_DIGIT = '0';
128.3240 + private static final char PATTERN_GROUPING_SEPARATOR = ',';
128.3241 + private static final char PATTERN_DECIMAL_SEPARATOR = '.';
128.3242 + private static final char PATTERN_PER_MILLE = '\u2030';
128.3243 + private static final char PATTERN_PERCENT = '%';
128.3244 + private static final char PATTERN_DIGIT = '#';
128.3245 + private static final char PATTERN_SEPARATOR = ';';
128.3246 + private static final String PATTERN_EXPONENT = "E";
128.3247 + private static final char PATTERN_MINUS = '-';
128.3248 +
128.3249 + /**
128.3250 + * The CURRENCY_SIGN is the standard Unicode symbol for currency. It
128.3251 + * is used in patterns and substituted with either the currency symbol,
128.3252 + * or if it is doubled, with the international currency symbol. If the
128.3253 + * CURRENCY_SIGN is seen in a pattern, then the decimal separator is
128.3254 + * replaced with the monetary decimal separator.
128.3255 + *
128.3256 + * The CURRENCY_SIGN is not localized.
128.3257 + */
128.3258 + private static final char CURRENCY_SIGN = '\u00A4';
128.3259 +
128.3260 + private static final char QUOTE = '\'';
128.3261 +
128.3262 + private static FieldPosition[] EmptyFieldPositionArray = new FieldPosition[0];
128.3263 +
128.3264 + // Upper limit on integer and fraction digits for a Java double
128.3265 + static final int DOUBLE_INTEGER_DIGITS = 309;
128.3266 + static final int DOUBLE_FRACTION_DIGITS = 340;
128.3267 +
128.3268 + // Upper limit on integer and fraction digits for BigDecimal and BigInteger
128.3269 + static final int MAXIMUM_INTEGER_DIGITS = Integer.MAX_VALUE;
128.3270 + static final int MAXIMUM_FRACTION_DIGITS = Integer.MAX_VALUE;
128.3271 +
128.3272 + // Proclaim JDK 1.1 serial compatibility.
128.3273 + static final long serialVersionUID = 864413376551465018L;
128.3274 +
128.3275 + /**
128.3276 + * Cache to hold the NumberPattern of a Locale.
128.3277 + */
128.3278 + private static final ConcurrentMap<Locale, String> cachedLocaleData
128.3279 + = new ConcurrentHashMap<Locale, String>(3);
128.3280 +}
129.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
129.2 +++ b/rt/emul/compact/src/main/java/java/text/DecimalFormatSymbols.java Tue Feb 11 13:31:42 2014 +0100
129.3 @@ -0,0 +1,834 @@
129.4 +/*
129.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
129.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
129.7 + *
129.8 + * This code is free software; you can redistribute it and/or modify it
129.9 + * under the terms of the GNU General Public License version 2 only, as
129.10 + * published by the Free Software Foundation. Oracle designates this
129.11 + * particular file as subject to the "Classpath" exception as provided
129.12 + * by Oracle in the LICENSE file that accompanied this code.
129.13 + *
129.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
129.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
129.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
129.17 + * version 2 for more details (a copy is included in the LICENSE file that
129.18 + * accompanied this code).
129.19 + *
129.20 + * You should have received a copy of the GNU General Public License version
129.21 + * 2 along with this work; if not, write to the Free Software Foundation,
129.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
129.23 + *
129.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
129.25 + * or visit www.oracle.com if you need additional information or have any
129.26 + * questions.
129.27 + */
129.28 +
129.29 +/*
129.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
129.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
129.32 + *
129.33 + * The original version of this source code and documentation is copyrighted
129.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
129.35 + * materials are provided under terms of a License Agreement between Taligent
129.36 + * and Sun. This technology is protected by multiple US and International
129.37 + * patents. This notice and attribution to Taligent may not be removed.
129.38 + * Taligent is a registered trademark of Taligent, Inc.
129.39 + *
129.40 + */
129.41 +
129.42 +package java.text;
129.43 +
129.44 +import java.io.IOException;
129.45 +import java.io.ObjectInputStream;
129.46 +import java.io.Serializable;
129.47 +import java.util.Currency;
129.48 +import java.util.Locale;
129.49 +import java.util.ResourceBundle;
129.50 +import java.util.concurrent.ConcurrentHashMap;
129.51 +
129.52 +/**
129.53 + * This class represents the set of symbols (such as the decimal separator,
129.54 + * the grouping separator, and so on) needed by <code>DecimalFormat</code>
129.55 + * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
129.56 + * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any
129.57 + * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
129.58 + * your <code>DecimalFormat</code> and modify it.
129.59 + *
129.60 + * @see java.util.Locale
129.61 + * @see DecimalFormat
129.62 + * @author Mark Davis
129.63 + * @author Alan Liu
129.64 + */
129.65 +
129.66 +public class DecimalFormatSymbols implements Cloneable, Serializable {
129.67 +
129.68 + /**
129.69 + * Create a DecimalFormatSymbols object for the default locale.
129.70 + * This constructor can only construct instances for the locales
129.71 + * supported by the Java runtime environment, not for those
129.72 + * supported by installed
129.73 + * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
129.74 + * implementations. For full locale coverage, use the
129.75 + * {@link #getInstance(Locale) getInstance} method.
129.76 + */
129.77 + public DecimalFormatSymbols() {
129.78 + initialize( Locale.getDefault(Locale.Category.FORMAT) );
129.79 + }
129.80 +
129.81 + /**
129.82 + * Create a DecimalFormatSymbols object for the given locale.
129.83 + * This constructor can only construct instances for the locales
129.84 + * supported by the Java runtime environment, not for those
129.85 + * supported by installed
129.86 + * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
129.87 + * implementations. For full locale coverage, use the
129.88 + * {@link #getInstance(Locale) getInstance} method.
129.89 + *
129.90 + * @exception NullPointerException if <code>locale</code> is null
129.91 + */
129.92 + public DecimalFormatSymbols( Locale locale ) {
129.93 + initialize( locale );
129.94 + }
129.95 +
129.96 + /**
129.97 + * Returns an array of all locales for which the
129.98 + * <code>getInstance</code> methods of this class can return
129.99 + * localized instances.
129.100 + * The returned array represents the union of locales supported by the Java
129.101 + * runtime and by installed
129.102 + * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
129.103 + * implementations. It must contain at least a <code>Locale</code>
129.104 + * instance equal to {@link java.util.Locale#US Locale.US}.
129.105 + *
129.106 + * @return An array of locales for which localized
129.107 + * <code>DecimalFormatSymbols</code> instances are available.
129.108 + * @since 1.6
129.109 + */
129.110 + public static Locale[] getAvailableLocales() {
129.111 + return new Locale[] { Locale.US };
129.112 +// LocaleServiceProviderPool pool =
129.113 +// LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
129.114 +// return pool.getAvailableLocales();
129.115 + }
129.116 +
129.117 + /**
129.118 + * Gets the <code>DecimalFormatSymbols</code> instance for the default
129.119 + * locale. This method provides access to <code>DecimalFormatSymbols</code>
129.120 + * instances for locales supported by the Java runtime itself as well
129.121 + * as for those supported by installed
129.122 + * {@link java.text.spi.DecimalFormatSymbolsProvider
129.123 + * DecimalFormatSymbolsProvider} implementations.
129.124 + * @return a <code>DecimalFormatSymbols</code> instance.
129.125 + * @since 1.6
129.126 + */
129.127 + public static final DecimalFormatSymbols getInstance() {
129.128 + return getInstance(Locale.getDefault(Locale.Category.FORMAT));
129.129 + }
129.130 +
129.131 + /**
129.132 + * Gets the <code>DecimalFormatSymbols</code> instance for the specified
129.133 + * locale. This method provides access to <code>DecimalFormatSymbols</code>
129.134 + * instances for locales supported by the Java runtime itself as well
129.135 + * as for those supported by installed
129.136 + * {@link java.text.spi.DecimalFormatSymbolsProvider
129.137 + * DecimalFormatSymbolsProvider} implementations.
129.138 + * @param locale the desired locale.
129.139 + * @return a <code>DecimalFormatSymbols</code> instance.
129.140 + * @exception NullPointerException if <code>locale</code> is null
129.141 + * @since 1.6
129.142 + */
129.143 + public static final DecimalFormatSymbols getInstance(Locale locale) {
129.144 +/*
129.145 + // Check whether a provider can provide an implementation that's closer
129.146 + // to the requested locale than what the Java runtime itself can provide.
129.147 + LocaleServiceProviderPool pool =
129.148 + LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
129.149 + if (pool.hasProviders()) {
129.150 + DecimalFormatSymbols providersInstance = pool.getLocalizedObject(
129.151 + DecimalFormatSymbolsGetter.INSTANCE, locale);
129.152 + if (providersInstance != null) {
129.153 + return providersInstance;
129.154 + }
129.155 + }
129.156 +*/
129.157 + return new DecimalFormatSymbols(locale);
129.158 + }
129.159 +
129.160 + /**
129.161 + * Gets the character used for zero. Different for Arabic, etc.
129.162 + */
129.163 + public char getZeroDigit() {
129.164 + return zeroDigit;
129.165 + }
129.166 +
129.167 + /**
129.168 + * Sets the character used for zero. Different for Arabic, etc.
129.169 + */
129.170 + public void setZeroDigit(char zeroDigit) {
129.171 + this.zeroDigit = zeroDigit;
129.172 + }
129.173 +
129.174 + /**
129.175 + * Gets the character used for thousands separator. Different for French, etc.
129.176 + */
129.177 + public char getGroupingSeparator() {
129.178 + return groupingSeparator;
129.179 + }
129.180 +
129.181 + /**
129.182 + * Sets the character used for thousands separator. Different for French, etc.
129.183 + */
129.184 + public void setGroupingSeparator(char groupingSeparator) {
129.185 + this.groupingSeparator = groupingSeparator;
129.186 + }
129.187 +
129.188 + /**
129.189 + * Gets the character used for decimal sign. Different for French, etc.
129.190 + */
129.191 + public char getDecimalSeparator() {
129.192 + return decimalSeparator;
129.193 + }
129.194 +
129.195 + /**
129.196 + * Sets the character used for decimal sign. Different for French, etc.
129.197 + */
129.198 + public void setDecimalSeparator(char decimalSeparator) {
129.199 + this.decimalSeparator = decimalSeparator;
129.200 + }
129.201 +
129.202 + /**
129.203 + * Gets the character used for per mille sign. Different for Arabic, etc.
129.204 + */
129.205 + public char getPerMill() {
129.206 + return perMill;
129.207 + }
129.208 +
129.209 + /**
129.210 + * Sets the character used for per mille sign. Different for Arabic, etc.
129.211 + */
129.212 + public void setPerMill(char perMill) {
129.213 + this.perMill = perMill;
129.214 + }
129.215 +
129.216 + /**
129.217 + * Gets the character used for percent sign. Different for Arabic, etc.
129.218 + */
129.219 + public char getPercent() {
129.220 + return percent;
129.221 + }
129.222 +
129.223 + /**
129.224 + * Sets the character used for percent sign. Different for Arabic, etc.
129.225 + */
129.226 + public void setPercent(char percent) {
129.227 + this.percent = percent;
129.228 + }
129.229 +
129.230 + /**
129.231 + * Gets the character used for a digit in a pattern.
129.232 + */
129.233 + public char getDigit() {
129.234 + return digit;
129.235 + }
129.236 +
129.237 + /**
129.238 + * Sets the character used for a digit in a pattern.
129.239 + */
129.240 + public void setDigit(char digit) {
129.241 + this.digit = digit;
129.242 + }
129.243 +
129.244 + /**
129.245 + * Gets the character used to separate positive and negative subpatterns
129.246 + * in a pattern.
129.247 + */
129.248 + public char getPatternSeparator() {
129.249 + return patternSeparator;
129.250 + }
129.251 +
129.252 + /**
129.253 + * Sets the character used to separate positive and negative subpatterns
129.254 + * in a pattern.
129.255 + */
129.256 + public void setPatternSeparator(char patternSeparator) {
129.257 + this.patternSeparator = patternSeparator;
129.258 + }
129.259 +
129.260 + /**
129.261 + * Gets the string used to represent infinity. Almost always left
129.262 + * unchanged.
129.263 + */
129.264 + public String getInfinity() {
129.265 + return infinity;
129.266 + }
129.267 +
129.268 + /**
129.269 + * Sets the string used to represent infinity. Almost always left
129.270 + * unchanged.
129.271 + */
129.272 + public void setInfinity(String infinity) {
129.273 + this.infinity = infinity;
129.274 + }
129.275 +
129.276 + /**
129.277 + * Gets the string used to represent "not a number". Almost always left
129.278 + * unchanged.
129.279 + */
129.280 + public String getNaN() {
129.281 + return NaN;
129.282 + }
129.283 +
129.284 + /**
129.285 + * Sets the string used to represent "not a number". Almost always left
129.286 + * unchanged.
129.287 + */
129.288 + public void setNaN(String NaN) {
129.289 + this.NaN = NaN;
129.290 + }
129.291 +
129.292 + /**
129.293 + * Gets the character used to represent minus sign. If no explicit
129.294 + * negative format is specified, one is formed by prefixing
129.295 + * minusSign to the positive format.
129.296 + */
129.297 + public char getMinusSign() {
129.298 + return minusSign;
129.299 + }
129.300 +
129.301 + /**
129.302 + * Sets the character used to represent minus sign. If no explicit
129.303 + * negative format is specified, one is formed by prefixing
129.304 + * minusSign to the positive format.
129.305 + */
129.306 + public void setMinusSign(char minusSign) {
129.307 + this.minusSign = minusSign;
129.308 + }
129.309 +
129.310 + /**
129.311 + * Returns the currency symbol for the currency of these
129.312 + * DecimalFormatSymbols in their locale.
129.313 + * @since 1.2
129.314 + */
129.315 + public String getCurrencySymbol()
129.316 + {
129.317 + return currencySymbol;
129.318 + }
129.319 +
129.320 + /**
129.321 + * Sets the currency symbol for the currency of these
129.322 + * DecimalFormatSymbols in their locale.
129.323 + * @since 1.2
129.324 + */
129.325 + public void setCurrencySymbol(String currency)
129.326 + {
129.327 + currencySymbol = currency;
129.328 + }
129.329 +
129.330 + /**
129.331 + * Returns the ISO 4217 currency code of the currency of these
129.332 + * DecimalFormatSymbols.
129.333 + * @since 1.2
129.334 + */
129.335 + public String getInternationalCurrencySymbol()
129.336 + {
129.337 + return intlCurrencySymbol;
129.338 + }
129.339 +
129.340 + /**
129.341 + * Sets the ISO 4217 currency code of the currency of these
129.342 + * DecimalFormatSymbols.
129.343 + * If the currency code is valid (as defined by
129.344 + * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
129.345 + * this also sets the currency attribute to the corresponding Currency
129.346 + * instance and the currency symbol attribute to the currency's symbol
129.347 + * in the DecimalFormatSymbols' locale. If the currency code is not valid,
129.348 + * then the currency attribute is set to null and the currency symbol
129.349 + * attribute is not modified.
129.350 + *
129.351 + * @see #setCurrency
129.352 + * @see #setCurrencySymbol
129.353 + * @since 1.2
129.354 + */
129.355 + public void setInternationalCurrencySymbol(String currencyCode)
129.356 + {
129.357 + intlCurrencySymbol = currencyCode;
129.358 + currency = null;
129.359 + if (currencyCode != null) {
129.360 + try {
129.361 + currency = Currency.getInstance(currencyCode);
129.362 + currencySymbol = currency.getSymbol();
129.363 + } catch (IllegalArgumentException e) {
129.364 + }
129.365 + }
129.366 + }
129.367 +
129.368 + /**
129.369 + * Gets the currency of these DecimalFormatSymbols. May be null if the
129.370 + * currency symbol attribute was previously set to a value that's not
129.371 + * a valid ISO 4217 currency code.
129.372 + *
129.373 + * @return the currency used, or null
129.374 + * @since 1.4
129.375 + */
129.376 + public Currency getCurrency() {
129.377 + return currency;
129.378 + }
129.379 +
129.380 + /**
129.381 + * Sets the currency of these DecimalFormatSymbols.
129.382 + * This also sets the currency symbol attribute to the currency's symbol
129.383 + * in the DecimalFormatSymbols' locale, and the international currency
129.384 + * symbol attribute to the currency's ISO 4217 currency code.
129.385 + *
129.386 + * @param currency the new currency to be used
129.387 + * @exception NullPointerException if <code>currency</code> is null
129.388 + * @since 1.4
129.389 + * @see #setCurrencySymbol
129.390 + * @see #setInternationalCurrencySymbol
129.391 + */
129.392 + public void setCurrency(Currency currency) {
129.393 + if (currency == null) {
129.394 + throw new NullPointerException();
129.395 + }
129.396 + this.currency = currency;
129.397 + intlCurrencySymbol = currency.getCurrencyCode();
129.398 + currencySymbol = currency.getSymbol(locale);
129.399 + }
129.400 +
129.401 +
129.402 + /**
129.403 + * Returns the monetary decimal separator.
129.404 + * @since 1.2
129.405 + */
129.406 + public char getMonetaryDecimalSeparator()
129.407 + {
129.408 + return monetarySeparator;
129.409 + }
129.410 +
129.411 + /**
129.412 + * Sets the monetary decimal separator.
129.413 + * @since 1.2
129.414 + */
129.415 + public void setMonetaryDecimalSeparator(char sep)
129.416 + {
129.417 + monetarySeparator = sep;
129.418 + }
129.419 +
129.420 + //------------------------------------------------------------
129.421 + // BEGIN Package Private methods ... to be made public later
129.422 + //------------------------------------------------------------
129.423 +
129.424 + /**
129.425 + * Returns the character used to separate the mantissa from the exponent.
129.426 + */
129.427 + char getExponentialSymbol()
129.428 + {
129.429 + return exponential;
129.430 + }
129.431 + /**
129.432 + * Returns the string used to separate the mantissa from the exponent.
129.433 + * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
129.434 + *
129.435 + * @return the exponent separator string
129.436 + * @see #setExponentSeparator(java.lang.String)
129.437 + * @since 1.6
129.438 + */
129.439 + public String getExponentSeparator()
129.440 + {
129.441 + return exponentialSeparator;
129.442 + }
129.443 +
129.444 + /**
129.445 + * Sets the character used to separate the mantissa from the exponent.
129.446 + */
129.447 + void setExponentialSymbol(char exp)
129.448 + {
129.449 + exponential = exp;
129.450 + }
129.451 +
129.452 + /**
129.453 + * Sets the string used to separate the mantissa from the exponent.
129.454 + * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
129.455 + *
129.456 + * @param exp the exponent separator string
129.457 + * @exception NullPointerException if <code>exp</code> is null
129.458 + * @see #getExponentSeparator()
129.459 + * @since 1.6
129.460 + */
129.461 + public void setExponentSeparator(String exp)
129.462 + {
129.463 + if (exp == null) {
129.464 + throw new NullPointerException();
129.465 + }
129.466 + exponentialSeparator = exp;
129.467 + }
129.468 +
129.469 +
129.470 + //------------------------------------------------------------
129.471 + // END Package Private methods ... to be made public later
129.472 + //------------------------------------------------------------
129.473 +
129.474 + /**
129.475 + * Standard override.
129.476 + */
129.477 + public Object clone() {
129.478 + try {
129.479 + return (DecimalFormatSymbols)super.clone();
129.480 + // other fields are bit-copied
129.481 + } catch (CloneNotSupportedException e) {
129.482 + throw new InternalError();
129.483 + }
129.484 + }
129.485 +
129.486 + /**
129.487 + * Override equals.
129.488 + */
129.489 + public boolean equals(Object obj) {
129.490 + if (obj == null) return false;
129.491 + if (this == obj) return true;
129.492 + if (getClass() != obj.getClass()) return false;
129.493 + DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
129.494 + return (zeroDigit == other.zeroDigit &&
129.495 + groupingSeparator == other.groupingSeparator &&
129.496 + decimalSeparator == other.decimalSeparator &&
129.497 + percent == other.percent &&
129.498 + perMill == other.perMill &&
129.499 + digit == other.digit &&
129.500 + minusSign == other.minusSign &&
129.501 + patternSeparator == other.patternSeparator &&
129.502 + infinity.equals(other.infinity) &&
129.503 + NaN.equals(other.NaN) &&
129.504 + currencySymbol.equals(other.currencySymbol) &&
129.505 + intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
129.506 + currency == other.currency &&
129.507 + monetarySeparator == other.monetarySeparator &&
129.508 + exponentialSeparator.equals(other.exponentialSeparator) &&
129.509 + locale.equals(other.locale));
129.510 + }
129.511 +
129.512 + /**
129.513 + * Override hashCode.
129.514 + */
129.515 + public int hashCode() {
129.516 + int result = zeroDigit;
129.517 + result = result * 37 + groupingSeparator;
129.518 + result = result * 37 + decimalSeparator;
129.519 + return result;
129.520 + }
129.521 +
129.522 + /**
129.523 + * Initializes the symbols from the FormatData resource bundle.
129.524 + */
129.525 + private void initialize( Locale locale ) {
129.526 + this.locale = locale;
129.527 +
129.528 + // get resource bundle data - try the cache first
129.529 + boolean needCacheUpdate = false;
129.530 + Object[] data = cachedLocaleData.get(locale);
129.531 + if (data == null) { /* cache miss */
129.532 + // When numbering system is thai (Locale's extension contains u-nu-thai),
129.533 + // we read the data from th_TH_TH.
129.534 + Locale lookupLocale = locale;
129.535 + String numberType = locale.getUnicodeLocaleType("nu");
129.536 + if (numberType != null && numberType.equals("thai")) {
129.537 + lookupLocale = new Locale("th", "TH", "TH");
129.538 + }
129.539 + data = new Object[3];
129.540 +// ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale);
129.541 +// data[0] = rb.getStringArray("NumberElements");
129.542 + needCacheUpdate = true;
129.543 + }
129.544 +
129.545 + String[] numberElements = (String[]) data[0];
129.546 +
129.547 + decimalSeparator = numberElements[0].charAt(0);
129.548 + groupingSeparator = numberElements[1].charAt(0);
129.549 + patternSeparator = numberElements[2].charAt(0);
129.550 + percent = numberElements[3].charAt(0);
129.551 + zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
129.552 + digit = numberElements[5].charAt(0);
129.553 + minusSign = numberElements[6].charAt(0);
129.554 + exponential = numberElements[7].charAt(0);
129.555 + exponentialSeparator = numberElements[7]; //string representation new since 1.6
129.556 + perMill = numberElements[8].charAt(0);
129.557 + infinity = numberElements[9];
129.558 + NaN = numberElements[10];
129.559 +
129.560 + // Try to obtain the currency used in the locale's country.
129.561 + // Check for empty country string separately because it's a valid
129.562 + // country ID for Locale (and used for the C locale), but not a valid
129.563 + // ISO 3166 country code, and exceptions are expensive.
129.564 + if (!"".equals(locale.getCountry())) {
129.565 + try {
129.566 + currency = Currency.getInstance(locale);
129.567 + } catch (IllegalArgumentException e) {
129.568 + // use default values below for compatibility
129.569 + }
129.570 + }
129.571 + if (currency != null) {
129.572 + intlCurrencySymbol = currency.getCurrencyCode();
129.573 + if (data[1] != null && data[1] == intlCurrencySymbol) {
129.574 + currencySymbol = (String) data[2];
129.575 + } else {
129.576 + currencySymbol = currency.getSymbol(locale);
129.577 + data[1] = intlCurrencySymbol;
129.578 + data[2] = currencySymbol;
129.579 + needCacheUpdate = true;
129.580 + }
129.581 + } else {
129.582 + // default values
129.583 + intlCurrencySymbol = "XXX";
129.584 + try {
129.585 + currency = Currency.getInstance(intlCurrencySymbol);
129.586 + } catch (IllegalArgumentException e) {
129.587 + }
129.588 + currencySymbol = "\u00A4";
129.589 + }
129.590 + // Currently the monetary decimal separator is the same as the
129.591 + // standard decimal separator for all locales that we support.
129.592 + // If that changes, add a new entry to NumberElements.
129.593 + monetarySeparator = decimalSeparator;
129.594 +
129.595 + if (needCacheUpdate) {
129.596 + cachedLocaleData.putIfAbsent(locale, data);
129.597 + }
129.598 + }
129.599 +
129.600 + /**
129.601 + * Reads the default serializable fields, provides default values for objects
129.602 + * in older serial versions, and initializes non-serializable fields.
129.603 + * If <code>serialVersionOnStream</code>
129.604 + * is less than 1, initializes <code>monetarySeparator</code> to be
129.605 + * the same as <code>decimalSeparator</code> and <code>exponential</code>
129.606 + * to be 'E'.
129.607 + * If <code>serialVersionOnStream</code> is less than 2,
129.608 + * initializes <code>locale</code>to the root locale, and initializes
129.609 + * If <code>serialVersionOnStream</code> is less than 3, it initializes
129.610 + * <code>exponentialSeparator</code> using <code>exponential</code>.
129.611 + * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
129.612 + * default serialization will work properly if this object is streamed out again.
129.613 + * Initializes the currency from the intlCurrencySymbol field.
129.614 + *
129.615 + * @since JDK 1.1.6
129.616 + */
129.617 + private void readObject(ObjectInputStream stream)
129.618 + throws IOException, ClassNotFoundException {
129.619 + stream.defaultReadObject();
129.620 + if (serialVersionOnStream < 1) {
129.621 + // Didn't have monetarySeparator or exponential field;
129.622 + // use defaults.
129.623 + monetarySeparator = decimalSeparator;
129.624 + exponential = 'E';
129.625 + }
129.626 + if (serialVersionOnStream < 2) {
129.627 + // didn't have locale; use root locale
129.628 + locale = Locale.ROOT;
129.629 + }
129.630 + if (serialVersionOnStream < 3) {
129.631 + // didn't have exponentialSeparator. Create one using exponential
129.632 + exponentialSeparator = Character.toString(exponential);
129.633 + }
129.634 + serialVersionOnStream = currentSerialVersion;
129.635 +
129.636 + if (intlCurrencySymbol != null) {
129.637 + try {
129.638 + currency = Currency.getInstance(intlCurrencySymbol);
129.639 + } catch (IllegalArgumentException e) {
129.640 + }
129.641 + }
129.642 + }
129.643 +
129.644 + /**
129.645 + * Character used for zero.
129.646 + *
129.647 + * @serial
129.648 + * @see #getZeroDigit
129.649 + */
129.650 + private char zeroDigit;
129.651 +
129.652 + /**
129.653 + * Character used for thousands separator.
129.654 + *
129.655 + * @serial
129.656 + * @see #getGroupingSeparator
129.657 + */
129.658 + private char groupingSeparator;
129.659 +
129.660 + /**
129.661 + * Character used for decimal sign.
129.662 + *
129.663 + * @serial
129.664 + * @see #getDecimalSeparator
129.665 + */
129.666 + private char decimalSeparator;
129.667 +
129.668 + /**
129.669 + * Character used for per mille sign.
129.670 + *
129.671 + * @serial
129.672 + * @see #getPerMill
129.673 + */
129.674 + private char perMill;
129.675 +
129.676 + /**
129.677 + * Character used for percent sign.
129.678 + * @serial
129.679 + * @see #getPercent
129.680 + */
129.681 + private char percent;
129.682 +
129.683 + /**
129.684 + * Character used for a digit in a pattern.
129.685 + *
129.686 + * @serial
129.687 + * @see #getDigit
129.688 + */
129.689 + private char digit;
129.690 +
129.691 + /**
129.692 + * Character used to separate positive and negative subpatterns
129.693 + * in a pattern.
129.694 + *
129.695 + * @serial
129.696 + * @see #getPatternSeparator
129.697 + */
129.698 + private char patternSeparator;
129.699 +
129.700 + /**
129.701 + * String used to represent infinity.
129.702 + * @serial
129.703 + * @see #getInfinity
129.704 + */
129.705 + private String infinity;
129.706 +
129.707 + /**
129.708 + * String used to represent "not a number".
129.709 + * @serial
129.710 + * @see #getNaN
129.711 + */
129.712 + private String NaN;
129.713 +
129.714 + /**
129.715 + * Character used to represent minus sign.
129.716 + * @serial
129.717 + * @see #getMinusSign
129.718 + */
129.719 + private char minusSign;
129.720 +
129.721 + /**
129.722 + * String denoting the local currency, e.g. "$".
129.723 + * @serial
129.724 + * @see #getCurrencySymbol
129.725 + */
129.726 + private String currencySymbol;
129.727 +
129.728 + /**
129.729 + * ISO 4217 currency code denoting the local currency, e.g. "USD".
129.730 + * @serial
129.731 + * @see #getInternationalCurrencySymbol
129.732 + */
129.733 + private String intlCurrencySymbol;
129.734 +
129.735 + /**
129.736 + * The decimal separator used when formatting currency values.
129.737 + * @serial
129.738 + * @since JDK 1.1.6
129.739 + * @see #getMonetaryDecimalSeparator
129.740 + */
129.741 + private char monetarySeparator; // Field new in JDK 1.1.6
129.742 +
129.743 + /**
129.744 + * The character used to distinguish the exponent in a number formatted
129.745 + * in exponential notation, e.g. 'E' for a number such as "1.23E45".
129.746 + * <p>
129.747 + * Note that the public API provides no way to set this field,
129.748 + * even though it is supported by the implementation and the stream format.
129.749 + * The intent is that this will be added to the API in the future.
129.750 + *
129.751 + * @serial
129.752 + * @since JDK 1.1.6
129.753 + */
129.754 + private char exponential; // Field new in JDK 1.1.6
129.755 +
129.756 + /**
129.757 + * The string used to separate the mantissa from the exponent.
129.758 + * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
129.759 + * <p>
129.760 + * If both <code>exponential</code> and <code>exponentialSeparator</code>
129.761 + * exist, this <code>exponentialSeparator</code> has the precedence.
129.762 + *
129.763 + * @serial
129.764 + * @since 1.6
129.765 + */
129.766 + private String exponentialSeparator; // Field new in JDK 1.6
129.767 +
129.768 + /**
129.769 + * The locale of these currency format symbols.
129.770 + *
129.771 + * @serial
129.772 + * @since 1.4
129.773 + */
129.774 + private Locale locale;
129.775 +
129.776 + // currency; only the ISO code is serialized.
129.777 + private transient Currency currency;
129.778 +
129.779 + // Proclaim JDK 1.1 FCS compatibility
129.780 + static final long serialVersionUID = 5772796243397350300L;
129.781 +
129.782 + // The internal serial version which says which version was written
129.783 + // - 0 (default) for version up to JDK 1.1.5
129.784 + // - 1 for version from JDK 1.1.6, which includes two new fields:
129.785 + // monetarySeparator and exponential.
129.786 + // - 2 for version from J2SE 1.4, which includes locale field.
129.787 + // - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
129.788 + private static final int currentSerialVersion = 3;
129.789 +
129.790 + /**
129.791 + * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
129.792 + * Possible values are:
129.793 + * <ul>
129.794 + * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
129.795 + *
129.796 + * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
129.797 + * two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
129.798 + * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
129.799 + * new <code>locale</code> field.
129.800 + * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
129.801 + * new <code>exponentialSeparator</code> field.
129.802 + * </ul>
129.803 + * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
129.804 + * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
129.805 + * is always written.
129.806 + *
129.807 + * @serial
129.808 + * @since JDK 1.1.6
129.809 + */
129.810 + private int serialVersionOnStream = currentSerialVersion;
129.811 +
129.812 + /**
129.813 + * cache to hold the NumberElements and the Currency
129.814 + * of a Locale.
129.815 + */
129.816 + private static final ConcurrentHashMap<Locale, Object[]> cachedLocaleData = new ConcurrentHashMap<Locale, Object[]>(3);
129.817 +
129.818 + /**
129.819 + * Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider
129.820 + * implementation.
129.821 + private static class DecimalFormatSymbolsGetter
129.822 + implements LocaleServiceProviderPool.LocalizedObjectGetter<DecimalFormatSymbolsProvider,
129.823 + DecimalFormatSymbols> {
129.824 + private static final DecimalFormatSymbolsGetter INSTANCE =
129.825 + new DecimalFormatSymbolsGetter();
129.826 +
129.827 + public DecimalFormatSymbols getObject(
129.828 + DecimalFormatSymbolsProvider decimalFormatSymbolsProvider,
129.829 + Locale locale,
129.830 + String key,
129.831 + Object... params) {
129.832 + assert params.length == 0;
129.833 + return decimalFormatSymbolsProvider.getInstance(locale);
129.834 + }
129.835 + }
129.836 + */
129.837 +}
130.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
130.2 +++ b/rt/emul/compact/src/main/java/java/text/DigitList.java Tue Feb 11 13:31:42 2014 +0100
130.3 @@ -0,0 +1,715 @@
130.4 +/*
130.5 + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
130.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
130.7 + *
130.8 + * This code is free software; you can redistribute it and/or modify it
130.9 + * under the terms of the GNU General Public License version 2 only, as
130.10 + * published by the Free Software Foundation. Oracle designates this
130.11 + * particular file as subject to the "Classpath" exception as provided
130.12 + * by Oracle in the LICENSE file that accompanied this code.
130.13 + *
130.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
130.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
130.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
130.17 + * version 2 for more details (a copy is included in the LICENSE file that
130.18 + * accompanied this code).
130.19 + *
130.20 + * You should have received a copy of the GNU General Public License version
130.21 + * 2 along with this work; if not, write to the Free Software Foundation,
130.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
130.23 + *
130.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
130.25 + * or visit www.oracle.com if you need additional information or have any
130.26 + * questions.
130.27 + */
130.28 +
130.29 +/*
130.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
130.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
130.32 + *
130.33 + * The original version of this source code and documentation is copyrighted
130.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
130.35 + * materials are provided under terms of a License Agreement between Taligent
130.36 + * and Sun. This technology is protected by multiple US and International
130.37 + * patents. This notice and attribution to Taligent may not be removed.
130.38 + * Taligent is a registered trademark of Taligent, Inc.
130.39 + *
130.40 + */
130.41 +
130.42 +package java.text;
130.43 +
130.44 +import java.math.BigDecimal;
130.45 +import java.math.BigInteger;
130.46 +import java.math.RoundingMode;
130.47 +
130.48 +/**
130.49 + * Digit List. Private to DecimalFormat.
130.50 + * Handles the transcoding
130.51 + * between numeric values and strings of characters. Only handles
130.52 + * non-negative numbers. The division of labor between DigitList and
130.53 + * DecimalFormat is that DigitList handles the radix 10 representation
130.54 + * issues; DecimalFormat handles the locale-specific issues such as
130.55 + * positive/negative, grouping, decimal point, currency, and so on.
130.56 + *
130.57 + * A DigitList is really a representation of a floating point value.
130.58 + * It may be an integer value; we assume that a double has sufficient
130.59 + * precision to represent all digits of a long.
130.60 + *
130.61 + * The DigitList representation consists of a string of characters,
130.62 + * which are the digits radix 10, from '0' to '9'. It also has a radix
130.63 + * 10 exponent associated with it. The value represented by a DigitList
130.64 + * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
130.65 + * derived by placing all the digits of the list to the right of the
130.66 + * decimal point, by 10^exponent.
130.67 + *
130.68 + * @see Locale
130.69 + * @see Format
130.70 + * @see NumberFormat
130.71 + * @see DecimalFormat
130.72 + * @see ChoiceFormat
130.73 + * @see MessageFormat
130.74 + * @author Mark Davis, Alan Liu
130.75 + */
130.76 +final class DigitList implements Cloneable {
130.77 + /**
130.78 + * The maximum number of significant digits in an IEEE 754 double, that
130.79 + * is, in a Java double. This must not be increased, or garbage digits
130.80 + * will be generated, and should not be decreased, or accuracy will be lost.
130.81 + */
130.82 + public static final int MAX_COUNT = 19; // == Long.toString(Long.MAX_VALUE).length()
130.83 +
130.84 + /**
130.85 + * These data members are intentionally public and can be set directly.
130.86 + *
130.87 + * The value represented is given by placing the decimal point before
130.88 + * digits[decimalAt]. If decimalAt is < 0, then leading zeros between
130.89 + * the decimal point and the first nonzero digit are implied. If decimalAt
130.90 + * is > count, then trailing zeros between the digits[count-1] and the
130.91 + * decimal point are implied.
130.92 + *
130.93 + * Equivalently, the represented value is given by f * 10^decimalAt. Here
130.94 + * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to
130.95 + * the right of the decimal.
130.96 + *
130.97 + * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We
130.98 + * don't allow denormalized numbers because our exponent is effectively of
130.99 + * unlimited magnitude. The count value contains the number of significant
130.100 + * digits present in digits[].
130.101 + *
130.102 + * Zero is represented by any DigitList with count == 0 or with each digits[i]
130.103 + * for all i <= count == '0'.
130.104 + */
130.105 + public int decimalAt = 0;
130.106 + public int count = 0;
130.107 + public char[] digits = new char[MAX_COUNT];
130.108 +
130.109 + private char[] data;
130.110 + private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
130.111 + private boolean isNegative = false;
130.112 +
130.113 + /**
130.114 + * Return true if the represented number is zero.
130.115 + */
130.116 + boolean isZero() {
130.117 + for (int i=0; i < count; ++i) {
130.118 + if (digits[i] != '0') {
130.119 + return false;
130.120 + }
130.121 + }
130.122 + return true;
130.123 + }
130.124 +
130.125 + /**
130.126 + * Set the rounding mode
130.127 + */
130.128 + void setRoundingMode(RoundingMode r) {
130.129 + roundingMode = r;
130.130 + }
130.131 +
130.132 + /**
130.133 + * Clears out the digits.
130.134 + * Use before appending them.
130.135 + * Typically, you set a series of digits with append, then at the point
130.136 + * you hit the decimal point, you set myDigitList.decimalAt = myDigitList.count;
130.137 + * then go on appending digits.
130.138 + */
130.139 + public void clear () {
130.140 + decimalAt = 0;
130.141 + count = 0;
130.142 + }
130.143 +
130.144 + /**
130.145 + * Appends a digit to the list, extending the list when necessary.
130.146 + */
130.147 + public void append(char digit) {
130.148 + if (count == digits.length) {
130.149 + char[] data = new char[count + 100];
130.150 + System.arraycopy(digits, 0, data, 0, count);
130.151 + digits = data;
130.152 + }
130.153 + digits[count++] = digit;
130.154 + }
130.155 +
130.156 + /**
130.157 + * Utility routine to get the value of the digit list
130.158 + * If (count == 0) this throws a NumberFormatException, which
130.159 + * mimics Long.parseLong().
130.160 + */
130.161 + public final double getDouble() {
130.162 + if (count == 0) {
130.163 + return 0.0;
130.164 + }
130.165 +
130.166 + StringBuffer temp = getStringBuffer();
130.167 + temp.append('.');
130.168 + temp.append(digits, 0, count);
130.169 + temp.append('E');
130.170 + temp.append(decimalAt);
130.171 + return Double.parseDouble(temp.toString());
130.172 + }
130.173 +
130.174 + /**
130.175 + * Utility routine to get the value of the digit list.
130.176 + * If (count == 0) this returns 0, unlike Long.parseLong().
130.177 + */
130.178 + public final long getLong() {
130.179 + // for now, simple implementation; later, do proper IEEE native stuff
130.180 +
130.181 + if (count == 0) {
130.182 + return 0;
130.183 + }
130.184 +
130.185 + // We have to check for this, because this is the one NEGATIVE value
130.186 + // we represent. If we tried to just pass the digits off to parseLong,
130.187 + // we'd get a parse failure.
130.188 + if (isLongMIN_VALUE()) {
130.189 + return Long.MIN_VALUE;
130.190 + }
130.191 +
130.192 + StringBuffer temp = getStringBuffer();
130.193 + temp.append(digits, 0, count);
130.194 + for (int i = count; i < decimalAt; ++i) {
130.195 + temp.append('0');
130.196 + }
130.197 + return Long.parseLong(temp.toString());
130.198 + }
130.199 +
130.200 + public final BigDecimal getBigDecimal() {
130.201 + if (count == 0) {
130.202 + if (decimalAt == 0) {
130.203 + return BigDecimal.ZERO;
130.204 + } else {
130.205 + return new BigDecimal("0E" + decimalAt);
130.206 + }
130.207 + }
130.208 +
130.209 + if (decimalAt == count) {
130.210 + return new BigDecimal(digits, 0, count);
130.211 + } else {
130.212 + return new BigDecimal(digits, 0, count).scaleByPowerOfTen(decimalAt - count);
130.213 + }
130.214 + }
130.215 +
130.216 + /**
130.217 + * Return true if the number represented by this object can fit into
130.218 + * a long.
130.219 + * @param isPositive true if this number should be regarded as positive
130.220 + * @param ignoreNegativeZero true if -0 should be regarded as identical to
130.221 + * +0; otherwise they are considered distinct
130.222 + * @return true if this number fits into a Java long
130.223 + */
130.224 + boolean fitsIntoLong(boolean isPositive, boolean ignoreNegativeZero) {
130.225 + // Figure out if the result will fit in a long. We have to
130.226 + // first look for nonzero digits after the decimal point;
130.227 + // then check the size. If the digit count is 18 or less, then
130.228 + // the value can definitely be represented as a long. If it is 19
130.229 + // then it may be too large.
130.230 +
130.231 + // Trim trailing zeros. This does not change the represented value.
130.232 + while (count > 0 && digits[count - 1] == '0') {
130.233 + --count;
130.234 + }
130.235 +
130.236 + if (count == 0) {
130.237 + // Positive zero fits into a long, but negative zero can only
130.238 + // be represented as a double. - bug 4162852
130.239 + return isPositive || ignoreNegativeZero;
130.240 + }
130.241 +
130.242 + if (decimalAt < count || decimalAt > MAX_COUNT) {
130.243 + return false;
130.244 + }
130.245 +
130.246 + if (decimalAt < MAX_COUNT) return true;
130.247 +
130.248 + // At this point we have decimalAt == count, and count == MAX_COUNT.
130.249 + // The number will overflow if it is larger than 9223372036854775807
130.250 + // or smaller than -9223372036854775808.
130.251 + for (int i=0; i<count; ++i) {
130.252 + char dig = digits[i], max = LONG_MIN_REP[i];
130.253 + if (dig > max) return false;
130.254 + if (dig < max) return true;
130.255 + }
130.256 +
130.257 + // At this point the first count digits match. If decimalAt is less
130.258 + // than count, then the remaining digits are zero, and we return true.
130.259 + if (count < decimalAt) return true;
130.260 +
130.261 + // Now we have a representation of Long.MIN_VALUE, without the leading
130.262 + // negative sign. If this represents a positive value, then it does
130.263 + // not fit; otherwise it fits.
130.264 + return !isPositive;
130.265 + }
130.266 +
130.267 + /**
130.268 + * Set the digit list to a representation of the given double value.
130.269 + * This method supports fixed-point notation.
130.270 + * @param isNegative Boolean value indicating whether the number is negative.
130.271 + * @param source Value to be converted; must not be Inf, -Inf, Nan,
130.272 + * or a value <= 0.
130.273 + * @param maximumFractionDigits The most fractional digits which should
130.274 + * be converted.
130.275 + */
130.276 + public final void set(boolean isNegative, double source, int maximumFractionDigits) {
130.277 + set(isNegative, source, maximumFractionDigits, true);
130.278 + }
130.279 +
130.280 + /**
130.281 + * Set the digit list to a representation of the given double value.
130.282 + * This method supports both fixed-point and exponential notation.
130.283 + * @param isNegative Boolean value indicating whether the number is negative.
130.284 + * @param source Value to be converted; must not be Inf, -Inf, Nan,
130.285 + * or a value <= 0.
130.286 + * @param maximumDigits The most fractional or total digits which should
130.287 + * be converted.
130.288 + * @param fixedPoint If true, then maximumDigits is the maximum
130.289 + * fractional digits to be converted. If false, total digits.
130.290 + */
130.291 + final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
130.292 + set(isNegative, Double.toString(source), maximumDigits, fixedPoint);
130.293 + }
130.294 +
130.295 + /**
130.296 + * Generate a representation of the form DDDDD, DDDDD.DDDDD, or
130.297 + * DDDDDE+/-DDDDD.
130.298 + */
130.299 + final void set(boolean isNegative, String s, int maximumDigits, boolean fixedPoint) {
130.300 + this.isNegative = isNegative;
130.301 + int len = s.length();
130.302 + char[] source = getDataChars(len);
130.303 + s.getChars(0, len, source, 0);
130.304 +
130.305 + decimalAt = -1;
130.306 + count = 0;
130.307 + int exponent = 0;
130.308 + // Number of zeros between decimal point and first non-zero digit after
130.309 + // decimal point, for numbers < 1.
130.310 + int leadingZerosAfterDecimal = 0;
130.311 + boolean nonZeroDigitSeen = false;
130.312 +
130.313 + for (int i = 0; i < len; ) {
130.314 + char c = source[i++];
130.315 + if (c == '.') {
130.316 + decimalAt = count;
130.317 + } else if (c == 'e' || c == 'E') {
130.318 + exponent = parseInt(source, i, len);
130.319 + break;
130.320 + } else {
130.321 + if (!nonZeroDigitSeen) {
130.322 + nonZeroDigitSeen = (c != '0');
130.323 + if (!nonZeroDigitSeen && decimalAt != -1)
130.324 + ++leadingZerosAfterDecimal;
130.325 + }
130.326 + if (nonZeroDigitSeen) {
130.327 + digits[count++] = c;
130.328 + }
130.329 + }
130.330 + }
130.331 + if (decimalAt == -1) {
130.332 + decimalAt = count;
130.333 + }
130.334 + if (nonZeroDigitSeen) {
130.335 + decimalAt += exponent - leadingZerosAfterDecimal;
130.336 + }
130.337 +
130.338 + if (fixedPoint) {
130.339 + // The negative of the exponent represents the number of leading
130.340 + // zeros between the decimal and the first non-zero digit, for
130.341 + // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
130.342 + // is more than the maximum fraction digits, then we have an underflow
130.343 + // for the printed representation.
130.344 + if (-decimalAt > maximumDigits) {
130.345 + // Handle an underflow to zero when we round something like
130.346 + // 0.0009 to 2 fractional digits.
130.347 + count = 0;
130.348 + return;
130.349 + } else if (-decimalAt == maximumDigits) {
130.350 + // If we round 0.0009 to 3 fractional digits, then we have to
130.351 + // create a new one digit in the least significant location.
130.352 + if (shouldRoundUp(0)) {
130.353 + count = 1;
130.354 + ++decimalAt;
130.355 + digits[0] = '1';
130.356 + } else {
130.357 + count = 0;
130.358 + }
130.359 + return;
130.360 + }
130.361 + // else fall through
130.362 + }
130.363 +
130.364 + // Eliminate trailing zeros.
130.365 + while (count > 1 && digits[count - 1] == '0') {
130.366 + --count;
130.367 + }
130.368 +
130.369 + // Eliminate digits beyond maximum digits to be displayed.
130.370 + // Round up if appropriate.
130.371 + round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits);
130.372 + }
130.373 +
130.374 + /**
130.375 + * Round the representation to the given number of digits.
130.376 + * @param maximumDigits The maximum number of digits to be shown.
130.377 + * Upon return, count will be less than or equal to maximumDigits.
130.378 + */
130.379 + private final void round(int maximumDigits) {
130.380 + // Eliminate digits beyond maximum digits to be displayed.
130.381 + // Round up if appropriate.
130.382 + if (maximumDigits >= 0 && maximumDigits < count) {
130.383 + if (shouldRoundUp(maximumDigits)) {
130.384 + // Rounding up involved incrementing digits from LSD to MSD.
130.385 + // In most cases this is simple, but in a worst case situation
130.386 + // (9999..99) we have to adjust the decimalAt value.
130.387 + for (;;) {
130.388 + --maximumDigits;
130.389 + if (maximumDigits < 0) {
130.390 + // We have all 9's, so we increment to a single digit
130.391 + // of one and adjust the exponent.
130.392 + digits[0] = '1';
130.393 + ++decimalAt;
130.394 + maximumDigits = 0; // Adjust the count
130.395 + break;
130.396 + }
130.397 +
130.398 + ++digits[maximumDigits];
130.399 + if (digits[maximumDigits] <= '9') break;
130.400 + // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
130.401 + }
130.402 + ++maximumDigits; // Increment for use as count
130.403 + }
130.404 + count = maximumDigits;
130.405 +
130.406 + // Eliminate trailing zeros.
130.407 + while (count > 1 && digits[count-1] == '0') {
130.408 + --count;
130.409 + }
130.410 + }
130.411 + }
130.412 +
130.413 +
130.414 + /**
130.415 + * Return true if truncating the representation to the given number
130.416 + * of digits will result in an increment to the last digit. This
130.417 + * method implements the rounding modes defined in the
130.418 + * java.math.RoundingMode class.
130.419 + * [bnf]
130.420 + * @param maximumDigits the number of digits to keep, from 0 to
130.421 + * <code>count-1</code>. If 0, then all digits are rounded away, and
130.422 + * this method returns true if a one should be generated (e.g., formatting
130.423 + * 0.09 with "#.#").
130.424 + * @exception ArithmeticException if rounding is needed with rounding
130.425 + * mode being set to RoundingMode.UNNECESSARY
130.426 + * @return true if digit <code>maximumDigits-1</code> should be
130.427 + * incremented
130.428 + */
130.429 + private boolean shouldRoundUp(int maximumDigits) {
130.430 + if (maximumDigits < count) {
130.431 + switch(roundingMode) {
130.432 + case UP:
130.433 + for (int i=maximumDigits; i<count; ++i) {
130.434 + if (digits[i] != '0') {
130.435 + return true;
130.436 + }
130.437 + }
130.438 + break;
130.439 + case DOWN:
130.440 + break;
130.441 + case CEILING:
130.442 + for (int i=maximumDigits; i<count; ++i) {
130.443 + if (digits[i] != '0') {
130.444 + return !isNegative;
130.445 + }
130.446 + }
130.447 + break;
130.448 + case FLOOR:
130.449 + for (int i=maximumDigits; i<count; ++i) {
130.450 + if (digits[i] != '0') {
130.451 + return isNegative;
130.452 + }
130.453 + }
130.454 + break;
130.455 + case HALF_UP:
130.456 + if (digits[maximumDigits] >= '5') {
130.457 + return true;
130.458 + }
130.459 + break;
130.460 + case HALF_DOWN:
130.461 + if (digits[maximumDigits] > '5') {
130.462 + return true;
130.463 + } else if (digits[maximumDigits] == '5' ) {
130.464 + for (int i=maximumDigits+1; i<count; ++i) {
130.465 + if (digits[i] != '0') {
130.466 + return true;
130.467 + }
130.468 + }
130.469 + }
130.470 + break;
130.471 + case HALF_EVEN:
130.472 + // Implement IEEE half-even rounding
130.473 + if (digits[maximumDigits] > '5') {
130.474 + return true;
130.475 + } else if (digits[maximumDigits] == '5' ) {
130.476 + for (int i=maximumDigits+1; i<count; ++i) {
130.477 + if (digits[i] != '0') {
130.478 + return true;
130.479 + }
130.480 + }
130.481 + return maximumDigits > 0 && (digits[maximumDigits-1] % 2 != 0);
130.482 + }
130.483 + break;
130.484 + case UNNECESSARY:
130.485 + for (int i=maximumDigits; i<count; ++i) {
130.486 + if (digits[i] != '0') {
130.487 + throw new ArithmeticException(
130.488 + "Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY");
130.489 + }
130.490 + }
130.491 + break;
130.492 + default:
130.493 + assert false;
130.494 + }
130.495 + }
130.496 + return false;
130.497 + }
130.498 +
130.499 + /**
130.500 + * Utility routine to set the value of the digit list from a long
130.501 + */
130.502 + public final void set(boolean isNegative, long source) {
130.503 + set(isNegative, source, 0);
130.504 + }
130.505 +
130.506 + /**
130.507 + * Set the digit list to a representation of the given long value.
130.508 + * @param isNegative Boolean value indicating whether the number is negative.
130.509 + * @param source Value to be converted; must be >= 0 or ==
130.510 + * Long.MIN_VALUE.
130.511 + * @param maximumDigits The most digits which should be converted.
130.512 + * If maximumDigits is lower than the number of significant digits
130.513 + * in source, the representation will be rounded. Ignored if <= 0.
130.514 + */
130.515 + public final void set(boolean isNegative, long source, int maximumDigits) {
130.516 + this.isNegative = isNegative;
130.517 +
130.518 + // This method does not expect a negative number. However,
130.519 + // "source" can be a Long.MIN_VALUE (-9223372036854775808),
130.520 + // if the number being formatted is a Long.MIN_VALUE. In that
130.521 + // case, it will be formatted as -Long.MIN_VALUE, a number
130.522 + // which is outside the legal range of a long, but which can
130.523 + // be represented by DigitList.
130.524 + if (source <= 0) {
130.525 + if (source == Long.MIN_VALUE) {
130.526 + decimalAt = count = MAX_COUNT;
130.527 + System.arraycopy(LONG_MIN_REP, 0, digits, 0, count);
130.528 + } else {
130.529 + decimalAt = count = 0; // Values <= 0 format as zero
130.530 + }
130.531 + } else {
130.532 + // Rewritten to improve performance. I used to call
130.533 + // Long.toString(), which was about 4x slower than this code.
130.534 + int left = MAX_COUNT;
130.535 + int right;
130.536 + while (source > 0) {
130.537 + digits[--left] = (char)('0' + (source % 10));
130.538 + source /= 10;
130.539 + }
130.540 + decimalAt = MAX_COUNT - left;
130.541 + // Don't copy trailing zeros. We are guaranteed that there is at
130.542 + // least one non-zero digit, so we don't have to check lower bounds.
130.543 + for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
130.544 + ;
130.545 + count = right - left + 1;
130.546 + System.arraycopy(digits, left, digits, 0, count);
130.547 + }
130.548 + if (maximumDigits > 0) round(maximumDigits);
130.549 + }
130.550 +
130.551 + /**
130.552 + * Set the digit list to a representation of the given BigDecimal value.
130.553 + * This method supports both fixed-point and exponential notation.
130.554 + * @param isNegative Boolean value indicating whether the number is negative.
130.555 + * @param source Value to be converted; must not be a value <= 0.
130.556 + * @param maximumDigits The most fractional or total digits which should
130.557 + * be converted.
130.558 + * @param fixedPoint If true, then maximumDigits is the maximum
130.559 + * fractional digits to be converted. If false, total digits.
130.560 + */
130.561 + final void set(boolean isNegative, BigDecimal source, int maximumDigits, boolean fixedPoint) {
130.562 + String s = source.toString();
130.563 + extendDigits(s.length());
130.564 +
130.565 + set(isNegative, s, maximumDigits, fixedPoint);
130.566 + }
130.567 +
130.568 + /**
130.569 + * Set the digit list to a representation of the given BigInteger value.
130.570 + * @param isNegative Boolean value indicating whether the number is negative.
130.571 + * @param source Value to be converted; must be >= 0.
130.572 + * @param maximumDigits The most digits which should be converted.
130.573 + * If maximumDigits is lower than the number of significant digits
130.574 + * in source, the representation will be rounded. Ignored if <= 0.
130.575 + */
130.576 + final void set(boolean isNegative, BigInteger source, int maximumDigits) {
130.577 + this.isNegative = isNegative;
130.578 + String s = source.toString();
130.579 + int len = s.length();
130.580 + extendDigits(len);
130.581 + s.getChars(0, len, digits, 0);
130.582 +
130.583 + decimalAt = len;
130.584 + int right;
130.585 + for (right = len - 1; right >= 0 && digits[right] == '0'; --right)
130.586 + ;
130.587 + count = right + 1;
130.588 +
130.589 + if (maximumDigits > 0) {
130.590 + round(maximumDigits);
130.591 + }
130.592 + }
130.593 +
130.594 + /**
130.595 + * equality test between two digit lists.
130.596 + */
130.597 + public boolean equals(Object obj) {
130.598 + if (this == obj) // quick check
130.599 + return true;
130.600 + if (!(obj instanceof DigitList)) // (1) same object?
130.601 + return false;
130.602 + DigitList other = (DigitList) obj;
130.603 + if (count != other.count ||
130.604 + decimalAt != other.decimalAt)
130.605 + return false;
130.606 + for (int i = 0; i < count; i++)
130.607 + if (digits[i] != other.digits[i])
130.608 + return false;
130.609 + return true;
130.610 + }
130.611 +
130.612 + /**
130.613 + * Generates the hash code for the digit list.
130.614 + */
130.615 + public int hashCode() {
130.616 + int hashcode = decimalAt;
130.617 +
130.618 + for (int i = 0; i < count; i++) {
130.619 + hashcode = hashcode * 37 + digits[i];
130.620 + }
130.621 +
130.622 + return hashcode;
130.623 + }
130.624 +
130.625 + /**
130.626 + * Creates a copy of this object.
130.627 + * @return a clone of this instance.
130.628 + */
130.629 + public Object clone() {
130.630 + try {
130.631 + DigitList other = (DigitList) super.clone();
130.632 + char[] newDigits = new char[digits.length];
130.633 + System.arraycopy(digits, 0, newDigits, 0, digits.length);
130.634 + other.digits = newDigits;
130.635 + other.tempBuffer = null;
130.636 + return other;
130.637 + } catch (CloneNotSupportedException e) {
130.638 + throw new InternalError();
130.639 + }
130.640 + }
130.641 +
130.642 + /**
130.643 + * Returns true if this DigitList represents Long.MIN_VALUE;
130.644 + * false, otherwise. This is required so that getLong() works.
130.645 + */
130.646 + private boolean isLongMIN_VALUE() {
130.647 + if (decimalAt != count || count != MAX_COUNT) {
130.648 + return false;
130.649 + }
130.650 +
130.651 + for (int i = 0; i < count; ++i) {
130.652 + if (digits[i] != LONG_MIN_REP[i]) return false;
130.653 + }
130.654 +
130.655 + return true;
130.656 + }
130.657 +
130.658 + private static final int parseInt(char[] str, int offset, int strLen) {
130.659 + char c;
130.660 + boolean positive = true;
130.661 + if ((c = str[offset]) == '-') {
130.662 + positive = false;
130.663 + offset++;
130.664 + } else if (c == '+') {
130.665 + offset++;
130.666 + }
130.667 +
130.668 + int value = 0;
130.669 + while (offset < strLen) {
130.670 + c = str[offset++];
130.671 + if (c >= '0' && c <= '9') {
130.672 + value = value * 10 + (c - '0');
130.673 + } else {
130.674 + break;
130.675 + }
130.676 + }
130.677 + return positive ? value : -value;
130.678 + }
130.679 +
130.680 + // The digit part of -9223372036854775808L
130.681 + private static final char[] LONG_MIN_REP = "9223372036854775808".toCharArray();
130.682 +
130.683 + public String toString() {
130.684 + if (isZero()) {
130.685 + return "0";
130.686 + }
130.687 + StringBuffer buf = getStringBuffer();
130.688 + buf.append("0.");
130.689 + buf.append(digits, 0, count);
130.690 + buf.append("x10^");
130.691 + buf.append(decimalAt);
130.692 + return buf.toString();
130.693 + }
130.694 +
130.695 + private StringBuffer tempBuffer;
130.696 +
130.697 + private StringBuffer getStringBuffer() {
130.698 + if (tempBuffer == null) {
130.699 + tempBuffer = new StringBuffer(MAX_COUNT);
130.700 + } else {
130.701 + tempBuffer.setLength(0);
130.702 + }
130.703 + return tempBuffer;
130.704 + }
130.705 +
130.706 + private void extendDigits(int len) {
130.707 + if (len > digits.length) {
130.708 + digits = new char[len];
130.709 + }
130.710 + }
130.711 +
130.712 + private final char[] getDataChars(int length) {
130.713 + if (data == null || data.length < length) {
130.714 + data = new char[length];
130.715 + }
130.716 + return data;
130.717 + }
130.718 +}
131.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
131.2 +++ b/rt/emul/compact/src/main/java/java/text/DontCareFieldPosition.java Tue Feb 11 13:31:42 2014 +0100
131.3 @@ -0,0 +1,53 @@
131.4 +/*
131.5 + * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
131.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
131.7 + *
131.8 + * This code is free software; you can redistribute it and/or modify it
131.9 + * under the terms of the GNU General Public License version 2 only, as
131.10 + * published by the Free Software Foundation. Oracle designates this
131.11 + * particular file as subject to the "Classpath" exception as provided
131.12 + * by Oracle in the LICENSE file that accompanied this code.
131.13 + *
131.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
131.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
131.17 + * version 2 for more details (a copy is included in the LICENSE file that
131.18 + * accompanied this code).
131.19 + *
131.20 + * You should have received a copy of the GNU General Public License version
131.21 + * 2 along with this work; if not, write to the Free Software Foundation,
131.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
131.23 + *
131.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
131.25 + * or visit www.oracle.com if you need additional information or have any
131.26 + * questions.
131.27 + */
131.28 +
131.29 +package java.text;
131.30 +
131.31 +/**
131.32 + * DontCareFieldPosition defines no-op FieldDelegate. Its
131.33 + * singleton is used for the format methods that don't take a
131.34 + * FieldPosition.
131.35 + */
131.36 +class DontCareFieldPosition extends FieldPosition {
131.37 + // The singleton of DontCareFieldPosition.
131.38 + static final FieldPosition INSTANCE = new DontCareFieldPosition();
131.39 +
131.40 + private final Format.FieldDelegate noDelegate = new Format.FieldDelegate() {
131.41 + public void formatted(Format.Field attr, Object value, int start,
131.42 + int end, StringBuffer buffer) {
131.43 + }
131.44 + public void formatted(int fieldID, Format.Field attr, Object value,
131.45 + int start, int end, StringBuffer buffer) {
131.46 + }
131.47 + };
131.48 +
131.49 + private DontCareFieldPosition() {
131.50 + super(0);
131.51 + }
131.52 +
131.53 + Format.FieldDelegate getFieldDelegate() {
131.54 + return noDelegate;
131.55 + }
131.56 +}
132.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
132.2 +++ b/rt/emul/compact/src/main/java/java/text/FieldPosition.java Tue Feb 11 13:31:42 2014 +0100
132.3 @@ -0,0 +1,303 @@
132.4 +/*
132.5 + * Copyright (c) 1996, 2002, Oracle and/or its affiliates. All rights reserved.
132.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
132.7 + *
132.8 + * This code is free software; you can redistribute it and/or modify it
132.9 + * under the terms of the GNU General Public License version 2 only, as
132.10 + * published by the Free Software Foundation. Oracle designates this
132.11 + * particular file as subject to the "Classpath" exception as provided
132.12 + * by Oracle in the LICENSE file that accompanied this code.
132.13 + *
132.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
132.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
132.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
132.17 + * version 2 for more details (a copy is included in the LICENSE file that
132.18 + * accompanied this code).
132.19 + *
132.20 + * You should have received a copy of the GNU General Public License version
132.21 + * 2 along with this work; if not, write to the Free Software Foundation,
132.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
132.23 + *
132.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
132.25 + * or visit www.oracle.com if you need additional information or have any
132.26 + * questions.
132.27 + */
132.28 +
132.29 +/*
132.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
132.31 + * (C) Copyright IBM Corp. 1996 - All Rights Reserved
132.32 + *
132.33 + * The original version of this source code and documentation is copyrighted
132.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
132.35 + * materials are provided under terms of a License Agreement between Taligent
132.36 + * and Sun. This technology is protected by multiple US and International
132.37 + * patents. This notice and attribution to Taligent may not be removed.
132.38 + * Taligent is a registered trademark of Taligent, Inc.
132.39 + *
132.40 + */
132.41 +
132.42 +package java.text;
132.43 +
132.44 +/**
132.45 + * <code>FieldPosition</code> is a simple class used by <code>Format</code>
132.46 + * and its subclasses to identify fields in formatted output. Fields can
132.47 + * be identified in two ways:
132.48 + * <ul>
132.49 + * <li>By an integer constant, whose names typically end with
132.50 + * <code>_FIELD</code>. The constants are defined in the various
132.51 + * subclasses of <code>Format</code>.
132.52 + * <li>By a <code>Format.Field</code> constant, see <code>ERA_FIELD</code>
132.53 + * and its friends in <code>DateFormat</code> for an example.
132.54 + * </ul>
132.55 + * <p>
132.56 + * <code>FieldPosition</code> keeps track of the position of the
132.57 + * field within the formatted output with two indices: the index
132.58 + * of the first character of the field and the index of the last
132.59 + * character of the field.
132.60 + *
132.61 + * <p>
132.62 + * One version of the <code>format</code> method in the various
132.63 + * <code>Format</code> classes requires a <code>FieldPosition</code>
132.64 + * object as an argument. You use this <code>format</code> method
132.65 + * to perform partial formatting or to get information about the
132.66 + * formatted output (such as the position of a field).
132.67 + *
132.68 + * <p>
132.69 + * If you are interested in the positions of all attributes in the
132.70 + * formatted string use the <code>Format</code> method
132.71 + * <code>formatToCharacterIterator</code>.
132.72 + *
132.73 + * @author Mark Davis
132.74 + * @see java.text.Format
132.75 + */
132.76 +public class FieldPosition {
132.77 +
132.78 + /**
132.79 + * Input: Desired field to determine start and end offsets for.
132.80 + * The meaning depends on the subclass of Format.
132.81 + */
132.82 + int field = 0;
132.83 +
132.84 + /**
132.85 + * Output: End offset of field in text.
132.86 + * If the field does not occur in the text, 0 is returned.
132.87 + */
132.88 + int endIndex = 0;
132.89 +
132.90 + /**
132.91 + * Output: Start offset of field in text.
132.92 + * If the field does not occur in the text, 0 is returned.
132.93 + */
132.94 + int beginIndex = 0;
132.95 +
132.96 + /**
132.97 + * Desired field this FieldPosition is for.
132.98 + */
132.99 + private Format.Field attribute;
132.100 +
132.101 + /**
132.102 + * Creates a FieldPosition object for the given field. Fields are
132.103 + * identified by constants, whose names typically end with _FIELD,
132.104 + * in the various subclasses of Format.
132.105 + *
132.106 + * @see java.text.NumberFormat#INTEGER_FIELD
132.107 + * @see java.text.NumberFormat#FRACTION_FIELD
132.108 + * @see java.text.DateFormat#YEAR_FIELD
132.109 + * @see java.text.DateFormat#MONTH_FIELD
132.110 + */
132.111 + public FieldPosition(int field) {
132.112 + this.field = field;
132.113 + }
132.114 +
132.115 + /**
132.116 + * Creates a FieldPosition object for the given field constant. Fields are
132.117 + * identified by constants defined in the various <code>Format</code>
132.118 + * subclasses. This is equivalent to calling
132.119 + * <code>new FieldPosition(attribute, -1)</code>.
132.120 + *
132.121 + * @param attribute Format.Field constant identifying a field
132.122 + * @since 1.4
132.123 + */
132.124 + public FieldPosition(Format.Field attribute) {
132.125 + this(attribute, -1);
132.126 + }
132.127 +
132.128 + /**
132.129 + * Creates a <code>FieldPosition</code> object for the given field.
132.130 + * The field is identified by an attribute constant from one of the
132.131 + * <code>Field</code> subclasses as well as an integer field ID
132.132 + * defined by the <code>Format</code> subclasses. <code>Format</code>
132.133 + * subclasses that are aware of <code>Field</code> should give precedence
132.134 + * to <code>attribute</code> and ignore <code>fieldID</code> if
132.135 + * <code>attribute</code> is not null. However, older <code>Format</code>
132.136 + * subclasses may not be aware of <code>Field</code> and rely on
132.137 + * <code>fieldID</code>. If the field has no corresponding integer
132.138 + * constant, <code>fieldID</code> should be -1.
132.139 + *
132.140 + * @param attribute Format.Field constant identifying a field
132.141 + * @param fieldID integer constantce identifying a field
132.142 + * @since 1.4
132.143 + */
132.144 + public FieldPosition(Format.Field attribute, int fieldID) {
132.145 + this.attribute = attribute;
132.146 + this.field = fieldID;
132.147 + }
132.148 +
132.149 + /**
132.150 + * Returns the field identifier as an attribute constant
132.151 + * from one of the <code>Field</code> subclasses. May return null if
132.152 + * the field is specified only by an integer field ID.
132.153 + *
132.154 + * @return Identifier for the field
132.155 + * @since 1.4
132.156 + */
132.157 + public Format.Field getFieldAttribute() {
132.158 + return attribute;
132.159 + }
132.160 +
132.161 + /**
132.162 + * Retrieves the field identifier.
132.163 + */
132.164 + public int getField() {
132.165 + return field;
132.166 + }
132.167 +
132.168 + /**
132.169 + * Retrieves the index of the first character in the requested field.
132.170 + */
132.171 + public int getBeginIndex() {
132.172 + return beginIndex;
132.173 + }
132.174 +
132.175 + /**
132.176 + * Retrieves the index of the character following the last character in the
132.177 + * requested field.
132.178 + */
132.179 + public int getEndIndex() {
132.180 + return endIndex;
132.181 + }
132.182 +
132.183 + /**
132.184 + * Sets the begin index. For use by subclasses of Format.
132.185 + * @since 1.2
132.186 + */
132.187 + public void setBeginIndex(int bi) {
132.188 + beginIndex = bi;
132.189 + }
132.190 +
132.191 + /**
132.192 + * Sets the end index. For use by subclasses of Format.
132.193 + * @since 1.2
132.194 + */
132.195 + public void setEndIndex(int ei) {
132.196 + endIndex = ei;
132.197 + }
132.198 +
132.199 + /**
132.200 + * Returns a <code>Format.FieldDelegate</code> instance that is associated
132.201 + * with the FieldPosition. When the delegate is notified of the same
132.202 + * field the FieldPosition is associated with, the begin/end will be
132.203 + * adjusted.
132.204 + */
132.205 + Format.FieldDelegate getFieldDelegate() {
132.206 + return new Delegate();
132.207 + }
132.208 +
132.209 + /**
132.210 + * Overrides equals
132.211 + */
132.212 + public boolean equals(Object obj)
132.213 + {
132.214 + if (obj == null) return false;
132.215 + if (!(obj instanceof FieldPosition))
132.216 + return false;
132.217 + FieldPosition other = (FieldPosition) obj;
132.218 + if (attribute == null) {
132.219 + if (other.attribute != null) {
132.220 + return false;
132.221 + }
132.222 + }
132.223 + else if (!attribute.equals(other.attribute)) {
132.224 + return false;
132.225 + }
132.226 + return (beginIndex == other.beginIndex
132.227 + && endIndex == other.endIndex
132.228 + && field == other.field);
132.229 + }
132.230 +
132.231 + /**
132.232 + * Returns a hash code for this FieldPosition.
132.233 + * @return a hash code value for this object
132.234 + */
132.235 + public int hashCode() {
132.236 + return (field << 24) | (beginIndex << 16) | endIndex;
132.237 + }
132.238 +
132.239 + /**
132.240 + * Return a string representation of this FieldPosition.
132.241 + * @return a string representation of this object
132.242 + */
132.243 + public String toString() {
132.244 + return getClass().getName() +
132.245 + "[field=" + field + ",attribute=" + attribute +
132.246 + ",beginIndex=" + beginIndex +
132.247 + ",endIndex=" + endIndex + ']';
132.248 + }
132.249 +
132.250 +
132.251 + /**
132.252 + * Return true if the receiver wants a <code>Format.Field</code> value and
132.253 + * <code>attribute</code> is equal to it.
132.254 + */
132.255 + private boolean matchesField(Format.Field attribute) {
132.256 + if (this.attribute != null) {
132.257 + return this.attribute.equals(attribute);
132.258 + }
132.259 + return false;
132.260 + }
132.261 +
132.262 + /**
132.263 + * Return true if the receiver wants a <code>Format.Field</code> value and
132.264 + * <code>attribute</code> is equal to it, or true if the receiver
132.265 + * represents an inteter constant and <code>field</code> equals it.
132.266 + */
132.267 + private boolean matchesField(Format.Field attribute, int field) {
132.268 + if (this.attribute != null) {
132.269 + return this.attribute.equals(attribute);
132.270 + }
132.271 + return (field == this.field);
132.272 + }
132.273 +
132.274 +
132.275 + /**
132.276 + * An implementation of FieldDelegate that will adjust the begin/end
132.277 + * of the FieldPosition if the arguments match the field of
132.278 + * the FieldPosition.
132.279 + */
132.280 + private class Delegate implements Format.FieldDelegate {
132.281 + /**
132.282 + * Indicates whether the field has been encountered before. If this
132.283 + * is true, and <code>formatted</code> is invoked, the begin/end
132.284 + * are not updated.
132.285 + */
132.286 + private boolean encounteredField;
132.287 +
132.288 + public void formatted(Format.Field attr, Object value, int start,
132.289 + int end, StringBuffer buffer) {
132.290 + if (!encounteredField && matchesField(attr)) {
132.291 + setBeginIndex(start);
132.292 + setEndIndex(end);
132.293 + encounteredField = (start != end);
132.294 + }
132.295 + }
132.296 +
132.297 + public void formatted(int fieldID, Format.Field attr, Object value,
132.298 + int start, int end, StringBuffer buffer) {
132.299 + if (!encounteredField && matchesField(attr, fieldID)) {
132.300 + setBeginIndex(start);
132.301 + setEndIndex(end);
132.302 + encounteredField = (start != end);
132.303 + }
132.304 + }
132.305 + }
132.306 +}
133.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
133.2 +++ b/rt/emul/compact/src/main/java/java/text/Format.java Tue Feb 11 13:31:42 2014 +0100
133.3 @@ -0,0 +1,406 @@
133.4 +/*
133.5 + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
133.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
133.7 + *
133.8 + * This code is free software; you can redistribute it and/or modify it
133.9 + * under the terms of the GNU General Public License version 2 only, as
133.10 + * published by the Free Software Foundation. Oracle designates this
133.11 + * particular file as subject to the "Classpath" exception as provided
133.12 + * by Oracle in the LICENSE file that accompanied this code.
133.13 + *
133.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
133.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
133.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
133.17 + * version 2 for more details (a copy is included in the LICENSE file that
133.18 + * accompanied this code).
133.19 + *
133.20 + * You should have received a copy of the GNU General Public License version
133.21 + * 2 along with this work; if not, write to the Free Software Foundation,
133.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
133.23 + *
133.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
133.25 + * or visit www.oracle.com if you need additional information or have any
133.26 + * questions.
133.27 + */
133.28 +
133.29 +/*
133.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
133.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
133.32 + *
133.33 + * The original version of this source code and documentation is copyrighted
133.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
133.35 + * materials are provided under terms of a License Agreement between Taligent
133.36 + * and Sun. This technology is protected by multiple US and International
133.37 + * patents. This notice and attribution to Taligent may not be removed.
133.38 + * Taligent is a registered trademark of Taligent, Inc.
133.39 + *
133.40 + */
133.41 +
133.42 +package java.text;
133.43 +
133.44 +import java.io.Serializable;
133.45 +
133.46 +/**
133.47 + * <code>Format</code> is an abstract base class for formatting locale-sensitive
133.48 + * information such as dates, messages, and numbers.
133.49 + *
133.50 + * <p>
133.51 + * <code>Format</code> defines the programming interface for formatting
133.52 + * locale-sensitive objects into <code>String</code>s (the
133.53 + * <code>format</code> method) and for parsing <code>String</code>s back
133.54 + * into objects (the <code>parseObject</code> method).
133.55 + *
133.56 + * <p>
133.57 + * Generally, a format's <code>parseObject</code> method must be able to parse
133.58 + * any string formatted by its <code>format</code> method. However, there may
133.59 + * be exceptional cases where this is not possible. For example, a
133.60 + * <code>format</code> method might create two adjacent integer numbers with
133.61 + * no separator in between, and in this case the <code>parseObject</code> could
133.62 + * not tell which digits belong to which number.
133.63 + *
133.64 + * <h4>Subclassing</h4>
133.65 + *
133.66 + * <p>
133.67 + * The Java Platform provides three specialized subclasses of <code>Format</code>--
133.68 + * <code>DateFormat</code>, <code>MessageFormat</code>, and
133.69 + * <code>NumberFormat</code>--for formatting dates, messages, and numbers,
133.70 + * respectively.
133.71 + * <p>
133.72 + * Concrete subclasses must implement three methods:
133.73 + * <ol>
133.74 + * <li> <code>format(Object obj, StringBuffer toAppendTo, FieldPosition pos)</code>
133.75 + * <li> <code>formatToCharacterIterator(Object obj)</code>
133.76 + * <li> <code>parseObject(String source, ParsePosition pos)</code>
133.77 + * </ol>
133.78 + * These general methods allow polymorphic parsing and formatting of objects
133.79 + * and are used, for example, by <code>MessageFormat</code>.
133.80 + * Subclasses often also provide additional <code>format</code> methods for
133.81 + * specific input types as well as <code>parse</code> methods for specific
133.82 + * result types. Any <code>parse</code> method that does not take a
133.83 + * <code>ParsePosition</code> argument should throw <code>ParseException</code>
133.84 + * when no text in the required format is at the beginning of the input text.
133.85 + *
133.86 + * <p>
133.87 + * Most subclasses will also implement the following factory methods:
133.88 + * <ol>
133.89 + * <li>
133.90 + * <code>getInstance</code> for getting a useful format object appropriate
133.91 + * for the current locale
133.92 + * <li>
133.93 + * <code>getInstance(Locale)</code> for getting a useful format
133.94 + * object appropriate for the specified locale
133.95 + * </ol>
133.96 + * In addition, some subclasses may also implement other
133.97 + * <code>getXxxxInstance</code> methods for more specialized control. For
133.98 + * example, the <code>NumberFormat</code> class provides
133.99 + * <code>getPercentInstance</code> and <code>getCurrencyInstance</code>
133.100 + * methods for getting specialized number formatters.
133.101 + *
133.102 + * <p>
133.103 + * Subclasses of <code>Format</code> that allow programmers to create objects
133.104 + * for locales (with <code>getInstance(Locale)</code> for example)
133.105 + * must also implement the following class method:
133.106 + * <blockquote>
133.107 + * <pre>
133.108 + * public static Locale[] getAvailableLocales()
133.109 + * </pre>
133.110 + * </blockquote>
133.111 + *
133.112 + * <p>
133.113 + * And finally subclasses may define a set of constants to identify the various
133.114 + * fields in the formatted output. These constants are used to create a FieldPosition
133.115 + * object which identifies what information is contained in the field and its
133.116 + * position in the formatted result. These constants should be named
133.117 + * <code><em>item</em>_FIELD</code> where <code><em>item</em></code> identifies
133.118 + * the field. For examples of these constants, see <code>ERA_FIELD</code> and its
133.119 + * friends in {@link DateFormat}.
133.120 + *
133.121 + * <h4><a name="synchronization">Synchronization</a></h4>
133.122 + *
133.123 + * <p>
133.124 + * Formats are generally not synchronized.
133.125 + * It is recommended to create separate format instances for each thread.
133.126 + * If multiple threads access a format concurrently, it must be synchronized
133.127 + * externally.
133.128 + *
133.129 + * @see java.text.ParsePosition
133.130 + * @see java.text.FieldPosition
133.131 + * @see java.text.NumberFormat
133.132 + * @see java.text.DateFormat
133.133 + * @see java.text.MessageFormat
133.134 + * @author Mark Davis
133.135 + */
133.136 +public abstract class Format implements Serializable, Cloneable {
133.137 +
133.138 + private static final long serialVersionUID = -299282585814624189L;
133.139 +
133.140 + /**
133.141 + * Sole constructor. (For invocation by subclass constructors, typically
133.142 + * implicit.)
133.143 + */
133.144 + protected Format() {
133.145 + }
133.146 +
133.147 + /**
133.148 + * Formats an object to produce a string. This is equivalent to
133.149 + * <blockquote>
133.150 + * {@link #format(Object, StringBuffer, FieldPosition) format}<code>(obj,
133.151 + * new StringBuffer(), new FieldPosition(0)).toString();</code>
133.152 + * </blockquote>
133.153 + *
133.154 + * @param obj The object to format
133.155 + * @return Formatted string.
133.156 + * @exception IllegalArgumentException if the Format cannot format the given
133.157 + * object
133.158 + */
133.159 + public final String format (Object obj) {
133.160 + return format(obj, new StringBuffer(), new FieldPosition(0)).toString();
133.161 + }
133.162 +
133.163 + /**
133.164 + * Formats an object and appends the resulting text to a given string
133.165 + * buffer.
133.166 + * If the <code>pos</code> argument identifies a field used by the format,
133.167 + * then its indices are set to the beginning and end of the first such
133.168 + * field encountered.
133.169 + *
133.170 + * @param obj The object to format
133.171 + * @param toAppendTo where the text is to be appended
133.172 + * @param pos A <code>FieldPosition</code> identifying a field
133.173 + * in the formatted text
133.174 + * @return the string buffer passed in as <code>toAppendTo</code>,
133.175 + * with formatted text appended
133.176 + * @exception NullPointerException if <code>toAppendTo</code> or
133.177 + * <code>pos</code> is null
133.178 + * @exception IllegalArgumentException if the Format cannot format the given
133.179 + * object
133.180 + */
133.181 + public abstract StringBuffer format(Object obj,
133.182 + StringBuffer toAppendTo,
133.183 + FieldPosition pos);
133.184 +
133.185 + /**
133.186 + * Formats an Object producing an <code>AttributedCharacterIterator</code>.
133.187 + * You can use the returned <code>AttributedCharacterIterator</code>
133.188 + * to build the resulting String, as well as to determine information
133.189 + * about the resulting String.
133.190 + * <p>
133.191 + * Each attribute key of the AttributedCharacterIterator will be of type
133.192 + * <code>Field</code>. It is up to each <code>Format</code> implementation
133.193 + * to define what the legal values are for each attribute in the
133.194 + * <code>AttributedCharacterIterator</code>, but typically the attribute
133.195 + * key is also used as the attribute value.
133.196 + * <p>The default implementation creates an
133.197 + * <code>AttributedCharacterIterator</code> with no attributes. Subclasses
133.198 + * that support fields should override this and create an
133.199 + * <code>AttributedCharacterIterator</code> with meaningful attributes.
133.200 + *
133.201 + * @exception NullPointerException if obj is null.
133.202 + * @exception IllegalArgumentException when the Format cannot format the
133.203 + * given object.
133.204 + * @param obj The object to format
133.205 + * @return AttributedCharacterIterator describing the formatted value.
133.206 + * @since 1.4
133.207 + */
133.208 + public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
133.209 + return createAttributedCharacterIterator(format(obj));
133.210 + }
133.211 +
133.212 + /**
133.213 + * Parses text from a string to produce an object.
133.214 + * <p>
133.215 + * The method attempts to parse text starting at the index given by
133.216 + * <code>pos</code>.
133.217 + * If parsing succeeds, then the index of <code>pos</code> is updated
133.218 + * to the index after the last character used (parsing does not necessarily
133.219 + * use all characters up to the end of the string), and the parsed
133.220 + * object is returned. The updated <code>pos</code> can be used to
133.221 + * indicate the starting point for the next call to this method.
133.222 + * If an error occurs, then the index of <code>pos</code> is not
133.223 + * changed, the error index of <code>pos</code> is set to the index of
133.224 + * the character where the error occurred, and null is returned.
133.225 + *
133.226 + * @param source A <code>String</code>, part of which should be parsed.
133.227 + * @param pos A <code>ParsePosition</code> object with index and error
133.228 + * index information as described above.
133.229 + * @return An <code>Object</code> parsed from the string. In case of
133.230 + * error, returns null.
133.231 + * @exception NullPointerException if <code>pos</code> is null.
133.232 + */
133.233 + public abstract Object parseObject (String source, ParsePosition pos);
133.234 +
133.235 + /**
133.236 + * Parses text from the beginning of the given string to produce an object.
133.237 + * The method may not use the entire text of the given string.
133.238 + *
133.239 + * @param source A <code>String</code> whose beginning should be parsed.
133.240 + * @return An <code>Object</code> parsed from the string.
133.241 + * @exception ParseException if the beginning of the specified string
133.242 + * cannot be parsed.
133.243 + */
133.244 + public Object parseObject(String source) throws ParseException {
133.245 + ParsePosition pos = new ParsePosition(0);
133.246 + Object result = parseObject(source, pos);
133.247 + if (pos.index == 0) {
133.248 + throw new ParseException("Format.parseObject(String) failed",
133.249 + pos.errorIndex);
133.250 + }
133.251 + return result;
133.252 + }
133.253 +
133.254 + /**
133.255 + * Creates and returns a copy of this object.
133.256 + *
133.257 + * @return a clone of this instance.
133.258 + */
133.259 + public Object clone() {
133.260 + try {
133.261 + return super.clone();
133.262 + } catch (CloneNotSupportedException e) {
133.263 + // will never happen
133.264 + return null;
133.265 + }
133.266 + }
133.267 +
133.268 + //
133.269 + // Convenience methods for creating AttributedCharacterIterators from
133.270 + // different parameters.
133.271 + //
133.272 +
133.273 + /**
133.274 + * Creates an <code>AttributedCharacterIterator</code> for the String
133.275 + * <code>s</code>.
133.276 + *
133.277 + * @param s String to create AttributedCharacterIterator from
133.278 + * @return AttributedCharacterIterator wrapping s
133.279 + */
133.280 + AttributedCharacterIterator createAttributedCharacterIterator(String s) {
133.281 + AttributedString as = new AttributedString(s);
133.282 +
133.283 + return as.getIterator();
133.284 + }
133.285 +
133.286 + /**
133.287 + * Creates an <code>AttributedCharacterIterator</code> containg the
133.288 + * concatenated contents of the passed in
133.289 + * <code>AttributedCharacterIterator</code>s.
133.290 + *
133.291 + * @param iterators AttributedCharacterIterators used to create resulting
133.292 + * AttributedCharacterIterators
133.293 + * @return AttributedCharacterIterator wrapping passed in
133.294 + * AttributedCharacterIterators
133.295 + */
133.296 + AttributedCharacterIterator createAttributedCharacterIterator(
133.297 + AttributedCharacterIterator[] iterators) {
133.298 + AttributedString as = new AttributedString(iterators);
133.299 +
133.300 + return as.getIterator();
133.301 + }
133.302 +
133.303 + /**
133.304 + * Returns an AttributedCharacterIterator with the String
133.305 + * <code>string</code> and additional key/value pair <code>key</code>,
133.306 + * <code>value</code>.
133.307 + *
133.308 + * @param string String to create AttributedCharacterIterator from
133.309 + * @param key Key for AttributedCharacterIterator
133.310 + * @param value Value associated with key in AttributedCharacterIterator
133.311 + * @return AttributedCharacterIterator wrapping args
133.312 + */
133.313 + AttributedCharacterIterator createAttributedCharacterIterator(
133.314 + String string, AttributedCharacterIterator.Attribute key,
133.315 + Object value) {
133.316 + AttributedString as = new AttributedString(string);
133.317 +
133.318 + as.addAttribute(key, value);
133.319 + return as.getIterator();
133.320 + }
133.321 +
133.322 + /**
133.323 + * Creates an AttributedCharacterIterator with the contents of
133.324 + * <code>iterator</code> and the additional attribute <code>key</code>
133.325 + * <code>value</code>.
133.326 + *
133.327 + * @param iterator Initial AttributedCharacterIterator to add arg to
133.328 + * @param key Key for AttributedCharacterIterator
133.329 + * @param value Value associated with key in AttributedCharacterIterator
133.330 + * @return AttributedCharacterIterator wrapping args
133.331 + */
133.332 + AttributedCharacterIterator createAttributedCharacterIterator(
133.333 + AttributedCharacterIterator iterator,
133.334 + AttributedCharacterIterator.Attribute key, Object value) {
133.335 + AttributedString as = new AttributedString(iterator);
133.336 +
133.337 + as.addAttribute(key, value);
133.338 + return as.getIterator();
133.339 + }
133.340 +
133.341 +
133.342 + /**
133.343 + * Defines constants that are used as attribute keys in the
133.344 + * <code>AttributedCharacterIterator</code> returned
133.345 + * from <code>Format.formatToCharacterIterator</code> and as
133.346 + * field identifiers in <code>FieldPosition</code>.
133.347 + *
133.348 + * @since 1.4
133.349 + */
133.350 + public static class Field extends AttributedCharacterIterator.Attribute {
133.351 +
133.352 + // Proclaim serial compatibility with 1.4 FCS
133.353 + private static final long serialVersionUID = 276966692217360283L;
133.354 +
133.355 + /**
133.356 + * Creates a Field with the specified name.
133.357 + *
133.358 + * @param name Name of the attribute
133.359 + */
133.360 + protected Field(String name) {
133.361 + super(name);
133.362 + }
133.363 + }
133.364 +
133.365 +
133.366 + /**
133.367 + * FieldDelegate is notified by the various <code>Format</code>
133.368 + * implementations as they are formatting the Objects. This allows for
133.369 + * storage of the individual sections of the formatted String for
133.370 + * later use, such as in a <code>FieldPosition</code> or for an
133.371 + * <code>AttributedCharacterIterator</code>.
133.372 + * <p>
133.373 + * Delegates should NOT assume that the <code>Format</code> will notify
133.374 + * the delegate of fields in any particular order.
133.375 + *
133.376 + * @see FieldPosition.Delegate
133.377 + * @see CharacterIteratorFieldDelegate
133.378 + */
133.379 + interface FieldDelegate {
133.380 + /**
133.381 + * Notified when a particular region of the String is formatted. This
133.382 + * method will be invoked if there is no corresponding integer field id
133.383 + * matching <code>attr</code>.
133.384 + *
133.385 + * @param attr Identifies the field matched
133.386 + * @param value Value associated with the field
133.387 + * @param start Beginning location of the field, will be >= 0
133.388 + * @param end End of the field, will be >= start and <= buffer.length()
133.389 + * @param buffer Contains current formatted value, receiver should
133.390 + * NOT modify it.
133.391 + */
133.392 + public void formatted(Format.Field attr, Object value, int start,
133.393 + int end, StringBuffer buffer);
133.394 +
133.395 + /**
133.396 + * Notified when a particular region of the String is formatted.
133.397 + *
133.398 + * @param fieldID Identifies the field by integer
133.399 + * @param attr Identifies the field matched
133.400 + * @param value Value associated with the field
133.401 + * @param start Beginning location of the field, will be >= 0
133.402 + * @param end End of the field, will be >= start and <= buffer.length()
133.403 + * @param buffer Contains current formatted value, receiver should
133.404 + * NOT modify it.
133.405 + */
133.406 + public void formatted(int fieldID, Format.Field attr, Object value,
133.407 + int start, int end, StringBuffer buffer);
133.408 + }
133.409 +}
134.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
134.2 +++ b/rt/emul/compact/src/main/java/java/text/MessageFormat.java Tue Feb 11 13:31:42 2014 +0100
134.3 @@ -0,0 +1,1594 @@
134.4 +/*
134.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
134.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
134.7 + *
134.8 + * This code is free software; you can redistribute it and/or modify it
134.9 + * under the terms of the GNU General Public License version 2 only, as
134.10 + * published by the Free Software Foundation. Oracle designates this
134.11 + * particular file as subject to the "Classpath" exception as provided
134.12 + * by Oracle in the LICENSE file that accompanied this code.
134.13 + *
134.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
134.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
134.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
134.17 + * version 2 for more details (a copy is included in the LICENSE file that
134.18 + * accompanied this code).
134.19 + *
134.20 + * You should have received a copy of the GNU General Public License version
134.21 + * 2 along with this work; if not, write to the Free Software Foundation,
134.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
134.23 + *
134.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
134.25 + * or visit www.oracle.com if you need additional information or have any
134.26 + * questions.
134.27 + */
134.28 +
134.29 +/*
134.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
134.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
134.32 + *
134.33 + * The original version of this source code and documentation is copyrighted
134.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
134.35 + * materials are provided under terms of a License Agreement between Taligent
134.36 + * and Sun. This technology is protected by multiple US and International
134.37 + * patents. This notice and attribution to Taligent may not be removed.
134.38 + * Taligent is a registered trademark of Taligent, Inc.
134.39 + *
134.40 + */
134.41 +
134.42 +package java.text;
134.43 +
134.44 +import java.io.InvalidObjectException;
134.45 +import java.io.IOException;
134.46 +import java.io.ObjectInputStream;
134.47 +import java.text.DecimalFormat;
134.48 +import java.util.ArrayList;
134.49 +import java.util.Arrays;
134.50 +import java.util.Date;
134.51 +import java.util.List;
134.52 +import java.util.Locale;
134.53 +
134.54 +
134.55 +/**
134.56 + * <code>MessageFormat</code> provides a means to produce concatenated
134.57 + * messages in a language-neutral way. Use this to construct messages
134.58 + * displayed for end users.
134.59 + *
134.60 + * <p>
134.61 + * <code>MessageFormat</code> takes a set of objects, formats them, then
134.62 + * inserts the formatted strings into the pattern at the appropriate places.
134.63 + *
134.64 + * <p>
134.65 + * <strong>Note:</strong>
134.66 + * <code>MessageFormat</code> differs from the other <code>Format</code>
134.67 + * classes in that you create a <code>MessageFormat</code> object with one
134.68 + * of its constructors (not with a <code>getInstance</code> style factory
134.69 + * method). The factory methods aren't necessary because <code>MessageFormat</code>
134.70 + * itself doesn't implement locale specific behavior. Any locale specific
134.71 + * behavior is defined by the pattern that you provide as well as the
134.72 + * subformats used for inserted arguments.
134.73 + *
134.74 + * <h4><a name="patterns">Patterns and Their Interpretation</a></h4>
134.75 + *
134.76 + * <code>MessageFormat</code> uses patterns of the following form:
134.77 + * <blockquote><pre>
134.78 + * <i>MessageFormatPattern:</i>
134.79 + * <i>String</i>
134.80 + * <i>MessageFormatPattern</i> <i>FormatElement</i> <i>String</i>
134.81 + *
134.82 + * <i>FormatElement:</i>
134.83 + * { <i>ArgumentIndex</i> }
134.84 + * { <i>ArgumentIndex</i> , <i>FormatType</i> }
134.85 + * { <i>ArgumentIndex</i> , <i>FormatType</i> , <i>FormatStyle</i> }
134.86 + *
134.87 + * <i>FormatType: one of </i>
134.88 + * number date time choice
134.89 + *
134.90 + * <i>FormatStyle:</i>
134.91 + * short
134.92 + * medium
134.93 + * long
134.94 + * full
134.95 + * integer
134.96 + * currency
134.97 + * percent
134.98 + * <i>SubformatPattern</i>
134.99 + * </pre></blockquote>
134.100 + *
134.101 + * <p>Within a <i>String</i>, a pair of single quotes can be used to
134.102 + * quote any arbitrary characters except single quotes. For example,
134.103 + * pattern string <code>"'{0}'"</code> represents string
134.104 + * <code>"{0}"</code>, not a <i>FormatElement</i>. A single quote itself
134.105 + * must be represented by doubled single quotes {@code ''} throughout a
134.106 + * <i>String</i>. For example, pattern string <code>"'{''}'"</code> is
134.107 + * interpreted as a sequence of <code>'{</code> (start of quoting and a
134.108 + * left curly brace), <code>''</code> (a single quote), and
134.109 + * <code>}'</code> (a right curly brace and end of quoting),
134.110 + * <em>not</em> <code>'{'</code> and <code>'}'</code> (quoted left and
134.111 + * right curly braces): representing string <code>"{'}"</code>,
134.112 + * <em>not</em> <code>"{}"</code>.
134.113 + *
134.114 + * <p>A <i>SubformatPattern</i> is interpreted by its corresponding
134.115 + * subformat, and subformat-dependent pattern rules apply. For example,
134.116 + * pattern string <code>"{1,number,<u>$'#',##</u>}"</code>
134.117 + * (<i>SubformatPattern</i> with underline) will produce a number format
134.118 + * with the pound-sign quoted, with a result such as: {@code
134.119 + * "$#31,45"}. Refer to each {@code Format} subclass documentation for
134.120 + * details.
134.121 + *
134.122 + * <p>Any unmatched quote is treated as closed at the end of the given
134.123 + * pattern. For example, pattern string {@code "'{0}"} is treated as
134.124 + * pattern {@code "'{0}'"}.
134.125 + *
134.126 + * <p>Any curly braces within an unquoted pattern must be balanced. For
134.127 + * example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code> are
134.128 + * valid patterns, but <code>"ab {0'}' de"</code>, <code>"ab } de"</code>
134.129 + * and <code>"''{''"</code> are not.
134.130 + *
134.131 + * <p>
134.132 + * <dl><dt><b>Warning:</b><dd>The rules for using quotes within message
134.133 + * format patterns unfortunately have shown to be somewhat confusing.
134.134 + * In particular, it isn't always obvious to localizers whether single
134.135 + * quotes need to be doubled or not. Make sure to inform localizers about
134.136 + * the rules, and tell them (for example, by using comments in resource
134.137 + * bundle source files) which strings will be processed by {@code MessageFormat}.
134.138 + * Note that localizers may need to use single quotes in translated
134.139 + * strings where the original version doesn't have them.
134.140 + * </dl>
134.141 + * <p>
134.142 + * The <i>ArgumentIndex</i> value is a non-negative integer written
134.143 + * using the digits {@code '0'} through {@code '9'}, and represents an index into the
134.144 + * {@code arguments} array passed to the {@code format} methods
134.145 + * or the result array returned by the {@code parse} methods.
134.146 + * <p>
134.147 + * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create
134.148 + * a {@code Format} instance for the format element. The following
134.149 + * table shows how the values map to {@code Format} instances. Combinations not
134.150 + * shown in the table are illegal. A <i>SubformatPattern</i> must
134.151 + * be a valid pattern string for the {@code Format} subclass used.
134.152 + * <p>
134.153 + * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances">
134.154 + * <tr>
134.155 + * <th id="ft" class="TableHeadingColor">FormatType
134.156 + * <th id="fs" class="TableHeadingColor">FormatStyle
134.157 + * <th id="sc" class="TableHeadingColor">Subformat Created
134.158 + * <tr>
134.159 + * <td headers="ft"><i>(none)</i>
134.160 + * <td headers="fs"><i>(none)</i>
134.161 + * <td headers="sc"><code>null</code>
134.162 + * <tr>
134.163 + * <td headers="ft" rowspan=5><code>number</code>
134.164 + * <td headers="fs"><i>(none)</i>
134.165 + * <td headers="sc">{@link NumberFormat#getInstance(Locale) NumberFormat.getInstance}{@code (getLocale())}
134.166 + * <tr>
134.167 + * <td headers="fs"><code>integer</code>
134.168 + * <td headers="sc">{@link NumberFormat#getIntegerInstance(Locale) NumberFormat.getIntegerInstance}{@code (getLocale())}
134.169 + * <tr>
134.170 + * <td headers="fs"><code>currency</code>
134.171 + * <td headers="sc">{@link NumberFormat#getCurrencyInstance(Locale) NumberFormat.getCurrencyInstance}{@code (getLocale())}
134.172 + * <tr>
134.173 + * <td headers="fs"><code>percent</code>
134.174 + * <td headers="sc">{@link NumberFormat#getPercentInstance(Locale) NumberFormat.getPercentInstance}{@code (getLocale())}
134.175 + * <tr>
134.176 + * <td headers="fs"><i>SubformatPattern</i>
134.177 + * <td headers="sc">{@code new} {@link DecimalFormat#DecimalFormat(String,DecimalFormatSymbols) DecimalFormat}{@code (subformatPattern,} {@link DecimalFormatSymbols#getInstance(Locale) DecimalFormatSymbols.getInstance}{@code (getLocale()))}
134.178 + * <tr>
134.179 + * <td headers="ft" rowspan=6><code>date</code>
134.180 + * <td headers="fs"><i>(none)</i>
134.181 + * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
134.182 + * <tr>
134.183 + * <td headers="fs"><code>short</code>
134.184 + * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
134.185 + * <tr>
134.186 + * <td headers="fs"><code>medium</code>
134.187 + * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
134.188 + * <tr>
134.189 + * <td headers="fs"><code>long</code>
134.190 + * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
134.191 + * <tr>
134.192 + * <td headers="fs"><code>full</code>
134.193 + * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
134.194 + * <tr>
134.195 + * <td headers="fs"><i>SubformatPattern</i>
134.196 + * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
134.197 + * <tr>
134.198 + * <td headers="ft" rowspan=6><code>time</code>
134.199 + * <td headers="fs"><i>(none)</i>
134.200 + * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
134.201 + * <tr>
134.202 + * <td headers="fs"><code>short</code>
134.203 + * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
134.204 + * <tr>
134.205 + * <td headers="fs"><code>medium</code>
134.206 + * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
134.207 + * <tr>
134.208 + * <td headers="fs"><code>long</code>
134.209 + * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
134.210 + * <tr>
134.211 + * <td headers="fs"><code>full</code>
134.212 + * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
134.213 + * <tr>
134.214 + * <td headers="fs"><i>SubformatPattern</i>
134.215 + * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
134.216 + * <tr>
134.217 + * <td headers="ft"><code>choice</code>
134.218 + * <td headers="fs"><i>SubformatPattern</i>
134.219 + * <td headers="sc">{@code new} {@link ChoiceFormat#ChoiceFormat(String) ChoiceFormat}{@code (subformatPattern)}
134.220 + * </table>
134.221 + * <p>
134.222 + *
134.223 + * <h4>Usage Information</h4>
134.224 + *
134.225 + * <p>
134.226 + * Here are some examples of usage.
134.227 + * In real internationalized programs, the message format pattern and other
134.228 + * static strings will, of course, be obtained from resource bundles.
134.229 + * Other parameters will be dynamically determined at runtime.
134.230 + * <p>
134.231 + * The first example uses the static method <code>MessageFormat.format</code>,
134.232 + * which internally creates a <code>MessageFormat</code> for one-time use:
134.233 + * <blockquote><pre>
134.234 + * int planet = 7;
134.235 + * String event = "a disturbance in the Force";
134.236 + *
134.237 + * String result = MessageFormat.format(
134.238 + * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
134.239 + * planet, new Date(), event);
134.240 + * </pre></blockquote>
134.241 + * The output is:
134.242 + * <blockquote><pre>
134.243 + * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7.
134.244 + * </pre></blockquote>
134.245 + *
134.246 + * <p>
134.247 + * The following example creates a <code>MessageFormat</code> instance that
134.248 + * can be used repeatedly:
134.249 + * <blockquote><pre>
134.250 + * int fileCount = 1273;
134.251 + * String diskName = "MyDisk";
134.252 + * Object[] testArgs = {new Long(fileCount), diskName};
134.253 + *
134.254 + * MessageFormat form = new MessageFormat(
134.255 + * "The disk \"{1}\" contains {0} file(s).");
134.256 + *
134.257 + * System.out.println(form.format(testArgs));
134.258 + * </pre></blockquote>
134.259 + * The output with different values for <code>fileCount</code>:
134.260 + * <blockquote><pre>
134.261 + * The disk "MyDisk" contains 0 file(s).
134.262 + * The disk "MyDisk" contains 1 file(s).
134.263 + * The disk "MyDisk" contains 1,273 file(s).
134.264 + * </pre></blockquote>
134.265 + *
134.266 + * <p>
134.267 + * For more sophisticated patterns, you can use a <code>ChoiceFormat</code>
134.268 + * to produce correct forms for singular and plural:
134.269 + * <blockquote><pre>
134.270 + * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
134.271 + * double[] filelimits = {0,1,2};
134.272 + * String[] filepart = {"no files","one file","{0,number} files"};
134.273 + * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
134.274 + * form.setFormatByArgumentIndex(0, fileform);
134.275 + *
134.276 + * int fileCount = 1273;
134.277 + * String diskName = "MyDisk";
134.278 + * Object[] testArgs = {new Long(fileCount), diskName};
134.279 + *
134.280 + * System.out.println(form.format(testArgs));
134.281 + * </pre></blockquote>
134.282 + * The output with different values for <code>fileCount</code>:
134.283 + * <blockquote><pre>
134.284 + * The disk "MyDisk" contains no files.
134.285 + * The disk "MyDisk" contains one file.
134.286 + * The disk "MyDisk" contains 1,273 files.
134.287 + * </pre></blockquote>
134.288 + *
134.289 + * <p>
134.290 + * You can create the <code>ChoiceFormat</code> programmatically, as in the
134.291 + * above example, or by using a pattern. See {@link ChoiceFormat}
134.292 + * for more information.
134.293 + * <blockquote><pre>
134.294 + * form.applyPattern(
134.295 + * "There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.");
134.296 + * </pre></blockquote>
134.297 + *
134.298 + * <p>
134.299 + * <strong>Note:</strong> As we see above, the string produced
134.300 + * by a <code>ChoiceFormat</code> in <code>MessageFormat</code> is treated as special;
134.301 + * occurrences of '{' are used to indicate subformats, and cause recursion.
134.302 + * If you create both a <code>MessageFormat</code> and <code>ChoiceFormat</code>
134.303 + * programmatically (instead of using the string patterns), then be careful not to
134.304 + * produce a format that recurses on itself, which will cause an infinite loop.
134.305 + * <p>
134.306 + * When a single argument is parsed more than once in the string, the last match
134.307 + * will be the final result of the parsing. For example,
134.308 + * <blockquote><pre>
134.309 + * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
134.310 + * Object[] objs = {new Double(3.1415)};
134.311 + * String result = mf.format( objs );
134.312 + * // result now equals "3.14, 3.1"
134.313 + * objs = null;
134.314 + * objs = mf.parse(result, new ParsePosition(0));
134.315 + * // objs now equals {new Double(3.1)}
134.316 + * </pre></blockquote>
134.317 + *
134.318 + * <p>
134.319 + * Likewise, parsing with a {@code MessageFormat} object using patterns containing
134.320 + * multiple occurrences of the same argument would return the last match. For
134.321 + * example,
134.322 + * <blockquote><pre>
134.323 + * MessageFormat mf = new MessageFormat("{0}, {0}, {0}");
134.324 + * String forParsing = "x, y, z";
134.325 + * Object[] objs = mf.parse(forParsing, new ParsePosition(0));
134.326 + * // result now equals {new String("z")}
134.327 + * </pre></blockquote>
134.328 + *
134.329 + * <h4><a name="synchronization">Synchronization</a></h4>
134.330 + *
134.331 + * <p>
134.332 + * Message formats are not synchronized.
134.333 + * It is recommended to create separate format instances for each thread.
134.334 + * If multiple threads access a format concurrently, it must be synchronized
134.335 + * externally.
134.336 + *
134.337 + * @see java.util.Locale
134.338 + * @see Format
134.339 + * @see NumberFormat
134.340 + * @see DecimalFormat
134.341 + * @see DecimalFormatSymbols
134.342 + * @see ChoiceFormat
134.343 + * @see DateFormat
134.344 + * @see SimpleDateFormat
134.345 + *
134.346 + * @author Mark Davis
134.347 + */
134.348 +
134.349 +public class MessageFormat extends Format {
134.350 +
134.351 + private static final long serialVersionUID = 6479157306784022952L;
134.352 +
134.353 + /**
134.354 + * Constructs a MessageFormat for the default locale and the
134.355 + * specified pattern.
134.356 + * The constructor first sets the locale, then parses the pattern and
134.357 + * creates a list of subformats for the format elements contained in it.
134.358 + * Patterns and their interpretation are specified in the
134.359 + * <a href="#patterns">class description</a>.
134.360 + *
134.361 + * @param pattern the pattern for this message format
134.362 + * @exception IllegalArgumentException if the pattern is invalid
134.363 + */
134.364 + public MessageFormat(String pattern) {
134.365 + this.locale = Locale.getDefault(Locale.Category.FORMAT);
134.366 + applyPattern(pattern);
134.367 + }
134.368 +
134.369 + /**
134.370 + * Constructs a MessageFormat for the specified locale and
134.371 + * pattern.
134.372 + * The constructor first sets the locale, then parses the pattern and
134.373 + * creates a list of subformats for the format elements contained in it.
134.374 + * Patterns and their interpretation are specified in the
134.375 + * <a href="#patterns">class description</a>.
134.376 + *
134.377 + * @param pattern the pattern for this message format
134.378 + * @param locale the locale for this message format
134.379 + * @exception IllegalArgumentException if the pattern is invalid
134.380 + * @since 1.4
134.381 + */
134.382 + public MessageFormat(String pattern, Locale locale) {
134.383 + this.locale = locale;
134.384 + applyPattern(pattern);
134.385 + }
134.386 +
134.387 + /**
134.388 + * Sets the locale to be used when creating or comparing subformats.
134.389 + * This affects subsequent calls
134.390 + * <ul>
134.391 + * <li>to the {@link #applyPattern applyPattern}
134.392 + * and {@link #toPattern toPattern} methods if format elements specify
134.393 + * a format type and therefore have the subformats created in the
134.394 + * <code>applyPattern</code> method, as well as
134.395 + * <li>to the <code>format</code> and
134.396 + * {@link #formatToCharacterIterator formatToCharacterIterator} methods
134.397 + * if format elements do not specify a format type and therefore have
134.398 + * the subformats created in the formatting methods.
134.399 + * </ul>
134.400 + * Subformats that have already been created are not affected.
134.401 + *
134.402 + * @param locale the locale to be used when creating or comparing subformats
134.403 + */
134.404 + public void setLocale(Locale locale) {
134.405 + this.locale = locale;
134.406 + }
134.407 +
134.408 + /**
134.409 + * Gets the locale that's used when creating or comparing subformats.
134.410 + *
134.411 + * @return the locale used when creating or comparing subformats
134.412 + */
134.413 + public Locale getLocale() {
134.414 + return locale;
134.415 + }
134.416 +
134.417 +
134.418 + /**
134.419 + * Sets the pattern used by this message format.
134.420 + * The method parses the pattern and creates a list of subformats
134.421 + * for the format elements contained in it.
134.422 + * Patterns and their interpretation are specified in the
134.423 + * <a href="#patterns">class description</a>.
134.424 + *
134.425 + * @param pattern the pattern for this message format
134.426 + * @exception IllegalArgumentException if the pattern is invalid
134.427 + */
134.428 + public void applyPattern(String pattern) {
134.429 + StringBuilder[] segments = new StringBuilder[4];
134.430 + // Allocate only segments[SEG_RAW] here. The rest are
134.431 + // allocated on demand.
134.432 + segments[SEG_RAW] = new StringBuilder();
134.433 +
134.434 + int part = SEG_RAW;
134.435 + int formatNumber = 0;
134.436 + boolean inQuote = false;
134.437 + int braceStack = 0;
134.438 + maxOffset = -1;
134.439 + for (int i = 0; i < pattern.length(); ++i) {
134.440 + char ch = pattern.charAt(i);
134.441 + if (part == SEG_RAW) {
134.442 + if (ch == '\'') {
134.443 + if (i + 1 < pattern.length()
134.444 + && pattern.charAt(i+1) == '\'') {
134.445 + segments[part].append(ch); // handle doubles
134.446 + ++i;
134.447 + } else {
134.448 + inQuote = !inQuote;
134.449 + }
134.450 + } else if (ch == '{' && !inQuote) {
134.451 + part = SEG_INDEX;
134.452 + if (segments[SEG_INDEX] == null) {
134.453 + segments[SEG_INDEX] = new StringBuilder();
134.454 + }
134.455 + } else {
134.456 + segments[part].append(ch);
134.457 + }
134.458 + } else {
134.459 + if (inQuote) { // just copy quotes in parts
134.460 + segments[part].append(ch);
134.461 + if (ch == '\'') {
134.462 + inQuote = false;
134.463 + }
134.464 + } else {
134.465 + switch (ch) {
134.466 + case ',':
134.467 + if (part < SEG_MODIFIER) {
134.468 + if (segments[++part] == null) {
134.469 + segments[part] = new StringBuilder();
134.470 + }
134.471 + } else {
134.472 + segments[part].append(ch);
134.473 + }
134.474 + break;
134.475 + case '{':
134.476 + ++braceStack;
134.477 + segments[part].append(ch);
134.478 + break;
134.479 + case '}':
134.480 + if (braceStack == 0) {
134.481 + part = SEG_RAW;
134.482 + makeFormat(i, formatNumber, segments);
134.483 + formatNumber++;
134.484 + // throw away other segments
134.485 + segments[SEG_INDEX] = null;
134.486 + segments[SEG_TYPE] = null;
134.487 + segments[SEG_MODIFIER] = null;
134.488 + } else {
134.489 + --braceStack;
134.490 + segments[part].append(ch);
134.491 + }
134.492 + break;
134.493 + case ' ':
134.494 + // Skip any leading space chars for SEG_TYPE.
134.495 + if (part != SEG_TYPE || segments[SEG_TYPE].length() > 0) {
134.496 + segments[part].append(ch);
134.497 + }
134.498 + break;
134.499 + case '\'':
134.500 + inQuote = true;
134.501 + // fall through, so we keep quotes in other parts
134.502 + default:
134.503 + segments[part].append(ch);
134.504 + break;
134.505 + }
134.506 + }
134.507 + }
134.508 + }
134.509 + if (braceStack == 0 && part != 0) {
134.510 + maxOffset = -1;
134.511 + throw new IllegalArgumentException("Unmatched braces in the pattern.");
134.512 + }
134.513 + this.pattern = segments[0].toString();
134.514 + }
134.515 +
134.516 +
134.517 + /**
134.518 + * Returns a pattern representing the current state of the message format.
134.519 + * The string is constructed from internal information and therefore
134.520 + * does not necessarily equal the previously applied pattern.
134.521 + *
134.522 + * @return a pattern representing the current state of the message format
134.523 + */
134.524 + public String toPattern() {
134.525 + // later, make this more extensible
134.526 + int lastOffset = 0;
134.527 + StringBuilder result = new StringBuilder();
134.528 + for (int i = 0; i <= maxOffset; ++i) {
134.529 + copyAndFixQuotes(pattern, lastOffset, offsets[i], result);
134.530 + lastOffset = offsets[i];
134.531 + result.append('{').append(argumentNumbers[i]);
134.532 + Format fmt = formats[i];
134.533 + if (fmt == null) {
134.534 + // do nothing, string format
134.535 + } else if (fmt instanceof NumberFormat) {
134.536 + if (fmt.equals(NumberFormat.getInstance(locale))) {
134.537 + result.append(",number");
134.538 + } else if (fmt.equals(NumberFormat.getCurrencyInstance(locale))) {
134.539 + result.append(",number,currency");
134.540 + } else if (fmt.equals(NumberFormat.getPercentInstance(locale))) {
134.541 + result.append(",number,percent");
134.542 + } else if (fmt.equals(NumberFormat.getIntegerInstance(locale))) {
134.543 + result.append(",number,integer");
134.544 + } else {
134.545 + if (fmt instanceof DecimalFormat) {
134.546 + result.append(",number,").append(((DecimalFormat)fmt).toPattern());
134.547 + } else if (fmt instanceof ChoiceFormat) {
134.548 + result.append(",choice,").append(((ChoiceFormat)fmt).toPattern());
134.549 + } else {
134.550 + // UNKNOWN
134.551 + }
134.552 + }
134.553 + } else if (fmt instanceof DateFormat) {
134.554 + int index;
134.555 + for (index = MODIFIER_DEFAULT; index < DATE_TIME_MODIFIERS.length; index++) {
134.556 + DateFormat df = DateFormat.getDateInstance(DATE_TIME_MODIFIERS[index],
134.557 + locale);
134.558 + if (fmt.equals(df)) {
134.559 + result.append(",date");
134.560 + break;
134.561 + }
134.562 + df = DateFormat.getTimeInstance(DATE_TIME_MODIFIERS[index],
134.563 + locale);
134.564 + if (fmt.equals(df)) {
134.565 + result.append(",time");
134.566 + break;
134.567 + }
134.568 + }
134.569 + if (index >= DATE_TIME_MODIFIERS.length) {
134.570 + if (fmt instanceof SimpleDateFormat) {
134.571 + result.append(",date,").append(((SimpleDateFormat)fmt).toPattern());
134.572 + } else {
134.573 + // UNKNOWN
134.574 + }
134.575 + } else if (index != MODIFIER_DEFAULT) {
134.576 + result.append(',').append(DATE_TIME_MODIFIER_KEYWORDS[index]);
134.577 + }
134.578 + } else {
134.579 + //result.append(", unknown");
134.580 + }
134.581 + result.append('}');
134.582 + }
134.583 + copyAndFixQuotes(pattern, lastOffset, pattern.length(), result);
134.584 + return result.toString();
134.585 + }
134.586 +
134.587 + /**
134.588 + * Sets the formats to use for the values passed into
134.589 + * <code>format</code> methods or returned from <code>parse</code>
134.590 + * methods. The indices of elements in <code>newFormats</code>
134.591 + * correspond to the argument indices used in the previously set
134.592 + * pattern string.
134.593 + * The order of formats in <code>newFormats</code> thus corresponds to
134.594 + * the order of elements in the <code>arguments</code> array passed
134.595 + * to the <code>format</code> methods or the result array returned
134.596 + * by the <code>parse</code> methods.
134.597 + * <p>
134.598 + * If an argument index is used for more than one format element
134.599 + * in the pattern string, then the corresponding new format is used
134.600 + * for all such format elements. If an argument index is not used
134.601 + * for any format element in the pattern string, then the
134.602 + * corresponding new format is ignored. If fewer formats are provided
134.603 + * than needed, then only the formats for argument indices less
134.604 + * than <code>newFormats.length</code> are replaced.
134.605 + *
134.606 + * @param newFormats the new formats to use
134.607 + * @exception NullPointerException if <code>newFormats</code> is null
134.608 + * @since 1.4
134.609 + */
134.610 + public void setFormatsByArgumentIndex(Format[] newFormats) {
134.611 + for (int i = 0; i <= maxOffset; i++) {
134.612 + int j = argumentNumbers[i];
134.613 + if (j < newFormats.length) {
134.614 + formats[i] = newFormats[j];
134.615 + }
134.616 + }
134.617 + }
134.618 +
134.619 + /**
134.620 + * Sets the formats to use for the format elements in the
134.621 + * previously set pattern string.
134.622 + * The order of formats in <code>newFormats</code> corresponds to
134.623 + * the order of format elements in the pattern string.
134.624 + * <p>
134.625 + * If more formats are provided than needed by the pattern string,
134.626 + * the remaining ones are ignored. If fewer formats are provided
134.627 + * than needed, then only the first <code>newFormats.length</code>
134.628 + * formats are replaced.
134.629 + * <p>
134.630 + * Since the order of format elements in a pattern string often
134.631 + * changes during localization, it is generally better to use the
134.632 + * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex}
134.633 + * method, which assumes an order of formats corresponding to the
134.634 + * order of elements in the <code>arguments</code> array passed to
134.635 + * the <code>format</code> methods or the result array returned by
134.636 + * the <code>parse</code> methods.
134.637 + *
134.638 + * @param newFormats the new formats to use
134.639 + * @exception NullPointerException if <code>newFormats</code> is null
134.640 + */
134.641 + public void setFormats(Format[] newFormats) {
134.642 + int runsToCopy = newFormats.length;
134.643 + if (runsToCopy > maxOffset + 1) {
134.644 + runsToCopy = maxOffset + 1;
134.645 + }
134.646 + for (int i = 0; i < runsToCopy; i++) {
134.647 + formats[i] = newFormats[i];
134.648 + }
134.649 + }
134.650 +
134.651 + /**
134.652 + * Sets the format to use for the format elements within the
134.653 + * previously set pattern string that use the given argument
134.654 + * index.
134.655 + * The argument index is part of the format element definition and
134.656 + * represents an index into the <code>arguments</code> array passed
134.657 + * to the <code>format</code> methods or the result array returned
134.658 + * by the <code>parse</code> methods.
134.659 + * <p>
134.660 + * If the argument index is used for more than one format element
134.661 + * in the pattern string, then the new format is used for all such
134.662 + * format elements. If the argument index is not used for any format
134.663 + * element in the pattern string, then the new format is ignored.
134.664 + *
134.665 + * @param argumentIndex the argument index for which to use the new format
134.666 + * @param newFormat the new format to use
134.667 + * @since 1.4
134.668 + */
134.669 + public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) {
134.670 + for (int j = 0; j <= maxOffset; j++) {
134.671 + if (argumentNumbers[j] == argumentIndex) {
134.672 + formats[j] = newFormat;
134.673 + }
134.674 + }
134.675 + }
134.676 +
134.677 + /**
134.678 + * Sets the format to use for the format element with the given
134.679 + * format element index within the previously set pattern string.
134.680 + * The format element index is the zero-based number of the format
134.681 + * element counting from the start of the pattern string.
134.682 + * <p>
134.683 + * Since the order of format elements in a pattern string often
134.684 + * changes during localization, it is generally better to use the
134.685 + * {@link #setFormatByArgumentIndex setFormatByArgumentIndex}
134.686 + * method, which accesses format elements based on the argument
134.687 + * index they specify.
134.688 + *
134.689 + * @param formatElementIndex the index of a format element within the pattern
134.690 + * @param newFormat the format to use for the specified format element
134.691 + * @exception ArrayIndexOutOfBoundsException if {@code formatElementIndex} is equal to or
134.692 + * larger than the number of format elements in the pattern string
134.693 + */
134.694 + public void setFormat(int formatElementIndex, Format newFormat) {
134.695 + formats[formatElementIndex] = newFormat;
134.696 + }
134.697 +
134.698 + /**
134.699 + * Gets the formats used for the values passed into
134.700 + * <code>format</code> methods or returned from <code>parse</code>
134.701 + * methods. The indices of elements in the returned array
134.702 + * correspond to the argument indices used in the previously set
134.703 + * pattern string.
134.704 + * The order of formats in the returned array thus corresponds to
134.705 + * the order of elements in the <code>arguments</code> array passed
134.706 + * to the <code>format</code> methods or the result array returned
134.707 + * by the <code>parse</code> methods.
134.708 + * <p>
134.709 + * If an argument index is used for more than one format element
134.710 + * in the pattern string, then the format used for the last such
134.711 + * format element is returned in the array. If an argument index
134.712 + * is not used for any format element in the pattern string, then
134.713 + * null is returned in the array.
134.714 + *
134.715 + * @return the formats used for the arguments within the pattern
134.716 + * @since 1.4
134.717 + */
134.718 + public Format[] getFormatsByArgumentIndex() {
134.719 + int maximumArgumentNumber = -1;
134.720 + for (int i = 0; i <= maxOffset; i++) {
134.721 + if (argumentNumbers[i] > maximumArgumentNumber) {
134.722 + maximumArgumentNumber = argumentNumbers[i];
134.723 + }
134.724 + }
134.725 + Format[] resultArray = new Format[maximumArgumentNumber + 1];
134.726 + for (int i = 0; i <= maxOffset; i++) {
134.727 + resultArray[argumentNumbers[i]] = formats[i];
134.728 + }
134.729 + return resultArray;
134.730 + }
134.731 +
134.732 + /**
134.733 + * Gets the formats used for the format elements in the
134.734 + * previously set pattern string.
134.735 + * The order of formats in the returned array corresponds to
134.736 + * the order of format elements in the pattern string.
134.737 + * <p>
134.738 + * Since the order of format elements in a pattern string often
134.739 + * changes during localization, it's generally better to use the
134.740 + * {@link #getFormatsByArgumentIndex getFormatsByArgumentIndex}
134.741 + * method, which assumes an order of formats corresponding to the
134.742 + * order of elements in the <code>arguments</code> array passed to
134.743 + * the <code>format</code> methods or the result array returned by
134.744 + * the <code>parse</code> methods.
134.745 + *
134.746 + * @return the formats used for the format elements in the pattern
134.747 + */
134.748 + public Format[] getFormats() {
134.749 + Format[] resultArray = new Format[maxOffset + 1];
134.750 + System.arraycopy(formats, 0, resultArray, 0, maxOffset + 1);
134.751 + return resultArray;
134.752 + }
134.753 +
134.754 + /**
134.755 + * Formats an array of objects and appends the <code>MessageFormat</code>'s
134.756 + * pattern, with format elements replaced by the formatted objects, to the
134.757 + * provided <code>StringBuffer</code>.
134.758 + * <p>
134.759 + * The text substituted for the individual format elements is derived from
134.760 + * the current subformat of the format element and the
134.761 + * <code>arguments</code> element at the format element's argument index
134.762 + * as indicated by the first matching line of the following table. An
134.763 + * argument is <i>unavailable</i> if <code>arguments</code> is
134.764 + * <code>null</code> or has fewer than argumentIndex+1 elements.
134.765 + * <p>
134.766 + * <table border=1 summary="Examples of subformat,argument,and formatted text">
134.767 + * <tr>
134.768 + * <th>Subformat
134.769 + * <th>Argument
134.770 + * <th>Formatted Text
134.771 + * <tr>
134.772 + * <td><i>any</i>
134.773 + * <td><i>unavailable</i>
134.774 + * <td><code>"{" + argumentIndex + "}"</code>
134.775 + * <tr>
134.776 + * <td><i>any</i>
134.777 + * <td><code>null</code>
134.778 + * <td><code>"null"</code>
134.779 + * <tr>
134.780 + * <td><code>instanceof ChoiceFormat</code>
134.781 + * <td><i>any</i>
134.782 + * <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br>
134.783 + * (new MessageFormat(subformat.format(argument), getLocale())).format(argument) :
134.784 + * subformat.format(argument)</code>
134.785 + * <tr>
134.786 + * <td><code>!= null</code>
134.787 + * <td><i>any</i>
134.788 + * <td><code>subformat.format(argument)</code>
134.789 + * <tr>
134.790 + * <td><code>null</code>
134.791 + * <td><code>instanceof Number</code>
134.792 + * <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>
134.793 + * <tr>
134.794 + * <td><code>null</code>
134.795 + * <td><code>instanceof Date</code>
134.796 + * <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code>
134.797 + * <tr>
134.798 + * <td><code>null</code>
134.799 + * <td><code>instanceof String</code>
134.800 + * <td><code>argument</code>
134.801 + * <tr>
134.802 + * <td><code>null</code>
134.803 + * <td><i>any</i>
134.804 + * <td><code>argument.toString()</code>
134.805 + * </table>
134.806 + * <p>
134.807 + * If <code>pos</code> is non-null, and refers to
134.808 + * <code>Field.ARGUMENT</code>, the location of the first formatted
134.809 + * string will be returned.
134.810 + *
134.811 + * @param arguments an array of objects to be formatted and substituted.
134.812 + * @param result where text is appended.
134.813 + * @param pos On input: an alignment field, if desired.
134.814 + * On output: the offsets of the alignment field.
134.815 + * @exception IllegalArgumentException if an argument in the
134.816 + * <code>arguments</code> array is not of the type
134.817 + * expected by the format element(s) that use it.
134.818 + */
134.819 + public final StringBuffer format(Object[] arguments, StringBuffer result,
134.820 + FieldPosition pos)
134.821 + {
134.822 + return subformat(arguments, result, pos, null);
134.823 + }
134.824 +
134.825 + /**
134.826 + * Creates a MessageFormat with the given pattern and uses it
134.827 + * to format the given arguments. This is equivalent to
134.828 + * <blockquote>
134.829 + * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
134.830 + * </blockquote>
134.831 + *
134.832 + * @exception IllegalArgumentException if the pattern is invalid,
134.833 + * or if an argument in the <code>arguments</code> array
134.834 + * is not of the type expected by the format element(s)
134.835 + * that use it.
134.836 + */
134.837 + public static String format(String pattern, Object ... arguments) {
134.838 + MessageFormat temp = new MessageFormat(pattern);
134.839 + return temp.format(arguments);
134.840 + }
134.841 +
134.842 + // Overrides
134.843 + /**
134.844 + * Formats an array of objects and appends the <code>MessageFormat</code>'s
134.845 + * pattern, with format elements replaced by the formatted objects, to the
134.846 + * provided <code>StringBuffer</code>.
134.847 + * This is equivalent to
134.848 + * <blockquote>
134.849 + * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
134.850 + * </blockquote>
134.851 + *
134.852 + * @param arguments an array of objects to be formatted and substituted.
134.853 + * @param result where text is appended.
134.854 + * @param pos On input: an alignment field, if desired.
134.855 + * On output: the offsets of the alignment field.
134.856 + * @exception IllegalArgumentException if an argument in the
134.857 + * <code>arguments</code> array is not of the type
134.858 + * expected by the format element(s) that use it.
134.859 + */
134.860 + public final StringBuffer format(Object arguments, StringBuffer result,
134.861 + FieldPosition pos)
134.862 + {
134.863 + return subformat((Object[]) arguments, result, pos, null);
134.864 + }
134.865 +
134.866 + /**
134.867 + * Formats an array of objects and inserts them into the
134.868 + * <code>MessageFormat</code>'s pattern, producing an
134.869 + * <code>AttributedCharacterIterator</code>.
134.870 + * You can use the returned <code>AttributedCharacterIterator</code>
134.871 + * to build the resulting String, as well as to determine information
134.872 + * about the resulting String.
134.873 + * <p>
134.874 + * The text of the returned <code>AttributedCharacterIterator</code> is
134.875 + * the same that would be returned by
134.876 + * <blockquote>
134.877 + * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
134.878 + * </blockquote>
134.879 + * <p>
134.880 + * In addition, the <code>AttributedCharacterIterator</code> contains at
134.881 + * least attributes indicating where text was generated from an
134.882 + * argument in the <code>arguments</code> array. The keys of these attributes are of
134.883 + * type <code>MessageFormat.Field</code>, their values are
134.884 + * <code>Integer</code> objects indicating the index in the <code>arguments</code>
134.885 + * array of the argument from which the text was generated.
134.886 + * <p>
134.887 + * The attributes/value from the underlying <code>Format</code>
134.888 + * instances that <code>MessageFormat</code> uses will also be
134.889 + * placed in the resulting <code>AttributedCharacterIterator</code>.
134.890 + * This allows you to not only find where an argument is placed in the
134.891 + * resulting String, but also which fields it contains in turn.
134.892 + *
134.893 + * @param arguments an array of objects to be formatted and substituted.
134.894 + * @return AttributedCharacterIterator describing the formatted value.
134.895 + * @exception NullPointerException if <code>arguments</code> is null.
134.896 + * @exception IllegalArgumentException if an argument in the
134.897 + * <code>arguments</code> array is not of the type
134.898 + * expected by the format element(s) that use it.
134.899 + * @since 1.4
134.900 + */
134.901 + public AttributedCharacterIterator formatToCharacterIterator(Object arguments) {
134.902 + StringBuffer result = new StringBuffer();
134.903 + ArrayList iterators = new ArrayList();
134.904 +
134.905 + if (arguments == null) {
134.906 + throw new NullPointerException(
134.907 + "formatToCharacterIterator must be passed non-null object");
134.908 + }
134.909 + subformat((Object[]) arguments, result, null, iterators);
134.910 + if (iterators.size() == 0) {
134.911 + return createAttributedCharacterIterator("");
134.912 + }
134.913 + return createAttributedCharacterIterator(
134.914 + (AttributedCharacterIterator[])iterators.toArray(
134.915 + new AttributedCharacterIterator[iterators.size()]));
134.916 + }
134.917 +
134.918 + /**
134.919 + * Parses the string.
134.920 + *
134.921 + * <p>Caveats: The parse may fail in a number of circumstances.
134.922 + * For example:
134.923 + * <ul>
134.924 + * <li>If one of the arguments does not occur in the pattern.
134.925 + * <li>If the format of an argument loses information, such as
134.926 + * with a choice format where a large number formats to "many".
134.927 + * <li>Does not yet handle recursion (where
134.928 + * the substituted strings contain {n} references.)
134.929 + * <li>Will not always find a match (or the correct match)
134.930 + * if some part of the parse is ambiguous.
134.931 + * For example, if the pattern "{1},{2}" is used with the
134.932 + * string arguments {"a,b", "c"}, it will format as "a,b,c".
134.933 + * When the result is parsed, it will return {"a", "b,c"}.
134.934 + * <li>If a single argument is parsed more than once in the string,
134.935 + * then the later parse wins.
134.936 + * </ul>
134.937 + * When the parse fails, use ParsePosition.getErrorIndex() to find out
134.938 + * where in the string the parsing failed. The returned error
134.939 + * index is the starting offset of the sub-patterns that the string
134.940 + * is comparing with. For example, if the parsing string "AAA {0} BBB"
134.941 + * is comparing against the pattern "AAD {0} BBB", the error index is
134.942 + * 0. When an error occurs, the call to this method will return null.
134.943 + * If the source is null, return an empty array.
134.944 + */
134.945 + public Object[] parse(String source, ParsePosition pos) {
134.946 + if (source == null) {
134.947 + Object[] empty = {};
134.948 + return empty;
134.949 + }
134.950 +
134.951 + int maximumArgumentNumber = -1;
134.952 + for (int i = 0; i <= maxOffset; i++) {
134.953 + if (argumentNumbers[i] > maximumArgumentNumber) {
134.954 + maximumArgumentNumber = argumentNumbers[i];
134.955 + }
134.956 + }
134.957 + Object[] resultArray = new Object[maximumArgumentNumber + 1];
134.958 +
134.959 + int patternOffset = 0;
134.960 + int sourceOffset = pos.index;
134.961 + ParsePosition tempStatus = new ParsePosition(0);
134.962 + for (int i = 0; i <= maxOffset; ++i) {
134.963 + // match up to format
134.964 + int len = offsets[i] - patternOffset;
134.965 + if (len == 0 || pattern.regionMatches(patternOffset,
134.966 + source, sourceOffset, len)) {
134.967 + sourceOffset += len;
134.968 + patternOffset += len;
134.969 + } else {
134.970 + pos.errorIndex = sourceOffset;
134.971 + return null; // leave index as is to signal error
134.972 + }
134.973 +
134.974 + // now use format
134.975 + if (formats[i] == null) { // string format
134.976 + // if at end, use longest possible match
134.977 + // otherwise uses first match to intervening string
134.978 + // does NOT recursively try all possibilities
134.979 + int tempLength = (i != maxOffset) ? offsets[i+1] : pattern.length();
134.980 +
134.981 + int next;
134.982 + if (patternOffset >= tempLength) {
134.983 + next = source.length();
134.984 + }else{
134.985 + next = source.indexOf(pattern.substring(patternOffset, tempLength),
134.986 + sourceOffset);
134.987 + }
134.988 +
134.989 + if (next < 0) {
134.990 + pos.errorIndex = sourceOffset;
134.991 + return null; // leave index as is to signal error
134.992 + } else {
134.993 + String strValue= source.substring(sourceOffset,next);
134.994 + if (!strValue.equals("{"+argumentNumbers[i]+"}"))
134.995 + resultArray[argumentNumbers[i]]
134.996 + = source.substring(sourceOffset,next);
134.997 + sourceOffset = next;
134.998 + }
134.999 + } else {
134.1000 + tempStatus.index = sourceOffset;
134.1001 + resultArray[argumentNumbers[i]]
134.1002 + = formats[i].parseObject(source,tempStatus);
134.1003 + if (tempStatus.index == sourceOffset) {
134.1004 + pos.errorIndex = sourceOffset;
134.1005 + return null; // leave index as is to signal error
134.1006 + }
134.1007 + sourceOffset = tempStatus.index; // update
134.1008 + }
134.1009 + }
134.1010 + int len = pattern.length() - patternOffset;
134.1011 + if (len == 0 || pattern.regionMatches(patternOffset,
134.1012 + source, sourceOffset, len)) {
134.1013 + pos.index = sourceOffset + len;
134.1014 + } else {
134.1015 + pos.errorIndex = sourceOffset;
134.1016 + return null; // leave index as is to signal error
134.1017 + }
134.1018 + return resultArray;
134.1019 + }
134.1020 +
134.1021 + /**
134.1022 + * Parses text from the beginning of the given string to produce an object
134.1023 + * array.
134.1024 + * The method may not use the entire text of the given string.
134.1025 + * <p>
134.1026 + * See the {@link #parse(String, ParsePosition)} method for more information
134.1027 + * on message parsing.
134.1028 + *
134.1029 + * @param source A <code>String</code> whose beginning should be parsed.
134.1030 + * @return An <code>Object</code> array parsed from the string.
134.1031 + * @exception ParseException if the beginning of the specified string
134.1032 + * cannot be parsed.
134.1033 + */
134.1034 + public Object[] parse(String source) throws ParseException {
134.1035 + ParsePosition pos = new ParsePosition(0);
134.1036 + Object[] result = parse(source, pos);
134.1037 + if (pos.index == 0) // unchanged, returned object is null
134.1038 + throw new ParseException("MessageFormat parse error!", pos.errorIndex);
134.1039 +
134.1040 + return result;
134.1041 + }
134.1042 +
134.1043 + /**
134.1044 + * Parses text from a string to produce an object array.
134.1045 + * <p>
134.1046 + * The method attempts to parse text starting at the index given by
134.1047 + * <code>pos</code>.
134.1048 + * If parsing succeeds, then the index of <code>pos</code> is updated
134.1049 + * to the index after the last character used (parsing does not necessarily
134.1050 + * use all characters up to the end of the string), and the parsed
134.1051 + * object array is returned. The updated <code>pos</code> can be used to
134.1052 + * indicate the starting point for the next call to this method.
134.1053 + * If an error occurs, then the index of <code>pos</code> is not
134.1054 + * changed, the error index of <code>pos</code> is set to the index of
134.1055 + * the character where the error occurred, and null is returned.
134.1056 + * <p>
134.1057 + * See the {@link #parse(String, ParsePosition)} method for more information
134.1058 + * on message parsing.
134.1059 + *
134.1060 + * @param source A <code>String</code>, part of which should be parsed.
134.1061 + * @param pos A <code>ParsePosition</code> object with index and error
134.1062 + * index information as described above.
134.1063 + * @return An <code>Object</code> array parsed from the string. In case of
134.1064 + * error, returns null.
134.1065 + * @exception NullPointerException if <code>pos</code> is null.
134.1066 + */
134.1067 + public Object parseObject(String source, ParsePosition pos) {
134.1068 + return parse(source, pos);
134.1069 + }
134.1070 +
134.1071 + /**
134.1072 + * Creates and returns a copy of this object.
134.1073 + *
134.1074 + * @return a clone of this instance.
134.1075 + */
134.1076 + public Object clone() {
134.1077 + MessageFormat other = (MessageFormat) super.clone();
134.1078 +
134.1079 + // clone arrays. Can't do with utility because of bug in Cloneable
134.1080 + other.formats = (Format[]) formats.clone(); // shallow clone
134.1081 + for (int i = 0; i < formats.length; ++i) {
134.1082 + if (formats[i] != null)
134.1083 + other.formats[i] = (Format)formats[i].clone();
134.1084 + }
134.1085 + // for primitives or immutables, shallow clone is enough
134.1086 + other.offsets = (int[]) offsets.clone();
134.1087 + other.argumentNumbers = (int[]) argumentNumbers.clone();
134.1088 +
134.1089 + return other;
134.1090 + }
134.1091 +
134.1092 + /**
134.1093 + * Equality comparison between two message format objects
134.1094 + */
134.1095 + public boolean equals(Object obj) {
134.1096 + if (this == obj) // quick check
134.1097 + return true;
134.1098 + if (obj == null || getClass() != obj.getClass())
134.1099 + return false;
134.1100 + MessageFormat other = (MessageFormat) obj;
134.1101 + return (maxOffset == other.maxOffset
134.1102 + && pattern.equals(other.pattern)
134.1103 + && ((locale != null && locale.equals(other.locale))
134.1104 + || (locale == null && other.locale == null))
134.1105 + && Arrays.equals(offsets,other.offsets)
134.1106 + && Arrays.equals(argumentNumbers,other.argumentNumbers)
134.1107 + && Arrays.equals(formats,other.formats));
134.1108 + }
134.1109 +
134.1110 + /**
134.1111 + * Generates a hash code for the message format object.
134.1112 + */
134.1113 + public int hashCode() {
134.1114 + return pattern.hashCode(); // enough for reasonable distribution
134.1115 + }
134.1116 +
134.1117 +
134.1118 + /**
134.1119 + * Defines constants that are used as attribute keys in the
134.1120 + * <code>AttributedCharacterIterator</code> returned
134.1121 + * from <code>MessageFormat.formatToCharacterIterator</code>.
134.1122 + *
134.1123 + * @since 1.4
134.1124 + */
134.1125 + public static class Field extends Format.Field {
134.1126 +
134.1127 + // Proclaim serial compatibility with 1.4 FCS
134.1128 + private static final long serialVersionUID = 7899943957617360810L;
134.1129 +
134.1130 + /**
134.1131 + * Creates a Field with the specified name.
134.1132 + *
134.1133 + * @param name Name of the attribute
134.1134 + */
134.1135 + protected Field(String name) {
134.1136 + super(name);
134.1137 + }
134.1138 +
134.1139 + /**
134.1140 + * Resolves instances being deserialized to the predefined constants.
134.1141 + *
134.1142 + * @throws InvalidObjectException if the constant could not be
134.1143 + * resolved.
134.1144 + * @return resolved MessageFormat.Field constant
134.1145 + */
134.1146 + protected Object readResolve() throws InvalidObjectException {
134.1147 + if (this.getClass() != MessageFormat.Field.class) {
134.1148 + throw new InvalidObjectException("subclass didn't correctly implement readResolve");
134.1149 + }
134.1150 +
134.1151 + return ARGUMENT;
134.1152 + }
134.1153 +
134.1154 + //
134.1155 + // The constants
134.1156 + //
134.1157 +
134.1158 + /**
134.1159 + * Constant identifying a portion of a message that was generated
134.1160 + * from an argument passed into <code>formatToCharacterIterator</code>.
134.1161 + * The value associated with the key will be an <code>Integer</code>
134.1162 + * indicating the index in the <code>arguments</code> array of the
134.1163 + * argument from which the text was generated.
134.1164 + */
134.1165 + public final static Field ARGUMENT =
134.1166 + new Field("message argument field");
134.1167 + }
134.1168 +
134.1169 + // ===========================privates============================
134.1170 +
134.1171 + /**
134.1172 + * The locale to use for formatting numbers and dates.
134.1173 + * @serial
134.1174 + */
134.1175 + private Locale locale;
134.1176 +
134.1177 + /**
134.1178 + * The string that the formatted values are to be plugged into. In other words, this
134.1179 + * is the pattern supplied on construction with all of the {} expressions taken out.
134.1180 + * @serial
134.1181 + */
134.1182 + private String pattern = "";
134.1183 +
134.1184 + /** The initially expected number of subformats in the format */
134.1185 + private static final int INITIAL_FORMATS = 10;
134.1186 +
134.1187 + /**
134.1188 + * An array of formatters, which are used to format the arguments.
134.1189 + * @serial
134.1190 + */
134.1191 + private Format[] formats = new Format[INITIAL_FORMATS];
134.1192 +
134.1193 + /**
134.1194 + * The positions where the results of formatting each argument are to be inserted
134.1195 + * into the pattern.
134.1196 + * @serial
134.1197 + */
134.1198 + private int[] offsets = new int[INITIAL_FORMATS];
134.1199 +
134.1200 + /**
134.1201 + * The argument numbers corresponding to each formatter. (The formatters are stored
134.1202 + * in the order they occur in the pattern, not in the order in which the arguments
134.1203 + * are specified.)
134.1204 + * @serial
134.1205 + */
134.1206 + private int[] argumentNumbers = new int[INITIAL_FORMATS];
134.1207 +
134.1208 + /**
134.1209 + * One less than the number of entries in <code>offsets</code>. Can also be thought of
134.1210 + * as the index of the highest-numbered element in <code>offsets</code> that is being used.
134.1211 + * All of these arrays should have the same number of elements being used as <code>offsets</code>
134.1212 + * does, and so this variable suffices to tell us how many entries are in all of them.
134.1213 + * @serial
134.1214 + */
134.1215 + private int maxOffset = -1;
134.1216 +
134.1217 + /**
134.1218 + * Internal routine used by format. If <code>characterIterators</code> is
134.1219 + * non-null, AttributedCharacterIterator will be created from the
134.1220 + * subformats as necessary. If <code>characterIterators</code> is null
134.1221 + * and <code>fp</code> is non-null and identifies
134.1222 + * <code>Field.MESSAGE_ARGUMENT</code>, the location of
134.1223 + * the first replaced argument will be set in it.
134.1224 + *
134.1225 + * @exception IllegalArgumentException if an argument in the
134.1226 + * <code>arguments</code> array is not of the type
134.1227 + * expected by the format element(s) that use it.
134.1228 + */
134.1229 + private StringBuffer subformat(Object[] arguments, StringBuffer result,
134.1230 + FieldPosition fp, List characterIterators) {
134.1231 + // note: this implementation assumes a fast substring & index.
134.1232 + // if this is not true, would be better to append chars one by one.
134.1233 + int lastOffset = 0;
134.1234 + int last = result.length();
134.1235 + for (int i = 0; i <= maxOffset; ++i) {
134.1236 + result.append(pattern.substring(lastOffset, offsets[i]));
134.1237 + lastOffset = offsets[i];
134.1238 + int argumentNumber = argumentNumbers[i];
134.1239 + if (arguments == null || argumentNumber >= arguments.length) {
134.1240 + result.append('{').append(argumentNumber).append('}');
134.1241 + continue;
134.1242 + }
134.1243 + // int argRecursion = ((recursionProtection >> (argumentNumber*2)) & 0x3);
134.1244 + if (false) { // if (argRecursion == 3){
134.1245 + // prevent loop!!!
134.1246 + result.append('\uFFFD');
134.1247 + } else {
134.1248 + Object obj = arguments[argumentNumber];
134.1249 + String arg = null;
134.1250 + Format subFormatter = null;
134.1251 + if (obj == null) {
134.1252 + arg = "null";
134.1253 + } else if (formats[i] != null) {
134.1254 + subFormatter = formats[i];
134.1255 + if (subFormatter instanceof ChoiceFormat) {
134.1256 + arg = formats[i].format(obj);
134.1257 + if (arg.indexOf('{') >= 0) {
134.1258 + subFormatter = new MessageFormat(arg, locale);
134.1259 + obj = arguments;
134.1260 + arg = null;
134.1261 + }
134.1262 + }
134.1263 + } else if (obj instanceof Number) {
134.1264 + // format number if can
134.1265 + subFormatter = NumberFormat.getInstance(locale);
134.1266 + } else if (obj instanceof Date) {
134.1267 + // format a Date if can
134.1268 + subFormatter = DateFormat.getDateTimeInstance(
134.1269 + DateFormat.SHORT, DateFormat.SHORT, locale);//fix
134.1270 + } else if (obj instanceof String) {
134.1271 + arg = (String) obj;
134.1272 +
134.1273 + } else {
134.1274 + arg = obj.toString();
134.1275 + if (arg == null) arg = "null";
134.1276 + }
134.1277 +
134.1278 + // At this point we are in two states, either subFormatter
134.1279 + // is non-null indicating we should format obj using it,
134.1280 + // or arg is non-null and we should use it as the value.
134.1281 +
134.1282 + if (characterIterators != null) {
134.1283 + // If characterIterators is non-null, it indicates we need
134.1284 + // to get the CharacterIterator from the child formatter.
134.1285 + if (last != result.length()) {
134.1286 + characterIterators.add(
134.1287 + createAttributedCharacterIterator(result.substring
134.1288 + (last)));
134.1289 + last = result.length();
134.1290 + }
134.1291 + if (subFormatter != null) {
134.1292 + AttributedCharacterIterator subIterator =
134.1293 + subFormatter.formatToCharacterIterator(obj);
134.1294 +
134.1295 + append(result, subIterator);
134.1296 + if (last != result.length()) {
134.1297 + characterIterators.add(
134.1298 + createAttributedCharacterIterator(
134.1299 + subIterator, Field.ARGUMENT,
134.1300 + Integer.valueOf(argumentNumber)));
134.1301 + last = result.length();
134.1302 + }
134.1303 + arg = null;
134.1304 + }
134.1305 + if (arg != null && arg.length() > 0) {
134.1306 + result.append(arg);
134.1307 + characterIterators.add(
134.1308 + createAttributedCharacterIterator(
134.1309 + arg, Field.ARGUMENT,
134.1310 + Integer.valueOf(argumentNumber)));
134.1311 + last = result.length();
134.1312 + }
134.1313 + }
134.1314 + else {
134.1315 + if (subFormatter != null) {
134.1316 + arg = subFormatter.format(obj);
134.1317 + }
134.1318 + last = result.length();
134.1319 + result.append(arg);
134.1320 + if (i == 0 && fp != null && Field.ARGUMENT.equals(
134.1321 + fp.getFieldAttribute())) {
134.1322 + fp.setBeginIndex(last);
134.1323 + fp.setEndIndex(result.length());
134.1324 + }
134.1325 + last = result.length();
134.1326 + }
134.1327 + }
134.1328 + }
134.1329 + result.append(pattern.substring(lastOffset, pattern.length()));
134.1330 + if (characterIterators != null && last != result.length()) {
134.1331 + characterIterators.add(createAttributedCharacterIterator(
134.1332 + result.substring(last)));
134.1333 + }
134.1334 + return result;
134.1335 + }
134.1336 +
134.1337 + /**
134.1338 + * Convenience method to append all the characters in
134.1339 + * <code>iterator</code> to the StringBuffer <code>result</code>.
134.1340 + */
134.1341 + private void append(StringBuffer result, CharacterIterator iterator) {
134.1342 + if (iterator.first() != CharacterIterator.DONE) {
134.1343 + char aChar;
134.1344 +
134.1345 + result.append(iterator.first());
134.1346 + while ((aChar = iterator.next()) != CharacterIterator.DONE) {
134.1347 + result.append(aChar);
134.1348 + }
134.1349 + }
134.1350 + }
134.1351 +
134.1352 + // Indices for segments
134.1353 + private static final int SEG_RAW = 0;
134.1354 + private static final int SEG_INDEX = 1;
134.1355 + private static final int SEG_TYPE = 2;
134.1356 + private static final int SEG_MODIFIER = 3; // modifier or subformat
134.1357 +
134.1358 + // Indices for type keywords
134.1359 + private static final int TYPE_NULL = 0;
134.1360 + private static final int TYPE_NUMBER = 1;
134.1361 + private static final int TYPE_DATE = 2;
134.1362 + private static final int TYPE_TIME = 3;
134.1363 + private static final int TYPE_CHOICE = 4;
134.1364 +
134.1365 + private static final String[] TYPE_KEYWORDS = {
134.1366 + "",
134.1367 + "number",
134.1368 + "date",
134.1369 + "time",
134.1370 + "choice"
134.1371 + };
134.1372 +
134.1373 + // Indices for number modifiers
134.1374 + private static final int MODIFIER_DEFAULT = 0; // common in number and date-time
134.1375 + private static final int MODIFIER_CURRENCY = 1;
134.1376 + private static final int MODIFIER_PERCENT = 2;
134.1377 + private static final int MODIFIER_INTEGER = 3;
134.1378 +
134.1379 + private static final String[] NUMBER_MODIFIER_KEYWORDS = {
134.1380 + "",
134.1381 + "currency",
134.1382 + "percent",
134.1383 + "integer"
134.1384 + };
134.1385 +
134.1386 + // Indices for date-time modifiers
134.1387 + private static final int MODIFIER_SHORT = 1;
134.1388 + private static final int MODIFIER_MEDIUM = 2;
134.1389 + private static final int MODIFIER_LONG = 3;
134.1390 + private static final int MODIFIER_FULL = 4;
134.1391 +
134.1392 + private static final String[] DATE_TIME_MODIFIER_KEYWORDS = {
134.1393 + "",
134.1394 + "short",
134.1395 + "medium",
134.1396 + "long",
134.1397 + "full"
134.1398 + };
134.1399 +
134.1400 + // Date-time style values corresponding to the date-time modifiers.
134.1401 + private static final int[] DATE_TIME_MODIFIERS = {
134.1402 + DateFormat.DEFAULT,
134.1403 + DateFormat.SHORT,
134.1404 + DateFormat.MEDIUM,
134.1405 + DateFormat.LONG,
134.1406 + DateFormat.FULL,
134.1407 + };
134.1408 +
134.1409 + private void makeFormat(int position, int offsetNumber,
134.1410 + StringBuilder[] textSegments)
134.1411 + {
134.1412 + String[] segments = new String[textSegments.length];
134.1413 + for (int i = 0; i < textSegments.length; i++) {
134.1414 + StringBuilder oneseg = textSegments[i];
134.1415 + segments[i] = (oneseg != null) ? oneseg.toString() : "";
134.1416 + }
134.1417 +
134.1418 + // get the argument number
134.1419 + int argumentNumber;
134.1420 + try {
134.1421 + argumentNumber = Integer.parseInt(segments[SEG_INDEX]); // always unlocalized!
134.1422 + } catch (NumberFormatException e) {
134.1423 + throw new IllegalArgumentException("can't parse argument number: "
134.1424 + + segments[SEG_INDEX], e);
134.1425 + }
134.1426 + if (argumentNumber < 0) {
134.1427 + throw new IllegalArgumentException("negative argument number: "
134.1428 + + argumentNumber);
134.1429 + }
134.1430 +
134.1431 + // resize format information arrays if necessary
134.1432 + if (offsetNumber >= formats.length) {
134.1433 + int newLength = formats.length * 2;
134.1434 + Format[] newFormats = new Format[newLength];
134.1435 + int[] newOffsets = new int[newLength];
134.1436 + int[] newArgumentNumbers = new int[newLength];
134.1437 + System.arraycopy(formats, 0, newFormats, 0, maxOffset + 1);
134.1438 + System.arraycopy(offsets, 0, newOffsets, 0, maxOffset + 1);
134.1439 + System.arraycopy(argumentNumbers, 0, newArgumentNumbers, 0, maxOffset + 1);
134.1440 + formats = newFormats;
134.1441 + offsets = newOffsets;
134.1442 + argumentNumbers = newArgumentNumbers;
134.1443 + }
134.1444 + int oldMaxOffset = maxOffset;
134.1445 + maxOffset = offsetNumber;
134.1446 + offsets[offsetNumber] = segments[SEG_RAW].length();
134.1447 + argumentNumbers[offsetNumber] = argumentNumber;
134.1448 +
134.1449 + // now get the format
134.1450 + Format newFormat = null;
134.1451 + if (segments[SEG_TYPE].length() != 0) {
134.1452 + int type = findKeyword(segments[SEG_TYPE], TYPE_KEYWORDS);
134.1453 + switch (type) {
134.1454 + case TYPE_NULL:
134.1455 + // Type "" is allowed. e.g., "{0,}", "{0,,}", and "{0,,#}"
134.1456 + // are treated as "{0}".
134.1457 + break;
134.1458 +
134.1459 + case TYPE_NUMBER:
134.1460 + switch (findKeyword(segments[SEG_MODIFIER], NUMBER_MODIFIER_KEYWORDS)) {
134.1461 + case MODIFIER_DEFAULT:
134.1462 + newFormat = NumberFormat.getInstance(locale);
134.1463 + break;
134.1464 + case MODIFIER_CURRENCY:
134.1465 + newFormat = NumberFormat.getCurrencyInstance(locale);
134.1466 + break;
134.1467 + case MODIFIER_PERCENT:
134.1468 + newFormat = NumberFormat.getPercentInstance(locale);
134.1469 + break;
134.1470 + case MODIFIER_INTEGER:
134.1471 + newFormat = NumberFormat.getIntegerInstance(locale);
134.1472 + break;
134.1473 + default: // DecimalFormat pattern
134.1474 + try {
134.1475 + newFormat = new DecimalFormat(segments[SEG_MODIFIER],
134.1476 + DecimalFormatSymbols.getInstance(locale));
134.1477 + } catch (IllegalArgumentException e) {
134.1478 + maxOffset = oldMaxOffset;
134.1479 + throw e;
134.1480 + }
134.1481 + break;
134.1482 + }
134.1483 + break;
134.1484 +
134.1485 + case TYPE_DATE:
134.1486 + case TYPE_TIME:
134.1487 + int mod = findKeyword(segments[SEG_MODIFIER], DATE_TIME_MODIFIER_KEYWORDS);
134.1488 + if (mod >= 0 && mod < DATE_TIME_MODIFIER_KEYWORDS.length) {
134.1489 + if (type == TYPE_DATE) {
134.1490 + newFormat = DateFormat.getDateInstance(DATE_TIME_MODIFIERS[mod],
134.1491 + locale);
134.1492 + } else {
134.1493 + newFormat = DateFormat.getTimeInstance(DATE_TIME_MODIFIERS[mod],
134.1494 + locale);
134.1495 + }
134.1496 + } else {
134.1497 + // SimpleDateFormat pattern
134.1498 + try {
134.1499 + newFormat = new SimpleDateFormat(segments[SEG_MODIFIER], locale);
134.1500 + } catch (IllegalArgumentException e) {
134.1501 + maxOffset = oldMaxOffset;
134.1502 + throw e;
134.1503 + }
134.1504 + }
134.1505 + break;
134.1506 +
134.1507 + case TYPE_CHOICE:
134.1508 + try {
134.1509 + // ChoiceFormat pattern
134.1510 + newFormat = new ChoiceFormat(segments[SEG_MODIFIER]);
134.1511 + } catch (Exception e) {
134.1512 + maxOffset = oldMaxOffset;
134.1513 + throw new IllegalArgumentException("Choice Pattern incorrect: "
134.1514 + + segments[SEG_MODIFIER], e);
134.1515 + }
134.1516 + break;
134.1517 +
134.1518 + default:
134.1519 + maxOffset = oldMaxOffset;
134.1520 + throw new IllegalArgumentException("unknown format type: " +
134.1521 + segments[SEG_TYPE]);
134.1522 + }
134.1523 + }
134.1524 + formats[offsetNumber] = newFormat;
134.1525 + }
134.1526 +
134.1527 + private static final int findKeyword(String s, String[] list) {
134.1528 + for (int i = 0; i < list.length; ++i) {
134.1529 + if (s.equals(list[i]))
134.1530 + return i;
134.1531 + }
134.1532 +
134.1533 + // Try trimmed lowercase.
134.1534 + String ls = s.trim().toLowerCase(Locale.ROOT);
134.1535 + if (ls != s) {
134.1536 + for (int i = 0; i < list.length; ++i) {
134.1537 + if (ls.equals(list[i]))
134.1538 + return i;
134.1539 + }
134.1540 + }
134.1541 + return -1;
134.1542 + }
134.1543 +
134.1544 + private static final void copyAndFixQuotes(String source, int start, int end,
134.1545 + StringBuilder target) {
134.1546 + boolean quoted = false;
134.1547 +
134.1548 + for (int i = start; i < end; ++i) {
134.1549 + char ch = source.charAt(i);
134.1550 + if (ch == '{') {
134.1551 + if (!quoted) {
134.1552 + target.append('\'');
134.1553 + quoted = true;
134.1554 + }
134.1555 + target.append(ch);
134.1556 + } else if (ch == '\'') {
134.1557 + target.append("''");
134.1558 + } else {
134.1559 + if (quoted) {
134.1560 + target.append('\'');
134.1561 + quoted = false;
134.1562 + }
134.1563 + target.append(ch);
134.1564 + }
134.1565 + }
134.1566 + if (quoted) {
134.1567 + target.append('\'');
134.1568 + }
134.1569 + }
134.1570 +
134.1571 + /**
134.1572 + * After reading an object from the input stream, do a simple verification
134.1573 + * to maintain class invariants.
134.1574 + * @throws InvalidObjectException if the objects read from the stream is invalid.
134.1575 + */
134.1576 + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
134.1577 + in.defaultReadObject();
134.1578 + boolean isValid = maxOffset >= -1
134.1579 + && formats.length > maxOffset
134.1580 + && offsets.length > maxOffset
134.1581 + && argumentNumbers.length > maxOffset;
134.1582 + if (isValid) {
134.1583 + int lastOffset = pattern.length() + 1;
134.1584 + for (int i = maxOffset; i >= 0; --i) {
134.1585 + if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
134.1586 + isValid = false;
134.1587 + break;
134.1588 + } else {
134.1589 + lastOffset = offsets[i];
134.1590 + }
134.1591 + }
134.1592 + }
134.1593 + if (!isValid) {
134.1594 + throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream.");
134.1595 + }
134.1596 + }
134.1597 +}
135.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
135.2 +++ b/rt/emul/compact/src/main/java/java/text/NumberFormat.java Tue Feb 11 13:31:42 2014 +0100
135.3 @@ -0,0 +1,1159 @@
135.4 +/*
135.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
135.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
135.7 + *
135.8 + * This code is free software; you can redistribute it and/or modify it
135.9 + * under the terms of the GNU General Public License version 2 only, as
135.10 + * published by the Free Software Foundation. Oracle designates this
135.11 + * particular file as subject to the "Classpath" exception as provided
135.12 + * by Oracle in the LICENSE file that accompanied this code.
135.13 + *
135.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
135.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
135.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
135.17 + * version 2 for more details (a copy is included in the LICENSE file that
135.18 + * accompanied this code).
135.19 + *
135.20 + * You should have received a copy of the GNU General Public License version
135.21 + * 2 along with this work; if not, write to the Free Software Foundation,
135.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
135.23 + *
135.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
135.25 + * or visit www.oracle.com if you need additional information or have any
135.26 + * questions.
135.27 + */
135.28 +
135.29 +/*
135.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
135.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
135.32 + *
135.33 + * The original version of this source code and documentation is copyrighted
135.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
135.35 + * materials are provided under terms of a License Agreement between Taligent
135.36 + * and Sun. This technology is protected by multiple US and International
135.37 + * patents. This notice and attribution to Taligent may not be removed.
135.38 + * Taligent is a registered trademark of Taligent, Inc.
135.39 + *
135.40 + */
135.41 +
135.42 +package java.text;
135.43 +
135.44 +import java.io.InvalidObjectException;
135.45 +import java.io.IOException;
135.46 +import java.io.ObjectInputStream;
135.47 +import java.io.ObjectOutputStream;
135.48 +import java.math.BigInteger;
135.49 +import java.math.RoundingMode;
135.50 +import java.util.Currency;
135.51 +import java.util.HashMap;
135.52 +import java.util.Hashtable;
135.53 +import java.util.Locale;
135.54 +import java.util.Map;
135.55 +import java.util.ResourceBundle;
135.56 +import java.util.concurrent.atomic.AtomicInteger;
135.57 +import java.util.concurrent.atomic.AtomicLong;
135.58 +
135.59 +/**
135.60 + * <code>NumberFormat</code> is the abstract base class for all number
135.61 + * formats. This class provides the interface for formatting and parsing
135.62 + * numbers. <code>NumberFormat</code> also provides methods for determining
135.63 + * which locales have number formats, and what their names are.
135.64 + *
135.65 + * <p>
135.66 + * <code>NumberFormat</code> helps you to format and parse numbers for any locale.
135.67 + * Your code can be completely independent of the locale conventions for
135.68 + * decimal points, thousands-separators, or even the particular decimal
135.69 + * digits used, or whether the number format is even decimal.
135.70 + *
135.71 + * <p>
135.72 + * To format a number for the current Locale, use one of the factory
135.73 + * class methods:
135.74 + * <blockquote>
135.75 + * <pre>
135.76 + * myString = NumberFormat.getInstance().format(myNumber);
135.77 + * </pre>
135.78 + * </blockquote>
135.79 + * If you are formatting multiple numbers, it is
135.80 + * more efficient to get the format and use it multiple times so that
135.81 + * the system doesn't have to fetch the information about the local
135.82 + * language and country conventions multiple times.
135.83 + * <blockquote>
135.84 + * <pre>
135.85 + * NumberFormat nf = NumberFormat.getInstance();
135.86 + * for (int i = 0; i < myNumber.length; ++i) {
135.87 + * output.println(nf.format(myNumber[i]) + "; ");
135.88 + * }
135.89 + * </pre>
135.90 + * </blockquote>
135.91 + * To format a number for a different Locale, specify it in the
135.92 + * call to <code>getInstance</code>.
135.93 + * <blockquote>
135.94 + * <pre>
135.95 + * NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
135.96 + * </pre>
135.97 + * </blockquote>
135.98 + * You can also use a <code>NumberFormat</code> to parse numbers:
135.99 + * <blockquote>
135.100 + * <pre>
135.101 + * myNumber = nf.parse(myString);
135.102 + * </pre>
135.103 + * </blockquote>
135.104 + * Use <code>getInstance</code> or <code>getNumberInstance</code> to get the
135.105 + * normal number format. Use <code>getIntegerInstance</code> to get an
135.106 + * integer number format. Use <code>getCurrencyInstance</code> to get the
135.107 + * currency number format. And use <code>getPercentInstance</code> to get a
135.108 + * format for displaying percentages. With this format, a fraction like
135.109 + * 0.53 is displayed as 53%.
135.110 + *
135.111 + * <p>
135.112 + * You can also control the display of numbers with such methods as
135.113 + * <code>setMinimumFractionDigits</code>.
135.114 + * If you want even more control over the format or parsing,
135.115 + * or want to give your users more control,
135.116 + * you can try casting the <code>NumberFormat</code> you get from the factory methods
135.117 + * to a <code>DecimalFormat</code>. This will work for the vast majority
135.118 + * of locales; just remember to put it in a <code>try</code> block in case you
135.119 + * encounter an unusual one.
135.120 + *
135.121 + * <p>
135.122 + * NumberFormat and DecimalFormat are designed such that some controls
135.123 + * work for formatting and others work for parsing. The following is
135.124 + * the detailed description for each these control methods,
135.125 + * <p>
135.126 + * setParseIntegerOnly : only affects parsing, e.g.
135.127 + * if true, "3456.78" -> 3456 (and leaves the parse position just after index 6)
135.128 + * if false, "3456.78" -> 3456.78 (and leaves the parse position just after index 8)
135.129 + * This is independent of formatting. If you want to not show a decimal point
135.130 + * where there might be no digits after the decimal point, use
135.131 + * setDecimalSeparatorAlwaysShown.
135.132 + * <p>
135.133 + * setDecimalSeparatorAlwaysShown : only affects formatting, and only where
135.134 + * there might be no digits after the decimal point, such as with a pattern
135.135 + * like "#,##0.##", e.g.,
135.136 + * if true, 3456.00 -> "3,456."
135.137 + * if false, 3456.00 -> "3456"
135.138 + * This is independent of parsing. If you want parsing to stop at the decimal
135.139 + * point, use setParseIntegerOnly.
135.140 + *
135.141 + * <p>
135.142 + * You can also use forms of the <code>parse</code> and <code>format</code>
135.143 + * methods with <code>ParsePosition</code> and <code>FieldPosition</code> to
135.144 + * allow you to:
135.145 + * <ul>
135.146 + * <li> progressively parse through pieces of a string
135.147 + * <li> align the decimal point and other areas
135.148 + * </ul>
135.149 + * For example, you can align numbers in two ways:
135.150 + * <ol>
135.151 + * <li> If you are using a monospaced font with spacing for alignment,
135.152 + * you can pass the <code>FieldPosition</code> in your format call, with
135.153 + * <code>field</code> = <code>INTEGER_FIELD</code>. On output,
135.154 + * <code>getEndIndex</code> will be set to the offset between the
135.155 + * last character of the integer and the decimal. Add
135.156 + * (desiredSpaceCount - getEndIndex) spaces at the front of the string.
135.157 + *
135.158 + * <li> If you are using proportional fonts,
135.159 + * instead of padding with spaces, measure the width
135.160 + * of the string in pixels from the start to <code>getEndIndex</code>.
135.161 + * Then move the pen by
135.162 + * (desiredPixelWidth - widthToAlignmentPoint) before drawing the text.
135.163 + * It also works where there is no decimal, but possibly additional
135.164 + * characters at the end, e.g., with parentheses in negative
135.165 + * numbers: "(12)" for -12.
135.166 + * </ol>
135.167 + *
135.168 + * <h4><a name="synchronization">Synchronization</a></h4>
135.169 + *
135.170 + * <p>
135.171 + * Number formats are generally not synchronized.
135.172 + * It is recommended to create separate format instances for each thread.
135.173 + * If multiple threads access a format concurrently, it must be synchronized
135.174 + * externally.
135.175 + *
135.176 + * @see DecimalFormat
135.177 + * @see ChoiceFormat
135.178 + * @author Mark Davis
135.179 + * @author Helena Shih
135.180 + */
135.181 +public abstract class NumberFormat extends Format {
135.182 +
135.183 + /**
135.184 + * Field constant used to construct a FieldPosition object. Signifies that
135.185 + * the position of the integer part of a formatted number should be returned.
135.186 + * @see java.text.FieldPosition
135.187 + */
135.188 + public static final int INTEGER_FIELD = 0;
135.189 +
135.190 + /**
135.191 + * Field constant used to construct a FieldPosition object. Signifies that
135.192 + * the position of the fraction part of a formatted number should be returned.
135.193 + * @see java.text.FieldPosition
135.194 + */
135.195 + public static final int FRACTION_FIELD = 1;
135.196 +
135.197 + /**
135.198 + * Sole constructor. (For invocation by subclass constructors, typically
135.199 + * implicit.)
135.200 + */
135.201 + protected NumberFormat() {
135.202 + }
135.203 +
135.204 + /**
135.205 + * Formats a number and appends the resulting text to the given string
135.206 + * buffer.
135.207 + * The number can be of any subclass of {@link java.lang.Number}.
135.208 + * <p>
135.209 + * This implementation extracts the number's value using
135.210 + * {@link java.lang.Number#longValue()} for all integral type values that
135.211 + * can be converted to <code>long</code> without loss of information,
135.212 + * including <code>BigInteger</code> values with a
135.213 + * {@link java.math.BigInteger#bitLength() bit length} of less than 64,
135.214 + * and {@link java.lang.Number#doubleValue()} for all other types. It
135.215 + * then calls
135.216 + * {@link #format(long,java.lang.StringBuffer,java.text.FieldPosition)}
135.217 + * or {@link #format(double,java.lang.StringBuffer,java.text.FieldPosition)}.
135.218 + * This may result in loss of magnitude information and precision for
135.219 + * <code>BigInteger</code> and <code>BigDecimal</code> values.
135.220 + * @param number the number to format
135.221 + * @param toAppendTo the <code>StringBuffer</code> to which the formatted
135.222 + * text is to be appended
135.223 + * @param pos On input: an alignment field, if desired.
135.224 + * On output: the offsets of the alignment field.
135.225 + * @return the value passed in as <code>toAppendTo</code>
135.226 + * @exception IllegalArgumentException if <code>number</code> is
135.227 + * null or not an instance of <code>Number</code>.
135.228 + * @exception NullPointerException if <code>toAppendTo</code> or
135.229 + * <code>pos</code> is null
135.230 + * @exception ArithmeticException if rounding is needed with rounding
135.231 + * mode being set to RoundingMode.UNNECESSARY
135.232 + * @see java.text.FieldPosition
135.233 + */
135.234 + public StringBuffer format(Object number,
135.235 + StringBuffer toAppendTo,
135.236 + FieldPosition pos) {
135.237 + if (number instanceof Long || number instanceof Integer ||
135.238 + number instanceof Short || number instanceof Byte ||
135.239 + number instanceof AtomicInteger || number instanceof AtomicLong ||
135.240 + (number instanceof BigInteger &&
135.241 + ((BigInteger)number).bitLength() < 64)) {
135.242 + return format(((Number)number).longValue(), toAppendTo, pos);
135.243 + } else if (number instanceof Number) {
135.244 + return format(((Number)number).doubleValue(), toAppendTo, pos);
135.245 + } else {
135.246 + throw new IllegalArgumentException("Cannot format given Object as a Number");
135.247 + }
135.248 + }
135.249 +
135.250 + /**
135.251 + * Parses text from a string to produce a <code>Number</code>.
135.252 + * <p>
135.253 + * The method attempts to parse text starting at the index given by
135.254 + * <code>pos</code>.
135.255 + * If parsing succeeds, then the index of <code>pos</code> is updated
135.256 + * to the index after the last character used (parsing does not necessarily
135.257 + * use all characters up to the end of the string), and the parsed
135.258 + * number is returned. The updated <code>pos</code> can be used to
135.259 + * indicate the starting point for the next call to this method.
135.260 + * If an error occurs, then the index of <code>pos</code> is not
135.261 + * changed, the error index of <code>pos</code> is set to the index of
135.262 + * the character where the error occurred, and null is returned.
135.263 + * <p>
135.264 + * See the {@link #parse(String, ParsePosition)} method for more information
135.265 + * on number parsing.
135.266 + *
135.267 + * @param source A <code>String</code>, part of which should be parsed.
135.268 + * @param pos A <code>ParsePosition</code> object with index and error
135.269 + * index information as described above.
135.270 + * @return A <code>Number</code> parsed from the string. In case of
135.271 + * error, returns null.
135.272 + * @exception NullPointerException if <code>pos</code> is null.
135.273 + */
135.274 + public final Object parseObject(String source, ParsePosition pos) {
135.275 + return parse(source, pos);
135.276 + }
135.277 +
135.278 + /**
135.279 + * Specialization of format.
135.280 + * @exception ArithmeticException if rounding is needed with rounding
135.281 + * mode being set to RoundingMode.UNNECESSARY
135.282 + * @see java.text.Format#format
135.283 + */
135.284 + public final String format(double number) {
135.285 + return format(number, new StringBuffer(),
135.286 + DontCareFieldPosition.INSTANCE).toString();
135.287 + }
135.288 +
135.289 + /**
135.290 + * Specialization of format.
135.291 + * @exception ArithmeticException if rounding is needed with rounding
135.292 + * mode being set to RoundingMode.UNNECESSARY
135.293 + * @see java.text.Format#format
135.294 + */
135.295 + public final String format(long number) {
135.296 + return format(number, new StringBuffer(),
135.297 + DontCareFieldPosition.INSTANCE).toString();
135.298 + }
135.299 +
135.300 + /**
135.301 + * Specialization of format.
135.302 + * @exception ArithmeticException if rounding is needed with rounding
135.303 + * mode being set to RoundingMode.UNNECESSARY
135.304 + * @see java.text.Format#format
135.305 + */
135.306 + public abstract StringBuffer format(double number,
135.307 + StringBuffer toAppendTo,
135.308 + FieldPosition pos);
135.309 +
135.310 + /**
135.311 + * Specialization of format.
135.312 + * @exception ArithmeticException if rounding is needed with rounding
135.313 + * mode being set to RoundingMode.UNNECESSARY
135.314 + * @see java.text.Format#format
135.315 + */
135.316 + public abstract StringBuffer format(long number,
135.317 + StringBuffer toAppendTo,
135.318 + FieldPosition pos);
135.319 +
135.320 + /**
135.321 + * Returns a Long if possible (e.g., within the range [Long.MIN_VALUE,
135.322 + * Long.MAX_VALUE] and with no decimals), otherwise a Double.
135.323 + * If IntegerOnly is set, will stop at a decimal
135.324 + * point (or equivalent; e.g., for rational numbers "1 2/3", will stop
135.325 + * after the 1).
135.326 + * Does not throw an exception; if no object can be parsed, index is
135.327 + * unchanged!
135.328 + * @see java.text.NumberFormat#isParseIntegerOnly
135.329 + * @see java.text.Format#parseObject
135.330 + */
135.331 + public abstract Number parse(String source, ParsePosition parsePosition);
135.332 +
135.333 + /**
135.334 + * Parses text from the beginning of the given string to produce a number.
135.335 + * The method may not use the entire text of the given string.
135.336 + * <p>
135.337 + * See the {@link #parse(String, ParsePosition)} method for more information
135.338 + * on number parsing.
135.339 + *
135.340 + * @param source A <code>String</code> whose beginning should be parsed.
135.341 + * @return A <code>Number</code> parsed from the string.
135.342 + * @exception ParseException if the beginning of the specified string
135.343 + * cannot be parsed.
135.344 + */
135.345 + public Number parse(String source) throws ParseException {
135.346 + ParsePosition parsePosition = new ParsePosition(0);
135.347 + Number result = parse(source, parsePosition);
135.348 + if (parsePosition.index == 0) {
135.349 + throw new ParseException("Unparseable number: \"" + source + "\"",
135.350 + parsePosition.errorIndex);
135.351 + }
135.352 + return result;
135.353 + }
135.354 +
135.355 + /**
135.356 + * Returns true if this format will parse numbers as integers only.
135.357 + * For example in the English locale, with ParseIntegerOnly true, the
135.358 + * string "1234." would be parsed as the integer value 1234 and parsing
135.359 + * would stop at the "." character. Of course, the exact format accepted
135.360 + * by the parse operation is locale dependant and determined by sub-classes
135.361 + * of NumberFormat.
135.362 + */
135.363 + public boolean isParseIntegerOnly() {
135.364 + return parseIntegerOnly;
135.365 + }
135.366 +
135.367 + /**
135.368 + * Sets whether or not numbers should be parsed as integers only.
135.369 + * @see #isParseIntegerOnly
135.370 + */
135.371 + public void setParseIntegerOnly(boolean value) {
135.372 + parseIntegerOnly = value;
135.373 + }
135.374 +
135.375 + //============== Locale Stuff =====================
135.376 +
135.377 + /**
135.378 + * Returns a general-purpose number format for the current default locale.
135.379 + * This is the same as calling
135.380 + * {@link #getNumberInstance() getNumberInstance()}.
135.381 + */
135.382 + public final static NumberFormat getInstance() {
135.383 + return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
135.384 + }
135.385 +
135.386 + /**
135.387 + * Returns a general-purpose number format for the specified locale.
135.388 + * This is the same as calling
135.389 + * {@link #getNumberInstance(java.util.Locale) getNumberInstance(inLocale)}.
135.390 + */
135.391 + public static NumberFormat getInstance(Locale inLocale) {
135.392 + return getInstance(inLocale, NUMBERSTYLE);
135.393 + }
135.394 +
135.395 + /** * Returns a general-purpose number format for the current default locale.
135.396 + */
135.397 + public final static NumberFormat getNumberInstance() {
135.398 + return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
135.399 + }
135.400 +
135.401 + /**
135.402 + * Returns a general-purpose number format for the specified locale.
135.403 + */
135.404 + public static NumberFormat getNumberInstance(Locale inLocale) {
135.405 + return getInstance(inLocale, NUMBERSTYLE);
135.406 + }
135.407 +
135.408 + /**
135.409 + * Returns an integer number format for the current default locale. The
135.410 + * returned number format is configured to round floating point numbers
135.411 + * to the nearest integer using half-even rounding (see {@link
135.412 + * java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}) for formatting,
135.413 + * and to parse only the integer part of an input string (see {@link
135.414 + * #isParseIntegerOnly isParseIntegerOnly}).
135.415 + *
135.416 + * @see #getRoundingMode()
135.417 + * @return a number format for integer values
135.418 + * @since 1.4
135.419 + */
135.420 + public final static NumberFormat getIntegerInstance() {
135.421 + return getInstance(Locale.getDefault(Locale.Category.FORMAT), INTEGERSTYLE);
135.422 + }
135.423 +
135.424 + /**
135.425 + * Returns an integer number format for the specified locale. The
135.426 + * returned number format is configured to round floating point numbers
135.427 + * to the nearest integer using half-even rounding (see {@link
135.428 + * java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}) for formatting,
135.429 + * and to parse only the integer part of an input string (see {@link
135.430 + * #isParseIntegerOnly isParseIntegerOnly}).
135.431 + *
135.432 + * @see #getRoundingMode()
135.433 + * @return a number format for integer values
135.434 + * @since 1.4
135.435 + */
135.436 + public static NumberFormat getIntegerInstance(Locale inLocale) {
135.437 + return getInstance(inLocale, INTEGERSTYLE);
135.438 + }
135.439 +
135.440 + /**
135.441 + * Returns a currency format for the current default locale.
135.442 + */
135.443 + public final static NumberFormat getCurrencyInstance() {
135.444 + return getInstance(Locale.getDefault(Locale.Category.FORMAT), CURRENCYSTYLE);
135.445 + }
135.446 +
135.447 + /**
135.448 + * Returns a currency format for the specified locale.
135.449 + */
135.450 + public static NumberFormat getCurrencyInstance(Locale inLocale) {
135.451 + return getInstance(inLocale, CURRENCYSTYLE);
135.452 + }
135.453 +
135.454 + /**
135.455 + * Returns a percentage format for the current default locale.
135.456 + */
135.457 + public final static NumberFormat getPercentInstance() {
135.458 + return getInstance(Locale.getDefault(Locale.Category.FORMAT), PERCENTSTYLE);
135.459 + }
135.460 +
135.461 + /**
135.462 + * Returns a percentage format for the specified locale.
135.463 + */
135.464 + public static NumberFormat getPercentInstance(Locale inLocale) {
135.465 + return getInstance(inLocale, PERCENTSTYLE);
135.466 + }
135.467 +
135.468 + /**
135.469 + * Returns a scientific format for the current default locale.
135.470 + */
135.471 + /*public*/ final static NumberFormat getScientificInstance() {
135.472 + return getInstance(Locale.getDefault(Locale.Category.FORMAT), SCIENTIFICSTYLE);
135.473 + }
135.474 +
135.475 + /**
135.476 + * Returns a scientific format for the specified locale.
135.477 + */
135.478 + /*public*/ static NumberFormat getScientificInstance(Locale inLocale) {
135.479 + return getInstance(inLocale, SCIENTIFICSTYLE);
135.480 + }
135.481 +
135.482 + /**
135.483 + * Returns an array of all locales for which the
135.484 + * <code>get*Instance</code> methods of this class can return
135.485 + * localized instances.
135.486 + * The returned array represents the union of locales supported by the Java
135.487 + * runtime and by installed
135.488 + * {@link java.text.spi.NumberFormatProvider NumberFormatProvider} implementations.
135.489 + * It must contain at least a <code>Locale</code> instance equal to
135.490 + * {@link java.util.Locale#US Locale.US}.
135.491 + *
135.492 + * @return An array of locales for which localized
135.493 + * <code>NumberFormat</code> instances are available.
135.494 + */
135.495 + public static Locale[] getAvailableLocales() {
135.496 + return new Locale[] { Locale.US };
135.497 +// LocaleServiceProviderPool pool =
135.498 +// LocaleServiceProviderPool.getPool(NumberFormatProvider.class);
135.499 +// return pool.getAvailableLocales();
135.500 + }
135.501 +
135.502 + /**
135.503 + * Overrides hashCode
135.504 + */
135.505 + public int hashCode() {
135.506 + return maximumIntegerDigits * 37 + maxFractionDigits;
135.507 + // just enough fields for a reasonable distribution
135.508 + }
135.509 +
135.510 + /**
135.511 + * Overrides equals
135.512 + */
135.513 + public boolean equals(Object obj) {
135.514 + if (obj == null) {
135.515 + return false;
135.516 + }
135.517 + if (this == obj) {
135.518 + return true;
135.519 + }
135.520 + if (getClass() != obj.getClass()) {
135.521 + return false;
135.522 + }
135.523 + NumberFormat other = (NumberFormat) obj;
135.524 + return (maximumIntegerDigits == other.maximumIntegerDigits
135.525 + && minimumIntegerDigits == other.minimumIntegerDigits
135.526 + && maximumFractionDigits == other.maximumFractionDigits
135.527 + && minimumFractionDigits == other.minimumFractionDigits
135.528 + && groupingUsed == other.groupingUsed
135.529 + && parseIntegerOnly == other.parseIntegerOnly);
135.530 + }
135.531 +
135.532 + /**
135.533 + * Overrides Cloneable
135.534 + */
135.535 + public Object clone() {
135.536 + NumberFormat other = (NumberFormat) super.clone();
135.537 + return other;
135.538 + }
135.539 +
135.540 + /**
135.541 + * Returns true if grouping is used in this format. For example, in the
135.542 + * English locale, with grouping on, the number 1234567 might be formatted
135.543 + * as "1,234,567". The grouping separator as well as the size of each group
135.544 + * is locale dependant and is determined by sub-classes of NumberFormat.
135.545 + * @see #setGroupingUsed
135.546 + */
135.547 + public boolean isGroupingUsed() {
135.548 + return groupingUsed;
135.549 + }
135.550 +
135.551 + /**
135.552 + * Set whether or not grouping will be used in this format.
135.553 + * @see #isGroupingUsed
135.554 + */
135.555 + public void setGroupingUsed(boolean newValue) {
135.556 + groupingUsed = newValue;
135.557 + }
135.558 +
135.559 + /**
135.560 + * Returns the maximum number of digits allowed in the integer portion of a
135.561 + * number.
135.562 + * @see #setMaximumIntegerDigits
135.563 + */
135.564 + public int getMaximumIntegerDigits() {
135.565 + return maximumIntegerDigits;
135.566 + }
135.567 +
135.568 + /**
135.569 + * Sets the maximum number of digits allowed in the integer portion of a
135.570 + * number. maximumIntegerDigits must be >= minimumIntegerDigits. If the
135.571 + * new value for maximumIntegerDigits is less than the current value
135.572 + * of minimumIntegerDigits, then minimumIntegerDigits will also be set to
135.573 + * the new value.
135.574 + * @param newValue the maximum number of integer digits to be shown; if
135.575 + * less than zero, then zero is used. The concrete subclass may enforce an
135.576 + * upper limit to this value appropriate to the numeric type being formatted.
135.577 + * @see #getMaximumIntegerDigits
135.578 + */
135.579 + public void setMaximumIntegerDigits(int newValue) {
135.580 + maximumIntegerDigits = Math.max(0,newValue);
135.581 + if (minimumIntegerDigits > maximumIntegerDigits) {
135.582 + minimumIntegerDigits = maximumIntegerDigits;
135.583 + }
135.584 + }
135.585 +
135.586 + /**
135.587 + * Returns the minimum number of digits allowed in the integer portion of a
135.588 + * number.
135.589 + * @see #setMinimumIntegerDigits
135.590 + */
135.591 + public int getMinimumIntegerDigits() {
135.592 + return minimumIntegerDigits;
135.593 + }
135.594 +
135.595 + /**
135.596 + * Sets the minimum number of digits allowed in the integer portion of a
135.597 + * number. minimumIntegerDigits must be <= maximumIntegerDigits. If the
135.598 + * new value for minimumIntegerDigits exceeds the current value
135.599 + * of maximumIntegerDigits, then maximumIntegerDigits will also be set to
135.600 + * the new value
135.601 + * @param newValue the minimum number of integer digits to be shown; if
135.602 + * less than zero, then zero is used. The concrete subclass may enforce an
135.603 + * upper limit to this value appropriate to the numeric type being formatted.
135.604 + * @see #getMinimumIntegerDigits
135.605 + */
135.606 + public void setMinimumIntegerDigits(int newValue) {
135.607 + minimumIntegerDigits = Math.max(0,newValue);
135.608 + if (minimumIntegerDigits > maximumIntegerDigits) {
135.609 + maximumIntegerDigits = minimumIntegerDigits;
135.610 + }
135.611 + }
135.612 +
135.613 + /**
135.614 + * Returns the maximum number of digits allowed in the fraction portion of a
135.615 + * number.
135.616 + * @see #setMaximumFractionDigits
135.617 + */
135.618 + public int getMaximumFractionDigits() {
135.619 + return maximumFractionDigits;
135.620 + }
135.621 +
135.622 + /**
135.623 + * Sets the maximum number of digits allowed in the fraction portion of a
135.624 + * number. maximumFractionDigits must be >= minimumFractionDigits. If the
135.625 + * new value for maximumFractionDigits is less than the current value
135.626 + * of minimumFractionDigits, then minimumFractionDigits will also be set to
135.627 + * the new value.
135.628 + * @param newValue the maximum number of fraction digits to be shown; if
135.629 + * less than zero, then zero is used. The concrete subclass may enforce an
135.630 + * upper limit to this value appropriate to the numeric type being formatted.
135.631 + * @see #getMaximumFractionDigits
135.632 + */
135.633 + public void setMaximumFractionDigits(int newValue) {
135.634 + maximumFractionDigits = Math.max(0,newValue);
135.635 + if (maximumFractionDigits < minimumFractionDigits) {
135.636 + minimumFractionDigits = maximumFractionDigits;
135.637 + }
135.638 + }
135.639 +
135.640 + /**
135.641 + * Returns the minimum number of digits allowed in the fraction portion of a
135.642 + * number.
135.643 + * @see #setMinimumFractionDigits
135.644 + */
135.645 + public int getMinimumFractionDigits() {
135.646 + return minimumFractionDigits;
135.647 + }
135.648 +
135.649 + /**
135.650 + * Sets the minimum number of digits allowed in the fraction portion of a
135.651 + * number. minimumFractionDigits must be <= maximumFractionDigits. If the
135.652 + * new value for minimumFractionDigits exceeds the current value
135.653 + * of maximumFractionDigits, then maximumIntegerDigits will also be set to
135.654 + * the new value
135.655 + * @param newValue the minimum number of fraction digits to be shown; if
135.656 + * less than zero, then zero is used. The concrete subclass may enforce an
135.657 + * upper limit to this value appropriate to the numeric type being formatted.
135.658 + * @see #getMinimumFractionDigits
135.659 + */
135.660 + public void setMinimumFractionDigits(int newValue) {
135.661 + minimumFractionDigits = Math.max(0,newValue);
135.662 + if (maximumFractionDigits < minimumFractionDigits) {
135.663 + maximumFractionDigits = minimumFractionDigits;
135.664 + }
135.665 + }
135.666 +
135.667 + /**
135.668 + * Gets the currency used by this number format when formatting
135.669 + * currency values. The initial value is derived in a locale dependent
135.670 + * way. The returned value may be null if no valid
135.671 + * currency could be determined and no currency has been set using
135.672 + * {@link #setCurrency(java.util.Currency) setCurrency}.
135.673 + * <p>
135.674 + * The default implementation throws
135.675 + * <code>UnsupportedOperationException</code>.
135.676 + *
135.677 + * @return the currency used by this number format, or <code>null</code>
135.678 + * @exception UnsupportedOperationException if the number format class
135.679 + * doesn't implement currency formatting
135.680 + * @since 1.4
135.681 + */
135.682 + public Currency getCurrency() {
135.683 + throw new UnsupportedOperationException();
135.684 + }
135.685 +
135.686 + /**
135.687 + * Sets the currency used by this number format when formatting
135.688 + * currency values. This does not update the minimum or maximum
135.689 + * number of fraction digits used by the number format.
135.690 + * <p>
135.691 + * The default implementation throws
135.692 + * <code>UnsupportedOperationException</code>.
135.693 + *
135.694 + * @param currency the new currency to be used by this number format
135.695 + * @exception UnsupportedOperationException if the number format class
135.696 + * doesn't implement currency formatting
135.697 + * @exception NullPointerException if <code>currency</code> is null
135.698 + * @since 1.4
135.699 + */
135.700 + public void setCurrency(Currency currency) {
135.701 + throw new UnsupportedOperationException();
135.702 + }
135.703 +
135.704 + /**
135.705 + * Gets the {@link java.math.RoundingMode} used in this NumberFormat.
135.706 + * The default implementation of this method in NumberFormat
135.707 + * always throws {@link java.lang.UnsupportedOperationException}.
135.708 + * Subclasses which handle different rounding modes should override
135.709 + * this method.
135.710 + *
135.711 + * @exception UnsupportedOperationException The default implementation
135.712 + * always throws this exception
135.713 + * @return The <code>RoundingMode</code> used for this NumberFormat.
135.714 + * @see #setRoundingMode(RoundingMode)
135.715 + * @since 1.6
135.716 + */
135.717 + public RoundingMode getRoundingMode() {
135.718 + throw new UnsupportedOperationException();
135.719 + }
135.720 +
135.721 + /**
135.722 + * Sets the {@link java.math.RoundingMode} used in this NumberFormat.
135.723 + * The default implementation of this method in NumberFormat always
135.724 + * throws {@link java.lang.UnsupportedOperationException}.
135.725 + * Subclasses which handle different rounding modes should override
135.726 + * this method.
135.727 + *
135.728 + * @exception UnsupportedOperationException The default implementation
135.729 + * always throws this exception
135.730 + * @exception NullPointerException if <code>roundingMode</code> is null
135.731 + * @param roundingMode The <code>RoundingMode</code> to be used
135.732 + * @see #getRoundingMode()
135.733 + * @since 1.6
135.734 + */
135.735 + public void setRoundingMode(RoundingMode roundingMode) {
135.736 + throw new UnsupportedOperationException();
135.737 + }
135.738 +
135.739 + // =======================privates===============================
135.740 +
135.741 + private static NumberFormat getInstance(Locale desiredLocale,
135.742 + int choice) {
135.743 + // Check whether a provider can provide an implementation that's closer
135.744 + // to the requested locale than what the Java runtime itself can provide.
135.745 +// LocaleServiceProviderPool pool =
135.746 +// LocaleServiceProviderPool.getPool(NumberFormatProvider.class);
135.747 +// if (pool.hasProviders()) {
135.748 +// NumberFormat providersInstance = pool.getLocalizedObject(
135.749 +// NumberFormatGetter.INSTANCE,
135.750 +// desiredLocale,
135.751 +// choice);
135.752 +// if (providersInstance != null) {
135.753 +// return providersInstance;
135.754 +// }
135.755 +// }
135.756 +
135.757 + /* try the cache first */
135.758 + String[] numberPatterns = (String[])cachedLocaleData.get(desiredLocale);
135.759 +// if (numberPatterns == null) { /* cache miss */
135.760 +// ResourceBundle resource = LocaleData.getNumberFormatData(desiredLocale);
135.761 +// numberPatterns = resource.getStringArray("NumberPatterns");
135.762 +// /* update cache */
135.763 +// cachedLocaleData.put(desiredLocale, numberPatterns);
135.764 +// }
135.765 +
135.766 + DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(desiredLocale);
135.767 + int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice;
135.768 + DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols);
135.769 +
135.770 + if (choice == INTEGERSTYLE) {
135.771 + format.setMaximumFractionDigits(0);
135.772 + format.setDecimalSeparatorAlwaysShown(false);
135.773 + format.setParseIntegerOnly(true);
135.774 + } else if (choice == CURRENCYSTYLE) {
135.775 + format.adjustForCurrencyDefaultFractionDigits();
135.776 + }
135.777 +
135.778 + return format;
135.779 + }
135.780 +
135.781 + /**
135.782 + * First, read in the default serializable data.
135.783 + *
135.784 + * Then, if <code>serialVersionOnStream</code> is less than 1, indicating that
135.785 + * the stream was written by JDK 1.1,
135.786 + * set the <code>int</code> fields such as <code>maximumIntegerDigits</code>
135.787 + * to be equal to the <code>byte</code> fields such as <code>maxIntegerDigits</code>,
135.788 + * since the <code>int</code> fields were not present in JDK 1.1.
135.789 + * Finally, set serialVersionOnStream back to the maximum allowed value so that
135.790 + * default serialization will work properly if this object is streamed out again.
135.791 + *
135.792 + * <p>If <code>minimumIntegerDigits</code> is greater than
135.793 + * <code>maximumIntegerDigits</code> or <code>minimumFractionDigits</code>
135.794 + * is greater than <code>maximumFractionDigits</code>, then the stream data
135.795 + * is invalid and this method throws an <code>InvalidObjectException</code>.
135.796 + * In addition, if any of these values is negative, then this method throws
135.797 + * an <code>InvalidObjectException</code>.
135.798 + *
135.799 + * @since 1.2
135.800 + */
135.801 + private void readObject(ObjectInputStream stream)
135.802 + throws IOException, ClassNotFoundException
135.803 + {
135.804 + stream.defaultReadObject();
135.805 + if (serialVersionOnStream < 1) {
135.806 + // Didn't have additional int fields, reassign to use them.
135.807 + maximumIntegerDigits = maxIntegerDigits;
135.808 + minimumIntegerDigits = minIntegerDigits;
135.809 + maximumFractionDigits = maxFractionDigits;
135.810 + minimumFractionDigits = minFractionDigits;
135.811 + }
135.812 + if (minimumIntegerDigits > maximumIntegerDigits ||
135.813 + minimumFractionDigits > maximumFractionDigits ||
135.814 + minimumIntegerDigits < 0 || minimumFractionDigits < 0) {
135.815 + throw new InvalidObjectException("Digit count range invalid");
135.816 + }
135.817 + serialVersionOnStream = currentSerialVersion;
135.818 + }
135.819 +
135.820 + /**
135.821 + * Write out the default serializable data, after first setting
135.822 + * the <code>byte</code> fields such as <code>maxIntegerDigits</code> to be
135.823 + * equal to the <code>int</code> fields such as <code>maximumIntegerDigits</code>
135.824 + * (or to <code>Byte.MAX_VALUE</code>, whichever is smaller), for compatibility
135.825 + * with the JDK 1.1 version of the stream format.
135.826 + *
135.827 + * @since 1.2
135.828 + */
135.829 + private void writeObject(ObjectOutputStream stream)
135.830 + throws IOException
135.831 + {
135.832 + maxIntegerDigits = (maximumIntegerDigits > Byte.MAX_VALUE) ?
135.833 + Byte.MAX_VALUE : (byte)maximumIntegerDigits;
135.834 + minIntegerDigits = (minimumIntegerDigits > Byte.MAX_VALUE) ?
135.835 + Byte.MAX_VALUE : (byte)minimumIntegerDigits;
135.836 + maxFractionDigits = (maximumFractionDigits > Byte.MAX_VALUE) ?
135.837 + Byte.MAX_VALUE : (byte)maximumFractionDigits;
135.838 + minFractionDigits = (minimumFractionDigits > Byte.MAX_VALUE) ?
135.839 + Byte.MAX_VALUE : (byte)minimumFractionDigits;
135.840 + stream.defaultWriteObject();
135.841 + }
135.842 +
135.843 + /**
135.844 + * Cache to hold the NumberPatterns of a Locale.
135.845 + */
135.846 + private static final Hashtable cachedLocaleData = new Hashtable(3);
135.847 +
135.848 + // Constants used by factory methods to specify a style of format.
135.849 + private static final int NUMBERSTYLE = 0;
135.850 + private static final int CURRENCYSTYLE = 1;
135.851 + private static final int PERCENTSTYLE = 2;
135.852 + private static final int SCIENTIFICSTYLE = 3;
135.853 + private static final int INTEGERSTYLE = 4;
135.854 +
135.855 + /**
135.856 + * True if the grouping (i.e. thousands) separator is used when
135.857 + * formatting and parsing numbers.
135.858 + *
135.859 + * @serial
135.860 + * @see #isGroupingUsed
135.861 + */
135.862 + private boolean groupingUsed = true;
135.863 +
135.864 + /**
135.865 + * The maximum number of digits allowed in the integer portion of a
135.866 + * number. <code>maxIntegerDigits</code> must be greater than or equal to
135.867 + * <code>minIntegerDigits</code>.
135.868 + * <p>
135.869 + * <strong>Note:</strong> This field exists only for serialization
135.870 + * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
135.871 + * <code>int</code> field <code>maximumIntegerDigits</code> is used instead.
135.872 + * When writing to a stream, <code>maxIntegerDigits</code> is set to
135.873 + * <code>maximumIntegerDigits</code> or <code>Byte.MAX_VALUE</code>,
135.874 + * whichever is smaller. When reading from a stream, this field is used
135.875 + * only if <code>serialVersionOnStream</code> is less than 1.
135.876 + *
135.877 + * @serial
135.878 + * @see #getMaximumIntegerDigits
135.879 + */
135.880 + private byte maxIntegerDigits = 40;
135.881 +
135.882 + /**
135.883 + * The minimum number of digits allowed in the integer portion of a
135.884 + * number. <code>minimumIntegerDigits</code> must be less than or equal to
135.885 + * <code>maximumIntegerDigits</code>.
135.886 + * <p>
135.887 + * <strong>Note:</strong> This field exists only for serialization
135.888 + * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
135.889 + * <code>int</code> field <code>minimumIntegerDigits</code> is used instead.
135.890 + * When writing to a stream, <code>minIntegerDigits</code> is set to
135.891 + * <code>minimumIntegerDigits</code> or <code>Byte.MAX_VALUE</code>,
135.892 + * whichever is smaller. When reading from a stream, this field is used
135.893 + * only if <code>serialVersionOnStream</code> is less than 1.
135.894 + *
135.895 + * @serial
135.896 + * @see #getMinimumIntegerDigits
135.897 + */
135.898 + private byte minIntegerDigits = 1;
135.899 +
135.900 + /**
135.901 + * The maximum number of digits allowed in the fractional portion of a
135.902 + * number. <code>maximumFractionDigits</code> must be greater than or equal to
135.903 + * <code>minimumFractionDigits</code>.
135.904 + * <p>
135.905 + * <strong>Note:</strong> This field exists only for serialization
135.906 + * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
135.907 + * <code>int</code> field <code>maximumFractionDigits</code> is used instead.
135.908 + * When writing to a stream, <code>maxFractionDigits</code> is set to
135.909 + * <code>maximumFractionDigits</code> or <code>Byte.MAX_VALUE</code>,
135.910 + * whichever is smaller. When reading from a stream, this field is used
135.911 + * only if <code>serialVersionOnStream</code> is less than 1.
135.912 + *
135.913 + * @serial
135.914 + * @see #getMaximumFractionDigits
135.915 + */
135.916 + private byte maxFractionDigits = 3; // invariant, >= minFractionDigits
135.917 +
135.918 + /**
135.919 + * The minimum number of digits allowed in the fractional portion of a
135.920 + * number. <code>minimumFractionDigits</code> must be less than or equal to
135.921 + * <code>maximumFractionDigits</code>.
135.922 + * <p>
135.923 + * <strong>Note:</strong> This field exists only for serialization
135.924 + * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
135.925 + * <code>int</code> field <code>minimumFractionDigits</code> is used instead.
135.926 + * When writing to a stream, <code>minFractionDigits</code> is set to
135.927 + * <code>minimumFractionDigits</code> or <code>Byte.MAX_VALUE</code>,
135.928 + * whichever is smaller. When reading from a stream, this field is used
135.929 + * only if <code>serialVersionOnStream</code> is less than 1.
135.930 + *
135.931 + * @serial
135.932 + * @see #getMinimumFractionDigits
135.933 + */
135.934 + private byte minFractionDigits = 0;
135.935 +
135.936 + /**
135.937 + * True if this format will parse numbers as integers only.
135.938 + *
135.939 + * @serial
135.940 + * @see #isParseIntegerOnly
135.941 + */
135.942 + private boolean parseIntegerOnly = false;
135.943 +
135.944 + // new fields for 1.2. byte is too small for integer digits.
135.945 +
135.946 + /**
135.947 + * The maximum number of digits allowed in the integer portion of a
135.948 + * number. <code>maximumIntegerDigits</code> must be greater than or equal to
135.949 + * <code>minimumIntegerDigits</code>.
135.950 + *
135.951 + * @serial
135.952 + * @since 1.2
135.953 + * @see #getMaximumIntegerDigits
135.954 + */
135.955 + private int maximumIntegerDigits = 40;
135.956 +
135.957 + /**
135.958 + * The minimum number of digits allowed in the integer portion of a
135.959 + * number. <code>minimumIntegerDigits</code> must be less than or equal to
135.960 + * <code>maximumIntegerDigits</code>.
135.961 + *
135.962 + * @serial
135.963 + * @since 1.2
135.964 + * @see #getMinimumIntegerDigits
135.965 + */
135.966 + private int minimumIntegerDigits = 1;
135.967 +
135.968 + /**
135.969 + * The maximum number of digits allowed in the fractional portion of a
135.970 + * number. <code>maximumFractionDigits</code> must be greater than or equal to
135.971 + * <code>minimumFractionDigits</code>.
135.972 + *
135.973 + * @serial
135.974 + * @since 1.2
135.975 + * @see #getMaximumFractionDigits
135.976 + */
135.977 + private int maximumFractionDigits = 3; // invariant, >= minFractionDigits
135.978 +
135.979 + /**
135.980 + * The minimum number of digits allowed in the fractional portion of a
135.981 + * number. <code>minimumFractionDigits</code> must be less than or equal to
135.982 + * <code>maximumFractionDigits</code>.
135.983 + *
135.984 + * @serial
135.985 + * @since 1.2
135.986 + * @see #getMinimumFractionDigits
135.987 + */
135.988 + private int minimumFractionDigits = 0;
135.989 +
135.990 + static final int currentSerialVersion = 1;
135.991 +
135.992 + /**
135.993 + * Describes the version of <code>NumberFormat</code> present on the stream.
135.994 + * Possible values are:
135.995 + * <ul>
135.996 + * <li><b>0</b> (or uninitialized): the JDK 1.1 version of the stream format.
135.997 + * In this version, the <code>int</code> fields such as
135.998 + * <code>maximumIntegerDigits</code> were not present, and the <code>byte</code>
135.999 + * fields such as <code>maxIntegerDigits</code> are used instead.
135.1000 + *
135.1001 + * <li><b>1</b>: the 1.2 version of the stream format. The values of the
135.1002 + * <code>byte</code> fields such as <code>maxIntegerDigits</code> are ignored,
135.1003 + * and the <code>int</code> fields such as <code>maximumIntegerDigits</code>
135.1004 + * are used instead.
135.1005 + * </ul>
135.1006 + * When streaming out a <code>NumberFormat</code>, the most recent format
135.1007 + * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
135.1008 + * is always written.
135.1009 + *
135.1010 + * @serial
135.1011 + * @since 1.2
135.1012 + */
135.1013 + private int serialVersionOnStream = currentSerialVersion;
135.1014 +
135.1015 + // Removed "implements Cloneable" clause. Needs to update serialization
135.1016 + // ID for backward compatibility.
135.1017 + static final long serialVersionUID = -2308460125733713944L;
135.1018 +
135.1019 +
135.1020 + //
135.1021 + // class for AttributedCharacterIterator attributes
135.1022 + //
135.1023 + /**
135.1024 + * Defines constants that are used as attribute keys in the
135.1025 + * <code>AttributedCharacterIterator</code> returned
135.1026 + * from <code>NumberFormat.formatToCharacterIterator</code> and as
135.1027 + * field identifiers in <code>FieldPosition</code>.
135.1028 + *
135.1029 + * @since 1.4
135.1030 + */
135.1031 + public static class Field extends Format.Field {
135.1032 +
135.1033 + // Proclaim serial compatibility with 1.4 FCS
135.1034 + private static final long serialVersionUID = 7494728892700160890L;
135.1035 +
135.1036 + // table of all instances in this class, used by readResolve
135.1037 + private static final Map instanceMap = new HashMap(11);
135.1038 +
135.1039 + /**
135.1040 + * Creates a Field instance with the specified
135.1041 + * name.
135.1042 + *
135.1043 + * @param name Name of the attribute
135.1044 + */
135.1045 + protected Field(String name) {
135.1046 + super(name);
135.1047 + if (this.getClass() == NumberFormat.Field.class) {
135.1048 + instanceMap.put(name, this);
135.1049 + }
135.1050 + }
135.1051 +
135.1052 + /**
135.1053 + * Resolves instances being deserialized to the predefined constants.
135.1054 + *
135.1055 + * @throws InvalidObjectException if the constant could not be resolved.
135.1056 + * @return resolved NumberFormat.Field constant
135.1057 + */
135.1058 + protected Object readResolve() throws InvalidObjectException {
135.1059 + if (this.getClass() != NumberFormat.Field.class) {
135.1060 + throw new InvalidObjectException("subclass didn't correctly implement readResolve");
135.1061 + }
135.1062 +
135.1063 + Object instance = instanceMap.get(getName());
135.1064 + if (instance != null) {
135.1065 + return instance;
135.1066 + } else {
135.1067 + throw new InvalidObjectException("unknown attribute name");
135.1068 + }
135.1069 + }
135.1070 +
135.1071 + /**
135.1072 + * Constant identifying the integer field.
135.1073 + */
135.1074 + public static final Field INTEGER = new Field("integer");
135.1075 +
135.1076 + /**
135.1077 + * Constant identifying the fraction field.
135.1078 + */
135.1079 + public static final Field FRACTION = new Field("fraction");
135.1080 +
135.1081 + /**
135.1082 + * Constant identifying the exponent field.
135.1083 + */
135.1084 + public static final Field EXPONENT = new Field("exponent");
135.1085 +
135.1086 + /**
135.1087 + * Constant identifying the decimal separator field.
135.1088 + */
135.1089 + public static final Field DECIMAL_SEPARATOR =
135.1090 + new Field("decimal separator");
135.1091 +
135.1092 + /**
135.1093 + * Constant identifying the sign field.
135.1094 + */
135.1095 + public static final Field SIGN = new Field("sign");
135.1096 +
135.1097 + /**
135.1098 + * Constant identifying the grouping separator field.
135.1099 + */
135.1100 + public static final Field GROUPING_SEPARATOR =
135.1101 + new Field("grouping separator");
135.1102 +
135.1103 + /**
135.1104 + * Constant identifying the exponent symbol field.
135.1105 + */
135.1106 + public static final Field EXPONENT_SYMBOL = new
135.1107 + Field("exponent symbol");
135.1108 +
135.1109 + /**
135.1110 + * Constant identifying the percent field.
135.1111 + */
135.1112 + public static final Field PERCENT = new Field("percent");
135.1113 +
135.1114 + /**
135.1115 + * Constant identifying the permille field.
135.1116 + */
135.1117 + public static final Field PERMILLE = new Field("per mille");
135.1118 +
135.1119 + /**
135.1120 + * Constant identifying the currency field.
135.1121 + */
135.1122 + public static final Field CURRENCY = new Field("currency");
135.1123 +
135.1124 + /**
135.1125 + * Constant identifying the exponent sign field.
135.1126 + */
135.1127 + public static final Field EXPONENT_SIGN = new Field("exponent sign");
135.1128 + }
135.1129 +
135.1130 + /**
135.1131 + * Obtains a NumberFormat instance from a NumberFormatProvider implementation.
135.1132 + *
135.1133 + private static class NumberFormatGetter
135.1134 + implements LocaleServiceProviderPool.LocalizedObjectGetter<NumberFormatProvider,
135.1135 + NumberFormat> {
135.1136 + private static final NumberFormatGetter INSTANCE = new NumberFormatGetter();
135.1137 +
135.1138 + public NumberFormat getObject(NumberFormatProvider numberFormatProvider,
135.1139 + Locale locale,
135.1140 + String key,
135.1141 + Object... params) {
135.1142 + assert params.length == 1;
135.1143 + int choice = (Integer)params[0];
135.1144 +
135.1145 + switch (choice) {
135.1146 + case NUMBERSTYLE:
135.1147 + return numberFormatProvider.getNumberInstance(locale);
135.1148 + case PERCENTSTYLE:
135.1149 + return numberFormatProvider.getPercentInstance(locale);
135.1150 + case CURRENCYSTYLE:
135.1151 + return numberFormatProvider.getCurrencyInstance(locale);
135.1152 + case INTEGERSTYLE:
135.1153 + return numberFormatProvider.getIntegerInstance(locale);
135.1154 + default:
135.1155 + assert false : choice;
135.1156 + }
135.1157 +
135.1158 + return null;
135.1159 + }
135.1160 + }
135.1161 + */
135.1162 +}
136.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
136.2 +++ b/rt/emul/compact/src/main/java/java/text/ParseException.java Tue Feb 11 13:31:42 2014 +0100
136.3 @@ -0,0 +1,78 @@
136.4 +/*
136.5 + * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved.
136.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
136.7 + *
136.8 + * This code is free software; you can redistribute it and/or modify it
136.9 + * under the terms of the GNU General Public License version 2 only, as
136.10 + * published by the Free Software Foundation. Oracle designates this
136.11 + * particular file as subject to the "Classpath" exception as provided
136.12 + * by Oracle in the LICENSE file that accompanied this code.
136.13 + *
136.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
136.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
136.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
136.17 + * version 2 for more details (a copy is included in the LICENSE file that
136.18 + * accompanied this code).
136.19 + *
136.20 + * You should have received a copy of the GNU General Public License version
136.21 + * 2 along with this work; if not, write to the Free Software Foundation,
136.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
136.23 + *
136.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
136.25 + * or visit www.oracle.com if you need additional information or have any
136.26 + * questions.
136.27 + */
136.28 +
136.29 +/*
136.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
136.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
136.32 + *
136.33 + * The original version of this source code and documentation is copyrighted
136.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
136.35 + * materials are provided under terms of a License Agreement between Taligent
136.36 + * and Sun. This technology is protected by multiple US and International
136.37 + * patents. This notice and attribution to Taligent may not be removed.
136.38 + * Taligent is a registered trademark of Taligent, Inc.
136.39 + *
136.40 + */
136.41 +
136.42 +package java.text;
136.43 +
136.44 +/**
136.45 + * Signals that an error has been reached unexpectedly
136.46 + * while parsing.
136.47 + * @see java.lang.Exception
136.48 + * @see java.text.Format
136.49 + * @see java.text.FieldPosition
136.50 + * @author Mark Davis
136.51 + */
136.52 +public
136.53 +class ParseException extends Exception {
136.54 +
136.55 + /**
136.56 + * Constructs a ParseException with the specified detail message and
136.57 + * offset.
136.58 + * A detail message is a String that describes this particular exception.
136.59 + * @param s the detail message
136.60 + * @param errorOffset the position where the error is found while parsing.
136.61 + */
136.62 + public ParseException(String s, int errorOffset) {
136.63 + super(s);
136.64 + this.errorOffset = errorOffset;
136.65 + }
136.66 +
136.67 + /**
136.68 + * Returns the position where the error was found.
136.69 + */
136.70 + public int getErrorOffset () {
136.71 + return errorOffset;
136.72 + }
136.73 +
136.74 + //============ privates ============
136.75 + /**
136.76 + * The zero-based character offset into the string being parsed at which
136.77 + * the error was found during parsing.
136.78 + * @serial
136.79 + */
136.80 + private int errorOffset;
136.81 +}
137.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
137.2 +++ b/rt/emul/compact/src/main/java/java/text/ParsePosition.java Tue Feb 11 13:31:42 2014 +0100
137.3 @@ -0,0 +1,139 @@
137.4 +/*
137.5 + * Copyright (c) 1996, 2002, Oracle and/or its affiliates. All rights reserved.
137.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
137.7 + *
137.8 + * This code is free software; you can redistribute it and/or modify it
137.9 + * under the terms of the GNU General Public License version 2 only, as
137.10 + * published by the Free Software Foundation. Oracle designates this
137.11 + * particular file as subject to the "Classpath" exception as provided
137.12 + * by Oracle in the LICENSE file that accompanied this code.
137.13 + *
137.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
137.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
137.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
137.17 + * version 2 for more details (a copy is included in the LICENSE file that
137.18 + * accompanied this code).
137.19 + *
137.20 + * You should have received a copy of the GNU General Public License version
137.21 + * 2 along with this work; if not, write to the Free Software Foundation,
137.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
137.23 + *
137.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
137.25 + * or visit www.oracle.com if you need additional information or have any
137.26 + * questions.
137.27 + */
137.28 +
137.29 +/*
137.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
137.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
137.32 + *
137.33 + * The original version of this source code and documentation is copyrighted
137.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
137.35 + * materials are provided under terms of a License Agreement between Taligent
137.36 + * and Sun. This technology is protected by multiple US and International
137.37 + * patents. This notice and attribution to Taligent may not be removed.
137.38 + * Taligent is a registered trademark of Taligent, Inc.
137.39 + *
137.40 + */
137.41 +
137.42 +package java.text;
137.43 +
137.44 +
137.45 +/**
137.46 + * <code>ParsePosition</code> is a simple class used by <code>Format</code>
137.47 + * and its subclasses to keep track of the current position during parsing.
137.48 + * The <code>parseObject</code> method in the various <code>Format</code>
137.49 + * classes requires a <code>ParsePosition</code> object as an argument.
137.50 + *
137.51 + * <p>
137.52 + * By design, as you parse through a string with different formats,
137.53 + * you can use the same <code>ParsePosition</code>, since the index parameter
137.54 + * records the current position.
137.55 + *
137.56 + * @author Mark Davis
137.57 + * @see java.text.Format
137.58 + */
137.59 +
137.60 +public class ParsePosition {
137.61 +
137.62 + /**
137.63 + * Input: the place you start parsing.
137.64 + * <br>Output: position where the parse stopped.
137.65 + * This is designed to be used serially,
137.66 + * with each call setting index up for the next one.
137.67 + */
137.68 + int index = 0;
137.69 + int errorIndex = -1;
137.70 +
137.71 + /**
137.72 + * Retrieve the current parse position. On input to a parse method, this
137.73 + * is the index of the character at which parsing will begin; on output, it
137.74 + * is the index of the character following the last character parsed.
137.75 + */
137.76 + public int getIndex() {
137.77 + return index;
137.78 + }
137.79 +
137.80 + /**
137.81 + * Set the current parse position.
137.82 + */
137.83 + public void setIndex(int index) {
137.84 + this.index = index;
137.85 + }
137.86 +
137.87 + /**
137.88 + * Create a new ParsePosition with the given initial index.
137.89 + */
137.90 + public ParsePosition(int index) {
137.91 + this.index = index;
137.92 + }
137.93 + /**
137.94 + * Set the index at which a parse error occurred. Formatters
137.95 + * should set this before returning an error code from their
137.96 + * parseObject method. The default value is -1 if this is not set.
137.97 + * @since 1.2
137.98 + */
137.99 + public void setErrorIndex(int ei)
137.100 + {
137.101 + errorIndex = ei;
137.102 + }
137.103 +
137.104 + /**
137.105 + * Retrieve the index at which an error occurred, or -1 if the
137.106 + * error index has not been set.
137.107 + * @since 1.2
137.108 + */
137.109 + public int getErrorIndex()
137.110 + {
137.111 + return errorIndex;
137.112 + }
137.113 + /**
137.114 + * Overrides equals
137.115 + */
137.116 + public boolean equals(Object obj)
137.117 + {
137.118 + if (obj == null) return false;
137.119 + if (!(obj instanceof ParsePosition))
137.120 + return false;
137.121 + ParsePosition other = (ParsePosition) obj;
137.122 + return (index == other.index && errorIndex == other.errorIndex);
137.123 + }
137.124 +
137.125 + /**
137.126 + * Returns a hash code for this ParsePosition.
137.127 + * @return a hash code value for this object
137.128 + */
137.129 + public int hashCode() {
137.130 + return (errorIndex << 16) | index;
137.131 + }
137.132 +
137.133 + /**
137.134 + * Return a string representation of this ParsePosition.
137.135 + * @return a string representation of this object
137.136 + */
137.137 + public String toString() {
137.138 + return getClass().getName() +
137.139 + "[index=" + index +
137.140 + ",errorIndex=" + errorIndex + ']';
137.141 + }
137.142 +}
138.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
138.2 +++ b/rt/emul/compact/src/main/java/java/text/SimpleDateFormat.java Tue Feb 11 13:31:42 2014 +0100
138.3 @@ -0,0 +1,2383 @@
138.4 +/*
138.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
138.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
138.7 + *
138.8 + * This code is free software; you can redistribute it and/or modify it
138.9 + * under the terms of the GNU General Public License version 2 only, as
138.10 + * published by the Free Software Foundation. Oracle designates this
138.11 + * particular file as subject to the "Classpath" exception as provided
138.12 + * by Oracle in the LICENSE file that accompanied this code.
138.13 + *
138.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
138.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
138.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
138.17 + * version 2 for more details (a copy is included in the LICENSE file that
138.18 + * accompanied this code).
138.19 + *
138.20 + * You should have received a copy of the GNU General Public License version
138.21 + * 2 along with this work; if not, write to the Free Software Foundation,
138.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
138.23 + *
138.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
138.25 + * or visit www.oracle.com if you need additional information or have any
138.26 + * questions.
138.27 + */
138.28 +
138.29 +/*
138.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
138.31 + * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
138.32 + *
138.33 + * The original version of this source code and documentation is copyrighted
138.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
138.35 + * materials are provided under terms of a License Agreement between Taligent
138.36 + * and Sun. This technology is protected by multiple US and International
138.37 + * patents. This notice and attribution to Taligent may not be removed.
138.38 + * Taligent is a registered trademark of Taligent, Inc.
138.39 + *
138.40 + */
138.41 +
138.42 +package java.text;
138.43 +
138.44 +import java.io.IOException;
138.45 +import java.io.InvalidObjectException;
138.46 +import java.io.ObjectInputStream;
138.47 +import java.util.Calendar;
138.48 +import java.util.Date;
138.49 +import java.util.Locale;
138.50 +import java.util.Map;
138.51 +import java.util.MissingResourceException;
138.52 +import java.util.ResourceBundle;
138.53 +import java.util.SimpleTimeZone;
138.54 +import java.util.TimeZone;
138.55 +import java.util.concurrent.ConcurrentHashMap;
138.56 +import java.util.concurrent.ConcurrentMap;
138.57 +
138.58 +import static java.text.DateFormatSymbols.*;
138.59 +
138.60 +/**
138.61 + * <code>SimpleDateFormat</code> is a concrete class for formatting and
138.62 + * parsing dates in a locale-sensitive manner. It allows for formatting
138.63 + * (date -> text), parsing (text -> date), and normalization.
138.64 + *
138.65 + * <p>
138.66 + * <code>SimpleDateFormat</code> allows you to start by choosing
138.67 + * any user-defined patterns for date-time formatting. However, you
138.68 + * are encouraged to create a date-time formatter with either
138.69 + * <code>getTimeInstance</code>, <code>getDateInstance</code>, or
138.70 + * <code>getDateTimeInstance</code> in <code>DateFormat</code>. Each
138.71 + * of these class methods can return a date/time formatter initialized
138.72 + * with a default format pattern. You may modify the format pattern
138.73 + * using the <code>applyPattern</code> methods as desired.
138.74 + * For more information on using these methods, see
138.75 + * {@link DateFormat}.
138.76 + *
138.77 + * <h4>Date and Time Patterns</h4>
138.78 + * <p>
138.79 + * Date and time formats are specified by <em>date and time pattern</em>
138.80 + * strings.
138.81 + * Within date and time pattern strings, unquoted letters from
138.82 + * <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
138.83 + * <code>'z'</code> are interpreted as pattern letters representing the
138.84 + * components of a date or time string.
138.85 + * Text can be quoted using single quotes (<code>'</code>) to avoid
138.86 + * interpretation.
138.87 + * <code>"''"</code> represents a single quote.
138.88 + * All other characters are not interpreted; they're simply copied into the
138.89 + * output string during formatting or matched against the input string
138.90 + * during parsing.
138.91 + * <p>
138.92 + * The following pattern letters are defined (all other characters from
138.93 + * <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
138.94 + * <code>'z'</code> are reserved):
138.95 + * <blockquote>
138.96 + * <table border=0 cellspacing=3 cellpadding=0 summary="Chart shows pattern letters, date/time component, presentation, and examples.">
138.97 + * <tr bgcolor="#ccccff">
138.98 + * <th align=left>Letter
138.99 + * <th align=left>Date or Time Component
138.100 + * <th align=left>Presentation
138.101 + * <th align=left>Examples
138.102 + * <tr>
138.103 + * <td><code>G</code>
138.104 + * <td>Era designator
138.105 + * <td><a href="#text">Text</a>
138.106 + * <td><code>AD</code>
138.107 + * <tr bgcolor="#eeeeff">
138.108 + * <td><code>y</code>
138.109 + * <td>Year
138.110 + * <td><a href="#year">Year</a>
138.111 + * <td><code>1996</code>; <code>96</code>
138.112 + * <tr>
138.113 + * <td><code>Y</code>
138.114 + * <td>Week year
138.115 + * <td><a href="#year">Year</a>
138.116 + * <td><code>2009</code>; <code>09</code>
138.117 + * <tr bgcolor="#eeeeff">
138.118 + * <td><code>M</code>
138.119 + * <td>Month in year
138.120 + * <td><a href="#month">Month</a>
138.121 + * <td><code>July</code>; <code>Jul</code>; <code>07</code>
138.122 + * <tr>
138.123 + * <td><code>w</code>
138.124 + * <td>Week in year
138.125 + * <td><a href="#number">Number</a>
138.126 + * <td><code>27</code>
138.127 + * <tr bgcolor="#eeeeff">
138.128 + * <td><code>W</code>
138.129 + * <td>Week in month
138.130 + * <td><a href="#number">Number</a>
138.131 + * <td><code>2</code>
138.132 + * <tr>
138.133 + * <td><code>D</code>
138.134 + * <td>Day in year
138.135 + * <td><a href="#number">Number</a>
138.136 + * <td><code>189</code>
138.137 + * <tr bgcolor="#eeeeff">
138.138 + * <td><code>d</code>
138.139 + * <td>Day in month
138.140 + * <td><a href="#number">Number</a>
138.141 + * <td><code>10</code>
138.142 + * <tr>
138.143 + * <td><code>F</code>
138.144 + * <td>Day of week in month
138.145 + * <td><a href="#number">Number</a>
138.146 + * <td><code>2</code>
138.147 + * <tr bgcolor="#eeeeff">
138.148 + * <td><code>E</code>
138.149 + * <td>Day name in week
138.150 + * <td><a href="#text">Text</a>
138.151 + * <td><code>Tuesday</code>; <code>Tue</code>
138.152 + * <tr>
138.153 + * <td><code>u</code>
138.154 + * <td>Day number of week (1 = Monday, ..., 7 = Sunday)
138.155 + * <td><a href="#number">Number</a>
138.156 + * <td><code>1</code>
138.157 + * <tr bgcolor="#eeeeff">
138.158 + * <td><code>a</code>
138.159 + * <td>Am/pm marker
138.160 + * <td><a href="#text">Text</a>
138.161 + * <td><code>PM</code>
138.162 + * <tr>
138.163 + * <td><code>H</code>
138.164 + * <td>Hour in day (0-23)
138.165 + * <td><a href="#number">Number</a>
138.166 + * <td><code>0</code>
138.167 + * <tr bgcolor="#eeeeff">
138.168 + * <td><code>k</code>
138.169 + * <td>Hour in day (1-24)
138.170 + * <td><a href="#number">Number</a>
138.171 + * <td><code>24</code>
138.172 + * <tr>
138.173 + * <td><code>K</code>
138.174 + * <td>Hour in am/pm (0-11)
138.175 + * <td><a href="#number">Number</a>
138.176 + * <td><code>0</code>
138.177 + * <tr bgcolor="#eeeeff">
138.178 + * <td><code>h</code>
138.179 + * <td>Hour in am/pm (1-12)
138.180 + * <td><a href="#number">Number</a>
138.181 + * <td><code>12</code>
138.182 + * <tr>
138.183 + * <td><code>m</code>
138.184 + * <td>Minute in hour
138.185 + * <td><a href="#number">Number</a>
138.186 + * <td><code>30</code>
138.187 + * <tr bgcolor="#eeeeff">
138.188 + * <td><code>s</code>
138.189 + * <td>Second in minute
138.190 + * <td><a href="#number">Number</a>
138.191 + * <td><code>55</code>
138.192 + * <tr>
138.193 + * <td><code>S</code>
138.194 + * <td>Millisecond
138.195 + * <td><a href="#number">Number</a>
138.196 + * <td><code>978</code>
138.197 + * <tr bgcolor="#eeeeff">
138.198 + * <td><code>z</code>
138.199 + * <td>Time zone
138.200 + * <td><a href="#timezone">General time zone</a>
138.201 + * <td><code>Pacific Standard Time</code>; <code>PST</code>; <code>GMT-08:00</code>
138.202 + * <tr>
138.203 + * <td><code>Z</code>
138.204 + * <td>Time zone
138.205 + * <td><a href="#rfc822timezone">RFC 822 time zone</a>
138.206 + * <td><code>-0800</code>
138.207 + * <tr bgcolor="#eeeeff">
138.208 + * <td><code>X</code>
138.209 + * <td>Time zone
138.210 + * <td><a href="#iso8601timezone">ISO 8601 time zone</a>
138.211 + * <td><code>-08</code>; <code>-0800</code>; <code>-08:00</code>
138.212 + * </table>
138.213 + * </blockquote>
138.214 + * Pattern letters are usually repeated, as their number determines the
138.215 + * exact presentation:
138.216 + * <ul>
138.217 + * <li><strong><a name="text">Text:</a></strong>
138.218 + * For formatting, if the number of pattern letters is 4 or more,
138.219 + * the full form is used; otherwise a short or abbreviated form
138.220 + * is used if available.
138.221 + * For parsing, both forms are accepted, independent of the number
138.222 + * of pattern letters.<br><br></li>
138.223 + * <li><strong><a name="number">Number:</a></strong>
138.224 + * For formatting, the number of pattern letters is the minimum
138.225 + * number of digits, and shorter numbers are zero-padded to this amount.
138.226 + * For parsing, the number of pattern letters is ignored unless
138.227 + * it's needed to separate two adjacent fields.<br><br></li>
138.228 + * <li><strong><a name="year">Year:</a></strong>
138.229 + * If the formatter's {@link #getCalendar() Calendar} is the Gregorian
138.230 + * calendar, the following rules are applied.<br>
138.231 + * <ul>
138.232 + * <li>For formatting, if the number of pattern letters is 2, the year
138.233 + * is truncated to 2 digits; otherwise it is interpreted as a
138.234 + * <a href="#number">number</a>.
138.235 + * <li>For parsing, if the number of pattern letters is more than 2,
138.236 + * the year is interpreted literally, regardless of the number of
138.237 + * digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to
138.238 + * Jan 11, 12 A.D.
138.239 + * <li>For parsing with the abbreviated year pattern ("y" or "yy"),
138.240 + * <code>SimpleDateFormat</code> must interpret the abbreviated year
138.241 + * relative to some century. It does this by adjusting dates to be
138.242 + * within 80 years before and 20 years after the time the <code>SimpleDateFormat</code>
138.243 + * instance is created. For example, using a pattern of "MM/dd/yy" and a
138.244 + * <code>SimpleDateFormat</code> instance created on Jan 1, 1997, the string
138.245 + * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64"
138.246 + * would be interpreted as May 4, 1964.
138.247 + * During parsing, only strings consisting of exactly two digits, as defined by
138.248 + * {@link Character#isDigit(char)}, will be parsed into the default century.
138.249 + * Any other numeric string, such as a one digit string, a three or more digit
138.250 + * string, or a two digit string that isn't all digits (for example, "-1"), is
138.251 + * interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the
138.252 + * same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
138.253 + * </ul>
138.254 + * Otherwise, calendar system specific forms are applied.
138.255 + * For both formatting and parsing, if the number of pattern
138.256 + * letters is 4 or more, a calendar specific {@linkplain
138.257 + * Calendar#LONG long form} is used. Otherwise, a calendar
138.258 + * specific {@linkplain Calendar#SHORT short or abbreviated form}
138.259 + * is used.<br>
138.260 + * <br>
138.261 + * If week year {@code 'Y'} is specified and the {@linkplain
138.262 + * #getCalendar() calendar} doesn't support any <a
138.263 + * href="../util/GregorianCalendar.html#week_year"> week
138.264 + * years</a>, the calendar year ({@code 'y'}) is used instead. The
138.265 + * support of week years can be tested with a call to {@link
138.266 + * DateFormat#getCalendar() getCalendar()}.{@link
138.267 + * java.util.Calendar#isWeekDateSupported()
138.268 + * isWeekDateSupported()}.<br><br></li>
138.269 + * <li><strong><a name="month">Month:</a></strong>
138.270 + * If the number of pattern letters is 3 or more, the month is
138.271 + * interpreted as <a href="#text">text</a>; otherwise,
138.272 + * it is interpreted as a <a href="#number">number</a>.<br><br></li>
138.273 + * <li><strong><a name="timezone">General time zone:</a></strong>
138.274 + * Time zones are interpreted as <a href="#text">text</a> if they have
138.275 + * names. For time zones representing a GMT offset value, the
138.276 + * following syntax is used:
138.277 + * <pre>
138.278 + * <a name="GMTOffsetTimeZone"><i>GMTOffsetTimeZone:</i></a>
138.279 + * <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
138.280 + * <i>Sign:</i> one of
138.281 + * <code>+ -</code>
138.282 + * <i>Hours:</i>
138.283 + * <i>Digit</i>
138.284 + * <i>Digit</i> <i>Digit</i>
138.285 + * <i>Minutes:</i>
138.286 + * <i>Digit</i> <i>Digit</i>
138.287 + * <i>Digit:</i> one of
138.288 + * <code>0 1 2 3 4 5 6 7 8 9</code></pre>
138.289 + * <i>Hours</i> must be between 0 and 23, and <i>Minutes</i> must be between
138.290 + * 00 and 59. The format is locale independent and digits must be taken
138.291 + * from the Basic Latin block of the Unicode standard.
138.292 + * <p>For parsing, <a href="#rfc822timezone">RFC 822 time zones</a> are also
138.293 + * accepted.<br><br></li>
138.294 + * <li><strong><a name="rfc822timezone">RFC 822 time zone:</a></strong>
138.295 + * For formatting, the RFC 822 4-digit time zone format is used:
138.296 + *
138.297 + * <pre>
138.298 + * <i>RFC822TimeZone:</i>
138.299 + * <i>Sign</i> <i>TwoDigitHours</i> <i>Minutes</i>
138.300 + * <i>TwoDigitHours:</i>
138.301 + * <i>Digit Digit</i></pre>
138.302 + * <i>TwoDigitHours</i> must be between 00 and 23. Other definitions
138.303 + * are as for <a href="#timezone">general time zones</a>.
138.304 + *
138.305 + * <p>For parsing, <a href="#timezone">general time zones</a> are also
138.306 + * accepted.
138.307 + * <li><strong><a name="iso8601timezone">ISO 8601 Time zone:</a></strong>
138.308 + * The number of pattern letters designates the format for both formatting
138.309 + * and parsing as follows:
138.310 + * <pre>
138.311 + * <i>ISO8601TimeZone:</i>
138.312 + * <i>OneLetterISO8601TimeZone</i>
138.313 + * <i>TwoLetterISO8601TimeZone</i>
138.314 + * <i>ThreeLetterISO8601TimeZone</i>
138.315 + * <i>OneLetterISO8601TimeZone:</i>
138.316 + * <i>Sign</i> <i>TwoDigitHours</i>
138.317 + * {@code Z}
138.318 + * <i>TwoLetterISO8601TimeZone:</i>
138.319 + * <i>Sign</i> <i>TwoDigitHours</i> <i>Minutes</i>
138.320 + * {@code Z}
138.321 + * <i>ThreeLetterISO8601TimeZone:</i>
138.322 + * <i>Sign</i> <i>TwoDigitHours</i> {@code :} <i>Minutes</i>
138.323 + * {@code Z}</pre>
138.324 + * Other definitions are as for <a href="#timezone">general time zones</a> or
138.325 + * <a href="#rfc822timezone">RFC 822 time zones</a>.
138.326 + *
138.327 + * <p>For formatting, if the offset value from GMT is 0, {@code "Z"} is
138.328 + * produced. If the number of pattern letters is 1, any fraction of an hour
138.329 + * is ignored. For example, if the pattern is {@code "X"} and the time zone is
138.330 + * {@code "GMT+05:30"}, {@code "+05"} is produced.
138.331 + *
138.332 + * <p>For parsing, {@code "Z"} is parsed as the UTC time zone designator.
138.333 + * <a href="#timezone">General time zones</a> are <em>not</em> accepted.
138.334 + *
138.335 + * <p>If the number of pattern letters is 4 or more, {@link
138.336 + * IllegalArgumentException} is thrown when constructing a {@code
138.337 + * SimpleDateFormat} or {@linkplain #applyPattern(String) applying a
138.338 + * pattern}.
138.339 + * </ul>
138.340 + * <code>SimpleDateFormat</code> also supports <em>localized date and time
138.341 + * pattern</em> strings. In these strings, the pattern letters described above
138.342 + * may be replaced with other, locale dependent, pattern letters.
138.343 + * <code>SimpleDateFormat</code> does not deal with the localization of text
138.344 + * other than the pattern letters; that's up to the client of the class.
138.345 + * <p>
138.346 + *
138.347 + * <h4>Examples</h4>
138.348 + *
138.349 + * The following examples show how date and time patterns are interpreted in
138.350 + * the U.S. locale. The given date and time are 2001-07-04 12:08:56 local time
138.351 + * in the U.S. Pacific Time time zone.
138.352 + * <blockquote>
138.353 + * <table border=0 cellspacing=3 cellpadding=0 summary="Examples of date and time patterns interpreted in the U.S. locale">
138.354 + * <tr bgcolor="#ccccff">
138.355 + * <th align=left>Date and Time Pattern
138.356 + * <th align=left>Result
138.357 + * <tr>
138.358 + * <td><code>"yyyy.MM.dd G 'at' HH:mm:ss z"</code>
138.359 + * <td><code>2001.07.04 AD at 12:08:56 PDT</code>
138.360 + * <tr bgcolor="#eeeeff">
138.361 + * <td><code>"EEE, MMM d, ''yy"</code>
138.362 + * <td><code>Wed, Jul 4, '01</code>
138.363 + * <tr>
138.364 + * <td><code>"h:mm a"</code>
138.365 + * <td><code>12:08 PM</code>
138.366 + * <tr bgcolor="#eeeeff">
138.367 + * <td><code>"hh 'o''clock' a, zzzz"</code>
138.368 + * <td><code>12 o'clock PM, Pacific Daylight Time</code>
138.369 + * <tr>
138.370 + * <td><code>"K:mm a, z"</code>
138.371 + * <td><code>0:08 PM, PDT</code>
138.372 + * <tr bgcolor="#eeeeff">
138.373 + * <td><code>"yyyyy.MMMMM.dd GGG hh:mm aaa"</code>
138.374 + * <td><code>02001.July.04 AD 12:08 PM</code>
138.375 + * <tr>
138.376 + * <td><code>"EEE, d MMM yyyy HH:mm:ss Z"</code>
138.377 + * <td><code>Wed, 4 Jul 2001 12:08:56 -0700</code>
138.378 + * <tr bgcolor="#eeeeff">
138.379 + * <td><code>"yyMMddHHmmssZ"</code>
138.380 + * <td><code>010704120856-0700</code>
138.381 + * <tr>
138.382 + * <td><code>"yyyy-MM-dd'T'HH:mm:ss.SSSZ"</code>
138.383 + * <td><code>2001-07-04T12:08:56.235-0700</code>
138.384 + * <tr bgcolor="#eeeeff">
138.385 + * <td><code>"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"</code>
138.386 + * <td><code>2001-07-04T12:08:56.235-07:00</code>
138.387 + * <tr>
138.388 + * <td><code>"YYYY-'W'ww-u"</code>
138.389 + * <td><code>2001-W27-3</code>
138.390 + * </table>
138.391 + * </blockquote>
138.392 + *
138.393 + * <h4><a name="synchronization">Synchronization</a></h4>
138.394 + *
138.395 + * <p>
138.396 + * Date formats are not synchronized.
138.397 + * It is recommended to create separate format instances for each thread.
138.398 + * If multiple threads access a format concurrently, it must be synchronized
138.399 + * externally.
138.400 + *
138.401 + * @see <a href="http://java.sun.com/docs/books/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
138.402 + * @see java.util.Calendar
138.403 + * @see java.util.TimeZone
138.404 + * @see DateFormat
138.405 + * @see DateFormatSymbols
138.406 + * @author Mark Davis, Chen-Lieh Huang, Alan Liu
138.407 + */
138.408 +public class SimpleDateFormat extends DateFormat {
138.409 +
138.410 + // the official serial version ID which says cryptically
138.411 + // which version we're compatible with
138.412 + static final long serialVersionUID = 4774881970558875024L;
138.413 +
138.414 + // the internal serial version which says which version was written
138.415 + // - 0 (default) for version up to JDK 1.1.3
138.416 + // - 1 for version from JDK 1.1.4, which includes a new field
138.417 + static final int currentSerialVersion = 1;
138.418 +
138.419 + /**
138.420 + * The version of the serialized data on the stream. Possible values:
138.421 + * <ul>
138.422 + * <li><b>0</b> or not present on stream: JDK 1.1.3. This version
138.423 + * has no <code>defaultCenturyStart</code> on stream.
138.424 + * <li><b>1</b> JDK 1.1.4 or later. This version adds
138.425 + * <code>defaultCenturyStart</code>.
138.426 + * </ul>
138.427 + * When streaming out this class, the most recent format
138.428 + * and the highest allowable <code>serialVersionOnStream</code>
138.429 + * is written.
138.430 + * @serial
138.431 + * @since JDK1.1.4
138.432 + */
138.433 + private int serialVersionOnStream = currentSerialVersion;
138.434 +
138.435 + /**
138.436 + * The pattern string of this formatter. This is always a non-localized
138.437 + * pattern. May not be null. See class documentation for details.
138.438 + * @serial
138.439 + */
138.440 + private String pattern;
138.441 +
138.442 + /**
138.443 + * Saved numberFormat and pattern.
138.444 + * @see SimpleDateFormat#checkNegativeNumberExpression
138.445 + */
138.446 + transient private NumberFormat originalNumberFormat;
138.447 + transient private String originalNumberPattern;
138.448 +
138.449 + /**
138.450 + * The minus sign to be used with format and parse.
138.451 + */
138.452 + transient private char minusSign = '-';
138.453 +
138.454 + /**
138.455 + * True when a negative sign follows a number.
138.456 + * (True as default in Arabic.)
138.457 + */
138.458 + transient private boolean hasFollowingMinusSign = false;
138.459 +
138.460 + /**
138.461 + * The compiled pattern.
138.462 + */
138.463 + transient private char[] compiledPattern;
138.464 +
138.465 + /**
138.466 + * Tags for the compiled pattern.
138.467 + */
138.468 + private final static int TAG_QUOTE_ASCII_CHAR = 100;
138.469 + private final static int TAG_QUOTE_CHARS = 101;
138.470 +
138.471 + /**
138.472 + * Locale dependent digit zero.
138.473 + * @see #zeroPaddingNumber
138.474 + * @see java.text.DecimalFormatSymbols#getZeroDigit
138.475 + */
138.476 + transient private char zeroDigit;
138.477 +
138.478 + /**
138.479 + * The symbols used by this formatter for week names, month names,
138.480 + * etc. May not be null.
138.481 + * @serial
138.482 + * @see java.text.DateFormatSymbols
138.483 + */
138.484 + private DateFormatSymbols formatData;
138.485 +
138.486 + /**
138.487 + * We map dates with two-digit years into the century starting at
138.488 + * <code>defaultCenturyStart</code>, which may be any date. May
138.489 + * not be null.
138.490 + * @serial
138.491 + * @since JDK1.1.4
138.492 + */
138.493 + private Date defaultCenturyStart;
138.494 +
138.495 + transient private int defaultCenturyStartYear;
138.496 +
138.497 + private static final int MILLIS_PER_MINUTE = 60 * 1000;
138.498 +
138.499 + // For time zones that have no names, use strings GMT+minutes and
138.500 + // GMT-minutes. For instance, in France the time zone is GMT+60.
138.501 + private static final String GMT = "GMT";
138.502 +
138.503 + /**
138.504 + * Cache to hold the DateTimePatterns of a Locale.
138.505 + */
138.506 + private static final ConcurrentMap<Locale, String[]> cachedLocaleData
138.507 + = new ConcurrentHashMap<Locale, String[]>(3);
138.508 +
138.509 + /**
138.510 + * Cache NumberFormat instances with Locale key.
138.511 + */
138.512 + private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
138.513 + = new ConcurrentHashMap<Locale, NumberFormat>(3);
138.514 +
138.515 + /**
138.516 + * The Locale used to instantiate this
138.517 + * <code>SimpleDateFormat</code>. The value may be null if this object
138.518 + * has been created by an older <code>SimpleDateFormat</code> and
138.519 + * deserialized.
138.520 + *
138.521 + * @serial
138.522 + * @since 1.6
138.523 + */
138.524 + private Locale locale;
138.525 +
138.526 + /**
138.527 + * Indicates whether this <code>SimpleDateFormat</code> should use
138.528 + * the DateFormatSymbols. If true, the format and parse methods
138.529 + * use the DateFormatSymbols values. If false, the format and
138.530 + * parse methods call Calendar.getDisplayName or
138.531 + * Calendar.getDisplayNames.
138.532 + */
138.533 + transient boolean useDateFormatSymbols;
138.534 +
138.535 + /**
138.536 + * Constructs a <code>SimpleDateFormat</code> using the default pattern and
138.537 + * date format symbols for the default locale.
138.538 + * <b>Note:</b> This constructor may not support all locales.
138.539 + * For full coverage, use the factory methods in the {@link DateFormat}
138.540 + * class.
138.541 + */
138.542 + public SimpleDateFormat() {
138.543 + this(SHORT, SHORT, Locale.getDefault(Locale.Category.FORMAT));
138.544 + }
138.545 +
138.546 + /**
138.547 + * Constructs a <code>SimpleDateFormat</code> using the given pattern and
138.548 + * the default date format symbols for the default locale.
138.549 + * <b>Note:</b> This constructor may not support all locales.
138.550 + * For full coverage, use the factory methods in the {@link DateFormat}
138.551 + * class.
138.552 + *
138.553 + * @param pattern the pattern describing the date and time format
138.554 + * @exception NullPointerException if the given pattern is null
138.555 + * @exception IllegalArgumentException if the given pattern is invalid
138.556 + */
138.557 + public SimpleDateFormat(String pattern)
138.558 + {
138.559 + this(pattern, Locale.getDefault(Locale.Category.FORMAT));
138.560 + }
138.561 +
138.562 + /**
138.563 + * Constructs a <code>SimpleDateFormat</code> using the given pattern and
138.564 + * the default date format symbols for the given locale.
138.565 + * <b>Note:</b> This constructor may not support all locales.
138.566 + * For full coverage, use the factory methods in the {@link DateFormat}
138.567 + * class.
138.568 + *
138.569 + * @param pattern the pattern describing the date and time format
138.570 + * @param locale the locale whose date format symbols should be used
138.571 + * @exception NullPointerException if the given pattern or locale is null
138.572 + * @exception IllegalArgumentException if the given pattern is invalid
138.573 + */
138.574 + public SimpleDateFormat(String pattern, Locale locale)
138.575 + {
138.576 + if (pattern == null || locale == null) {
138.577 + throw new NullPointerException();
138.578 + }
138.579 +
138.580 + initializeCalendar(locale);
138.581 + this.pattern = pattern;
138.582 + this.formatData = DateFormatSymbols.getInstanceRef(locale);
138.583 + this.locale = locale;
138.584 + initialize(locale);
138.585 + }
138.586 +
138.587 + /**
138.588 + * Constructs a <code>SimpleDateFormat</code> using the given pattern and
138.589 + * date format symbols.
138.590 + *
138.591 + * @param pattern the pattern describing the date and time format
138.592 + * @param formatSymbols the date format symbols to be used for formatting
138.593 + * @exception NullPointerException if the given pattern or formatSymbols is null
138.594 + * @exception IllegalArgumentException if the given pattern is invalid
138.595 + */
138.596 + public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
138.597 + {
138.598 + if (pattern == null || formatSymbols == null) {
138.599 + throw new NullPointerException();
138.600 + }
138.601 +
138.602 + this.pattern = pattern;
138.603 + this.formatData = (DateFormatSymbols) formatSymbols.clone();
138.604 + this.locale = Locale.getDefault(Locale.Category.FORMAT);
138.605 + initializeCalendar(this.locale);
138.606 + initialize(this.locale);
138.607 + useDateFormatSymbols = true;
138.608 + }
138.609 +
138.610 + /* Package-private, called by DateFormat factory methods */
138.611 + SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) {
138.612 + if (loc == null) {
138.613 + throw new NullPointerException();
138.614 + }
138.615 +
138.616 + this.locale = loc;
138.617 + // initialize calendar and related fields
138.618 + initializeCalendar(loc);
138.619 +
138.620 + /* try the cache first */
138.621 + String[] dateTimePatterns = cachedLocaleData.get(loc);
138.622 + if (dateTimePatterns == null) { /* cache miss */
138.623 + ResourceBundle r = null; // LocaleData.getDateFormatData(loc);
138.624 + if (!isGregorianCalendar()) {
138.625 + try {
138.626 + dateTimePatterns = r.getStringArray(getCalendarName() + ".DateTimePatterns");
138.627 + } catch (MissingResourceException e) {
138.628 + }
138.629 + }
138.630 + if (dateTimePatterns == null) {
138.631 + dateTimePatterns = r.getStringArray("DateTimePatterns");
138.632 + }
138.633 + /* update cache */
138.634 + cachedLocaleData.putIfAbsent(loc, dateTimePatterns);
138.635 + }
138.636 + formatData = DateFormatSymbols.getInstanceRef(loc);
138.637 + if ((timeStyle >= 0) && (dateStyle >= 0)) {
138.638 + Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
138.639 + dateTimePatterns[dateStyle + 4]};
138.640 + pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
138.641 + }
138.642 + else if (timeStyle >= 0) {
138.643 + pattern = dateTimePatterns[timeStyle];
138.644 + }
138.645 + else if (dateStyle >= 0) {
138.646 + pattern = dateTimePatterns[dateStyle + 4];
138.647 + }
138.648 + else {
138.649 + throw new IllegalArgumentException("No date or time style specified");
138.650 + }
138.651 +
138.652 + initialize(loc);
138.653 + }
138.654 +
138.655 + /* Initialize compiledPattern and numberFormat fields */
138.656 + private void initialize(Locale loc) {
138.657 + // Verify and compile the given pattern.
138.658 + compiledPattern = compile(pattern);
138.659 +
138.660 + /* try the cache first */
138.661 + numberFormat = cachedNumberFormatData.get(loc);
138.662 + if (numberFormat == null) { /* cache miss */
138.663 + numberFormat = NumberFormat.getIntegerInstance(loc);
138.664 + numberFormat.setGroupingUsed(false);
138.665 +
138.666 + /* update cache */
138.667 + cachedNumberFormatData.putIfAbsent(loc, numberFormat);
138.668 + }
138.669 + numberFormat = (NumberFormat) numberFormat.clone();
138.670 +
138.671 + initializeDefaultCentury();
138.672 + }
138.673 +
138.674 + private void initializeCalendar(Locale loc) {
138.675 + if (calendar == null) {
138.676 + assert loc != null;
138.677 + // The format object must be constructed using the symbols for this zone.
138.678 + // However, the calendar should use the current default TimeZone.
138.679 + // If this is not contained in the locale zone strings, then the zone
138.680 + // will be formatted using generic GMT+/-H:MM nomenclature.
138.681 + calendar = Calendar.getInstance(TimeZone.getDefault(), loc);
138.682 + }
138.683 + }
138.684 +
138.685 + /**
138.686 + * Returns the compiled form of the given pattern. The syntax of
138.687 + * the compiled pattern is:
138.688 + * <blockquote>
138.689 + * CompiledPattern:
138.690 + * EntryList
138.691 + * EntryList:
138.692 + * Entry
138.693 + * EntryList Entry
138.694 + * Entry:
138.695 + * TagField
138.696 + * TagField data
138.697 + * TagField:
138.698 + * Tag Length
138.699 + * TaggedData
138.700 + * Tag:
138.701 + * pattern_char_index
138.702 + * TAG_QUOTE_CHARS
138.703 + * Length:
138.704 + * short_length
138.705 + * long_length
138.706 + * TaggedData:
138.707 + * TAG_QUOTE_ASCII_CHAR ascii_char
138.708 + *
138.709 + * </blockquote>
138.710 + *
138.711 + * where `short_length' is an 8-bit unsigned integer between 0 and
138.712 + * 254. `long_length' is a sequence of an 8-bit integer 255 and a
138.713 + * 32-bit signed integer value which is split into upper and lower
138.714 + * 16-bit fields in two char's. `pattern_char_index' is an 8-bit
138.715 + * integer between 0 and 18. `ascii_char' is an 7-bit ASCII
138.716 + * character value. `data' depends on its Tag value.
138.717 + * <p>
138.718 + * If Length is short_length, Tag and short_length are packed in a
138.719 + * single char, as illustrated below.
138.720 + * <blockquote>
138.721 + * char[0] = (Tag << 8) | short_length;
138.722 + * </blockquote>
138.723 + *
138.724 + * If Length is long_length, Tag and 255 are packed in the first
138.725 + * char and a 32-bit integer, as illustrated below.
138.726 + * <blockquote>
138.727 + * char[0] = (Tag << 8) | 255;
138.728 + * char[1] = (char) (long_length >>> 16);
138.729 + * char[2] = (char) (long_length & 0xffff);
138.730 + * </blockquote>
138.731 + * <p>
138.732 + * If Tag is a pattern_char_index, its Length is the number of
138.733 + * pattern characters. For example, if the given pattern is
138.734 + * "yyyy", Tag is 1 and Length is 4, followed by no data.
138.735 + * <p>
138.736 + * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's
138.737 + * following the TagField. For example, if the given pattern is
138.738 + * "'o''clock'", Length is 7 followed by a char sequence of
138.739 + * <code>o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k</code>.
138.740 + * <p>
138.741 + * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII
138.742 + * character in place of Length. For example, if the given pattern
138.743 + * is "'o'", the TaggedData entry is
138.744 + * <code>((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')</code>.
138.745 + *
138.746 + * @exception NullPointerException if the given pattern is null
138.747 + * @exception IllegalArgumentException if the given pattern is invalid
138.748 + */
138.749 + private char[] compile(String pattern) {
138.750 + int length = pattern.length();
138.751 + boolean inQuote = false;
138.752 + StringBuilder compiledPattern = new StringBuilder(length * 2);
138.753 + StringBuilder tmpBuffer = null;
138.754 + int count = 0;
138.755 + int lastTag = -1;
138.756 +
138.757 + for (int i = 0; i < length; i++) {
138.758 + char c = pattern.charAt(i);
138.759 +
138.760 + if (c == '\'') {
138.761 + // '' is treated as a single quote regardless of being
138.762 + // in a quoted section.
138.763 + if ((i + 1) < length) {
138.764 + c = pattern.charAt(i + 1);
138.765 + if (c == '\'') {
138.766 + i++;
138.767 + if (count != 0) {
138.768 + encode(lastTag, count, compiledPattern);
138.769 + lastTag = -1;
138.770 + count = 0;
138.771 + }
138.772 + if (inQuote) {
138.773 + tmpBuffer.append(c);
138.774 + } else {
138.775 + compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
138.776 + }
138.777 + continue;
138.778 + }
138.779 + }
138.780 + if (!inQuote) {
138.781 + if (count != 0) {
138.782 + encode(lastTag, count, compiledPattern);
138.783 + lastTag = -1;
138.784 + count = 0;
138.785 + }
138.786 + if (tmpBuffer == null) {
138.787 + tmpBuffer = new StringBuilder(length);
138.788 + } else {
138.789 + tmpBuffer.setLength(0);
138.790 + }
138.791 + inQuote = true;
138.792 + } else {
138.793 + int len = tmpBuffer.length();
138.794 + if (len == 1) {
138.795 + char ch = tmpBuffer.charAt(0);
138.796 + if (ch < 128) {
138.797 + compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
138.798 + } else {
138.799 + compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1));
138.800 + compiledPattern.append(ch);
138.801 + }
138.802 + } else {
138.803 + encode(TAG_QUOTE_CHARS, len, compiledPattern);
138.804 + compiledPattern.append(tmpBuffer);
138.805 + }
138.806 + inQuote = false;
138.807 + }
138.808 + continue;
138.809 + }
138.810 + if (inQuote) {
138.811 + tmpBuffer.append(c);
138.812 + continue;
138.813 + }
138.814 + if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
138.815 + if (count != 0) {
138.816 + encode(lastTag, count, compiledPattern);
138.817 + lastTag = -1;
138.818 + count = 0;
138.819 + }
138.820 + if (c < 128) {
138.821 + // In most cases, c would be a delimiter, such as ':'.
138.822 + compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
138.823 + } else {
138.824 + // Take any contiguous non-ASCII alphabet characters and
138.825 + // put them in a single TAG_QUOTE_CHARS.
138.826 + int j;
138.827 + for (j = i + 1; j < length; j++) {
138.828 + char d = pattern.charAt(j);
138.829 + if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) {
138.830 + break;
138.831 + }
138.832 + }
138.833 + compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
138.834 + for (; i < j; i++) {
138.835 + compiledPattern.append(pattern.charAt(i));
138.836 + }
138.837 + i--;
138.838 + }
138.839 + continue;
138.840 + }
138.841 +
138.842 + int tag;
138.843 + if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) {
138.844 + throw new IllegalArgumentException("Illegal pattern character " +
138.845 + "'" + c + "'");
138.846 + }
138.847 + if (lastTag == -1 || lastTag == tag) {
138.848 + lastTag = tag;
138.849 + count++;
138.850 + continue;
138.851 + }
138.852 + encode(lastTag, count, compiledPattern);
138.853 + lastTag = tag;
138.854 + count = 1;
138.855 + }
138.856 +
138.857 + if (inQuote) {
138.858 + throw new IllegalArgumentException("Unterminated quote");
138.859 + }
138.860 +
138.861 + if (count != 0) {
138.862 + encode(lastTag, count, compiledPattern);
138.863 + }
138.864 +
138.865 + // Copy the compiled pattern to a char array
138.866 + int len = compiledPattern.length();
138.867 + char[] r = new char[len];
138.868 + compiledPattern.getChars(0, len, r, 0);
138.869 + return r;
138.870 + }
138.871 +
138.872 + /**
138.873 + * Encodes the given tag and length and puts encoded char(s) into buffer.
138.874 + */
138.875 + private static final void encode(int tag, int length, StringBuilder buffer) {
138.876 + if (tag == PATTERN_ISO_ZONE && length >= 4) {
138.877 + throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
138.878 + }
138.879 + if (length < 255) {
138.880 + buffer.append((char)(tag << 8 | length));
138.881 + } else {
138.882 + buffer.append((char)((tag << 8) | 0xff));
138.883 + buffer.append((char)(length >>> 16));
138.884 + buffer.append((char)(length & 0xffff));
138.885 + }
138.886 + }
138.887 +
138.888 + /* Initialize the fields we use to disambiguate ambiguous years. Separate
138.889 + * so we can call it from readObject().
138.890 + */
138.891 + private void initializeDefaultCentury() {
138.892 + calendar.setTimeInMillis(System.currentTimeMillis());
138.893 + calendar.add( Calendar.YEAR, -80 );
138.894 + parseAmbiguousDatesAsAfter(calendar.getTime());
138.895 + }
138.896 +
138.897 + /* Define one-century window into which to disambiguate dates using
138.898 + * two-digit years.
138.899 + */
138.900 + private void parseAmbiguousDatesAsAfter(Date startDate) {
138.901 + defaultCenturyStart = startDate;
138.902 + calendar.setTime(startDate);
138.903 + defaultCenturyStartYear = calendar.get(Calendar.YEAR);
138.904 + }
138.905 +
138.906 + /**
138.907 + * Sets the 100-year period 2-digit years will be interpreted as being in
138.908 + * to begin on the date the user specifies.
138.909 + *
138.910 + * @param startDate During parsing, two digit years will be placed in the range
138.911 + * <code>startDate</code> to <code>startDate + 100 years</code>.
138.912 + * @see #get2DigitYearStart
138.913 + * @since 1.2
138.914 + */
138.915 + public void set2DigitYearStart(Date startDate) {
138.916 + parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
138.917 + }
138.918 +
138.919 + /**
138.920 + * Returns the beginning date of the 100-year period 2-digit years are interpreted
138.921 + * as being within.
138.922 + *
138.923 + * @return the start of the 100-year period into which two digit years are
138.924 + * parsed
138.925 + * @see #set2DigitYearStart
138.926 + * @since 1.2
138.927 + */
138.928 + public Date get2DigitYearStart() {
138.929 + return (Date) defaultCenturyStart.clone();
138.930 + }
138.931 +
138.932 + /**
138.933 + * Formats the given <code>Date</code> into a date/time string and appends
138.934 + * the result to the given <code>StringBuffer</code>.
138.935 + *
138.936 + * @param date the date-time value to be formatted into a date-time string.
138.937 + * @param toAppendTo where the new date-time text is to be appended.
138.938 + * @param pos the formatting position. On input: an alignment field,
138.939 + * if desired. On output: the offsets of the alignment field.
138.940 + * @return the formatted date-time string.
138.941 + * @exception NullPointerException if the given {@code date} is {@code null}.
138.942 + */
138.943 + public StringBuffer format(Date date, StringBuffer toAppendTo,
138.944 + FieldPosition pos)
138.945 + {
138.946 + pos.beginIndex = pos.endIndex = 0;
138.947 + return format(date, toAppendTo, pos.getFieldDelegate());
138.948 + }
138.949 +
138.950 + // Called from Format after creating a FieldDelegate
138.951 + private StringBuffer format(Date date, StringBuffer toAppendTo,
138.952 + FieldDelegate delegate) {
138.953 + // Convert input date to time field list
138.954 + calendar.setTime(date);
138.955 +
138.956 + boolean useDateFormatSymbols = useDateFormatSymbols();
138.957 +
138.958 + for (int i = 0; i < compiledPattern.length; ) {
138.959 + int tag = compiledPattern[i] >>> 8;
138.960 + int count = compiledPattern[i++] & 0xff;
138.961 + if (count == 255) {
138.962 + count = compiledPattern[i++] << 16;
138.963 + count |= compiledPattern[i++];
138.964 + }
138.965 +
138.966 + switch (tag) {
138.967 + case TAG_QUOTE_ASCII_CHAR:
138.968 + toAppendTo.append((char)count);
138.969 + break;
138.970 +
138.971 + case TAG_QUOTE_CHARS:
138.972 + toAppendTo.append(compiledPattern, i, count);
138.973 + i += count;
138.974 + break;
138.975 +
138.976 + default:
138.977 + subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
138.978 + break;
138.979 + }
138.980 + }
138.981 + return toAppendTo;
138.982 + }
138.983 +
138.984 + /**
138.985 + * Formats an Object producing an <code>AttributedCharacterIterator</code>.
138.986 + * You can use the returned <code>AttributedCharacterIterator</code>
138.987 + * to build the resulting String, as well as to determine information
138.988 + * about the resulting String.
138.989 + * <p>
138.990 + * Each attribute key of the AttributedCharacterIterator will be of type
138.991 + * <code>DateFormat.Field</code>, with the corresponding attribute value
138.992 + * being the same as the attribute key.
138.993 + *
138.994 + * @exception NullPointerException if obj is null.
138.995 + * @exception IllegalArgumentException if the Format cannot format the
138.996 + * given object, or if the Format's pattern string is invalid.
138.997 + * @param obj The object to format
138.998 + * @return AttributedCharacterIterator describing the formatted value.
138.999 + * @since 1.4
138.1000 + */
138.1001 + public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
138.1002 + StringBuffer sb = new StringBuffer();
138.1003 + CharacterIteratorFieldDelegate delegate = new
138.1004 + CharacterIteratorFieldDelegate();
138.1005 +
138.1006 + if (obj instanceof Date) {
138.1007 + format((Date)obj, sb, delegate);
138.1008 + }
138.1009 + else if (obj instanceof Number) {
138.1010 + format(new Date(((Number)obj).longValue()), sb, delegate);
138.1011 + }
138.1012 + else if (obj == null) {
138.1013 + throw new NullPointerException(
138.1014 + "formatToCharacterIterator must be passed non-null object");
138.1015 + }
138.1016 + else {
138.1017 + throw new IllegalArgumentException(
138.1018 + "Cannot format given Object as a Date");
138.1019 + }
138.1020 + return delegate.getIterator(sb.toString());
138.1021 + }
138.1022 +
138.1023 + // Map index into pattern character string to Calendar field number
138.1024 + private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD =
138.1025 + {
138.1026 + Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE,
138.1027 + Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE,
138.1028 + Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK,
138.1029 + Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
138.1030 + Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
138.1031 + Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
138.1032 + Calendar.ZONE_OFFSET,
138.1033 + // Pseudo Calendar fields
138.1034 + CalendarBuilder.WEEK_YEAR,
138.1035 + CalendarBuilder.ISO_DAY_OF_WEEK,
138.1036 + Calendar.ZONE_OFFSET
138.1037 + };
138.1038 +
138.1039 + // Map index into pattern character string to DateFormat field number
138.1040 + private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
138.1041 + DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD,
138.1042 + DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD,
138.1043 + DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD,
138.1044 + DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD,
138.1045 + DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD,
138.1046 + DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD,
138.1047 + DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
138.1048 + DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD,
138.1049 + DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD,
138.1050 + DateFormat.YEAR_FIELD, DateFormat.DAY_OF_WEEK_FIELD,
138.1051 + DateFormat.TIMEZONE_FIELD
138.1052 + };
138.1053 +
138.1054 + // Maps from DecimalFormatSymbols index to Field constant
138.1055 + private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
138.1056 + Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH,
138.1057 + Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE,
138.1058 + Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK,
138.1059 + Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH,
138.1060 + Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH,
138.1061 + Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE,
138.1062 + Field.TIME_ZONE,
138.1063 + Field.YEAR, Field.DAY_OF_WEEK,
138.1064 + Field.TIME_ZONE
138.1065 + };
138.1066 +
138.1067 + /**
138.1068 + * Private member function that does the real date/time formatting.
138.1069 + */
138.1070 + private void subFormat(int patternCharIndex, int count,
138.1071 + FieldDelegate delegate, StringBuffer buffer,
138.1072 + boolean useDateFormatSymbols)
138.1073 + {
138.1074 + int maxIntCount = Integer.MAX_VALUE;
138.1075 + String current = null;
138.1076 + int beginOffset = buffer.length();
138.1077 +
138.1078 + int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
138.1079 + int value;
138.1080 + if (field == CalendarBuilder.WEEK_YEAR) {
138.1081 + if (calendar.isWeekDateSupported()) {
138.1082 + value = calendar.getWeekYear();
138.1083 + } else {
138.1084 + // use calendar year 'y' instead
138.1085 + patternCharIndex = PATTERN_YEAR;
138.1086 + field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
138.1087 + value = calendar.get(field);
138.1088 + }
138.1089 + } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) {
138.1090 + value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
138.1091 + } else {
138.1092 + value = calendar.get(field);
138.1093 + }
138.1094 +
138.1095 + int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
138.1096 + if (!useDateFormatSymbols && field != CalendarBuilder.ISO_DAY_OF_WEEK) {
138.1097 + current = calendar.getDisplayName(field, style, locale);
138.1098 + }
138.1099 +
138.1100 + // Note: zeroPaddingNumber() assumes that maxDigits is either
138.1101 + // 2 or maxIntCount. If we make any changes to this,
138.1102 + // zeroPaddingNumber() must be fixed.
138.1103 +
138.1104 + switch (patternCharIndex) {
138.1105 + case PATTERN_ERA: // 'G'
138.1106 + if (useDateFormatSymbols) {
138.1107 + String[] eras = formatData.getEras();
138.1108 + if (value < eras.length)
138.1109 + current = eras[value];
138.1110 + }
138.1111 + if (current == null)
138.1112 + current = "";
138.1113 + break;
138.1114 +
138.1115 + case PATTERN_WEEK_YEAR: // 'Y'
138.1116 + case PATTERN_YEAR: // 'y'
138.1117 + if (calendar instanceof GregorianCalendar) {
138.1118 + if (count != 2)
138.1119 + zeroPaddingNumber(value, count, maxIntCount, buffer);
138.1120 + else // count == 2
138.1121 + zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96
138.1122 + } else {
138.1123 + if (current == null) {
138.1124 + zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
138.1125 + maxIntCount, buffer);
138.1126 + }
138.1127 + }
138.1128 + break;
138.1129 +
138.1130 + case PATTERN_MONTH: // 'M'
138.1131 + if (useDateFormatSymbols) {
138.1132 + String[] months;
138.1133 + if (count >= 4) {
138.1134 + months = formatData.getMonths();
138.1135 + current = months[value];
138.1136 + } else if (count == 3) {
138.1137 + months = formatData.getShortMonths();
138.1138 + current = months[value];
138.1139 + }
138.1140 + } else {
138.1141 + if (count < 3) {
138.1142 + current = null;
138.1143 + }
138.1144 + }
138.1145 + if (current == null) {
138.1146 + zeroPaddingNumber(value+1, count, maxIntCount, buffer);
138.1147 + }
138.1148 + break;
138.1149 +
138.1150 + case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59
138.1151 + if (current == null) {
138.1152 + if (value == 0)
138.1153 + zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1,
138.1154 + count, maxIntCount, buffer);
138.1155 + else
138.1156 + zeroPaddingNumber(value, count, maxIntCount, buffer);
138.1157 + }
138.1158 + break;
138.1159 +
138.1160 + case PATTERN_DAY_OF_WEEK: // 'E'
138.1161 + if (useDateFormatSymbols) {
138.1162 + String[] weekdays;
138.1163 + if (count >= 4) {
138.1164 + weekdays = formatData.getWeekdays();
138.1165 + current = weekdays[value];
138.1166 + } else { // count < 4, use abbreviated form if exists
138.1167 + weekdays = formatData.getShortWeekdays();
138.1168 + current = weekdays[value];
138.1169 + }
138.1170 + }
138.1171 + break;
138.1172 +
138.1173 + case PATTERN_AM_PM: // 'a'
138.1174 + if (useDateFormatSymbols) {
138.1175 + String[] ampm = formatData.getAmPmStrings();
138.1176 + current = ampm[value];
138.1177 + }
138.1178 + break;
138.1179 +
138.1180 + case PATTERN_HOUR1: // 'h' 1-based. eg, 11PM + 1 hour =>> 12 AM
138.1181 + if (current == null) {
138.1182 + if (value == 0)
138.1183 + zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1,
138.1184 + count, maxIntCount, buffer);
138.1185 + else
138.1186 + zeroPaddingNumber(value, count, maxIntCount, buffer);
138.1187 + }
138.1188 + break;
138.1189 +
138.1190 + case PATTERN_ZONE_NAME: // 'z'
138.1191 + if (current == null) {
138.1192 + if (formatData.locale == null || formatData.isZoneStringsSet) {
138.1193 + int zoneIndex =
138.1194 + formatData.getZoneIndex(calendar.getTimeZone().getID());
138.1195 + if (zoneIndex == -1) {
138.1196 + value = calendar.get(Calendar.ZONE_OFFSET) +
138.1197 + calendar.get(Calendar.DST_OFFSET);
138.1198 +// buffer.append(ZoneInfoFile.toCustomID(value));
138.1199 + } else {
138.1200 + int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3;
138.1201 + if (count < 4) {
138.1202 + // Use the short name
138.1203 + index++;
138.1204 + }
138.1205 + String[][] zoneStrings = formatData.getZoneStringsWrapper();
138.1206 + buffer.append(zoneStrings[zoneIndex][index]);
138.1207 + }
138.1208 + } else {
138.1209 + TimeZone tz = calendar.getTimeZone();
138.1210 + boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
138.1211 + int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG);
138.1212 + buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale));
138.1213 + }
138.1214 + }
138.1215 + break;
138.1216 +
138.1217 + case PATTERN_ZONE_VALUE: // 'Z' ("-/+hhmm" form)
138.1218 + value = (calendar.get(Calendar.ZONE_OFFSET) +
138.1219 + calendar.get(Calendar.DST_OFFSET)) / 60000;
138.1220 +
138.1221 + int width = 4;
138.1222 + if (value >= 0) {
138.1223 + buffer.append('+');
138.1224 + } else {
138.1225 + width++;
138.1226 + }
138.1227 +
138.1228 + int num = (value / 60) * 100 + (value % 60);
138.1229 +// CalendarUtils.sprintf0d(buffer, num, width);
138.1230 + break;
138.1231 +
138.1232 + case PATTERN_ISO_ZONE: // 'X'
138.1233 + value = calendar.get(Calendar.ZONE_OFFSET)
138.1234 + + calendar.get(Calendar.DST_OFFSET);
138.1235 +
138.1236 + if (value == 0) {
138.1237 + buffer.append('Z');
138.1238 + break;
138.1239 + }
138.1240 +
138.1241 + value /= 60000;
138.1242 + if (value >= 0) {
138.1243 + buffer.append('+');
138.1244 + } else {
138.1245 + buffer.append('-');
138.1246 + value = -value;
138.1247 + }
138.1248 +
138.1249 +// CalendarUtils.sprintf0d(buffer, value / 60, 2);
138.1250 + if (count == 1) {
138.1251 + break;
138.1252 + }
138.1253 +
138.1254 + if (count == 3) {
138.1255 + buffer.append(':');
138.1256 + }
138.1257 +// CalendarUtils.sprintf0d(buffer, value % 60, 2);
138.1258 + break;
138.1259 +
138.1260 + default:
138.1261 + // case PATTERN_DAY_OF_MONTH: // 'd'
138.1262 + // case PATTERN_HOUR_OF_DAY0: // 'H' 0-based. eg, 23:59 + 1 hour =>> 00:59
138.1263 + // case PATTERN_MINUTE: // 'm'
138.1264 + // case PATTERN_SECOND: // 's'
138.1265 + // case PATTERN_MILLISECOND: // 'S'
138.1266 + // case PATTERN_DAY_OF_YEAR: // 'D'
138.1267 + // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F'
138.1268 + // case PATTERN_WEEK_OF_YEAR: // 'w'
138.1269 + // case PATTERN_WEEK_OF_MONTH: // 'W'
138.1270 + // case PATTERN_HOUR0: // 'K' eg, 11PM + 1 hour =>> 0 AM
138.1271 + // case PATTERN_ISO_DAY_OF_WEEK: // 'u' pseudo field, Monday = 1, ..., Sunday = 7
138.1272 + if (current == null) {
138.1273 + zeroPaddingNumber(value, count, maxIntCount, buffer);
138.1274 + }
138.1275 + break;
138.1276 + } // switch (patternCharIndex)
138.1277 +
138.1278 + if (current != null) {
138.1279 + buffer.append(current);
138.1280 + }
138.1281 +
138.1282 + int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex];
138.1283 + Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex];
138.1284 +
138.1285 + delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer);
138.1286 + }
138.1287 +
138.1288 + /**
138.1289 + * Formats a number with the specified minimum and maximum number of digits.
138.1290 + */
138.1291 + private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
138.1292 + {
138.1293 + // Optimization for 1, 2 and 4 digit numbers. This should
138.1294 + // cover most cases of formatting date/time related items.
138.1295 + // Note: This optimization code assumes that maxDigits is
138.1296 + // either 2 or Integer.MAX_VALUE (maxIntCount in format()).
138.1297 + try {
138.1298 + if (zeroDigit == 0) {
138.1299 + zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
138.1300 + }
138.1301 + if (value >= 0) {
138.1302 + if (value < 100 && minDigits >= 1 && minDigits <= 2) {
138.1303 + if (value < 10) {
138.1304 + if (minDigits == 2) {
138.1305 + buffer.append(zeroDigit);
138.1306 + }
138.1307 + buffer.append((char)(zeroDigit + value));
138.1308 + } else {
138.1309 + buffer.append((char)(zeroDigit + value / 10));
138.1310 + buffer.append((char)(zeroDigit + value % 10));
138.1311 + }
138.1312 + return;
138.1313 + } else if (value >= 1000 && value < 10000) {
138.1314 + if (minDigits == 4) {
138.1315 + buffer.append((char)(zeroDigit + value / 1000));
138.1316 + value %= 1000;
138.1317 + buffer.append((char)(zeroDigit + value / 100));
138.1318 + value %= 100;
138.1319 + buffer.append((char)(zeroDigit + value / 10));
138.1320 + buffer.append((char)(zeroDigit + value % 10));
138.1321 + return;
138.1322 + }
138.1323 + if (minDigits == 2 && maxDigits == 2) {
138.1324 + zeroPaddingNumber(value % 100, 2, 2, buffer);
138.1325 + return;
138.1326 + }
138.1327 + }
138.1328 + }
138.1329 + } catch (Exception e) {
138.1330 + }
138.1331 +
138.1332 + numberFormat.setMinimumIntegerDigits(minDigits);
138.1333 + numberFormat.setMaximumIntegerDigits(maxDigits);
138.1334 + numberFormat.format((long)value, buffer, DontCareFieldPosition.INSTANCE);
138.1335 + }
138.1336 +
138.1337 +
138.1338 + /**
138.1339 + * Parses text from a string to produce a <code>Date</code>.
138.1340 + * <p>
138.1341 + * The method attempts to parse text starting at the index given by
138.1342 + * <code>pos</code>.
138.1343 + * If parsing succeeds, then the index of <code>pos</code> is updated
138.1344 + * to the index after the last character used (parsing does not necessarily
138.1345 + * use all characters up to the end of the string), and the parsed
138.1346 + * date is returned. The updated <code>pos</code> can be used to
138.1347 + * indicate the starting point for the next call to this method.
138.1348 + * If an error occurs, then the index of <code>pos</code> is not
138.1349 + * changed, the error index of <code>pos</code> is set to the index of
138.1350 + * the character where the error occurred, and null is returned.
138.1351 + *
138.1352 + * <p>This parsing operation uses the {@link DateFormat#calendar
138.1353 + * calendar} to produce a {@code Date}. All of the {@code
138.1354 + * calendar}'s date-time fields are {@linkplain Calendar#clear()
138.1355 + * cleared} before parsing, and the {@code calendar}'s default
138.1356 + * values of the date-time fields are used for any missing
138.1357 + * date-time information. For example, the year value of the
138.1358 + * parsed {@code Date} is 1970 with {@link GregorianCalendar} if
138.1359 + * no year value is given from the parsing operation. The {@code
138.1360 + * TimeZone} value may be overwritten, depending on the given
138.1361 + * pattern and the time zone value in {@code text}. Any {@code
138.1362 + * TimeZone} value that has previously been set by a call to
138.1363 + * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
138.1364 + * to be restored for further operations.
138.1365 + *
138.1366 + * @param text A <code>String</code>, part of which should be parsed.
138.1367 + * @param pos A <code>ParsePosition</code> object with index and error
138.1368 + * index information as described above.
138.1369 + * @return A <code>Date</code> parsed from the string. In case of
138.1370 + * error, returns null.
138.1371 + * @exception NullPointerException if <code>text</code> or <code>pos</code> is null.
138.1372 + */
138.1373 + public Date parse(String text, ParsePosition pos)
138.1374 + {
138.1375 + checkNegativeNumberExpression();
138.1376 +
138.1377 + int start = pos.index;
138.1378 + int oldStart = start;
138.1379 + int textLength = text.length();
138.1380 +
138.1381 + boolean[] ambiguousYear = {false};
138.1382 +
138.1383 + CalendarBuilder calb = new CalendarBuilder();
138.1384 +
138.1385 + for (int i = 0; i < compiledPattern.length; ) {
138.1386 + int tag = compiledPattern[i] >>> 8;
138.1387 + int count = compiledPattern[i++] & 0xff;
138.1388 + if (count == 255) {
138.1389 + count = compiledPattern[i++] << 16;
138.1390 + count |= compiledPattern[i++];
138.1391 + }
138.1392 +
138.1393 + switch (tag) {
138.1394 + case TAG_QUOTE_ASCII_CHAR:
138.1395 + if (start >= textLength || text.charAt(start) != (char)count) {
138.1396 + pos.index = oldStart;
138.1397 + pos.errorIndex = start;
138.1398 + return null;
138.1399 + }
138.1400 + start++;
138.1401 + break;
138.1402 +
138.1403 + case TAG_QUOTE_CHARS:
138.1404 + while (count-- > 0) {
138.1405 + if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {
138.1406 + pos.index = oldStart;
138.1407 + pos.errorIndex = start;
138.1408 + return null;
138.1409 + }
138.1410 + start++;
138.1411 + }
138.1412 + break;
138.1413 +
138.1414 + default:
138.1415 + // Peek the next pattern to determine if we need to
138.1416 + // obey the number of pattern letters for
138.1417 + // parsing. It's required when parsing contiguous
138.1418 + // digit text (e.g., "20010704") with a pattern which
138.1419 + // has no delimiters between fields, like "yyyyMMdd".
138.1420 + boolean obeyCount = false;
138.1421 +
138.1422 + // In Arabic, a minus sign for a negative number is put after
138.1423 + // the number. Even in another locale, a minus sign can be
138.1424 + // put after a number using DateFormat.setNumberFormat().
138.1425 + // If both the minus sign and the field-delimiter are '-',
138.1426 + // subParse() needs to determine whether a '-' after a number
138.1427 + // in the given text is a delimiter or is a minus sign for the
138.1428 + // preceding number. We give subParse() a clue based on the
138.1429 + // information in compiledPattern.
138.1430 + boolean useFollowingMinusSignAsDelimiter = false;
138.1431 +
138.1432 + if (i < compiledPattern.length) {
138.1433 + int nextTag = compiledPattern[i] >>> 8;
138.1434 + if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||
138.1435 + nextTag == TAG_QUOTE_CHARS)) {
138.1436 + obeyCount = true;
138.1437 + }
138.1438 +
138.1439 + if (hasFollowingMinusSign &&
138.1440 + (nextTag == TAG_QUOTE_ASCII_CHAR ||
138.1441 + nextTag == TAG_QUOTE_CHARS)) {
138.1442 + int c;
138.1443 + if (nextTag == TAG_QUOTE_ASCII_CHAR) {
138.1444 + c = compiledPattern[i] & 0xff;
138.1445 + } else {
138.1446 + c = compiledPattern[i+1];
138.1447 + }
138.1448 +
138.1449 + if (c == minusSign) {
138.1450 + useFollowingMinusSignAsDelimiter = true;
138.1451 + }
138.1452 + }
138.1453 + }
138.1454 + start = subParse(text, start, tag, count, obeyCount,
138.1455 + ambiguousYear, pos,
138.1456 + useFollowingMinusSignAsDelimiter, calb);
138.1457 + if (start < 0) {
138.1458 + pos.index = oldStart;
138.1459 + return null;
138.1460 + }
138.1461 + }
138.1462 + }
138.1463 +
138.1464 + // At this point the fields of Calendar have been set. Calendar
138.1465 + // will fill in default values for missing fields when the time
138.1466 + // is computed.
138.1467 +
138.1468 + pos.index = start;
138.1469 +
138.1470 + Date parsedDate;
138.1471 + try {
138.1472 + parsedDate = calb.establish(calendar).getTime();
138.1473 + // If the year value is ambiguous,
138.1474 + // then the two-digit year == the default start year
138.1475 + if (ambiguousYear[0]) {
138.1476 + if (parsedDate.before(defaultCenturyStart)) {
138.1477 + parsedDate = calb.addYear(100).establish(calendar).getTime();
138.1478 + }
138.1479 + }
138.1480 + }
138.1481 + // An IllegalArgumentException will be thrown by Calendar.getTime()
138.1482 + // if any fields are out of range, e.g., MONTH == 17.
138.1483 + catch (IllegalArgumentException e) {
138.1484 + pos.errorIndex = start;
138.1485 + pos.index = oldStart;
138.1486 + return null;
138.1487 + }
138.1488 +
138.1489 + return parsedDate;
138.1490 + }
138.1491 +
138.1492 + /**
138.1493 + * Private code-size reduction function used by subParse.
138.1494 + * @param text the time text being parsed.
138.1495 + * @param start where to start parsing.
138.1496 + * @param field the date field being parsed.
138.1497 + * @param data the string array to parsed.
138.1498 + * @return the new start position if matching succeeded; a negative number
138.1499 + * indicating matching failure, otherwise.
138.1500 + */
138.1501 + private int matchString(String text, int start, int field, String[] data, CalendarBuilder calb)
138.1502 + {
138.1503 + int i = 0;
138.1504 + int count = data.length;
138.1505 +
138.1506 + if (field == Calendar.DAY_OF_WEEK) i = 1;
138.1507 +
138.1508 + // There may be multiple strings in the data[] array which begin with
138.1509 + // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
138.1510 + // We keep track of the longest match, and return that. Note that this
138.1511 + // unfortunately requires us to test all array elements.
138.1512 + int bestMatchLength = 0, bestMatch = -1;
138.1513 + for (; i<count; ++i)
138.1514 + {
138.1515 + int length = data[i].length();
138.1516 + // Always compare if we have no match yet; otherwise only compare
138.1517 + // against potentially better matches (longer strings).
138.1518 + if (length > bestMatchLength &&
138.1519 + text.regionMatches(true, start, data[i], 0, length))
138.1520 + {
138.1521 + bestMatch = i;
138.1522 + bestMatchLength = length;
138.1523 + }
138.1524 + }
138.1525 + if (bestMatch >= 0)
138.1526 + {
138.1527 + calb.set(field, bestMatch);
138.1528 + return start + bestMatchLength;
138.1529 + }
138.1530 + return -start;
138.1531 + }
138.1532 +
138.1533 + /**
138.1534 + * Performs the same thing as matchString(String, int, int,
138.1535 + * String[]). This method takes a Map<String, Integer> instead of
138.1536 + * String[].
138.1537 + */
138.1538 + private int matchString(String text, int start, int field,
138.1539 + Map<String,Integer> data, CalendarBuilder calb) {
138.1540 + if (data != null) {
138.1541 + String bestMatch = null;
138.1542 +
138.1543 + for (String name : data.keySet()) {
138.1544 + int length = name.length();
138.1545 + if (bestMatch == null || length > bestMatch.length()) {
138.1546 + if (text.regionMatches(true, start, name, 0, length)) {
138.1547 + bestMatch = name;
138.1548 + }
138.1549 + }
138.1550 + }
138.1551 +
138.1552 + if (bestMatch != null) {
138.1553 + calb.set(field, data.get(bestMatch));
138.1554 + return start + bestMatch.length();
138.1555 + }
138.1556 + }
138.1557 + return -start;
138.1558 + }
138.1559 +
138.1560 + private int matchZoneString(String text, int start, String[] zoneNames) {
138.1561 + for (int i = 1; i <= 4; ++i) {
138.1562 + // Checking long and short zones [1 & 2],
138.1563 + // and long and short daylight [3 & 4].
138.1564 + String zoneName = zoneNames[i];
138.1565 + if (text.regionMatches(true, start,
138.1566 + zoneName, 0, zoneName.length())) {
138.1567 + return i;
138.1568 + }
138.1569 + }
138.1570 + return -1;
138.1571 + }
138.1572 +
138.1573 + private boolean matchDSTString(String text, int start, int zoneIndex, int standardIndex,
138.1574 + String[][] zoneStrings) {
138.1575 + int index = standardIndex + 2;
138.1576 + String zoneName = zoneStrings[zoneIndex][index];
138.1577 + if (text.regionMatches(true, start,
138.1578 + zoneName, 0, zoneName.length())) {
138.1579 + return true;
138.1580 + }
138.1581 + return false;
138.1582 + }
138.1583 +
138.1584 + /**
138.1585 + * find time zone 'text' matched zoneStrings and set to internal
138.1586 + * calendar.
138.1587 + */
138.1588 + private int subParseZoneString(String text, int start, CalendarBuilder calb) {
138.1589 + boolean useSameName = false; // true if standard and daylight time use the same abbreviation.
138.1590 + TimeZone currentTimeZone = getTimeZone();
138.1591 +
138.1592 + // At this point, check for named time zones by looking through
138.1593 + // the locale data from the TimeZoneNames strings.
138.1594 + // Want to be able to parse both short and long forms.
138.1595 + int zoneIndex = formatData.getZoneIndex(currentTimeZone.getID());
138.1596 + TimeZone tz = null;
138.1597 + String[][] zoneStrings = formatData.getZoneStringsWrapper();
138.1598 + String[] zoneNames = null;
138.1599 + int nameIndex = 0;
138.1600 + if (zoneIndex != -1) {
138.1601 + zoneNames = zoneStrings[zoneIndex];
138.1602 + if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
138.1603 + if (nameIndex <= 2) {
138.1604 + // Check if the standard name (abbr) and the daylight name are the same.
138.1605 + useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
138.1606 + }
138.1607 + tz = TimeZone.getTimeZone(zoneNames[0]);
138.1608 + }
138.1609 + }
138.1610 + if (tz == null) {
138.1611 + zoneIndex = formatData.getZoneIndex(TimeZone.getDefault().getID());
138.1612 + if (zoneIndex != -1) {
138.1613 + zoneNames = zoneStrings[zoneIndex];
138.1614 + if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
138.1615 + if (nameIndex <= 2) {
138.1616 + useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
138.1617 + }
138.1618 + tz = TimeZone.getTimeZone(zoneNames[0]);
138.1619 + }
138.1620 + }
138.1621 + }
138.1622 +
138.1623 + if (tz == null) {
138.1624 + int len = zoneStrings.length;
138.1625 + for (int i = 0; i < len; i++) {
138.1626 + zoneNames = zoneStrings[i];
138.1627 + if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
138.1628 + if (nameIndex <= 2) {
138.1629 + useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
138.1630 + }
138.1631 + tz = TimeZone.getTimeZone(zoneNames[0]);
138.1632 + break;
138.1633 + }
138.1634 + }
138.1635 + }
138.1636 + if (tz != null) { // Matched any ?
138.1637 + if (!tz.equals(currentTimeZone)) {
138.1638 + setTimeZone(tz);
138.1639 + }
138.1640 + // If the time zone matched uses the same name
138.1641 + // (abbreviation) for both standard and daylight time,
138.1642 + // let the time zone in the Calendar decide which one.
138.1643 + //
138.1644 + // Also if tz.getDSTSaving() returns 0 for DST, use tz to
138.1645 + // determine the local time. (6645292)
138.1646 + int dstAmount = (nameIndex >= 3) ? tz.getDSTSavings() : 0;
138.1647 + if (!(useSameName || (nameIndex >= 3 && dstAmount == 0))) {
138.1648 + calb.set(Calendar.ZONE_OFFSET, tz.getRawOffset())
138.1649 + .set(Calendar.DST_OFFSET, dstAmount);
138.1650 + }
138.1651 + return (start + zoneNames[nameIndex].length());
138.1652 + }
138.1653 + return 0;
138.1654 + }
138.1655 +
138.1656 + /**
138.1657 + * Parses numeric forms of time zone offset, such as "hh:mm", and
138.1658 + * sets calb to the parsed value.
138.1659 + *
138.1660 + * @param text the text to be parsed
138.1661 + * @param start the character position to start parsing
138.1662 + * @param sign 1: positive; -1: negative
138.1663 + * @param count 0: 'Z' or "GMT+hh:mm" parsing; 1 - 3: the number of 'X's
138.1664 + * @param colon true - colon required between hh and mm; false - no colon required
138.1665 + * @param calb a CalendarBuilder in which the parsed value is stored
138.1666 + * @return updated parsed position, or its negative value to indicate a parsing error
138.1667 + */
138.1668 + private int subParseNumericZone(String text, int start, int sign, int count,
138.1669 + boolean colon, CalendarBuilder calb) {
138.1670 + int index = start;
138.1671 +
138.1672 + parse:
138.1673 + try {
138.1674 + char c = text.charAt(index++);
138.1675 + // Parse hh
138.1676 + int hours;
138.1677 + if (!isDigit(c)) {
138.1678 + break parse;
138.1679 + }
138.1680 + hours = c - '0';
138.1681 + c = text.charAt(index++);
138.1682 + if (isDigit(c)) {
138.1683 + hours = hours * 10 + (c - '0');
138.1684 + } else {
138.1685 + // If no colon in RFC 822 or 'X' (ISO), two digits are
138.1686 + // required.
138.1687 + if (count > 0 || !colon) {
138.1688 + break parse;
138.1689 + }
138.1690 + --index;
138.1691 + }
138.1692 + if (hours > 23) {
138.1693 + break parse;
138.1694 + }
138.1695 + int minutes = 0;
138.1696 + if (count != 1) {
138.1697 + // Proceed with parsing mm
138.1698 + c = text.charAt(index++);
138.1699 + if (colon) {
138.1700 + if (c != ':') {
138.1701 + break parse;
138.1702 + }
138.1703 + c = text.charAt(index++);
138.1704 + }
138.1705 + if (!isDigit(c)) {
138.1706 + break parse;
138.1707 + }
138.1708 + minutes = c - '0';
138.1709 + c = text.charAt(index++);
138.1710 + if (!isDigit(c)) {
138.1711 + break parse;
138.1712 + }
138.1713 + minutes = minutes * 10 + (c - '0');
138.1714 + if (minutes > 59) {
138.1715 + break parse;
138.1716 + }
138.1717 + }
138.1718 + minutes += hours * 60;
138.1719 + calb.set(Calendar.ZONE_OFFSET, minutes * MILLIS_PER_MINUTE * sign)
138.1720 + .set(Calendar.DST_OFFSET, 0);
138.1721 + return index;
138.1722 + } catch (IndexOutOfBoundsException e) {
138.1723 + }
138.1724 + return 1 - index; // -(index - 1)
138.1725 + }
138.1726 +
138.1727 + private boolean isDigit(char c) {
138.1728 + return c >= '0' && c <= '9';
138.1729 + }
138.1730 +
138.1731 + /**
138.1732 + * Private member function that converts the parsed date strings into
138.1733 + * timeFields. Returns -start (for ParsePosition) if failed.
138.1734 + * @param text the time text to be parsed.
138.1735 + * @param start where to start parsing.
138.1736 + * @param ch the pattern character for the date field text to be parsed.
138.1737 + * @param count the count of a pattern character.
138.1738 + * @param obeyCount if true, then the next field directly abuts this one,
138.1739 + * and we should use the count to know when to stop parsing.
138.1740 + * @param ambiguousYear return parameter; upon return, if ambiguousYear[0]
138.1741 + * is true, then a two-digit year was parsed and may need to be readjusted.
138.1742 + * @param origPos origPos.errorIndex is used to return an error index
138.1743 + * at which a parse error occurred, if matching failure occurs.
138.1744 + * @return the new start position if matching succeeded; -1 indicating
138.1745 + * matching failure, otherwise. In case matching failure occurred,
138.1746 + * an error index is set to origPos.errorIndex.
138.1747 + */
138.1748 + private int subParse(String text, int start, int patternCharIndex, int count,
138.1749 + boolean obeyCount, boolean[] ambiguousYear,
138.1750 + ParsePosition origPos,
138.1751 + boolean useFollowingMinusSignAsDelimiter, CalendarBuilder calb) {
138.1752 + Number number = null;
138.1753 + int value = 0;
138.1754 + ParsePosition pos = new ParsePosition(0);
138.1755 + pos.index = start;
138.1756 + if (patternCharIndex == PATTERN_WEEK_YEAR && !calendar.isWeekDateSupported()) {
138.1757 + // use calendar year 'y' instead
138.1758 + patternCharIndex = PATTERN_YEAR;
138.1759 + }
138.1760 + int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
138.1761 +
138.1762 + // If there are any spaces here, skip over them. If we hit the end
138.1763 + // of the string, then fail.
138.1764 + for (;;) {
138.1765 + if (pos.index >= text.length()) {
138.1766 + origPos.errorIndex = start;
138.1767 + return -1;
138.1768 + }
138.1769 + char c = text.charAt(pos.index);
138.1770 + if (c != ' ' && c != '\t') break;
138.1771 + ++pos.index;
138.1772 + }
138.1773 +
138.1774 + parsing:
138.1775 + {
138.1776 + // We handle a few special cases here where we need to parse
138.1777 + // a number value. We handle further, more generic cases below. We need
138.1778 + // to handle some of them here because some fields require extra processing on
138.1779 + // the parsed value.
138.1780 + if (patternCharIndex == PATTERN_HOUR_OF_DAY1 ||
138.1781 + patternCharIndex == PATTERN_HOUR1 ||
138.1782 + (patternCharIndex == PATTERN_MONTH && count <= 2) ||
138.1783 + patternCharIndex == PATTERN_YEAR ||
138.1784 + patternCharIndex == PATTERN_WEEK_YEAR) {
138.1785 + // It would be good to unify this with the obeyCount logic below,
138.1786 + // but that's going to be difficult.
138.1787 + if (obeyCount) {
138.1788 + if ((start+count) > text.length()) {
138.1789 + break parsing;
138.1790 + }
138.1791 + number = numberFormat.parse(text.substring(0, start+count), pos);
138.1792 + } else {
138.1793 + number = numberFormat.parse(text, pos);
138.1794 + }
138.1795 + if (number == null) {
138.1796 + if (patternCharIndex != PATTERN_YEAR || calendar instanceof GregorianCalendar) {
138.1797 + break parsing;
138.1798 + }
138.1799 + } else {
138.1800 + value = number.intValue();
138.1801 +
138.1802 + if (useFollowingMinusSignAsDelimiter && (value < 0) &&
138.1803 + (((pos.index < text.length()) &&
138.1804 + (text.charAt(pos.index) != minusSign)) ||
138.1805 + ((pos.index == text.length()) &&
138.1806 + (text.charAt(pos.index-1) == minusSign)))) {
138.1807 + value = -value;
138.1808 + pos.index--;
138.1809 + }
138.1810 + }
138.1811 + }
138.1812 +
138.1813 + boolean useDateFormatSymbols = useDateFormatSymbols();
138.1814 +
138.1815 + int index;
138.1816 + switch (patternCharIndex) {
138.1817 + case PATTERN_ERA: // 'G'
138.1818 + if (useDateFormatSymbols) {
138.1819 + if ((index = matchString(text, start, Calendar.ERA, formatData.getEras(), calb)) > 0) {
138.1820 + return index;
138.1821 + }
138.1822 + } else {
138.1823 + Map<String, Integer> map = calendar.getDisplayNames(field,
138.1824 + Calendar.ALL_STYLES,
138.1825 + locale);
138.1826 + if ((index = matchString(text, start, field, map, calb)) > 0) {
138.1827 + return index;
138.1828 + }
138.1829 + }
138.1830 + break parsing;
138.1831 +
138.1832 + case PATTERN_WEEK_YEAR: // 'Y'
138.1833 + case PATTERN_YEAR: // 'y'
138.1834 + if (!(calendar instanceof GregorianCalendar)) {
138.1835 + // calendar might have text representations for year values,
138.1836 + // such as "\u5143" in JapaneseImperialCalendar.
138.1837 + int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
138.1838 + Map<String, Integer> map = calendar.getDisplayNames(field, style, locale);
138.1839 + if (map != null) {
138.1840 + if ((index = matchString(text, start, field, map, calb)) > 0) {
138.1841 + return index;
138.1842 + }
138.1843 + }
138.1844 + calb.set(field, value);
138.1845 + return pos.index;
138.1846 + }
138.1847 +
138.1848 + // If there are 3 or more YEAR pattern characters, this indicates
138.1849 + // that the year value is to be treated literally, without any
138.1850 + // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
138.1851 + // we made adjustments to place the 2-digit year in the proper
138.1852 + // century, for parsed strings from "00" to "99". Any other string
138.1853 + // is treated literally: "2250", "-1", "1", "002".
138.1854 + if (count <= 2 && (pos.index - start) == 2
138.1855 + && Character.isDigit(text.charAt(start))
138.1856 + && Character.isDigit(text.charAt(start+1))) {
138.1857 + // Assume for example that the defaultCenturyStart is 6/18/1903.
138.1858 + // This means that two-digit years will be forced into the range
138.1859 + // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
138.1860 + // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
138.1861 + // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
138.1862 + // other fields specify a date before 6/18, or 1903 if they specify a
138.1863 + // date afterwards. As a result, 03 is an ambiguous year. All other
138.1864 + // two-digit years are unambiguous.
138.1865 + int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
138.1866 + ambiguousYear[0] = value == ambiguousTwoDigitYear;
138.1867 + value += (defaultCenturyStartYear/100)*100 +
138.1868 + (value < ambiguousTwoDigitYear ? 100 : 0);
138.1869 + }
138.1870 + calb.set(field, value);
138.1871 + return pos.index;
138.1872 +
138.1873 + case PATTERN_MONTH: // 'M'
138.1874 + if (count <= 2) // i.e., M or MM.
138.1875 + {
138.1876 + // Don't want to parse the month if it is a string
138.1877 + // while pattern uses numeric style: M or MM.
138.1878 + // [We computed 'value' above.]
138.1879 + calb.set(Calendar.MONTH, value - 1);
138.1880 + return pos.index;
138.1881 + }
138.1882 +
138.1883 + if (useDateFormatSymbols) {
138.1884 + // count >= 3 // i.e., MMM or MMMM
138.1885 + // Want to be able to parse both short and long forms.
138.1886 + // Try count == 4 first:
138.1887 + int newStart = 0;
138.1888 + if ((newStart = matchString(text, start, Calendar.MONTH,
138.1889 + formatData.getMonths(), calb)) > 0) {
138.1890 + return newStart;
138.1891 + }
138.1892 + // count == 4 failed, now try count == 3
138.1893 + if ((index = matchString(text, start, Calendar.MONTH,
138.1894 + formatData.getShortMonths(), calb)) > 0) {
138.1895 + return index;
138.1896 + }
138.1897 + } else {
138.1898 + Map<String, Integer> map = calendar.getDisplayNames(field,
138.1899 + Calendar.ALL_STYLES,
138.1900 + locale);
138.1901 + if ((index = matchString(text, start, field, map, calb)) > 0) {
138.1902 + return index;
138.1903 + }
138.1904 + }
138.1905 + break parsing;
138.1906 +
138.1907 + case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59
138.1908 + if (!isLenient()) {
138.1909 + // Validate the hour value in non-lenient
138.1910 + if (value < 1 || value > 24) {
138.1911 + break parsing;
138.1912 + }
138.1913 + }
138.1914 + // [We computed 'value' above.]
138.1915 + if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY)+1)
138.1916 + value = 0;
138.1917 + calb.set(Calendar.HOUR_OF_DAY, value);
138.1918 + return pos.index;
138.1919 +
138.1920 + case PATTERN_DAY_OF_WEEK: // 'E'
138.1921 + {
138.1922 + if (useDateFormatSymbols) {
138.1923 + // Want to be able to parse both short and long forms.
138.1924 + // Try count == 4 (DDDD) first:
138.1925 + int newStart = 0;
138.1926 + if ((newStart=matchString(text, start, Calendar.DAY_OF_WEEK,
138.1927 + formatData.getWeekdays(), calb)) > 0) {
138.1928 + return newStart;
138.1929 + }
138.1930 + // DDDD failed, now try DDD
138.1931 + if ((index = matchString(text, start, Calendar.DAY_OF_WEEK,
138.1932 + formatData.getShortWeekdays(), calb)) > 0) {
138.1933 + return index;
138.1934 + }
138.1935 + } else {
138.1936 + int[] styles = { Calendar.LONG, Calendar.SHORT };
138.1937 + for (int style : styles) {
138.1938 + Map<String,Integer> map = calendar.getDisplayNames(field, style, locale);
138.1939 + if ((index = matchString(text, start, field, map, calb)) > 0) {
138.1940 + return index;
138.1941 + }
138.1942 + }
138.1943 + }
138.1944 + }
138.1945 + break parsing;
138.1946 +
138.1947 + case PATTERN_AM_PM: // 'a'
138.1948 + if (useDateFormatSymbols) {
138.1949 + if ((index = matchString(text, start, Calendar.AM_PM,
138.1950 + formatData.getAmPmStrings(), calb)) > 0) {
138.1951 + return index;
138.1952 + }
138.1953 + } else {
138.1954 + Map<String,Integer> map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale);
138.1955 + if ((index = matchString(text, start, field, map, calb)) > 0) {
138.1956 + return index;
138.1957 + }
138.1958 + }
138.1959 + break parsing;
138.1960 +
138.1961 + case PATTERN_HOUR1: // 'h' 1-based. eg, 11PM + 1 hour =>> 12 AM
138.1962 + if (!isLenient()) {
138.1963 + // Validate the hour value in non-lenient
138.1964 + if (value < 1 || value > 12) {
138.1965 + break parsing;
138.1966 + }
138.1967 + }
138.1968 + // [We computed 'value' above.]
138.1969 + if (value == calendar.getLeastMaximum(Calendar.HOUR)+1)
138.1970 + value = 0;
138.1971 + calb.set(Calendar.HOUR, value);
138.1972 + return pos.index;
138.1973 +
138.1974 + case PATTERN_ZONE_NAME: // 'z'
138.1975 + case PATTERN_ZONE_VALUE: // 'Z'
138.1976 + {
138.1977 + int sign = 0;
138.1978 + try {
138.1979 + char c = text.charAt(pos.index);
138.1980 + if (c == '+') {
138.1981 + sign = 1;
138.1982 + } else if (c == '-') {
138.1983 + sign = -1;
138.1984 + }
138.1985 + if (sign == 0) {
138.1986 + // Try parsing a custom time zone "GMT+hh:mm" or "GMT".
138.1987 + if ((c == 'G' || c == 'g')
138.1988 + && (text.length() - start) >= GMT.length()
138.1989 + && text.regionMatches(true, start, GMT, 0, GMT.length())) {
138.1990 + pos.index = start + GMT.length();
138.1991 +
138.1992 + if ((text.length() - pos.index) > 0) {
138.1993 + c = text.charAt(pos.index);
138.1994 + if (c == '+') {
138.1995 + sign = 1;
138.1996 + } else if (c == '-') {
138.1997 + sign = -1;
138.1998 + }
138.1999 + }
138.2000 +
138.2001 + if (sign == 0) { /* "GMT" without offset */
138.2002 + calb.set(Calendar.ZONE_OFFSET, 0)
138.2003 + .set(Calendar.DST_OFFSET, 0);
138.2004 + return pos.index;
138.2005 + }
138.2006 +
138.2007 + // Parse the rest as "hh:mm"
138.2008 + int i = subParseNumericZone(text, ++pos.index,
138.2009 + sign, 0, true, calb);
138.2010 + if (i > 0) {
138.2011 + return i;
138.2012 + }
138.2013 + pos.index = -i;
138.2014 + } else {
138.2015 + // Try parsing the text as a time zone
138.2016 + // name or abbreviation.
138.2017 + int i = subParseZoneString(text, pos.index, calb);
138.2018 + if (i > 0) {
138.2019 + return i;
138.2020 + }
138.2021 + pos.index = -i;
138.2022 + }
138.2023 + } else {
138.2024 + // Parse the rest as "hhmm" (RFC 822)
138.2025 + int i = subParseNumericZone(text, ++pos.index,
138.2026 + sign, 0, false, calb);
138.2027 + if (i > 0) {
138.2028 + return i;
138.2029 + }
138.2030 + pos.index = -i;
138.2031 + }
138.2032 + } catch (IndexOutOfBoundsException e) {
138.2033 + }
138.2034 + }
138.2035 + break parsing;
138.2036 +
138.2037 + case PATTERN_ISO_ZONE: // 'X'
138.2038 + {
138.2039 + if ((text.length() - pos.index) <= 0) {
138.2040 + break parsing;
138.2041 + }
138.2042 +
138.2043 + int sign = 0;
138.2044 + char c = text.charAt(pos.index);
138.2045 + if (c == 'Z') {
138.2046 + calb.set(Calendar.ZONE_OFFSET, 0).set(Calendar.DST_OFFSET, 0);
138.2047 + return ++pos.index;
138.2048 + }
138.2049 +
138.2050 + // parse text as "+/-hh[[:]mm]" based on count
138.2051 + if (c == '+') {
138.2052 + sign = 1;
138.2053 + } else if (c == '-') {
138.2054 + sign = -1;
138.2055 + } else {
138.2056 + ++pos.index;
138.2057 + break parsing;
138.2058 + }
138.2059 + int i = subParseNumericZone(text, ++pos.index, sign, count,
138.2060 + count == 3, calb);
138.2061 + if (i > 0) {
138.2062 + return i;
138.2063 + }
138.2064 + pos.index = -i;
138.2065 + }
138.2066 + break parsing;
138.2067 +
138.2068 + default:
138.2069 + // case PATTERN_DAY_OF_MONTH: // 'd'
138.2070 + // case PATTERN_HOUR_OF_DAY0: // 'H' 0-based. eg, 23:59 + 1 hour =>> 00:59
138.2071 + // case PATTERN_MINUTE: // 'm'
138.2072 + // case PATTERN_SECOND: // 's'
138.2073 + // case PATTERN_MILLISECOND: // 'S'
138.2074 + // case PATTERN_DAY_OF_YEAR: // 'D'
138.2075 + // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F'
138.2076 + // case PATTERN_WEEK_OF_YEAR: // 'w'
138.2077 + // case PATTERN_WEEK_OF_MONTH: // 'W'
138.2078 + // case PATTERN_HOUR0: // 'K' 0-based. eg, 11PM + 1 hour =>> 0 AM
138.2079 + // case PATTERN_ISO_DAY_OF_WEEK: // 'u' (pseudo field);
138.2080 +
138.2081 + // Handle "generic" fields
138.2082 + if (obeyCount) {
138.2083 + if ((start+count) > text.length()) {
138.2084 + break parsing;
138.2085 + }
138.2086 + number = numberFormat.parse(text.substring(0, start+count), pos);
138.2087 + } else {
138.2088 + number = numberFormat.parse(text, pos);
138.2089 + }
138.2090 + if (number != null) {
138.2091 + value = number.intValue();
138.2092 +
138.2093 + if (useFollowingMinusSignAsDelimiter && (value < 0) &&
138.2094 + (((pos.index < text.length()) &&
138.2095 + (text.charAt(pos.index) != minusSign)) ||
138.2096 + ((pos.index == text.length()) &&
138.2097 + (text.charAt(pos.index-1) == minusSign)))) {
138.2098 + value = -value;
138.2099 + pos.index--;
138.2100 + }
138.2101 +
138.2102 + calb.set(field, value);
138.2103 + return pos.index;
138.2104 + }
138.2105 + break parsing;
138.2106 + }
138.2107 + }
138.2108 +
138.2109 + // Parsing failed.
138.2110 + origPos.errorIndex = pos.index;
138.2111 + return -1;
138.2112 + }
138.2113 +
138.2114 + private final String getCalendarName() {
138.2115 + return calendar.getClass().getName();
138.2116 + }
138.2117 +
138.2118 + private boolean useDateFormatSymbols() {
138.2119 + if (useDateFormatSymbols) {
138.2120 + return true;
138.2121 + }
138.2122 + return isGregorianCalendar() || locale == null;
138.2123 + }
138.2124 +
138.2125 + private boolean isGregorianCalendar() {
138.2126 + return "java.util.GregorianCalendar".equals(getCalendarName());
138.2127 + }
138.2128 +
138.2129 + /**
138.2130 + * Translates a pattern, mapping each character in the from string to the
138.2131 + * corresponding character in the to string.
138.2132 + *
138.2133 + * @exception IllegalArgumentException if the given pattern is invalid
138.2134 + */
138.2135 + private String translatePattern(String pattern, String from, String to) {
138.2136 + StringBuilder result = new StringBuilder();
138.2137 + boolean inQuote = false;
138.2138 + for (int i = 0; i < pattern.length(); ++i) {
138.2139 + char c = pattern.charAt(i);
138.2140 + if (inQuote) {
138.2141 + if (c == '\'')
138.2142 + inQuote = false;
138.2143 + }
138.2144 + else {
138.2145 + if (c == '\'')
138.2146 + inQuote = true;
138.2147 + else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
138.2148 + int ci = from.indexOf(c);
138.2149 + if (ci >= 0) {
138.2150 + // patternChars is longer than localPatternChars due
138.2151 + // to serialization compatibility. The pattern letters
138.2152 + // unsupported by localPatternChars pass through.
138.2153 + if (ci < to.length()) {
138.2154 + c = to.charAt(ci);
138.2155 + }
138.2156 + } else {
138.2157 + throw new IllegalArgumentException("Illegal pattern " +
138.2158 + " character '" +
138.2159 + c + "'");
138.2160 + }
138.2161 + }
138.2162 + }
138.2163 + result.append(c);
138.2164 + }
138.2165 + if (inQuote)
138.2166 + throw new IllegalArgumentException("Unfinished quote in pattern");
138.2167 + return result.toString();
138.2168 + }
138.2169 +
138.2170 + /**
138.2171 + * Returns a pattern string describing this date format.
138.2172 + *
138.2173 + * @return a pattern string describing this date format.
138.2174 + */
138.2175 + public String toPattern() {
138.2176 + return pattern;
138.2177 + }
138.2178 +
138.2179 + /**
138.2180 + * Returns a localized pattern string describing this date format.
138.2181 + *
138.2182 + * @return a localized pattern string describing this date format.
138.2183 + */
138.2184 + public String toLocalizedPattern() {
138.2185 + return translatePattern(pattern,
138.2186 + DateFormatSymbols.patternChars,
138.2187 + formatData.getLocalPatternChars());
138.2188 + }
138.2189 +
138.2190 + /**
138.2191 + * Applies the given pattern string to this date format.
138.2192 + *
138.2193 + * @param pattern the new date and time pattern for this date format
138.2194 + * @exception NullPointerException if the given pattern is null
138.2195 + * @exception IllegalArgumentException if the given pattern is invalid
138.2196 + */
138.2197 + public void applyPattern(String pattern)
138.2198 + {
138.2199 + compiledPattern = compile(pattern);
138.2200 + this.pattern = pattern;
138.2201 + }
138.2202 +
138.2203 + /**
138.2204 + * Applies the given localized pattern string to this date format.
138.2205 + *
138.2206 + * @param pattern a String to be mapped to the new date and time format
138.2207 + * pattern for this format
138.2208 + * @exception NullPointerException if the given pattern is null
138.2209 + * @exception IllegalArgumentException if the given pattern is invalid
138.2210 + */
138.2211 + public void applyLocalizedPattern(String pattern) {
138.2212 + String p = translatePattern(pattern,
138.2213 + formatData.getLocalPatternChars(),
138.2214 + DateFormatSymbols.patternChars);
138.2215 + compiledPattern = compile(p);
138.2216 + this.pattern = p;
138.2217 + }
138.2218 +
138.2219 + /**
138.2220 + * Gets a copy of the date and time format symbols of this date format.
138.2221 + *
138.2222 + * @return the date and time format symbols of this date format
138.2223 + * @see #setDateFormatSymbols
138.2224 + */
138.2225 + public DateFormatSymbols getDateFormatSymbols()
138.2226 + {
138.2227 + return (DateFormatSymbols)formatData.clone();
138.2228 + }
138.2229 +
138.2230 + /**
138.2231 + * Sets the date and time format symbols of this date format.
138.2232 + *
138.2233 + * @param newFormatSymbols the new date and time format symbols
138.2234 + * @exception NullPointerException if the given newFormatSymbols is null
138.2235 + * @see #getDateFormatSymbols
138.2236 + */
138.2237 + public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
138.2238 + {
138.2239 + this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
138.2240 + useDateFormatSymbols = true;
138.2241 + }
138.2242 +
138.2243 + /**
138.2244 + * Creates a copy of this <code>SimpleDateFormat</code>. This also
138.2245 + * clones the format's date format symbols.
138.2246 + *
138.2247 + * @return a clone of this <code>SimpleDateFormat</code>
138.2248 + */
138.2249 + public Object clone() {
138.2250 + SimpleDateFormat other = (SimpleDateFormat) super.clone();
138.2251 + other.formatData = (DateFormatSymbols) formatData.clone();
138.2252 + return other;
138.2253 + }
138.2254 +
138.2255 + /**
138.2256 + * Returns the hash code value for this <code>SimpleDateFormat</code> object.
138.2257 + *
138.2258 + * @return the hash code value for this <code>SimpleDateFormat</code> object.
138.2259 + */
138.2260 + public int hashCode()
138.2261 + {
138.2262 + return pattern.hashCode();
138.2263 + // just enough fields for a reasonable distribution
138.2264 + }
138.2265 +
138.2266 + /**
138.2267 + * Compares the given object with this <code>SimpleDateFormat</code> for
138.2268 + * equality.
138.2269 + *
138.2270 + * @return true if the given object is equal to this
138.2271 + * <code>SimpleDateFormat</code>
138.2272 + */
138.2273 + public boolean equals(Object obj)
138.2274 + {
138.2275 + if (!super.equals(obj)) return false; // super does class check
138.2276 + SimpleDateFormat that = (SimpleDateFormat) obj;
138.2277 + return (pattern.equals(that.pattern)
138.2278 + && formatData.equals(that.formatData));
138.2279 + }
138.2280 +
138.2281 + /**
138.2282 + * After reading an object from the input stream, the format
138.2283 + * pattern in the object is verified.
138.2284 + * <p>
138.2285 + * @exception InvalidObjectException if the pattern is invalid
138.2286 + */
138.2287 + private void readObject(ObjectInputStream stream)
138.2288 + throws IOException, ClassNotFoundException {
138.2289 + stream.defaultReadObject();
138.2290 +
138.2291 + try {
138.2292 + compiledPattern = compile(pattern);
138.2293 + } catch (Exception e) {
138.2294 + throw new InvalidObjectException("invalid pattern");
138.2295 + }
138.2296 +
138.2297 + if (serialVersionOnStream < 1) {
138.2298 + // didn't have defaultCenturyStart field
138.2299 + initializeDefaultCentury();
138.2300 + }
138.2301 + else {
138.2302 + // fill in dependent transient field
138.2303 + parseAmbiguousDatesAsAfter(defaultCenturyStart);
138.2304 + }
138.2305 + serialVersionOnStream = currentSerialVersion;
138.2306 +
138.2307 + // If the deserialized object has a SimpleTimeZone, try
138.2308 + // to replace it with a ZoneInfo equivalent in order to
138.2309 + // be compatible with the SimpleTimeZone-based
138.2310 + // implementation as much as possible.
138.2311 + TimeZone tz = getTimeZone();
138.2312 + if (tz instanceof SimpleTimeZone) {
138.2313 + String id = tz.getID();
138.2314 + TimeZone zi = TimeZone.getTimeZone(id);
138.2315 + if (zi != null && zi.hasSameRules(tz) && zi.getID().equals(id)) {
138.2316 + setTimeZone(zi);
138.2317 + }
138.2318 + }
138.2319 + }
138.2320 +
138.2321 + /**
138.2322 + * Analyze the negative subpattern of DecimalFormat and set/update values
138.2323 + * as necessary.
138.2324 + */
138.2325 + private void checkNegativeNumberExpression() {
138.2326 + if ((numberFormat instanceof DecimalFormat) &&
138.2327 + !numberFormat.equals(originalNumberFormat)) {
138.2328 + String numberPattern = ((DecimalFormat)numberFormat).toPattern();
138.2329 + if (!numberPattern.equals(originalNumberPattern)) {
138.2330 + hasFollowingMinusSign = false;
138.2331 +
138.2332 + int separatorIndex = numberPattern.indexOf(';');
138.2333 + // If the negative subpattern is not absent, we have to analayze
138.2334 + // it in order to check if it has a following minus sign.
138.2335 + if (separatorIndex > -1) {
138.2336 + int minusIndex = numberPattern.indexOf('-', separatorIndex);
138.2337 + if ((minusIndex > numberPattern.lastIndexOf('0')) &&
138.2338 + (minusIndex > numberPattern.lastIndexOf('#'))) {
138.2339 + hasFollowingMinusSign = true;
138.2340 + minusSign = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getMinusSign();
138.2341 + }
138.2342 + }
138.2343 + originalNumberPattern = numberPattern;
138.2344 + }
138.2345 + originalNumberFormat = numberFormat;
138.2346 + }
138.2347 + }
138.2348 +
138.2349 + private static final class GregorianCalendar extends Calendar {
138.2350 + @Override
138.2351 + protected void computeTime() {
138.2352 + }
138.2353 +
138.2354 + @Override
138.2355 + protected void computeFields() {
138.2356 + }
138.2357 +
138.2358 + @Override
138.2359 + public void add(int field, int amount) {
138.2360 + }
138.2361 +
138.2362 + @Override
138.2363 + public void roll(int field, boolean up) {
138.2364 + }
138.2365 +
138.2366 + @Override
138.2367 + public int getMinimum(int field) {
138.2368 + return 0;
138.2369 + }
138.2370 +
138.2371 + @Override
138.2372 + public int getMaximum(int field) {
138.2373 + return 0;
138.2374 + }
138.2375 +
138.2376 + @Override
138.2377 + public int getGreatestMinimum(int field) {
138.2378 + return 0;
138.2379 + }
138.2380 +
138.2381 + @Override
138.2382 + public int getLeastMaximum(int field) {
138.2383 + return 0;
138.2384 + }
138.2385 + }
138.2386 +}
139.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
139.2 +++ b/rt/emul/compact/src/main/java/java/util/BitSet.java Tue Feb 11 13:31:42 2014 +0100
139.3 @@ -0,0 +1,1188 @@
139.4 +/*
139.5 + * Copyright (c) 1995, 2007, Oracle and/or its affiliates. All rights reserved.
139.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
139.7 + *
139.8 + * This code is free software; you can redistribute it and/or modify it
139.9 + * under the terms of the GNU General Public License version 2 only, as
139.10 + * published by the Free Software Foundation. Oracle designates this
139.11 + * particular file as subject to the "Classpath" exception as provided
139.12 + * by Oracle in the LICENSE file that accompanied this code.
139.13 + *
139.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
139.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
139.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
139.17 + * version 2 for more details (a copy is included in the LICENSE file that
139.18 + * accompanied this code).
139.19 + *
139.20 + * You should have received a copy of the GNU General Public License version
139.21 + * 2 along with this work; if not, write to the Free Software Foundation,
139.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
139.23 + *
139.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
139.25 + * or visit www.oracle.com if you need additional information or have any
139.26 + * questions.
139.27 + */
139.28 +
139.29 +package java.util;
139.30 +
139.31 +import java.io.*;
139.32 +
139.33 +/**
139.34 + * This class implements a vector of bits that grows as needed. Each
139.35 + * component of the bit set has a {@code boolean} value. The
139.36 + * bits of a {@code BitSet} are indexed by nonnegative integers.
139.37 + * Individual indexed bits can be examined, set, or cleared. One
139.38 + * {@code BitSet} may be used to modify the contents of another
139.39 + * {@code BitSet} through logical AND, logical inclusive OR, and
139.40 + * logical exclusive OR operations.
139.41 + *
139.42 + * <p>By default, all bits in the set initially have the value
139.43 + * {@code false}.
139.44 + *
139.45 + * <p>Every bit set has a current size, which is the number of bits
139.46 + * of space currently in use by the bit set. Note that the size is
139.47 + * related to the implementation of a bit set, so it may change with
139.48 + * implementation. The length of a bit set relates to logical length
139.49 + * of a bit set and is defined independently of implementation.
139.50 + *
139.51 + * <p>Unless otherwise noted, passing a null parameter to any of the
139.52 + * methods in a {@code BitSet} will result in a
139.53 + * {@code NullPointerException}.
139.54 + *
139.55 + * <p>A {@code BitSet} is not safe for multithreaded use without
139.56 + * external synchronization.
139.57 + *
139.58 + * @author Arthur van Hoff
139.59 + * @author Michael McCloskey
139.60 + * @author Martin Buchholz
139.61 + * @since JDK1.0
139.62 + */
139.63 +public class BitSet implements Cloneable, java.io.Serializable {
139.64 + /*
139.65 + * BitSets are packed into arrays of "words." Currently a word is
139.66 + * a long, which consists of 64 bits, requiring 6 address bits.
139.67 + * The choice of word size is determined purely by performance concerns.
139.68 + */
139.69 + private final static int ADDRESS_BITS_PER_WORD = 6;
139.70 + private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
139.71 + private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;
139.72 +
139.73 + /* Used to shift left or right for a partial word mask */
139.74 + private static final long WORD_MASK = 0xffffffffffffffffL;
139.75 +
139.76 + /**
139.77 + * @serialField bits long[]
139.78 + *
139.79 + * The bits in this BitSet. The ith bit is stored in bits[i/64] at
139.80 + * bit position i % 64 (where bit position 0 refers to the least
139.81 + * significant bit and 63 refers to the most significant bit).
139.82 + */
139.83 + private static final ObjectStreamField[] serialPersistentFields = {
139.84 + new ObjectStreamField("bits", long[].class),
139.85 + };
139.86 +
139.87 + /**
139.88 + * The internal field corresponding to the serialField "bits".
139.89 + */
139.90 + private long[] words;
139.91 +
139.92 + /**
139.93 + * The number of words in the logical size of this BitSet.
139.94 + */
139.95 + private transient int wordsInUse = 0;
139.96 +
139.97 + /**
139.98 + * Whether the size of "words" is user-specified. If so, we assume
139.99 + * the user knows what he's doing and try harder to preserve it.
139.100 + */
139.101 + private transient boolean sizeIsSticky = false;
139.102 +
139.103 + /* use serialVersionUID from JDK 1.0.2 for interoperability */
139.104 + private static final long serialVersionUID = 7997698588986878753L;
139.105 +
139.106 + /**
139.107 + * Given a bit index, return word index containing it.
139.108 + */
139.109 + private static int wordIndex(int bitIndex) {
139.110 + return bitIndex >> ADDRESS_BITS_PER_WORD;
139.111 + }
139.112 +
139.113 + /**
139.114 + * Every public method must preserve these invariants.
139.115 + */
139.116 + private void checkInvariants() {
139.117 + assert(wordsInUse == 0 || words[wordsInUse - 1] != 0);
139.118 + assert(wordsInUse >= 0 && wordsInUse <= words.length);
139.119 + assert(wordsInUse == words.length || words[wordsInUse] == 0);
139.120 + }
139.121 +
139.122 + /**
139.123 + * Sets the field wordsInUse to the logical size in words of the bit set.
139.124 + * WARNING:This method assumes that the number of words actually in use is
139.125 + * less than or equal to the current value of wordsInUse!
139.126 + */
139.127 + private void recalculateWordsInUse() {
139.128 + // Traverse the bitset until a used word is found
139.129 + int i;
139.130 + for (i = wordsInUse-1; i >= 0; i--)
139.131 + if (words[i] != 0)
139.132 + break;
139.133 +
139.134 + wordsInUse = i+1; // The new logical size
139.135 + }
139.136 +
139.137 + /**
139.138 + * Creates a new bit set. All bits are initially {@code false}.
139.139 + */
139.140 + public BitSet() {
139.141 + initWords(BITS_PER_WORD);
139.142 + sizeIsSticky = false;
139.143 + }
139.144 +
139.145 + /**
139.146 + * Creates a bit set whose initial size is large enough to explicitly
139.147 + * represent bits with indices in the range {@code 0} through
139.148 + * {@code nbits-1}. All bits are initially {@code false}.
139.149 + *
139.150 + * @param nbits the initial size of the bit set
139.151 + * @throws NegativeArraySizeException if the specified initial size
139.152 + * is negative
139.153 + */
139.154 + public BitSet(int nbits) {
139.155 + // nbits can't be negative; size 0 is OK
139.156 + if (nbits < 0)
139.157 + throw new NegativeArraySizeException("nbits < 0: " + nbits);
139.158 +
139.159 + initWords(nbits);
139.160 + sizeIsSticky = true;
139.161 + }
139.162 +
139.163 + private void initWords(int nbits) {
139.164 + words = new long[wordIndex(nbits-1) + 1];
139.165 + }
139.166 +
139.167 + /**
139.168 + * Creates a bit set using words as the internal representation.
139.169 + * The last word (if there is one) must be non-zero.
139.170 + */
139.171 + private BitSet(long[] words) {
139.172 + this.words = words;
139.173 + this.wordsInUse = words.length;
139.174 + checkInvariants();
139.175 + }
139.176 +
139.177 + /**
139.178 + * Returns a new bit set containing all the bits in the given long array.
139.179 + *
139.180 + * <p>More precisely,
139.181 + * <br>{@code BitSet.valueOf(longs).get(n) == ((longs[n/64] & (1L<<(n%64))) != 0)}
139.182 + * <br>for all {@code n < 64 * longs.length}.
139.183 + *
139.184 + * <p>This method is equivalent to
139.185 + * {@code BitSet.valueOf(LongBuffer.wrap(longs))}.
139.186 + *
139.187 + * @param longs a long array containing a little-endian representation
139.188 + * of a sequence of bits to be used as the initial bits of the
139.189 + * new bit set
139.190 + * @since 1.7
139.191 + */
139.192 + public static BitSet valueOf(long[] longs) {
139.193 + int n;
139.194 + for (n = longs.length; n > 0 && longs[n - 1] == 0; n--)
139.195 + ;
139.196 + return new BitSet(Arrays.copyOf(longs, n));
139.197 + }
139.198 +
139.199 + /**
139.200 + * Returns a new bit set containing all the bits in the given long
139.201 + * buffer between its position and limit.
139.202 + *
139.203 + * <p>More precisely,
139.204 + * <br>{@code BitSet.valueOf(lb).get(n) == ((lb.get(lb.position()+n/64) & (1L<<(n%64))) != 0)}
139.205 + * <br>for all {@code n < 64 * lb.remaining()}.
139.206 + *
139.207 + * <p>The long buffer is not modified by this method, and no
139.208 + * reference to the buffer is retained by the bit set.
139.209 + *
139.210 + * @param lb a long buffer containing a little-endian representation
139.211 + * of a sequence of bits between its position and limit, to be
139.212 + * used as the initial bits of the new bit set
139.213 + * @since 1.7
139.214 + */
139.215 +// public static BitSet valueOf(LongBuffer lb) {
139.216 +// lb = lb.slice();
139.217 +// int n;
139.218 +// for (n = lb.remaining(); n > 0 && lb.get(n - 1) == 0; n--)
139.219 +// ;
139.220 +// long[] words = new long[n];
139.221 +// lb.get(words);
139.222 +// return new BitSet(words);
139.223 +// }
139.224 +
139.225 + /**
139.226 + * Returns a new bit set containing all the bits in the given byte array.
139.227 + *
139.228 + * <p>More precisely,
139.229 + * <br>{@code BitSet.valueOf(bytes).get(n) == ((bytes[n/8] & (1<<(n%8))) != 0)}
139.230 + * <br>for all {@code n < 8 * bytes.length}.
139.231 + *
139.232 + * <p>This method is equivalent to
139.233 + * {@code BitSet.valueOf(ByteBuffer.wrap(bytes))}.
139.234 + *
139.235 + * @param bytes a byte array containing a little-endian
139.236 + * representation of a sequence of bits to be used as the
139.237 + * initial bits of the new bit set
139.238 + * @since 1.7
139.239 + */
139.240 +// public static BitSet valueOf(byte[] bytes) {
139.241 +// return BitSet.valueOf(ByteBuffer.wrap(bytes));
139.242 +// }
139.243 +
139.244 + /**
139.245 + * Returns a new bit set containing all the bits in the given byte
139.246 + * buffer between its position and limit.
139.247 + *
139.248 + * <p>More precisely,
139.249 + * <br>{@code BitSet.valueOf(bb).get(n) == ((bb.get(bb.position()+n/8) & (1<<(n%8))) != 0)}
139.250 + * <br>for all {@code n < 8 * bb.remaining()}.
139.251 + *
139.252 + * <p>The byte buffer is not modified by this method, and no
139.253 + * reference to the buffer is retained by the bit set.
139.254 + *
139.255 + * @param bb a byte buffer containing a little-endian representation
139.256 + * of a sequence of bits between its position and limit, to be
139.257 + * used as the initial bits of the new bit set
139.258 + * @since 1.7
139.259 + */
139.260 +// public static BitSet valueOf(ByteBuffer bb) {
139.261 +// bb = bb.slice().order(ByteOrder.LITTLE_ENDIAN);
139.262 +// int n;
139.263 +// for (n = bb.remaining(); n > 0 && bb.get(n - 1) == 0; n--)
139.264 +// ;
139.265 +// long[] words = new long[(n + 7) / 8];
139.266 +// bb.limit(n);
139.267 +// int i = 0;
139.268 +// while (bb.remaining() >= 8)
139.269 +// words[i++] = bb.getLong();
139.270 +// for (int remaining = bb.remaining(), j = 0; j < remaining; j++)
139.271 +// words[i] |= (bb.get() & 0xffL) << (8 * j);
139.272 +// return new BitSet(words);
139.273 +// }
139.274 +
139.275 + /**
139.276 + * Returns a new byte array containing all the bits in this bit set.
139.277 + *
139.278 + * <p>More precisely, if
139.279 + * <br>{@code byte[] bytes = s.toByteArray();}
139.280 + * <br>then {@code bytes.length == (s.length()+7)/8} and
139.281 + * <br>{@code s.get(n) == ((bytes[n/8] & (1<<(n%8))) != 0)}
139.282 + * <br>for all {@code n < 8 * bytes.length}.
139.283 + *
139.284 + * @return a byte array containing a little-endian representation
139.285 + * of all the bits in this bit set
139.286 + * @since 1.7
139.287 + */
139.288 +// public byte[] toByteArray() {
139.289 +// int n = wordsInUse;
139.290 +// if (n == 0)
139.291 +// return new byte[0];
139.292 +// int len = 8 * (n-1);
139.293 +// for (long x = words[n - 1]; x != 0; x >>>= 8)
139.294 +// len++;
139.295 +// byte[] bytes = new byte[len];
139.296 +// ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
139.297 +// for (int i = 0; i < n - 1; i++)
139.298 +// bb.putLong(words[i]);
139.299 +// for (long x = words[n - 1]; x != 0; x >>>= 8)
139.300 +// bb.put((byte) (x & 0xff));
139.301 +// return bytes;
139.302 +// }
139.303 +
139.304 + /**
139.305 + * Returns a new long array containing all the bits in this bit set.
139.306 + *
139.307 + * <p>More precisely, if
139.308 + * <br>{@code long[] longs = s.toLongArray();}
139.309 + * <br>then {@code longs.length == (s.length()+63)/64} and
139.310 + * <br>{@code s.get(n) == ((longs[n/64] & (1L<<(n%64))) != 0)}
139.311 + * <br>for all {@code n < 64 * longs.length}.
139.312 + *
139.313 + * @return a long array containing a little-endian representation
139.314 + * of all the bits in this bit set
139.315 + * @since 1.7
139.316 + */
139.317 + public long[] toLongArray() {
139.318 + return Arrays.copyOf(words, wordsInUse);
139.319 + }
139.320 +
139.321 + /**
139.322 + * Ensures that the BitSet can hold enough words.
139.323 + * @param wordsRequired the minimum acceptable number of words.
139.324 + */
139.325 + private void ensureCapacity(int wordsRequired) {
139.326 + if (words.length < wordsRequired) {
139.327 + // Allocate larger of doubled size or required size
139.328 + int request = Math.max(2 * words.length, wordsRequired);
139.329 + words = Arrays.copyOf(words, request);
139.330 + sizeIsSticky = false;
139.331 + }
139.332 + }
139.333 +
139.334 + /**
139.335 + * Ensures that the BitSet can accommodate a given wordIndex,
139.336 + * temporarily violating the invariants. The caller must
139.337 + * restore the invariants before returning to the user,
139.338 + * possibly using recalculateWordsInUse().
139.339 + * @param wordIndex the index to be accommodated.
139.340 + */
139.341 + private void expandTo(int wordIndex) {
139.342 + int wordsRequired = wordIndex+1;
139.343 + if (wordsInUse < wordsRequired) {
139.344 + ensureCapacity(wordsRequired);
139.345 + wordsInUse = wordsRequired;
139.346 + }
139.347 + }
139.348 +
139.349 + /**
139.350 + * Checks that fromIndex ... toIndex is a valid range of bit indices.
139.351 + */
139.352 + private static void checkRange(int fromIndex, int toIndex) {
139.353 + if (fromIndex < 0)
139.354 + throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
139.355 + if (toIndex < 0)
139.356 + throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex);
139.357 + if (fromIndex > toIndex)
139.358 + throw new IndexOutOfBoundsException("fromIndex: " + fromIndex +
139.359 + " > toIndex: " + toIndex);
139.360 + }
139.361 +
139.362 + /**
139.363 + * Sets the bit at the specified index to the complement of its
139.364 + * current value.
139.365 + *
139.366 + * @param bitIndex the index of the bit to flip
139.367 + * @throws IndexOutOfBoundsException if the specified index is negative
139.368 + * @since 1.4
139.369 + */
139.370 + public void flip(int bitIndex) {
139.371 + if (bitIndex < 0)
139.372 + throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
139.373 +
139.374 + int wordIndex = wordIndex(bitIndex);
139.375 + expandTo(wordIndex);
139.376 +
139.377 + words[wordIndex] ^= (1L << bitIndex);
139.378 +
139.379 + recalculateWordsInUse();
139.380 + checkInvariants();
139.381 + }
139.382 +
139.383 + /**
139.384 + * Sets each bit from the specified {@code fromIndex} (inclusive) to the
139.385 + * specified {@code toIndex} (exclusive) to the complement of its current
139.386 + * value.
139.387 + *
139.388 + * @param fromIndex index of the first bit to flip
139.389 + * @param toIndex index after the last bit to flip
139.390 + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative,
139.391 + * or {@code toIndex} is negative, or {@code fromIndex} is
139.392 + * larger than {@code toIndex}
139.393 + * @since 1.4
139.394 + */
139.395 + public void flip(int fromIndex, int toIndex) {
139.396 + checkRange(fromIndex, toIndex);
139.397 +
139.398 + if (fromIndex == toIndex)
139.399 + return;
139.400 +
139.401 + int startWordIndex = wordIndex(fromIndex);
139.402 + int endWordIndex = wordIndex(toIndex - 1);
139.403 + expandTo(endWordIndex);
139.404 +
139.405 + long firstWordMask = WORD_MASK << fromIndex;
139.406 + long lastWordMask = WORD_MASK >>> -toIndex;
139.407 + if (startWordIndex == endWordIndex) {
139.408 + // Case 1: One word
139.409 + words[startWordIndex] ^= (firstWordMask & lastWordMask);
139.410 + } else {
139.411 + // Case 2: Multiple words
139.412 + // Handle first word
139.413 + words[startWordIndex] ^= firstWordMask;
139.414 +
139.415 + // Handle intermediate words, if any
139.416 + for (int i = startWordIndex+1; i < endWordIndex; i++)
139.417 + words[i] ^= WORD_MASK;
139.418 +
139.419 + // Handle last word
139.420 + words[endWordIndex] ^= lastWordMask;
139.421 + }
139.422 +
139.423 + recalculateWordsInUse();
139.424 + checkInvariants();
139.425 + }
139.426 +
139.427 + /**
139.428 + * Sets the bit at the specified index to {@code true}.
139.429 + *
139.430 + * @param bitIndex a bit index
139.431 + * @throws IndexOutOfBoundsException if the specified index is negative
139.432 + * @since JDK1.0
139.433 + */
139.434 + public void set(int bitIndex) {
139.435 + if (bitIndex < 0)
139.436 + throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
139.437 +
139.438 + int wordIndex = wordIndex(bitIndex);
139.439 + expandTo(wordIndex);
139.440 +
139.441 + words[wordIndex] |= (1L << bitIndex); // Restores invariants
139.442 +
139.443 + checkInvariants();
139.444 + }
139.445 +
139.446 + /**
139.447 + * Sets the bit at the specified index to the specified value.
139.448 + *
139.449 + * @param bitIndex a bit index
139.450 + * @param value a boolean value to set
139.451 + * @throws IndexOutOfBoundsException if the specified index is negative
139.452 + * @since 1.4
139.453 + */
139.454 + public void set(int bitIndex, boolean value) {
139.455 + if (value)
139.456 + set(bitIndex);
139.457 + else
139.458 + clear(bitIndex);
139.459 + }
139.460 +
139.461 + /**
139.462 + * Sets the bits from the specified {@code fromIndex} (inclusive) to the
139.463 + * specified {@code toIndex} (exclusive) to {@code true}.
139.464 + *
139.465 + * @param fromIndex index of the first bit to be set
139.466 + * @param toIndex index after the last bit to be set
139.467 + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative,
139.468 + * or {@code toIndex} is negative, or {@code fromIndex} is
139.469 + * larger than {@code toIndex}
139.470 + * @since 1.4
139.471 + */
139.472 + public void set(int fromIndex, int toIndex) {
139.473 + checkRange(fromIndex, toIndex);
139.474 +
139.475 + if (fromIndex == toIndex)
139.476 + return;
139.477 +
139.478 + // Increase capacity if necessary
139.479 + int startWordIndex = wordIndex(fromIndex);
139.480 + int endWordIndex = wordIndex(toIndex - 1);
139.481 + expandTo(endWordIndex);
139.482 +
139.483 + long firstWordMask = WORD_MASK << fromIndex;
139.484 + long lastWordMask = WORD_MASK >>> -toIndex;
139.485 + if (startWordIndex == endWordIndex) {
139.486 + // Case 1: One word
139.487 + words[startWordIndex] |= (firstWordMask & lastWordMask);
139.488 + } else {
139.489 + // Case 2: Multiple words
139.490 + // Handle first word
139.491 + words[startWordIndex] |= firstWordMask;
139.492 +
139.493 + // Handle intermediate words, if any
139.494 + for (int i = startWordIndex+1; i < endWordIndex; i++)
139.495 + words[i] = WORD_MASK;
139.496 +
139.497 + // Handle last word (restores invariants)
139.498 + words[endWordIndex] |= lastWordMask;
139.499 + }
139.500 +
139.501 + checkInvariants();
139.502 + }
139.503 +
139.504 + /**
139.505 + * Sets the bits from the specified {@code fromIndex} (inclusive) to the
139.506 + * specified {@code toIndex} (exclusive) to the specified value.
139.507 + *
139.508 + * @param fromIndex index of the first bit to be set
139.509 + * @param toIndex index after the last bit to be set
139.510 + * @param value value to set the selected bits to
139.511 + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative,
139.512 + * or {@code toIndex} is negative, or {@code fromIndex} is
139.513 + * larger than {@code toIndex}
139.514 + * @since 1.4
139.515 + */
139.516 + public void set(int fromIndex, int toIndex, boolean value) {
139.517 + if (value)
139.518 + set(fromIndex, toIndex);
139.519 + else
139.520 + clear(fromIndex, toIndex);
139.521 + }
139.522 +
139.523 + /**
139.524 + * Sets the bit specified by the index to {@code false}.
139.525 + *
139.526 + * @param bitIndex the index of the bit to be cleared
139.527 + * @throws IndexOutOfBoundsException if the specified index is negative
139.528 + * @since JDK1.0
139.529 + */
139.530 + public void clear(int bitIndex) {
139.531 + if (bitIndex < 0)
139.532 + throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
139.533 +
139.534 + int wordIndex = wordIndex(bitIndex);
139.535 + if (wordIndex >= wordsInUse)
139.536 + return;
139.537 +
139.538 + words[wordIndex] &= ~(1L << bitIndex);
139.539 +
139.540 + recalculateWordsInUse();
139.541 + checkInvariants();
139.542 + }
139.543 +
139.544 + /**
139.545 + * Sets the bits from the specified {@code fromIndex} (inclusive) to the
139.546 + * specified {@code toIndex} (exclusive) to {@code false}.
139.547 + *
139.548 + * @param fromIndex index of the first bit to be cleared
139.549 + * @param toIndex index after the last bit to be cleared
139.550 + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative,
139.551 + * or {@code toIndex} is negative, or {@code fromIndex} is
139.552 + * larger than {@code toIndex}
139.553 + * @since 1.4
139.554 + */
139.555 + public void clear(int fromIndex, int toIndex) {
139.556 + checkRange(fromIndex, toIndex);
139.557 +
139.558 + if (fromIndex == toIndex)
139.559 + return;
139.560 +
139.561 + int startWordIndex = wordIndex(fromIndex);
139.562 + if (startWordIndex >= wordsInUse)
139.563 + return;
139.564 +
139.565 + int endWordIndex = wordIndex(toIndex - 1);
139.566 + if (endWordIndex >= wordsInUse) {
139.567 + toIndex = length();
139.568 + endWordIndex = wordsInUse - 1;
139.569 + }
139.570 +
139.571 + long firstWordMask = WORD_MASK << fromIndex;
139.572 + long lastWordMask = WORD_MASK >>> -toIndex;
139.573 + if (startWordIndex == endWordIndex) {
139.574 + // Case 1: One word
139.575 + words[startWordIndex] &= ~(firstWordMask & lastWordMask);
139.576 + } else {
139.577 + // Case 2: Multiple words
139.578 + // Handle first word
139.579 + words[startWordIndex] &= ~firstWordMask;
139.580 +
139.581 + // Handle intermediate words, if any
139.582 + for (int i = startWordIndex+1; i < endWordIndex; i++)
139.583 + words[i] = 0;
139.584 +
139.585 + // Handle last word
139.586 + words[endWordIndex] &= ~lastWordMask;
139.587 + }
139.588 +
139.589 + recalculateWordsInUse();
139.590 + checkInvariants();
139.591 + }
139.592 +
139.593 + /**
139.594 + * Sets all of the bits in this BitSet to {@code false}.
139.595 + *
139.596 + * @since 1.4
139.597 + */
139.598 + public void clear() {
139.599 + while (wordsInUse > 0)
139.600 + words[--wordsInUse] = 0;
139.601 + }
139.602 +
139.603 + /**
139.604 + * Returns the value of the bit with the specified index. The value
139.605 + * is {@code true} if the bit with the index {@code bitIndex}
139.606 + * is currently set in this {@code BitSet}; otherwise, the result
139.607 + * is {@code false}.
139.608 + *
139.609 + * @param bitIndex the bit index
139.610 + * @return the value of the bit with the specified index
139.611 + * @throws IndexOutOfBoundsException if the specified index is negative
139.612 + */
139.613 + public boolean get(int bitIndex) {
139.614 + if (bitIndex < 0)
139.615 + throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
139.616 +
139.617 + checkInvariants();
139.618 +
139.619 + int wordIndex = wordIndex(bitIndex);
139.620 + return (wordIndex < wordsInUse)
139.621 + && ((words[wordIndex] & (1L << bitIndex)) != 0);
139.622 + }
139.623 +
139.624 + /**
139.625 + * Returns a new {@code BitSet} composed of bits from this {@code BitSet}
139.626 + * from {@code fromIndex} (inclusive) to {@code toIndex} (exclusive).
139.627 + *
139.628 + * @param fromIndex index of the first bit to include
139.629 + * @param toIndex index after the last bit to include
139.630 + * @return a new {@code BitSet} from a range of this {@code BitSet}
139.631 + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative,
139.632 + * or {@code toIndex} is negative, or {@code fromIndex} is
139.633 + * larger than {@code toIndex}
139.634 + * @since 1.4
139.635 + */
139.636 + public BitSet get(int fromIndex, int toIndex) {
139.637 + checkRange(fromIndex, toIndex);
139.638 +
139.639 + checkInvariants();
139.640 +
139.641 + int len = length();
139.642 +
139.643 + // If no set bits in range return empty bitset
139.644 + if (len <= fromIndex || fromIndex == toIndex)
139.645 + return new BitSet(0);
139.646 +
139.647 + // An optimization
139.648 + if (toIndex > len)
139.649 + toIndex = len;
139.650 +
139.651 + BitSet result = new BitSet(toIndex - fromIndex);
139.652 + int targetWords = wordIndex(toIndex - fromIndex - 1) + 1;
139.653 + int sourceIndex = wordIndex(fromIndex);
139.654 + boolean wordAligned = ((fromIndex & BIT_INDEX_MASK) == 0);
139.655 +
139.656 + // Process all words but the last word
139.657 + for (int i = 0; i < targetWords - 1; i++, sourceIndex++)
139.658 + result.words[i] = wordAligned ? words[sourceIndex] :
139.659 + (words[sourceIndex] >>> fromIndex) |
139.660 + (words[sourceIndex+1] << -fromIndex);
139.661 +
139.662 + // Process the last word
139.663 + long lastWordMask = WORD_MASK >>> -toIndex;
139.664 + result.words[targetWords - 1] =
139.665 + ((toIndex-1) & BIT_INDEX_MASK) < (fromIndex & BIT_INDEX_MASK)
139.666 + ? /* straddles source words */
139.667 + ((words[sourceIndex] >>> fromIndex) |
139.668 + (words[sourceIndex+1] & lastWordMask) << -fromIndex)
139.669 + :
139.670 + ((words[sourceIndex] & lastWordMask) >>> fromIndex);
139.671 +
139.672 + // Set wordsInUse correctly
139.673 + result.wordsInUse = targetWords;
139.674 + result.recalculateWordsInUse();
139.675 + result.checkInvariants();
139.676 +
139.677 + return result;
139.678 + }
139.679 +
139.680 + /**
139.681 + * Returns the index of the first bit that is set to {@code true}
139.682 + * that occurs on or after the specified starting index. If no such
139.683 + * bit exists then {@code -1} is returned.
139.684 + *
139.685 + * <p>To iterate over the {@code true} bits in a {@code BitSet},
139.686 + * use the following loop:
139.687 + *
139.688 + * <pre> {@code
139.689 + * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
139.690 + * // operate on index i here
139.691 + * }}</pre>
139.692 + *
139.693 + * @param fromIndex the index to start checking from (inclusive)
139.694 + * @return the index of the next set bit, or {@code -1} if there
139.695 + * is no such bit
139.696 + * @throws IndexOutOfBoundsException if the specified index is negative
139.697 + * @since 1.4
139.698 + */
139.699 + public int nextSetBit(int fromIndex) {
139.700 + if (fromIndex < 0)
139.701 + throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
139.702 +
139.703 + checkInvariants();
139.704 +
139.705 + int u = wordIndex(fromIndex);
139.706 + if (u >= wordsInUse)
139.707 + return -1;
139.708 +
139.709 + long word = words[u] & (WORD_MASK << fromIndex);
139.710 +
139.711 + while (true) {
139.712 + if (word != 0)
139.713 + return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word);
139.714 + if (++u == wordsInUse)
139.715 + return -1;
139.716 + word = words[u];
139.717 + }
139.718 + }
139.719 +
139.720 + /**
139.721 + * Returns the index of the first bit that is set to {@code false}
139.722 + * that occurs on or after the specified starting index.
139.723 + *
139.724 + * @param fromIndex the index to start checking from (inclusive)
139.725 + * @return the index of the next clear bit
139.726 + * @throws IndexOutOfBoundsException if the specified index is negative
139.727 + * @since 1.4
139.728 + */
139.729 + public int nextClearBit(int fromIndex) {
139.730 + // Neither spec nor implementation handle bitsets of maximal length.
139.731 + // See 4816253.
139.732 + if (fromIndex < 0)
139.733 + throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
139.734 +
139.735 + checkInvariants();
139.736 +
139.737 + int u = wordIndex(fromIndex);
139.738 + if (u >= wordsInUse)
139.739 + return fromIndex;
139.740 +
139.741 + long word = ~words[u] & (WORD_MASK << fromIndex);
139.742 +
139.743 + while (true) {
139.744 + if (word != 0)
139.745 + return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word);
139.746 + if (++u == wordsInUse)
139.747 + return wordsInUse * BITS_PER_WORD;
139.748 + word = ~words[u];
139.749 + }
139.750 + }
139.751 +
139.752 + /**
139.753 + * Returns the index of the nearest bit that is set to {@code true}
139.754 + * that occurs on or before the specified starting index.
139.755 + * If no such bit exists, or if {@code -1} is given as the
139.756 + * starting index, then {@code -1} is returned.
139.757 + *
139.758 + * <p>To iterate over the {@code true} bits in a {@code BitSet},
139.759 + * use the following loop:
139.760 + *
139.761 + * <pre> {@code
139.762 + * for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
139.763 + * // operate on index i here
139.764 + * }}</pre>
139.765 + *
139.766 + * @param fromIndex the index to start checking from (inclusive)
139.767 + * @return the index of the previous set bit, or {@code -1} if there
139.768 + * is no such bit
139.769 + * @throws IndexOutOfBoundsException if the specified index is less
139.770 + * than {@code -1}
139.771 + * @since 1.7
139.772 + */
139.773 + public int previousSetBit(int fromIndex) {
139.774 + if (fromIndex < 0) {
139.775 + if (fromIndex == -1)
139.776 + return -1;
139.777 + throw new IndexOutOfBoundsException(
139.778 + "fromIndex < -1: " + fromIndex);
139.779 + }
139.780 +
139.781 + checkInvariants();
139.782 +
139.783 + int u = wordIndex(fromIndex);
139.784 + if (u >= wordsInUse)
139.785 + return length() - 1;
139.786 +
139.787 + long word = words[u] & (WORD_MASK >>> -(fromIndex+1));
139.788 +
139.789 + while (true) {
139.790 + if (word != 0)
139.791 + return (u+1) * BITS_PER_WORD - 1 - Long.numberOfLeadingZeros(word);
139.792 + if (u-- == 0)
139.793 + return -1;
139.794 + word = words[u];
139.795 + }
139.796 + }
139.797 +
139.798 + /**
139.799 + * Returns the index of the nearest bit that is set to {@code false}
139.800 + * that occurs on or before the specified starting index.
139.801 + * If no such bit exists, or if {@code -1} is given as the
139.802 + * starting index, then {@code -1} is returned.
139.803 + *
139.804 + * @param fromIndex the index to start checking from (inclusive)
139.805 + * @return the index of the previous clear bit, or {@code -1} if there
139.806 + * is no such bit
139.807 + * @throws IndexOutOfBoundsException if the specified index is less
139.808 + * than {@code -1}
139.809 + * @since 1.7
139.810 + */
139.811 + public int previousClearBit(int fromIndex) {
139.812 + if (fromIndex < 0) {
139.813 + if (fromIndex == -1)
139.814 + return -1;
139.815 + throw new IndexOutOfBoundsException(
139.816 + "fromIndex < -1: " + fromIndex);
139.817 + }
139.818 +
139.819 + checkInvariants();
139.820 +
139.821 + int u = wordIndex(fromIndex);
139.822 + if (u >= wordsInUse)
139.823 + return fromIndex;
139.824 +
139.825 + long word = ~words[u] & (WORD_MASK >>> -(fromIndex+1));
139.826 +
139.827 + while (true) {
139.828 + if (word != 0)
139.829 + return (u+1) * BITS_PER_WORD -1 - Long.numberOfLeadingZeros(word);
139.830 + if (u-- == 0)
139.831 + return -1;
139.832 + word = ~words[u];
139.833 + }
139.834 + }
139.835 +
139.836 + /**
139.837 + * Returns the "logical size" of this {@code BitSet}: the index of
139.838 + * the highest set bit in the {@code BitSet} plus one. Returns zero
139.839 + * if the {@code BitSet} contains no set bits.
139.840 + *
139.841 + * @return the logical size of this {@code BitSet}
139.842 + * @since 1.2
139.843 + */
139.844 + public int length() {
139.845 + if (wordsInUse == 0)
139.846 + return 0;
139.847 +
139.848 + return BITS_PER_WORD * (wordsInUse - 1) +
139.849 + (BITS_PER_WORD - Long.numberOfLeadingZeros(words[wordsInUse - 1]));
139.850 + }
139.851 +
139.852 + /**
139.853 + * Returns true if this {@code BitSet} contains no bits that are set
139.854 + * to {@code true}.
139.855 + *
139.856 + * @return boolean indicating whether this {@code BitSet} is empty
139.857 + * @since 1.4
139.858 + */
139.859 + public boolean isEmpty() {
139.860 + return wordsInUse == 0;
139.861 + }
139.862 +
139.863 + /**
139.864 + * Returns true if the specified {@code BitSet} has any bits set to
139.865 + * {@code true} that are also set to {@code true} in this {@code BitSet}.
139.866 + *
139.867 + * @param set {@code BitSet} to intersect with
139.868 + * @return boolean indicating whether this {@code BitSet} intersects
139.869 + * the specified {@code BitSet}
139.870 + * @since 1.4
139.871 + */
139.872 + public boolean intersects(BitSet set) {
139.873 + for (int i = Math.min(wordsInUse, set.wordsInUse) - 1; i >= 0; i--)
139.874 + if ((words[i] & set.words[i]) != 0)
139.875 + return true;
139.876 + return false;
139.877 + }
139.878 +
139.879 + /**
139.880 + * Returns the number of bits set to {@code true} in this {@code BitSet}.
139.881 + *
139.882 + * @return the number of bits set to {@code true} in this {@code BitSet}
139.883 + * @since 1.4
139.884 + */
139.885 + public int cardinality() {
139.886 + int sum = 0;
139.887 + for (int i = 0; i < wordsInUse; i++)
139.888 + sum += Long.bitCount(words[i]);
139.889 + return sum;
139.890 + }
139.891 +
139.892 + /**
139.893 + * Performs a logical <b>AND</b> of this target bit set with the
139.894 + * argument bit set. This bit set is modified so that each bit in it
139.895 + * has the value {@code true} if and only if it both initially
139.896 + * had the value {@code true} and the corresponding bit in the
139.897 + * bit set argument also had the value {@code true}.
139.898 + *
139.899 + * @param set a bit set
139.900 + */
139.901 + public void and(BitSet set) {
139.902 + if (this == set)
139.903 + return;
139.904 +
139.905 + while (wordsInUse > set.wordsInUse)
139.906 + words[--wordsInUse] = 0;
139.907 +
139.908 + // Perform logical AND on words in common
139.909 + for (int i = 0; i < wordsInUse; i++)
139.910 + words[i] &= set.words[i];
139.911 +
139.912 + recalculateWordsInUse();
139.913 + checkInvariants();
139.914 + }
139.915 +
139.916 + /**
139.917 + * Performs a logical <b>OR</b> of this bit set with the bit set
139.918 + * argument. This bit set is modified so that a bit in it has the
139.919 + * value {@code true} if and only if it either already had the
139.920 + * value {@code true} or the corresponding bit in the bit set
139.921 + * argument has the value {@code true}.
139.922 + *
139.923 + * @param set a bit set
139.924 + */
139.925 + public void or(BitSet set) {
139.926 + if (this == set)
139.927 + return;
139.928 +
139.929 + int wordsInCommon = Math.min(wordsInUse, set.wordsInUse);
139.930 +
139.931 + if (wordsInUse < set.wordsInUse) {
139.932 + ensureCapacity(set.wordsInUse);
139.933 + wordsInUse = set.wordsInUse;
139.934 + }
139.935 +
139.936 + // Perform logical OR on words in common
139.937 + for (int i = 0; i < wordsInCommon; i++)
139.938 + words[i] |= set.words[i];
139.939 +
139.940 + // Copy any remaining words
139.941 + if (wordsInCommon < set.wordsInUse)
139.942 + System.arraycopy(set.words, wordsInCommon,
139.943 + words, wordsInCommon,
139.944 + wordsInUse - wordsInCommon);
139.945 +
139.946 + // recalculateWordsInUse() is unnecessary
139.947 + checkInvariants();
139.948 + }
139.949 +
139.950 + /**
139.951 + * Performs a logical <b>XOR</b> of this bit set with the bit set
139.952 + * argument. This bit set is modified so that a bit in it has the
139.953 + * value {@code true} if and only if one of the following
139.954 + * statements holds:
139.955 + * <ul>
139.956 + * <li>The bit initially has the value {@code true}, and the
139.957 + * corresponding bit in the argument has the value {@code false}.
139.958 + * <li>The bit initially has the value {@code false}, and the
139.959 + * corresponding bit in the argument has the value {@code true}.
139.960 + * </ul>
139.961 + *
139.962 + * @param set a bit set
139.963 + */
139.964 + public void xor(BitSet set) {
139.965 + int wordsInCommon = Math.min(wordsInUse, set.wordsInUse);
139.966 +
139.967 + if (wordsInUse < set.wordsInUse) {
139.968 + ensureCapacity(set.wordsInUse);
139.969 + wordsInUse = set.wordsInUse;
139.970 + }
139.971 +
139.972 + // Perform logical XOR on words in common
139.973 + for (int i = 0; i < wordsInCommon; i++)
139.974 + words[i] ^= set.words[i];
139.975 +
139.976 + // Copy any remaining words
139.977 + if (wordsInCommon < set.wordsInUse)
139.978 + System.arraycopy(set.words, wordsInCommon,
139.979 + words, wordsInCommon,
139.980 + set.wordsInUse - wordsInCommon);
139.981 +
139.982 + recalculateWordsInUse();
139.983 + checkInvariants();
139.984 + }
139.985 +
139.986 + /**
139.987 + * Clears all of the bits in this {@code BitSet} whose corresponding
139.988 + * bit is set in the specified {@code BitSet}.
139.989 + *
139.990 + * @param set the {@code BitSet} with which to mask this
139.991 + * {@code BitSet}
139.992 + * @since 1.2
139.993 + */
139.994 + public void andNot(BitSet set) {
139.995 + // Perform logical (a & !b) on words in common
139.996 + for (int i = Math.min(wordsInUse, set.wordsInUse) - 1; i >= 0; i--)
139.997 + words[i] &= ~set.words[i];
139.998 +
139.999 + recalculateWordsInUse();
139.1000 + checkInvariants();
139.1001 + }
139.1002 +
139.1003 + /**
139.1004 + * Returns the hash code value for this bit set. The hash code depends
139.1005 + * only on which bits are set within this {@code BitSet}.
139.1006 + *
139.1007 + * <p>The hash code is defined to be the result of the following
139.1008 + * calculation:
139.1009 + * <pre> {@code
139.1010 + * public int hashCode() {
139.1011 + * long h = 1234;
139.1012 + * long[] words = toLongArray();
139.1013 + * for (int i = words.length; --i >= 0; )
139.1014 + * h ^= words[i] * (i + 1);
139.1015 + * return (int)((h >> 32) ^ h);
139.1016 + * }}</pre>
139.1017 + * Note that the hash code changes if the set of bits is altered.
139.1018 + *
139.1019 + * @return the hash code value for this bit set
139.1020 + */
139.1021 + public int hashCode() {
139.1022 + long h = 1234;
139.1023 + for (int i = wordsInUse; --i >= 0; )
139.1024 + h ^= words[i] * (i + 1);
139.1025 +
139.1026 + return (int)((h >> 32) ^ h);
139.1027 + }
139.1028 +
139.1029 + /**
139.1030 + * Returns the number of bits of space actually in use by this
139.1031 + * {@code BitSet} to represent bit values.
139.1032 + * The maximum element in the set is the size - 1st element.
139.1033 + *
139.1034 + * @return the number of bits currently in this bit set
139.1035 + */
139.1036 + public int size() {
139.1037 + return words.length * BITS_PER_WORD;
139.1038 + }
139.1039 +
139.1040 + /**
139.1041 + * Compares this object against the specified object.
139.1042 + * The result is {@code true} if and only if the argument is
139.1043 + * not {@code null} and is a {@code Bitset} object that has
139.1044 + * exactly the same set of bits set to {@code true} as this bit
139.1045 + * set. That is, for every nonnegative {@code int} index {@code k},
139.1046 + * <pre>((BitSet)obj).get(k) == this.get(k)</pre>
139.1047 + * must be true. The current sizes of the two bit sets are not compared.
139.1048 + *
139.1049 + * @param obj the object to compare with
139.1050 + * @return {@code true} if the objects are the same;
139.1051 + * {@code false} otherwise
139.1052 + * @see #size()
139.1053 + */
139.1054 + public boolean equals(Object obj) {
139.1055 + if (!(obj instanceof BitSet))
139.1056 + return false;
139.1057 + if (this == obj)
139.1058 + return true;
139.1059 +
139.1060 + BitSet set = (BitSet) obj;
139.1061 +
139.1062 + checkInvariants();
139.1063 + set.checkInvariants();
139.1064 +
139.1065 + if (wordsInUse != set.wordsInUse)
139.1066 + return false;
139.1067 +
139.1068 + // Check words in use by both BitSets
139.1069 + for (int i = 0; i < wordsInUse; i++)
139.1070 + if (words[i] != set.words[i])
139.1071 + return false;
139.1072 +
139.1073 + return true;
139.1074 + }
139.1075 +
139.1076 + /**
139.1077 + * Cloning this {@code BitSet} produces a new {@code BitSet}
139.1078 + * that is equal to it.
139.1079 + * The clone of the bit set is another bit set that has exactly the
139.1080 + * same bits set to {@code true} as this bit set.
139.1081 + *
139.1082 + * @return a clone of this bit set
139.1083 + * @see #size()
139.1084 + */
139.1085 + public Object clone() {
139.1086 + if (! sizeIsSticky)
139.1087 + trimToSize();
139.1088 +
139.1089 + try {
139.1090 + BitSet result = (BitSet) super.clone();
139.1091 + result.words = words.clone();
139.1092 + result.checkInvariants();
139.1093 + return result;
139.1094 + } catch (CloneNotSupportedException e) {
139.1095 + throw new InternalError();
139.1096 + }
139.1097 + }
139.1098 +
139.1099 + /**
139.1100 + * Attempts to reduce internal storage used for the bits in this bit set.
139.1101 + * Calling this method may, but is not required to, affect the value
139.1102 + * returned by a subsequent call to the {@link #size()} method.
139.1103 + */
139.1104 + private void trimToSize() {
139.1105 + if (wordsInUse != words.length) {
139.1106 + words = Arrays.copyOf(words, wordsInUse);
139.1107 + checkInvariants();
139.1108 + }
139.1109 + }
139.1110 +
139.1111 + /**
139.1112 + * Save the state of the {@code BitSet} instance to a stream (i.e.,
139.1113 + * serialize it).
139.1114 + */
139.1115 + private void writeObject(ObjectOutputStream s)
139.1116 + throws IOException {
139.1117 +
139.1118 + checkInvariants();
139.1119 +
139.1120 + if (! sizeIsSticky)
139.1121 + trimToSize();
139.1122 +
139.1123 + ObjectOutputStream.PutField fields = s.putFields();
139.1124 + fields.put("bits", words);
139.1125 + s.writeFields();
139.1126 + }
139.1127 +
139.1128 + /**
139.1129 + * Reconstitute the {@code BitSet} instance from a stream (i.e.,
139.1130 + * deserialize it).
139.1131 + */
139.1132 + private void readObject(ObjectInputStream s)
139.1133 + throws IOException, ClassNotFoundException {
139.1134 +
139.1135 + ObjectInputStream.GetField fields = s.readFields();
139.1136 + words = (long[]) fields.get("bits", null);
139.1137 +
139.1138 + // Assume maximum length then find real length
139.1139 + // because recalculateWordsInUse assumes maintenance
139.1140 + // or reduction in logical size
139.1141 + wordsInUse = words.length;
139.1142 + recalculateWordsInUse();
139.1143 + sizeIsSticky = (words.length > 0 && words[words.length-1] == 0L); // heuristic
139.1144 + checkInvariants();
139.1145 + }
139.1146 +
139.1147 + /**
139.1148 + * Returns a string representation of this bit set. For every index
139.1149 + * for which this {@code BitSet} contains a bit in the set
139.1150 + * state, the decimal representation of that index is included in
139.1151 + * the result. Such indices are listed in order from lowest to
139.1152 + * highest, separated by ", " (a comma and a space) and
139.1153 + * surrounded by braces, resulting in the usual mathematical
139.1154 + * notation for a set of integers.
139.1155 + *
139.1156 + * <p>Example:
139.1157 + * <pre>
139.1158 + * BitSet drPepper = new BitSet();</pre>
139.1159 + * Now {@code drPepper.toString()} returns "{@code {}}".<p>
139.1160 + * <pre>
139.1161 + * drPepper.set(2);</pre>
139.1162 + * Now {@code drPepper.toString()} returns "{@code {2}}".<p>
139.1163 + * <pre>
139.1164 + * drPepper.set(4);
139.1165 + * drPepper.set(10);</pre>
139.1166 + * Now {@code drPepper.toString()} returns "{@code {2, 4, 10}}".
139.1167 + *
139.1168 + * @return a string representation of this bit set
139.1169 + */
139.1170 + public String toString() {
139.1171 + checkInvariants();
139.1172 +
139.1173 + int numBits = (wordsInUse > 128) ?
139.1174 + cardinality() : wordsInUse * BITS_PER_WORD;
139.1175 + StringBuilder b = new StringBuilder(6*numBits + 2);
139.1176 + b.append('{');
139.1177 +
139.1178 + int i = nextSetBit(0);
139.1179 + if (i != -1) {
139.1180 + b.append(i);
139.1181 + for (i = nextSetBit(i+1); i >= 0; i = nextSetBit(i+1)) {
139.1182 + int endOfRun = nextClearBit(i);
139.1183 + do { b.append(", ").append(i); }
139.1184 + while (++i < endOfRun);
139.1185 + }
139.1186 + }
139.1187 +
139.1188 + b.append('}');
139.1189 + return b.toString();
139.1190 + }
139.1191 +}
140.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
140.2 +++ b/rt/emul/compact/src/main/java/java/util/Calendar.java Tue Feb 11 13:31:42 2014 +0100
140.3 @@ -0,0 +1,2806 @@
140.4 +/*
140.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
140.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
140.7 + *
140.8 + * This code is free software; you can redistribute it and/or modify it
140.9 + * under the terms of the GNU General Public License version 2 only, as
140.10 + * published by the Free Software Foundation. Oracle designates this
140.11 + * particular file as subject to the "Classpath" exception as provided
140.12 + * by Oracle in the LICENSE file that accompanied this code.
140.13 + *
140.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
140.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
140.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
140.17 + * version 2 for more details (a copy is included in the LICENSE file that
140.18 + * accompanied this code).
140.19 + *
140.20 + * You should have received a copy of the GNU General Public License version
140.21 + * 2 along with this work; if not, write to the Free Software Foundation,
140.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
140.23 + *
140.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
140.25 + * or visit www.oracle.com if you need additional information or have any
140.26 + * questions.
140.27 + */
140.28 +
140.29 +/*
140.30 + * (C) Copyright Taligent, Inc. 1996-1998 - All Rights Reserved
140.31 + * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
140.32 + *
140.33 + * The original version of this source code and documentation is copyrighted
140.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
140.35 + * materials are provided under terms of a License Agreement between Taligent
140.36 + * and Sun. This technology is protected by multiple US and International
140.37 + * patents. This notice and attribution to Taligent may not be removed.
140.38 + * Taligent is a registered trademark of Taligent, Inc.
140.39 + *
140.40 + */
140.41 +
140.42 +package java.util;
140.43 +
140.44 +import java.io.IOException;
140.45 +import java.io.ObjectInputStream;
140.46 +import java.io.ObjectOutputStream;
140.47 +import java.io.OptionalDataException;
140.48 +import java.io.Serializable;
140.49 +import java.security.AccessController;
140.50 +import java.security.PrivilegedActionException;
140.51 +import java.security.PrivilegedExceptionAction;
140.52 +import java.text.DateFormat;
140.53 +import java.text.DateFormatSymbols;
140.54 +import java.util.concurrent.ConcurrentHashMap;
140.55 +import java.util.concurrent.ConcurrentMap;
140.56 +
140.57 +/**
140.58 + * The <code>Calendar</code> class is an abstract class that provides methods
140.59 + * for converting between a specific instant in time and a set of {@link
140.60 + * #fields calendar fields} such as <code>YEAR</code>, <code>MONTH</code>,
140.61 + * <code>DAY_OF_MONTH</code>, <code>HOUR</code>, and so on, and for
140.62 + * manipulating the calendar fields, such as getting the date of the next
140.63 + * week. An instant in time can be represented by a millisecond value that is
140.64 + * an offset from the <a name="Epoch"><em>Epoch</em></a>, January 1, 1970
140.65 + * 00:00:00.000 GMT (Gregorian).
140.66 + *
140.67 + * <p>The class also provides additional fields and methods for
140.68 + * implementing a concrete calendar system outside the package. Those
140.69 + * fields and methods are defined as <code>protected</code>.
140.70 + *
140.71 + * <p>
140.72 + * Like other locale-sensitive classes, <code>Calendar</code> provides a
140.73 + * class method, <code>getInstance</code>, for getting a generally useful
140.74 + * object of this type. <code>Calendar</code>'s <code>getInstance</code> method
140.75 + * returns a <code>Calendar</code> object whose
140.76 + * calendar fields have been initialized with the current date and time:
140.77 + * <blockquote>
140.78 + * <pre>
140.79 + * Calendar rightNow = Calendar.getInstance();
140.80 + * </pre>
140.81 + * </blockquote>
140.82 + *
140.83 + * <p>A <code>Calendar</code> object can produce all the calendar field values
140.84 + * needed to implement the date-time formatting for a particular language and
140.85 + * calendar style (for example, Japanese-Gregorian, Japanese-Traditional).
140.86 + * <code>Calendar</code> defines the range of values returned by
140.87 + * certain calendar fields, as well as their meaning. For example,
140.88 + * the first month of the calendar system has value <code>MONTH ==
140.89 + * JANUARY</code> for all calendars. Other values are defined by the
140.90 + * concrete subclass, such as <code>ERA</code>. See individual field
140.91 + * documentation and subclass documentation for details.
140.92 + *
140.93 + * <h4>Getting and Setting Calendar Field Values</h4>
140.94 + *
140.95 + * <p>The calendar field values can be set by calling the <code>set</code>
140.96 + * methods. Any field values set in a <code>Calendar</code> will not be
140.97 + * interpreted until it needs to calculate its time value (milliseconds from
140.98 + * the Epoch) or values of the calendar fields. Calling the
140.99 + * <code>get</code>, <code>getTimeInMillis</code>, <code>getTime</code>,
140.100 + * <code>add</code> and <code>roll</code> involves such calculation.
140.101 + *
140.102 + * <h4>Leniency</h4>
140.103 + *
140.104 + * <p><code>Calendar</code> has two modes for interpreting the calendar
140.105 + * fields, <em>lenient</em> and <em>non-lenient</em>. When a
140.106 + * <code>Calendar</code> is in lenient mode, it accepts a wider range of
140.107 + * calendar field values than it produces. When a <code>Calendar</code>
140.108 + * recomputes calendar field values for return by <code>get()</code>, all of
140.109 + * the calendar fields are normalized. For example, a lenient
140.110 + * <code>GregorianCalendar</code> interprets <code>MONTH == JANUARY</code>,
140.111 + * <code>DAY_OF_MONTH == 32</code> as February 1.
140.112 +
140.113 + * <p>When a <code>Calendar</code> is in non-lenient mode, it throws an
140.114 + * exception if there is any inconsistency in its calendar fields. For
140.115 + * example, a <code>GregorianCalendar</code> always produces
140.116 + * <code>DAY_OF_MONTH</code> values between 1 and the length of the month. A
140.117 + * non-lenient <code>GregorianCalendar</code> throws an exception upon
140.118 + * calculating its time or calendar field values if any out-of-range field
140.119 + * value has been set.
140.120 + *
140.121 + * <h4><a name="first_week">First Week</a></h4>
140.122 + *
140.123 + * <code>Calendar</code> defines a locale-specific seven day week using two
140.124 + * parameters: the first day of the week and the minimal days in first week
140.125 + * (from 1 to 7). These numbers are taken from the locale resource data when a
140.126 + * <code>Calendar</code> is constructed. They may also be specified explicitly
140.127 + * through the methods for setting their values.
140.128 + *
140.129 + * <p>When setting or getting the <code>WEEK_OF_MONTH</code> or
140.130 + * <code>WEEK_OF_YEAR</code> fields, <code>Calendar</code> must determine the
140.131 + * first week of the month or year as a reference point. The first week of a
140.132 + * month or year is defined as the earliest seven day period beginning on
140.133 + * <code>getFirstDayOfWeek()</code> and containing at least
140.134 + * <code>getMinimalDaysInFirstWeek()</code> days of that month or year. Weeks
140.135 + * numbered ..., -1, 0 precede the first week; weeks numbered 2, 3,... follow
140.136 + * it. Note that the normalized numbering returned by <code>get()</code> may be
140.137 + * different. For example, a specific <code>Calendar</code> subclass may
140.138 + * designate the week before week 1 of a year as week <code><i>n</i></code> of
140.139 + * the previous year.
140.140 + *
140.141 + * <h4>Calendar Fields Resolution</h4>
140.142 + *
140.143 + * When computing a date and time from the calendar fields, there
140.144 + * may be insufficient information for the computation (such as only
140.145 + * year and month with no day of month), or there may be inconsistent
140.146 + * information (such as Tuesday, July 15, 1996 (Gregorian) -- July 15,
140.147 + * 1996 is actually a Monday). <code>Calendar</code> will resolve
140.148 + * calendar field values to determine the date and time in the
140.149 + * following way.
140.150 + *
140.151 + * <p>If there is any conflict in calendar field values,
140.152 + * <code>Calendar</code> gives priorities to calendar fields that have been set
140.153 + * more recently. The following are the default combinations of the
140.154 + * calendar fields. The most recent combination, as determined by the
140.155 + * most recently set single field, will be used.
140.156 + *
140.157 + * <p><a name="date_resolution">For the date fields</a>:
140.158 + * <blockquote>
140.159 + * <pre>
140.160 + * YEAR + MONTH + DAY_OF_MONTH
140.161 + * YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
140.162 + * YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
140.163 + * YEAR + DAY_OF_YEAR
140.164 + * YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
140.165 + * </pre></blockquote>
140.166 + *
140.167 + * <a name="time_resolution">For the time of day fields</a>:
140.168 + * <blockquote>
140.169 + * <pre>
140.170 + * HOUR_OF_DAY
140.171 + * AM_PM + HOUR
140.172 + * </pre></blockquote>
140.173 + *
140.174 + * <p>If there are any calendar fields whose values haven't been set in the selected
140.175 + * field combination, <code>Calendar</code> uses their default values. The default
140.176 + * value of each field may vary by concrete calendar systems. For example, in
140.177 + * <code>GregorianCalendar</code>, the default of a field is the same as that
140.178 + * of the start of the Epoch: i.e., <code>YEAR = 1970</code>, <code>MONTH =
140.179 + * JANUARY</code>, <code>DAY_OF_MONTH = 1</code>, etc.
140.180 + *
140.181 + * <p>
140.182 + * <strong>Note:</strong> There are certain possible ambiguities in
140.183 + * interpretation of certain singular times, which are resolved in the
140.184 + * following ways:
140.185 + * <ol>
140.186 + * <li> 23:59 is the last minute of the day and 00:00 is the first
140.187 + * minute of the next day. Thus, 23:59 on Dec 31, 1999 < 00:00 on
140.188 + * Jan 1, 2000 < 00:01 on Jan 1, 2000.
140.189 + *
140.190 + * <li> Although historically not precise, midnight also belongs to "am",
140.191 + * and noon belongs to "pm", so on the same day,
140.192 + * 12:00 am (midnight) < 12:01 am, and 12:00 pm (noon) < 12:01 pm
140.193 + * </ol>
140.194 + *
140.195 + * <p>
140.196 + * The date or time format strings are not part of the definition of a
140.197 + * calendar, as those must be modifiable or overridable by the user at
140.198 + * runtime. Use {@link DateFormat}
140.199 + * to format dates.
140.200 + *
140.201 + * <h4>Field Manipulation</h4>
140.202 + *
140.203 + * The calendar fields can be changed using three methods:
140.204 + * <code>set()</code>, <code>add()</code>, and <code>roll()</code>.</p>
140.205 + *
140.206 + * <p><strong><code>set(f, value)</code></strong> changes calendar field
140.207 + * <code>f</code> to <code>value</code>. In addition, it sets an
140.208 + * internal member variable to indicate that calendar field <code>f</code> has
140.209 + * been changed. Although calendar field <code>f</code> is changed immediately,
140.210 + * the calendar's time value in milliseconds is not recomputed until the next call to
140.211 + * <code>get()</code>, <code>getTime()</code>, <code>getTimeInMillis()</code>,
140.212 + * <code>add()</code>, or <code>roll()</code> is made. Thus, multiple calls to
140.213 + * <code>set()</code> do not trigger multiple, unnecessary
140.214 + * computations. As a result of changing a calendar field using
140.215 + * <code>set()</code>, other calendar fields may also change, depending on the
140.216 + * calendar field, the calendar field value, and the calendar system. In addition,
140.217 + * <code>get(f)</code> will not necessarily return <code>value</code> set by
140.218 + * the call to the <code>set</code> method
140.219 + * after the calendar fields have been recomputed. The specifics are determined by
140.220 + * the concrete calendar class.</p>
140.221 + *
140.222 + * <p><em>Example</em>: Consider a <code>GregorianCalendar</code>
140.223 + * originally set to August 31, 1999. Calling <code>set(Calendar.MONTH,
140.224 + * Calendar.SEPTEMBER)</code> sets the date to September 31,
140.225 + * 1999. This is a temporary internal representation that resolves to
140.226 + * October 1, 1999 if <code>getTime()</code>is then called. However, a
140.227 + * call to <code>set(Calendar.DAY_OF_MONTH, 30)</code> before the call to
140.228 + * <code>getTime()</code> sets the date to September 30, 1999, since
140.229 + * no recomputation occurs after <code>set()</code> itself.</p>
140.230 + *
140.231 + * <p><strong><code>add(f, delta)</code></strong> adds <code>delta</code>
140.232 + * to field <code>f</code>. This is equivalent to calling <code>set(f,
140.233 + * get(f) + delta)</code> with two adjustments:</p>
140.234 + *
140.235 + * <blockquote>
140.236 + * <p><strong>Add rule 1</strong>. The value of field <code>f</code>
140.237 + * after the call minus the value of field <code>f</code> before the
140.238 + * call is <code>delta</code>, modulo any overflow that has occurred in
140.239 + * field <code>f</code>. Overflow occurs when a field value exceeds its
140.240 + * range and, as a result, the next larger field is incremented or
140.241 + * decremented and the field value is adjusted back into its range.</p>
140.242 + *
140.243 + * <p><strong>Add rule 2</strong>. If a smaller field is expected to be
140.244 + * invariant, but it is impossible for it to be equal to its
140.245 + * prior value because of changes in its minimum or maximum after field
140.246 + * <code>f</code> is changed or other constraints, such as time zone
140.247 + * offset changes, then its value is adjusted to be as close
140.248 + * as possible to its expected value. A smaller field represents a
140.249 + * smaller unit of time. <code>HOUR</code> is a smaller field than
140.250 + * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
140.251 + * that are not expected to be invariant. The calendar system
140.252 + * determines what fields are expected to be invariant.</p>
140.253 + * </blockquote>
140.254 + *
140.255 + * <p>In addition, unlike <code>set()</code>, <code>add()</code> forces
140.256 + * an immediate recomputation of the calendar's milliseconds and all
140.257 + * fields.</p>
140.258 + *
140.259 + * <p><em>Example</em>: Consider a <code>GregorianCalendar</code>
140.260 + * originally set to August 31, 1999. Calling <code>add(Calendar.MONTH,
140.261 + * 13)</code> sets the calendar to September 30, 2000. <strong>Add rule
140.262 + * 1</strong> sets the <code>MONTH</code> field to September, since
140.263 + * adding 13 months to August gives September of the next year. Since
140.264 + * <code>DAY_OF_MONTH</code> cannot be 31 in September in a
140.265 + * <code>GregorianCalendar</code>, <strong>add rule 2</strong> sets the
140.266 + * <code>DAY_OF_MONTH</code> to 30, the closest possible value. Although
140.267 + * it is a smaller field, <code>DAY_OF_WEEK</code> is not adjusted by
140.268 + * rule 2, since it is expected to change when the month changes in a
140.269 + * <code>GregorianCalendar</code>.</p>
140.270 + *
140.271 + * <p><strong><code>roll(f, delta)</code></strong> adds
140.272 + * <code>delta</code> to field <code>f</code> without changing larger
140.273 + * fields. This is equivalent to calling <code>add(f, delta)</code> with
140.274 + * the following adjustment:</p>
140.275 + *
140.276 + * <blockquote>
140.277 + * <p><strong>Roll rule</strong>. Larger fields are unchanged after the
140.278 + * call. A larger field represents a larger unit of
140.279 + * time. <code>DAY_OF_MONTH</code> is a larger field than
140.280 + * <code>HOUR</code>.</p>
140.281 + * </blockquote>
140.282 + *
140.283 + * <p><em>Example</em>: See {@link java.util.GregorianCalendar#roll(int, int)}.
140.284 + *
140.285 + * <p><strong>Usage model</strong>. To motivate the behavior of
140.286 + * <code>add()</code> and <code>roll()</code>, consider a user interface
140.287 + * component with increment and decrement buttons for the month, day, and
140.288 + * year, and an underlying <code>GregorianCalendar</code>. If the
140.289 + * interface reads January 31, 1999 and the user presses the month
140.290 + * increment button, what should it read? If the underlying
140.291 + * implementation uses <code>set()</code>, it might read March 3, 1999. A
140.292 + * better result would be February 28, 1999. Furthermore, if the user
140.293 + * presses the month increment button again, it should read March 31,
140.294 + * 1999, not March 28, 1999. By saving the original date and using either
140.295 + * <code>add()</code> or <code>roll()</code>, depending on whether larger
140.296 + * fields should be affected, the user interface can behave as most users
140.297 + * will intuitively expect.</p>
140.298 + *
140.299 + * @see java.lang.System#currentTimeMillis()
140.300 + * @see Date
140.301 + * @see GregorianCalendar
140.302 + * @see TimeZone
140.303 + * @see java.text.DateFormat
140.304 + * @author Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu
140.305 + * @since JDK1.1
140.306 + */
140.307 +public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
140.308 +
140.309 + // Data flow in Calendar
140.310 + // ---------------------
140.311 +
140.312 + // The current time is represented in two ways by Calendar: as UTC
140.313 + // milliseconds from the epoch (1 January 1970 0:00 UTC), and as local
140.314 + // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
140.315 + // millis from the fields, and vice versa. The data needed to do this
140.316 + // conversion is encapsulated by a TimeZone object owned by the Calendar.
140.317 + // The data provided by the TimeZone object may also be overridden if the
140.318 + // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
140.319 + // keeps track of what information was most recently set by the caller, and
140.320 + // uses that to compute any other information as needed.
140.321 +
140.322 + // If the user sets the fields using set(), the data flow is as follows.
140.323 + // This is implemented by the Calendar subclass's computeTime() method.
140.324 + // During this process, certain fields may be ignored. The disambiguation
140.325 + // algorithm for resolving which fields to pay attention to is described
140.326 + // in the class documentation.
140.327 +
140.328 + // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
140.329 + // |
140.330 + // | Using Calendar-specific algorithm
140.331 + // V
140.332 + // local standard millis
140.333 + // |
140.334 + // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
140.335 + // V
140.336 + // UTC millis (in time data member)
140.337 +
140.338 + // If the user sets the UTC millis using setTime() or setTimeInMillis(),
140.339 + // the data flow is as follows. This is implemented by the Calendar
140.340 + // subclass's computeFields() method.
140.341 +
140.342 + // UTC millis (in time data member)
140.343 + // |
140.344 + // | Using TimeZone getOffset()
140.345 + // V
140.346 + // local standard millis
140.347 + // |
140.348 + // | Using Calendar-specific algorithm
140.349 + // V
140.350 + // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
140.351 +
140.352 + // In general, a round trip from fields, through local and UTC millis, and
140.353 + // back out to fields is made when necessary. This is implemented by the
140.354 + // complete() method. Resolving a partial set of fields into a UTC millis
140.355 + // value allows all remaining fields to be generated from that value. If
140.356 + // the Calendar is lenient, the fields are also renormalized to standard
140.357 + // ranges when they are regenerated.
140.358 +
140.359 + /**
140.360 + * Field number for <code>get</code> and <code>set</code> indicating the
140.361 + * era, e.g., AD or BC in the Julian calendar. This is a calendar-specific
140.362 + * value; see subclass documentation.
140.363 + *
140.364 + * @see GregorianCalendar#AD
140.365 + * @see GregorianCalendar#BC
140.366 + */
140.367 + public final static int ERA = 0;
140.368 +
140.369 + /**
140.370 + * Field number for <code>get</code> and <code>set</code> indicating the
140.371 + * year. This is a calendar-specific value; see subclass documentation.
140.372 + */
140.373 + public final static int YEAR = 1;
140.374 +
140.375 + /**
140.376 + * Field number for <code>get</code> and <code>set</code> indicating the
140.377 + * month. This is a calendar-specific value. The first month of
140.378 + * the year in the Gregorian and Julian calendars is
140.379 + * <code>JANUARY</code> which is 0; the last depends on the number
140.380 + * of months in a year.
140.381 + *
140.382 + * @see #JANUARY
140.383 + * @see #FEBRUARY
140.384 + * @see #MARCH
140.385 + * @see #APRIL
140.386 + * @see #MAY
140.387 + * @see #JUNE
140.388 + * @see #JULY
140.389 + * @see #AUGUST
140.390 + * @see #SEPTEMBER
140.391 + * @see #OCTOBER
140.392 + * @see #NOVEMBER
140.393 + * @see #DECEMBER
140.394 + * @see #UNDECIMBER
140.395 + */
140.396 + public final static int MONTH = 2;
140.397 +
140.398 + /**
140.399 + * Field number for <code>get</code> and <code>set</code> indicating the
140.400 + * week number within the current year. The first week of the year, as
140.401 + * defined by <code>getFirstDayOfWeek()</code> and
140.402 + * <code>getMinimalDaysInFirstWeek()</code>, has value 1. Subclasses define
140.403 + * the value of <code>WEEK_OF_YEAR</code> for days before the first week of
140.404 + * the year.
140.405 + *
140.406 + * @see #getFirstDayOfWeek
140.407 + * @see #getMinimalDaysInFirstWeek
140.408 + */
140.409 + public final static int WEEK_OF_YEAR = 3;
140.410 +
140.411 + /**
140.412 + * Field number for <code>get</code> and <code>set</code> indicating the
140.413 + * week number within the current month. The first week of the month, as
140.414 + * defined by <code>getFirstDayOfWeek()</code> and
140.415 + * <code>getMinimalDaysInFirstWeek()</code>, has value 1. Subclasses define
140.416 + * the value of <code>WEEK_OF_MONTH</code> for days before the first week of
140.417 + * the month.
140.418 + *
140.419 + * @see #getFirstDayOfWeek
140.420 + * @see #getMinimalDaysInFirstWeek
140.421 + */
140.422 + public final static int WEEK_OF_MONTH = 4;
140.423 +
140.424 + /**
140.425 + * Field number for <code>get</code> and <code>set</code> indicating the
140.426 + * day of the month. This is a synonym for <code>DAY_OF_MONTH</code>.
140.427 + * The first day of the month has value 1.
140.428 + *
140.429 + * @see #DAY_OF_MONTH
140.430 + */
140.431 + public final static int DATE = 5;
140.432 +
140.433 + /**
140.434 + * Field number for <code>get</code> and <code>set</code> indicating the
140.435 + * day of the month. This is a synonym for <code>DATE</code>.
140.436 + * The first day of the month has value 1.
140.437 + *
140.438 + * @see #DATE
140.439 + */
140.440 + public final static int DAY_OF_MONTH = 5;
140.441 +
140.442 + /**
140.443 + * Field number for <code>get</code> and <code>set</code> indicating the day
140.444 + * number within the current year. The first day of the year has value 1.
140.445 + */
140.446 + public final static int DAY_OF_YEAR = 6;
140.447 +
140.448 + /**
140.449 + * Field number for <code>get</code> and <code>set</code> indicating the day
140.450 + * of the week. This field takes values <code>SUNDAY</code>,
140.451 + * <code>MONDAY</code>, <code>TUESDAY</code>, <code>WEDNESDAY</code>,
140.452 + * <code>THURSDAY</code>, <code>FRIDAY</code>, and <code>SATURDAY</code>.
140.453 + *
140.454 + * @see #SUNDAY
140.455 + * @see #MONDAY
140.456 + * @see #TUESDAY
140.457 + * @see #WEDNESDAY
140.458 + * @see #THURSDAY
140.459 + * @see #FRIDAY
140.460 + * @see #SATURDAY
140.461 + */
140.462 + public final static int DAY_OF_WEEK = 7;
140.463 +
140.464 + /**
140.465 + * Field number for <code>get</code> and <code>set</code> indicating the
140.466 + * ordinal number of the day of the week within the current month. Together
140.467 + * with the <code>DAY_OF_WEEK</code> field, this uniquely specifies a day
140.468 + * within a month. Unlike <code>WEEK_OF_MONTH</code> and
140.469 + * <code>WEEK_OF_YEAR</code>, this field's value does <em>not</em> depend on
140.470 + * <code>getFirstDayOfWeek()</code> or
140.471 + * <code>getMinimalDaysInFirstWeek()</code>. <code>DAY_OF_MONTH 1</code>
140.472 + * through <code>7</code> always correspond to <code>DAY_OF_WEEK_IN_MONTH
140.473 + * 1</code>; <code>8</code> through <code>14</code> correspond to
140.474 + * <code>DAY_OF_WEEK_IN_MONTH 2</code>, and so on.
140.475 + * <code>DAY_OF_WEEK_IN_MONTH 0</code> indicates the week before
140.476 + * <code>DAY_OF_WEEK_IN_MONTH 1</code>. Negative values count back from the
140.477 + * end of the month, so the last Sunday of a month is specified as
140.478 + * <code>DAY_OF_WEEK = SUNDAY, DAY_OF_WEEK_IN_MONTH = -1</code>. Because
140.479 + * negative values count backward they will usually be aligned differently
140.480 + * within the month than positive values. For example, if a month has 31
140.481 + * days, <code>DAY_OF_WEEK_IN_MONTH -1</code> will overlap
140.482 + * <code>DAY_OF_WEEK_IN_MONTH 5</code> and the end of <code>4</code>.
140.483 + *
140.484 + * @see #DAY_OF_WEEK
140.485 + * @see #WEEK_OF_MONTH
140.486 + */
140.487 + public final static int DAY_OF_WEEK_IN_MONTH = 8;
140.488 +
140.489 + /**
140.490 + * Field number for <code>get</code> and <code>set</code> indicating
140.491 + * whether the <code>HOUR</code> is before or after noon.
140.492 + * E.g., at 10:04:15.250 PM the <code>AM_PM</code> is <code>PM</code>.
140.493 + *
140.494 + * @see #AM
140.495 + * @see #PM
140.496 + * @see #HOUR
140.497 + */
140.498 + public final static int AM_PM = 9;
140.499 +
140.500 + /**
140.501 + * Field number for <code>get</code> and <code>set</code> indicating the
140.502 + * hour of the morning or afternoon. <code>HOUR</code> is used for the
140.503 + * 12-hour clock (0 - 11). Noon and midnight are represented by 0, not by 12.
140.504 + * E.g., at 10:04:15.250 PM the <code>HOUR</code> is 10.
140.505 + *
140.506 + * @see #AM_PM
140.507 + * @see #HOUR_OF_DAY
140.508 + */
140.509 + public final static int HOUR = 10;
140.510 +
140.511 + /**
140.512 + * Field number for <code>get</code> and <code>set</code> indicating the
140.513 + * hour of the day. <code>HOUR_OF_DAY</code> is used for the 24-hour clock.
140.514 + * E.g., at 10:04:15.250 PM the <code>HOUR_OF_DAY</code> is 22.
140.515 + *
140.516 + * @see #HOUR
140.517 + */
140.518 + public final static int HOUR_OF_DAY = 11;
140.519 +
140.520 + /**
140.521 + * Field number for <code>get</code> and <code>set</code> indicating the
140.522 + * minute within the hour.
140.523 + * E.g., at 10:04:15.250 PM the <code>MINUTE</code> is 4.
140.524 + */
140.525 + public final static int MINUTE = 12;
140.526 +
140.527 + /**
140.528 + * Field number for <code>get</code> and <code>set</code> indicating the
140.529 + * second within the minute.
140.530 + * E.g., at 10:04:15.250 PM the <code>SECOND</code> is 15.
140.531 + */
140.532 + public final static int SECOND = 13;
140.533 +
140.534 + /**
140.535 + * Field number for <code>get</code> and <code>set</code> indicating the
140.536 + * millisecond within the second.
140.537 + * E.g., at 10:04:15.250 PM the <code>MILLISECOND</code> is 250.
140.538 + */
140.539 + public final static int MILLISECOND = 14;
140.540 +
140.541 + /**
140.542 + * Field number for <code>get</code> and <code>set</code>
140.543 + * indicating the raw offset from GMT in milliseconds.
140.544 + * <p>
140.545 + * This field reflects the correct GMT offset value of the time
140.546 + * zone of this <code>Calendar</code> if the
140.547 + * <code>TimeZone</code> implementation subclass supports
140.548 + * historical GMT offset changes.
140.549 + */
140.550 + public final static int ZONE_OFFSET = 15;
140.551 +
140.552 + /**
140.553 + * Field number for <code>get</code> and <code>set</code> indicating the
140.554 + * daylight saving offset in milliseconds.
140.555 + * <p>
140.556 + * This field reflects the correct daylight saving offset value of
140.557 + * the time zone of this <code>Calendar</code> if the
140.558 + * <code>TimeZone</code> implementation subclass supports
140.559 + * historical Daylight Saving Time schedule changes.
140.560 + */
140.561 + public final static int DST_OFFSET = 16;
140.562 +
140.563 + /**
140.564 + * The number of distinct fields recognized by <code>get</code> and <code>set</code>.
140.565 + * Field numbers range from <code>0..FIELD_COUNT-1</code>.
140.566 + */
140.567 + public final static int FIELD_COUNT = 17;
140.568 +
140.569 + /**
140.570 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.571 + * Sunday.
140.572 + */
140.573 + public final static int SUNDAY = 1;
140.574 +
140.575 + /**
140.576 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.577 + * Monday.
140.578 + */
140.579 + public final static int MONDAY = 2;
140.580 +
140.581 + /**
140.582 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.583 + * Tuesday.
140.584 + */
140.585 + public final static int TUESDAY = 3;
140.586 +
140.587 + /**
140.588 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.589 + * Wednesday.
140.590 + */
140.591 + public final static int WEDNESDAY = 4;
140.592 +
140.593 + /**
140.594 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.595 + * Thursday.
140.596 + */
140.597 + public final static int THURSDAY = 5;
140.598 +
140.599 + /**
140.600 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.601 + * Friday.
140.602 + */
140.603 + public final static int FRIDAY = 6;
140.604 +
140.605 + /**
140.606 + * Value of the {@link #DAY_OF_WEEK} field indicating
140.607 + * Saturday.
140.608 + */
140.609 + public final static int SATURDAY = 7;
140.610 +
140.611 + /**
140.612 + * Value of the {@link #MONTH} field indicating the
140.613 + * first month of the year in the Gregorian and Julian calendars.
140.614 + */
140.615 + public final static int JANUARY = 0;
140.616 +
140.617 + /**
140.618 + * Value of the {@link #MONTH} field indicating the
140.619 + * second month of the year in the Gregorian and Julian calendars.
140.620 + */
140.621 + public final static int FEBRUARY = 1;
140.622 +
140.623 + /**
140.624 + * Value of the {@link #MONTH} field indicating the
140.625 + * third month of the year in the Gregorian and Julian calendars.
140.626 + */
140.627 + public final static int MARCH = 2;
140.628 +
140.629 + /**
140.630 + * Value of the {@link #MONTH} field indicating the
140.631 + * fourth month of the year in the Gregorian and Julian calendars.
140.632 + */
140.633 + public final static int APRIL = 3;
140.634 +
140.635 + /**
140.636 + * Value of the {@link #MONTH} field indicating the
140.637 + * fifth month of the year in the Gregorian and Julian calendars.
140.638 + */
140.639 + public final static int MAY = 4;
140.640 +
140.641 + /**
140.642 + * Value of the {@link #MONTH} field indicating the
140.643 + * sixth month of the year in the Gregorian and Julian calendars.
140.644 + */
140.645 + public final static int JUNE = 5;
140.646 +
140.647 + /**
140.648 + * Value of the {@link #MONTH} field indicating the
140.649 + * seventh month of the year in the Gregorian and Julian calendars.
140.650 + */
140.651 + public final static int JULY = 6;
140.652 +
140.653 + /**
140.654 + * Value of the {@link #MONTH} field indicating the
140.655 + * eighth month of the year in the Gregorian and Julian calendars.
140.656 + */
140.657 + public final static int AUGUST = 7;
140.658 +
140.659 + /**
140.660 + * Value of the {@link #MONTH} field indicating the
140.661 + * ninth month of the year in the Gregorian and Julian calendars.
140.662 + */
140.663 + public final static int SEPTEMBER = 8;
140.664 +
140.665 + /**
140.666 + * Value of the {@link #MONTH} field indicating the
140.667 + * tenth month of the year in the Gregorian and Julian calendars.
140.668 + */
140.669 + public final static int OCTOBER = 9;
140.670 +
140.671 + /**
140.672 + * Value of the {@link #MONTH} field indicating the
140.673 + * eleventh month of the year in the Gregorian and Julian calendars.
140.674 + */
140.675 + public final static int NOVEMBER = 10;
140.676 +
140.677 + /**
140.678 + * Value of the {@link #MONTH} field indicating the
140.679 + * twelfth month of the year in the Gregorian and Julian calendars.
140.680 + */
140.681 + public final static int DECEMBER = 11;
140.682 +
140.683 + /**
140.684 + * Value of the {@link #MONTH} field indicating the
140.685 + * thirteenth month of the year. Although <code>GregorianCalendar</code>
140.686 + * does not use this value, lunar calendars do.
140.687 + */
140.688 + public final static int UNDECIMBER = 12;
140.689 +
140.690 + /**
140.691 + * Value of the {@link #AM_PM} field indicating the
140.692 + * period of the day from midnight to just before noon.
140.693 + */
140.694 + public final static int AM = 0;
140.695 +
140.696 + /**
140.697 + * Value of the {@link #AM_PM} field indicating the
140.698 + * period of the day from noon to just before midnight.
140.699 + */
140.700 + public final static int PM = 1;
140.701 +
140.702 + /**
140.703 + * A style specifier for {@link #getDisplayNames(int, int, Locale)
140.704 + * getDisplayNames} indicating names in all styles, such as
140.705 + * "January" and "Jan".
140.706 + *
140.707 + * @see #SHORT
140.708 + * @see #LONG
140.709 + * @since 1.6
140.710 + */
140.711 + public static final int ALL_STYLES = 0;
140.712 +
140.713 + /**
140.714 + * A style specifier for {@link #getDisplayName(int, int, Locale)
140.715 + * getDisplayName} and {@link #getDisplayNames(int, int, Locale)
140.716 + * getDisplayNames} indicating a short name, such as "Jan".
140.717 + *
140.718 + * @see #LONG
140.719 + * @since 1.6
140.720 + */
140.721 + public static final int SHORT = 1;
140.722 +
140.723 + /**
140.724 + * A style specifier for {@link #getDisplayName(int, int, Locale)
140.725 + * getDisplayName} and {@link #getDisplayNames(int, int, Locale)
140.726 + * getDisplayNames} indicating a long name, such as "January".
140.727 + *
140.728 + * @see #SHORT
140.729 + * @since 1.6
140.730 + */
140.731 + public static final int LONG = 2;
140.732 +
140.733 + // Internal notes:
140.734 + // Calendar contains two kinds of time representations: current "time" in
140.735 + // milliseconds, and a set of calendar "fields" representing the current time.
140.736 + // The two representations are usually in sync, but can get out of sync
140.737 + // as follows.
140.738 + // 1. Initially, no fields are set, and the time is invalid.
140.739 + // 2. If the time is set, all fields are computed and in sync.
140.740 + // 3. If a single field is set, the time is invalid.
140.741 + // Recomputation of the time and fields happens when the object needs
140.742 + // to return a result to the user, or use a result for a computation.
140.743 +
140.744 + /**
140.745 + * The calendar field values for the currently set time for this calendar.
140.746 + * This is an array of <code>FIELD_COUNT</code> integers, with index values
140.747 + * <code>ERA</code> through <code>DST_OFFSET</code>.
140.748 + * @serial
140.749 + */
140.750 + protected int fields[];
140.751 +
140.752 + /**
140.753 + * The flags which tell if a specified calendar field for the calendar is set.
140.754 + * A new object has no fields set. After the first call to a method
140.755 + * which generates the fields, they all remain set after that.
140.756 + * This is an array of <code>FIELD_COUNT</code> booleans, with index values
140.757 + * <code>ERA</code> through <code>DST_OFFSET</code>.
140.758 + * @serial
140.759 + */
140.760 + protected boolean isSet[];
140.761 +
140.762 + /**
140.763 + * Pseudo-time-stamps which specify when each field was set. There
140.764 + * are two special values, UNSET and COMPUTED. Values from
140.765 + * MINIMUM_USER_SET to Integer.MAX_VALUE are legal user set values.
140.766 + */
140.767 + transient private int stamp[];
140.768 +
140.769 + /**
140.770 + * The currently set time for this calendar, expressed in milliseconds after
140.771 + * January 1, 1970, 0:00:00 GMT.
140.772 + * @see #isTimeSet
140.773 + * @serial
140.774 + */
140.775 + protected long time;
140.776 +
140.777 + /**
140.778 + * True if then the value of <code>time</code> is valid.
140.779 + * The time is made invalid by a change to an item of <code>field[]</code>.
140.780 + * @see #time
140.781 + * @serial
140.782 + */
140.783 + protected boolean isTimeSet;
140.784 +
140.785 + /**
140.786 + * True if <code>fields[]</code> are in sync with the currently set time.
140.787 + * If false, then the next attempt to get the value of a field will
140.788 + * force a recomputation of all fields from the current value of
140.789 + * <code>time</code>.
140.790 + * @serial
140.791 + */
140.792 + protected boolean areFieldsSet;
140.793 +
140.794 + /**
140.795 + * True if all fields have been set.
140.796 + * @serial
140.797 + */
140.798 + transient boolean areAllFieldsSet;
140.799 +
140.800 + /**
140.801 + * <code>True</code> if this calendar allows out-of-range field values during computation
140.802 + * of <code>time</code> from <code>fields[]</code>.
140.803 + * @see #setLenient
140.804 + * @see #isLenient
140.805 + * @serial
140.806 + */
140.807 + private boolean lenient = true;
140.808 +
140.809 + /**
140.810 + * The <code>TimeZone</code> used by this calendar. <code>Calendar</code>
140.811 + * uses the time zone data to translate between locale and GMT time.
140.812 + * @serial
140.813 + */
140.814 + private TimeZone zone;
140.815 +
140.816 + /**
140.817 + * <code>True</code> if zone references to a shared TimeZone object.
140.818 + */
140.819 + transient private boolean sharedZone = false;
140.820 +
140.821 + /**
140.822 + * The first day of the week, with possible values <code>SUNDAY</code>,
140.823 + * <code>MONDAY</code>, etc. This is a locale-dependent value.
140.824 + * @serial
140.825 + */
140.826 + private int firstDayOfWeek;
140.827 +
140.828 + /**
140.829 + * The number of days required for the first week in a month or year,
140.830 + * with possible values from 1 to 7. This is a locale-dependent value.
140.831 + * @serial
140.832 + */
140.833 + private int minimalDaysInFirstWeek;
140.834 +
140.835 + /**
140.836 + * Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek
140.837 + * of a Locale.
140.838 + */
140.839 + private static final ConcurrentMap<Locale, int[]> cachedLocaleData
140.840 + = new ConcurrentHashMap<Locale, int[]>(3);
140.841 +
140.842 + // Special values of stamp[]
140.843 + /**
140.844 + * The corresponding fields[] has no value.
140.845 + */
140.846 + private static final int UNSET = 0;
140.847 +
140.848 + /**
140.849 + * The value of the corresponding fields[] has been calculated internally.
140.850 + */
140.851 + private static final int COMPUTED = 1;
140.852 +
140.853 + /**
140.854 + * The value of the corresponding fields[] has been set externally. Stamp
140.855 + * values which are greater than 1 represents the (pseudo) time when the
140.856 + * corresponding fields[] value was set.
140.857 + */
140.858 + private static final int MINIMUM_USER_STAMP = 2;
140.859 +
140.860 + /**
140.861 + * The mask value that represents all of the fields.
140.862 + */
140.863 + static final int ALL_FIELDS = (1 << FIELD_COUNT) - 1;
140.864 +
140.865 + /**
140.866 + * The next available value for <code>stamp[]</code>, an internal array.
140.867 + * This actually should not be written out to the stream, and will probably
140.868 + * be removed from the stream in the near future. In the meantime,
140.869 + * a value of <code>MINIMUM_USER_STAMP</code> should be used.
140.870 + * @serial
140.871 + */
140.872 + private int nextStamp = MINIMUM_USER_STAMP;
140.873 +
140.874 + // the internal serial version which says which version was written
140.875 + // - 0 (default) for version up to JDK 1.1.5
140.876 + // - 1 for version from JDK 1.1.6, which writes a correct 'time' value
140.877 + // as well as compatible values for other fields. This is a
140.878 + // transitional format.
140.879 + // - 2 (not implemented yet) a future version, in which fields[],
140.880 + // areFieldsSet, and isTimeSet become transient, and isSet[] is
140.881 + // removed. In JDK 1.1.6 we write a format compatible with version 2.
140.882 + static final int currentSerialVersion = 1;
140.883 +
140.884 + /**
140.885 + * The version of the serialized data on the stream. Possible values:
140.886 + * <dl>
140.887 + * <dt><b>0</b> or not present on stream</dt>
140.888 + * <dd>
140.889 + * JDK 1.1.5 or earlier.
140.890 + * </dd>
140.891 + * <dt><b>1</b></dt>
140.892 + * <dd>
140.893 + * JDK 1.1.6 or later. Writes a correct 'time' value
140.894 + * as well as compatible values for other fields. This is a
140.895 + * transitional format.
140.896 + * </dd>
140.897 + * </dl>
140.898 + * When streaming out this class, the most recent format
140.899 + * and the highest allowable <code>serialVersionOnStream</code>
140.900 + * is written.
140.901 + * @serial
140.902 + * @since JDK1.1.6
140.903 + */
140.904 + private int serialVersionOnStream = currentSerialVersion;
140.905 +
140.906 + // Proclaim serialization compatibility with JDK 1.1
140.907 + static final long serialVersionUID = -1807547505821590642L;
140.908 +
140.909 + // Mask values for calendar fields
140.910 + final static int ERA_MASK = (1 << ERA);
140.911 + final static int YEAR_MASK = (1 << YEAR);
140.912 + final static int MONTH_MASK = (1 << MONTH);
140.913 + final static int WEEK_OF_YEAR_MASK = (1 << WEEK_OF_YEAR);
140.914 + final static int WEEK_OF_MONTH_MASK = (1 << WEEK_OF_MONTH);
140.915 + final static int DAY_OF_MONTH_MASK = (1 << DAY_OF_MONTH);
140.916 + final static int DATE_MASK = DAY_OF_MONTH_MASK;
140.917 + final static int DAY_OF_YEAR_MASK = (1 << DAY_OF_YEAR);
140.918 + final static int DAY_OF_WEEK_MASK = (1 << DAY_OF_WEEK);
140.919 + final static int DAY_OF_WEEK_IN_MONTH_MASK = (1 << DAY_OF_WEEK_IN_MONTH);
140.920 + final static int AM_PM_MASK = (1 << AM_PM);
140.921 + final static int HOUR_MASK = (1 << HOUR);
140.922 + final static int HOUR_OF_DAY_MASK = (1 << HOUR_OF_DAY);
140.923 + final static int MINUTE_MASK = (1 << MINUTE);
140.924 + final static int SECOND_MASK = (1 << SECOND);
140.925 + final static int MILLISECOND_MASK = (1 << MILLISECOND);
140.926 + final static int ZONE_OFFSET_MASK = (1 << ZONE_OFFSET);
140.927 + final static int DST_OFFSET_MASK = (1 << DST_OFFSET);
140.928 +
140.929 + /**
140.930 + * Constructs a Calendar with the default time zone
140.931 + * and locale.
140.932 + * @see TimeZone#getDefault
140.933 + */
140.934 + protected Calendar()
140.935 + {
140.936 + this(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
140.937 + sharedZone = true;
140.938 + }
140.939 +
140.940 + /**
140.941 + * Constructs a calendar with the specified time zone and locale.
140.942 + *
140.943 + * @param zone the time zone to use
140.944 + * @param aLocale the locale for the week data
140.945 + */
140.946 + protected Calendar(TimeZone zone, Locale aLocale)
140.947 + {
140.948 + fields = new int[FIELD_COUNT];
140.949 + isSet = new boolean[FIELD_COUNT];
140.950 + stamp = new int[FIELD_COUNT];
140.951 +
140.952 + this.zone = zone;
140.953 + setWeekCountData(aLocale);
140.954 + }
140.955 +
140.956 + /**
140.957 + * Gets a calendar using the default time zone and locale. The
140.958 + * <code>Calendar</code> returned is based on the current time
140.959 + * in the default time zone with the default locale.
140.960 + *
140.961 + * @return a Calendar.
140.962 + */
140.963 + public static Calendar getInstance()
140.964 + {
140.965 + Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
140.966 + cal.sharedZone = true;
140.967 + return cal;
140.968 + }
140.969 +
140.970 + /**
140.971 + * Gets a calendar using the specified time zone and default locale.
140.972 + * The <code>Calendar</code> returned is based on the current time
140.973 + * in the given time zone with the default locale.
140.974 + *
140.975 + * @param zone the time zone to use
140.976 + * @return a Calendar.
140.977 + */
140.978 + public static Calendar getInstance(TimeZone zone)
140.979 + {
140.980 + return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
140.981 + }
140.982 +
140.983 + /**
140.984 + * Gets a calendar using the default time zone and specified locale.
140.985 + * The <code>Calendar</code> returned is based on the current time
140.986 + * in the default time zone with the given locale.
140.987 + *
140.988 + * @param aLocale the locale for the week data
140.989 + * @return a Calendar.
140.990 + */
140.991 + public static Calendar getInstance(Locale aLocale)
140.992 + {
140.993 + Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale);
140.994 + cal.sharedZone = true;
140.995 + return cal;
140.996 + }
140.997 +
140.998 + /**
140.999 + * Gets a calendar with the specified time zone and locale.
140.1000 + * The <code>Calendar</code> returned is based on the current time
140.1001 + * in the given time zone with the given locale.
140.1002 + *
140.1003 + * @param zone the time zone to use
140.1004 + * @param aLocale the locale for the week data
140.1005 + * @return a Calendar.
140.1006 + */
140.1007 + public static Calendar getInstance(TimeZone zone,
140.1008 + Locale aLocale)
140.1009 + {
140.1010 + return createCalendar(zone, aLocale);
140.1011 + }
140.1012 +
140.1013 + private static Calendar createCalendar(TimeZone zone,
140.1014 + Locale aLocale)
140.1015 + {
140.1016 + Calendar cal = null;
140.1017 +
140.1018 + String caltype = aLocale.getUnicodeLocaleType("ca");
140.1019 + if (caltype == null) {
140.1020 + // Calendar type is not specified.
140.1021 + // If the specified locale is a Thai locale,
140.1022 + // returns a BuddhistCalendar instance.
140.1023 + if ("th".equals(aLocale.getLanguage())
140.1024 + && ("TH".equals(aLocale.getCountry()))) {
140.1025 +// cal = new BuddhistCalendar(zone, aLocale);
140.1026 + } else {
140.1027 +// cal = new GregorianCalendar(zone, aLocale);
140.1028 + }
140.1029 + } else if (caltype.equals("japanese")) {
140.1030 +// cal = new JapaneseImperialCalendar(zone, aLocale);
140.1031 + } else if (caltype.equals("buddhist")) {
140.1032 +// cal = new BuddhistCalendar(zone, aLocale);
140.1033 + } else {
140.1034 + // Unsupported calendar type.
140.1035 + // Use Gregorian calendar as a fallback.
140.1036 +// cal = new GregorianCalendar(zone, aLocale);
140.1037 + }
140.1038 +
140.1039 + return cal;
140.1040 + }
140.1041 +
140.1042 + /**
140.1043 + * Returns an array of all locales for which the <code>getInstance</code>
140.1044 + * methods of this class can return localized instances.
140.1045 + * The array returned must contain at least a <code>Locale</code>
140.1046 + * instance equal to {@link java.util.Locale#US Locale.US}.
140.1047 + *
140.1048 + * @return An array of locales for which localized
140.1049 + * <code>Calendar</code> instances are available.
140.1050 + */
140.1051 + public static synchronized Locale[] getAvailableLocales()
140.1052 + {
140.1053 + return DateFormat.getAvailableLocales();
140.1054 + }
140.1055 +
140.1056 + /**
140.1057 + * Converts the current calendar field values in {@link #fields fields[]}
140.1058 + * to the millisecond time value
140.1059 + * {@link #time}.
140.1060 + *
140.1061 + * @see #complete()
140.1062 + * @see #computeFields()
140.1063 + */
140.1064 + protected abstract void computeTime();
140.1065 +
140.1066 + /**
140.1067 + * Converts the current millisecond time value {@link #time}
140.1068 + * to calendar field values in {@link #fields fields[]}.
140.1069 + * This allows you to sync up the calendar field values with
140.1070 + * a new time that is set for the calendar. The time is <em>not</em>
140.1071 + * recomputed first; to recompute the time, then the fields, call the
140.1072 + * {@link #complete()} method.
140.1073 + *
140.1074 + * @see #computeTime()
140.1075 + */
140.1076 + protected abstract void computeFields();
140.1077 +
140.1078 + /**
140.1079 + * Returns a <code>Date</code> object representing this
140.1080 + * <code>Calendar</code>'s time value (millisecond offset from the <a
140.1081 + * href="#Epoch">Epoch</a>").
140.1082 + *
140.1083 + * @return a <code>Date</code> representing the time value.
140.1084 + * @see #setTime(Date)
140.1085 + * @see #getTimeInMillis()
140.1086 + */
140.1087 + public final Date getTime() {
140.1088 + return new Date(getTimeInMillis());
140.1089 + }
140.1090 +
140.1091 + /**
140.1092 + * Sets this Calendar's time with the given <code>Date</code>.
140.1093 + * <p>
140.1094 + * Note: Calling <code>setTime()</code> with
140.1095 + * <code>Date(Long.MAX_VALUE)</code> or <code>Date(Long.MIN_VALUE)</code>
140.1096 + * may yield incorrect field values from <code>get()</code>.
140.1097 + *
140.1098 + * @param date the given Date.
140.1099 + * @see #getTime()
140.1100 + * @see #setTimeInMillis(long)
140.1101 + */
140.1102 + public final void setTime(Date date) {
140.1103 + setTimeInMillis(date.getTime());
140.1104 + }
140.1105 +
140.1106 + /**
140.1107 + * Returns this Calendar's time value in milliseconds.
140.1108 + *
140.1109 + * @return the current time as UTC milliseconds from the epoch.
140.1110 + * @see #getTime()
140.1111 + * @see #setTimeInMillis(long)
140.1112 + */
140.1113 + public long getTimeInMillis() {
140.1114 + if (!isTimeSet) {
140.1115 + updateTime();
140.1116 + }
140.1117 + return time;
140.1118 + }
140.1119 +
140.1120 + /**
140.1121 + * Sets this Calendar's current time from the given long value.
140.1122 + *
140.1123 + * @param millis the new time in UTC milliseconds from the epoch.
140.1124 + * @see #setTime(Date)
140.1125 + * @see #getTimeInMillis()
140.1126 + */
140.1127 + public void setTimeInMillis(long millis) {
140.1128 + // If we don't need to recalculate the calendar field values,
140.1129 + // do nothing.
140.1130 +// if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
140.1131 +// && (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
140.1132 +// return;
140.1133 +// }
140.1134 + time = millis;
140.1135 + isTimeSet = true;
140.1136 + areFieldsSet = false;
140.1137 + computeFields();
140.1138 + areAllFieldsSet = areFieldsSet = true;
140.1139 + }
140.1140 +
140.1141 + /**
140.1142 + * Returns the value of the given calendar field. In lenient mode,
140.1143 + * all calendar fields are normalized. In non-lenient mode, all
140.1144 + * calendar fields are validated and this method throws an
140.1145 + * exception if any calendar fields have out-of-range values. The
140.1146 + * normalization and validation are handled by the
140.1147 + * {@link #complete()} method, which process is calendar
140.1148 + * system dependent.
140.1149 + *
140.1150 + * @param field the given calendar field.
140.1151 + * @return the value for the given calendar field.
140.1152 + * @throws ArrayIndexOutOfBoundsException if the specified field is out of range
140.1153 + * (<code>field < 0 || field >= FIELD_COUNT</code>).
140.1154 + * @see #set(int,int)
140.1155 + * @see #complete()
140.1156 + */
140.1157 + public int get(int field)
140.1158 + {
140.1159 + complete();
140.1160 + return internalGet(field);
140.1161 + }
140.1162 +
140.1163 + /**
140.1164 + * Returns the value of the given calendar field. This method does
140.1165 + * not involve normalization or validation of the field value.
140.1166 + *
140.1167 + * @param field the given calendar field.
140.1168 + * @return the value for the given calendar field.
140.1169 + * @see #get(int)
140.1170 + */
140.1171 + protected final int internalGet(int field)
140.1172 + {
140.1173 + return fields[field];
140.1174 + }
140.1175 +
140.1176 + /**
140.1177 + * Sets the value of the given calendar field. This method does
140.1178 + * not affect any setting state of the field in this
140.1179 + * <code>Calendar</code> instance.
140.1180 + *
140.1181 + * @throws IndexOutOfBoundsException if the specified field is out of range
140.1182 + * (<code>field < 0 || field >= FIELD_COUNT</code>).
140.1183 + * @see #areFieldsSet
140.1184 + * @see #isTimeSet
140.1185 + * @see #areAllFieldsSet
140.1186 + * @see #set(int,int)
140.1187 + */
140.1188 + final void internalSet(int field, int value)
140.1189 + {
140.1190 + fields[field] = value;
140.1191 + }
140.1192 +
140.1193 + /**
140.1194 + * Sets the given calendar field to the given value. The value is not
140.1195 + * interpreted by this method regardless of the leniency mode.
140.1196 + *
140.1197 + * @param field the given calendar field.
140.1198 + * @param value the value to be set for the given calendar field.
140.1199 + * @throws ArrayIndexOutOfBoundsException if the specified field is out of range
140.1200 + * (<code>field < 0 || field >= FIELD_COUNT</code>).
140.1201 + * in non-lenient mode.
140.1202 + * @see #set(int,int,int)
140.1203 + * @see #set(int,int,int,int,int)
140.1204 + * @see #set(int,int,int,int,int,int)
140.1205 + * @see #get(int)
140.1206 + */
140.1207 + public void set(int field, int value)
140.1208 + {
140.1209 + // If the fields are partially normalized, calculate all the
140.1210 + // fields before changing any fields.
140.1211 + if (areFieldsSet && !areAllFieldsSet) {
140.1212 + computeFields();
140.1213 + }
140.1214 + internalSet(field, value);
140.1215 + isTimeSet = false;
140.1216 + areFieldsSet = false;
140.1217 + isSet[field] = true;
140.1218 + stamp[field] = nextStamp++;
140.1219 + if (nextStamp == Integer.MAX_VALUE) {
140.1220 + adjustStamp();
140.1221 + }
140.1222 + }
140.1223 +
140.1224 + /**
140.1225 + * Sets the values for the calendar fields <code>YEAR</code>,
140.1226 + * <code>MONTH</code>, and <code>DAY_OF_MONTH</code>.
140.1227 + * Previous values of other calendar fields are retained. If this is not desired,
140.1228 + * call {@link #clear()} first.
140.1229 + *
140.1230 + * @param year the value used to set the <code>YEAR</code> calendar field.
140.1231 + * @param month the value used to set the <code>MONTH</code> calendar field.
140.1232 + * Month value is 0-based. e.g., 0 for January.
140.1233 + * @param date the value used to set the <code>DAY_OF_MONTH</code> calendar field.
140.1234 + * @see #set(int,int)
140.1235 + * @see #set(int,int,int,int,int)
140.1236 + * @see #set(int,int,int,int,int,int)
140.1237 + */
140.1238 + public final void set(int year, int month, int date)
140.1239 + {
140.1240 + set(YEAR, year);
140.1241 + set(MONTH, month);
140.1242 + set(DATE, date);
140.1243 + }
140.1244 +
140.1245 + /**
140.1246 + * Sets the values for the calendar fields <code>YEAR</code>,
140.1247 + * <code>MONTH</code>, <code>DAY_OF_MONTH</code>,
140.1248 + * <code>HOUR_OF_DAY</code>, and <code>MINUTE</code>.
140.1249 + * Previous values of other fields are retained. If this is not desired,
140.1250 + * call {@link #clear()} first.
140.1251 + *
140.1252 + * @param year the value used to set the <code>YEAR</code> calendar field.
140.1253 + * @param month the value used to set the <code>MONTH</code> calendar field.
140.1254 + * Month value is 0-based. e.g., 0 for January.
140.1255 + * @param date the value used to set the <code>DAY_OF_MONTH</code> calendar field.
140.1256 + * @param hourOfDay the value used to set the <code>HOUR_OF_DAY</code> calendar field.
140.1257 + * @param minute the value used to set the <code>MINUTE</code> calendar field.
140.1258 + * @see #set(int,int)
140.1259 + * @see #set(int,int,int)
140.1260 + * @see #set(int,int,int,int,int,int)
140.1261 + */
140.1262 + public final void set(int year, int month, int date, int hourOfDay, int minute)
140.1263 + {
140.1264 + set(YEAR, year);
140.1265 + set(MONTH, month);
140.1266 + set(DATE, date);
140.1267 + set(HOUR_OF_DAY, hourOfDay);
140.1268 + set(MINUTE, minute);
140.1269 + }
140.1270 +
140.1271 + /**
140.1272 + * Sets the values for the fields <code>YEAR</code>, <code>MONTH</code>,
140.1273 + * <code>DAY_OF_MONTH</code>, <code>HOUR</code>, <code>MINUTE</code>, and
140.1274 + * <code>SECOND</code>.
140.1275 + * Previous values of other fields are retained. If this is not desired,
140.1276 + * call {@link #clear()} first.
140.1277 + *
140.1278 + * @param year the value used to set the <code>YEAR</code> calendar field.
140.1279 + * @param month the value used to set the <code>MONTH</code> calendar field.
140.1280 + * Month value is 0-based. e.g., 0 for January.
140.1281 + * @param date the value used to set the <code>DAY_OF_MONTH</code> calendar field.
140.1282 + * @param hourOfDay the value used to set the <code>HOUR_OF_DAY</code> calendar field.
140.1283 + * @param minute the value used to set the <code>MINUTE</code> calendar field.
140.1284 + * @param second the value used to set the <code>SECOND</code> calendar field.
140.1285 + * @see #set(int,int)
140.1286 + * @see #set(int,int,int)
140.1287 + * @see #set(int,int,int,int,int)
140.1288 + */
140.1289 + public final void set(int year, int month, int date, int hourOfDay, int minute,
140.1290 + int second)
140.1291 + {
140.1292 + set(YEAR, year);
140.1293 + set(MONTH, month);
140.1294 + set(DATE, date);
140.1295 + set(HOUR_OF_DAY, hourOfDay);
140.1296 + set(MINUTE, minute);
140.1297 + set(SECOND, second);
140.1298 + }
140.1299 +
140.1300 + /**
140.1301 + * Sets all the calendar field values and the time value
140.1302 + * (millisecond offset from the <a href="#Epoch">Epoch</a>) of
140.1303 + * this <code>Calendar</code> undefined. This means that {@link
140.1304 + * #isSet(int) isSet()} will return <code>false</code> for all the
140.1305 + * calendar fields, and the date and time calculations will treat
140.1306 + * the fields as if they had never been set. A
140.1307 + * <code>Calendar</code> implementation class may use its specific
140.1308 + * default field values for date/time calculations. For example,
140.1309 + * <code>GregorianCalendar</code> uses 1970 if the
140.1310 + * <code>YEAR</code> field value is undefined.
140.1311 + *
140.1312 + * @see #clear(int)
140.1313 + */
140.1314 + public final void clear()
140.1315 + {
140.1316 + for (int i = 0; i < fields.length; ) {
140.1317 + stamp[i] = fields[i] = 0; // UNSET == 0
140.1318 + isSet[i++] = false;
140.1319 + }
140.1320 + areAllFieldsSet = areFieldsSet = false;
140.1321 + isTimeSet = false;
140.1322 + }
140.1323 +
140.1324 + /**
140.1325 + * Sets the given calendar field value and the time value
140.1326 + * (millisecond offset from the <a href="#Epoch">Epoch</a>) of
140.1327 + * this <code>Calendar</code> undefined. This means that {@link
140.1328 + * #isSet(int) isSet(field)} will return <code>false</code>, and
140.1329 + * the date and time calculations will treat the field as if it
140.1330 + * had never been set. A <code>Calendar</code> implementation
140.1331 + * class may use the field's specific default value for date and
140.1332 + * time calculations.
140.1333 + *
140.1334 + * <p>The {@link #HOUR_OF_DAY}, {@link #HOUR} and {@link #AM_PM}
140.1335 + * fields are handled independently and the <a
140.1336 + * href="#time_resolution">the resolution rule for the time of
140.1337 + * day</a> is applied. Clearing one of the fields doesn't reset
140.1338 + * the hour of day value of this <code>Calendar</code>. Use {@link
140.1339 + * #set(int,int) set(Calendar.HOUR_OF_DAY, 0)} to reset the hour
140.1340 + * value.
140.1341 + *
140.1342 + * @param field the calendar field to be cleared.
140.1343 + * @see #clear()
140.1344 + */
140.1345 + public final void clear(int field)
140.1346 + {
140.1347 + fields[field] = 0;
140.1348 + stamp[field] = UNSET;
140.1349 + isSet[field] = false;
140.1350 +
140.1351 + areAllFieldsSet = areFieldsSet = false;
140.1352 + isTimeSet = false;
140.1353 + }
140.1354 +
140.1355 + /**
140.1356 + * Determines if the given calendar field has a value set,
140.1357 + * including cases that the value has been set by internal fields
140.1358 + * calculations triggered by a <code>get</code> method call.
140.1359 + *
140.1360 + * @return <code>true</code> if the given calendar field has a value set;
140.1361 + * <code>false</code> otherwise.
140.1362 + */
140.1363 + public final boolean isSet(int field)
140.1364 + {
140.1365 + return stamp[field] != UNSET;
140.1366 + }
140.1367 +
140.1368 + /**
140.1369 + * Returns the string representation of the calendar
140.1370 + * <code>field</code> value in the given <code>style</code> and
140.1371 + * <code>locale</code>. If no string representation is
140.1372 + * applicable, <code>null</code> is returned. This method calls
140.1373 + * {@link Calendar#get(int) get(field)} to get the calendar
140.1374 + * <code>field</code> value if the string representation is
140.1375 + * applicable to the given calendar <code>field</code>.
140.1376 + *
140.1377 + * <p>For example, if this <code>Calendar</code> is a
140.1378 + * <code>GregorianCalendar</code> and its date is 2005-01-01, then
140.1379 + * the string representation of the {@link #MONTH} field would be
140.1380 + * "January" in the long style in an English locale or "Jan" in
140.1381 + * the short style. However, no string representation would be
140.1382 + * available for the {@link #DAY_OF_MONTH} field, and this method
140.1383 + * would return <code>null</code>.
140.1384 + *
140.1385 + * <p>The default implementation supports the calendar fields for
140.1386 + * which a {@link DateFormatSymbols} has names in the given
140.1387 + * <code>locale</code>.
140.1388 + *
140.1389 + * @param field
140.1390 + * the calendar field for which the string representation
140.1391 + * is returned
140.1392 + * @param style
140.1393 + * the style applied to the string representation; one of
140.1394 + * {@link #SHORT} or {@link #LONG}.
140.1395 + * @param locale
140.1396 + * the locale for the string representation
140.1397 + * @return the string representation of the given
140.1398 + * <code>field</code> in the given <code>style</code>, or
140.1399 + * <code>null</code> if no string representation is
140.1400 + * applicable.
140.1401 + * @exception IllegalArgumentException
140.1402 + * if <code>field</code> or <code>style</code> is invalid,
140.1403 + * or if this <code>Calendar</code> is non-lenient and any
140.1404 + * of the calendar fields have invalid values
140.1405 + * @exception NullPointerException
140.1406 + * if <code>locale</code> is null
140.1407 + * @since 1.6
140.1408 + */
140.1409 + public String getDisplayName(int field, int style, Locale locale) {
140.1410 + if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
140.1411 + ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
140.1412 + return null;
140.1413 + }
140.1414 +
140.1415 + DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
140.1416 + String[] strings = getFieldStrings(field, style, symbols);
140.1417 + if (strings != null) {
140.1418 + int fieldValue = get(field);
140.1419 + if (fieldValue < strings.length) {
140.1420 + return strings[fieldValue];
140.1421 + }
140.1422 + }
140.1423 + return null;
140.1424 + }
140.1425 +
140.1426 + /**
140.1427 + * Returns a <code>Map</code> containing all names of the calendar
140.1428 + * <code>field</code> in the given <code>style</code> and
140.1429 + * <code>locale</code> and their corresponding field values. For
140.1430 + * example, if this <code>Calendar</code> is a {@link
140.1431 + * GregorianCalendar}, the returned map would contain "Jan" to
140.1432 + * {@link #JANUARY}, "Feb" to {@link #FEBRUARY}, and so on, in the
140.1433 + * {@linkplain #SHORT short} style in an English locale.
140.1434 + *
140.1435 + * <p>The values of other calendar fields may be taken into
140.1436 + * account to determine a set of display names. For example, if
140.1437 + * this <code>Calendar</code> is a lunisolar calendar system and
140.1438 + * the year value given by the {@link #YEAR} field has a leap
140.1439 + * month, this method would return month names containing the leap
140.1440 + * month name, and month names are mapped to their values specific
140.1441 + * for the year.
140.1442 + *
140.1443 + * <p>The default implementation supports display names contained in
140.1444 + * a {@link DateFormatSymbols}. For example, if <code>field</code>
140.1445 + * is {@link #MONTH} and <code>style</code> is {@link
140.1446 + * #ALL_STYLES}, this method returns a <code>Map</code> containing
140.1447 + * all strings returned by {@link DateFormatSymbols#getShortMonths()}
140.1448 + * and {@link DateFormatSymbols#getMonths()}.
140.1449 + *
140.1450 + * @param field
140.1451 + * the calendar field for which the display names are returned
140.1452 + * @param style
140.1453 + * the style applied to the display names; one of {@link
140.1454 + * #SHORT}, {@link #LONG}, or {@link #ALL_STYLES}.
140.1455 + * @param locale
140.1456 + * the locale for the display names
140.1457 + * @return a <code>Map</code> containing all display names in
140.1458 + * <code>style</code> and <code>locale</code> and their
140.1459 + * field values, or <code>null</code> if no display names
140.1460 + * are defined for <code>field</code>
140.1461 + * @exception IllegalArgumentException
140.1462 + * if <code>field</code> or <code>style</code> is invalid,
140.1463 + * or if this <code>Calendar</code> is non-lenient and any
140.1464 + * of the calendar fields have invalid values
140.1465 + * @exception NullPointerException
140.1466 + * if <code>locale</code> is null
140.1467 + * @since 1.6
140.1468 + */
140.1469 + public Map<String, Integer> getDisplayNames(int field, int style, Locale locale) {
140.1470 + if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
140.1471 + ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
140.1472 + return null;
140.1473 + }
140.1474 +
140.1475 + // ALL_STYLES
140.1476 + if (style == ALL_STYLES) {
140.1477 + Map<String,Integer> shortNames = getDisplayNamesImpl(field, SHORT, locale);
140.1478 + if (field == ERA || field == AM_PM) {
140.1479 + return shortNames;
140.1480 + }
140.1481 + Map<String,Integer> longNames = getDisplayNamesImpl(field, LONG, locale);
140.1482 + if (shortNames == null) {
140.1483 + return longNames;
140.1484 + }
140.1485 + if (longNames != null) {
140.1486 + shortNames.putAll(longNames);
140.1487 + }
140.1488 + return shortNames;
140.1489 + }
140.1490 +
140.1491 + // SHORT or LONG
140.1492 + return getDisplayNamesImpl(field, style, locale);
140.1493 + }
140.1494 +
140.1495 + private Map<String,Integer> getDisplayNamesImpl(int field, int style, Locale locale) {
140.1496 + DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
140.1497 + String[] strings = getFieldStrings(field, style, symbols);
140.1498 + if (strings != null) {
140.1499 + Map<String,Integer> names = new HashMap<String,Integer>();
140.1500 + for (int i = 0; i < strings.length; i++) {
140.1501 + if (strings[i].length() == 0) {
140.1502 + continue;
140.1503 + }
140.1504 + names.put(strings[i], i);
140.1505 + }
140.1506 + return names;
140.1507 + }
140.1508 + return null;
140.1509 + }
140.1510 +
140.1511 + boolean checkDisplayNameParams(int field, int style, int minStyle, int maxStyle,
140.1512 + Locale locale, int fieldMask) {
140.1513 + if (field < 0 || field >= fields.length ||
140.1514 + style < minStyle || style > maxStyle) {
140.1515 + throw new IllegalArgumentException();
140.1516 + }
140.1517 + if (locale == null) {
140.1518 + throw new NullPointerException();
140.1519 + }
140.1520 + return isFieldSet(fieldMask, field);
140.1521 + }
140.1522 +
140.1523 + private String[] getFieldStrings(int field, int style, DateFormatSymbols symbols) {
140.1524 + String[] strings = null;
140.1525 + switch (field) {
140.1526 + case ERA:
140.1527 + strings = symbols.getEras();
140.1528 + break;
140.1529 +
140.1530 + case MONTH:
140.1531 + strings = (style == LONG) ? symbols.getMonths() : symbols.getShortMonths();
140.1532 + break;
140.1533 +
140.1534 + case DAY_OF_WEEK:
140.1535 + strings = (style == LONG) ? symbols.getWeekdays() : symbols.getShortWeekdays();
140.1536 + break;
140.1537 +
140.1538 + case AM_PM:
140.1539 + strings = symbols.getAmPmStrings();
140.1540 + break;
140.1541 + }
140.1542 + return strings;
140.1543 + }
140.1544 +
140.1545 + /**
140.1546 + * Fills in any unset fields in the calendar fields. First, the {@link
140.1547 + * #computeTime()} method is called if the time value (millisecond offset
140.1548 + * from the <a href="#Epoch">Epoch</a>) has not been calculated from
140.1549 + * calendar field values. Then, the {@link #computeFields()} method is
140.1550 + * called to calculate all calendar field values.
140.1551 + */
140.1552 + protected void complete()
140.1553 + {
140.1554 + if (!isTimeSet)
140.1555 + updateTime();
140.1556 + if (!areFieldsSet || !areAllFieldsSet) {
140.1557 + computeFields(); // fills in unset fields
140.1558 + areAllFieldsSet = areFieldsSet = true;
140.1559 + }
140.1560 + }
140.1561 +
140.1562 + /**
140.1563 + * Returns whether the value of the specified calendar field has been set
140.1564 + * externally by calling one of the setter methods rather than by the
140.1565 + * internal time calculation.
140.1566 + *
140.1567 + * @return <code>true</code> if the field has been set externally,
140.1568 + * <code>false</code> otherwise.
140.1569 + * @exception IndexOutOfBoundsException if the specified
140.1570 + * <code>field</code> is out of range
140.1571 + * (<code>field < 0 || field >= FIELD_COUNT</code>).
140.1572 + * @see #selectFields()
140.1573 + * @see #setFieldsComputed(int)
140.1574 + */
140.1575 + final boolean isExternallySet(int field) {
140.1576 + return stamp[field] >= MINIMUM_USER_STAMP;
140.1577 + }
140.1578 +
140.1579 + /**
140.1580 + * Returns a field mask (bit mask) indicating all calendar fields that
140.1581 + * have the state of externally or internally set.
140.1582 + *
140.1583 + * @return a bit mask indicating set state fields
140.1584 + */
140.1585 + final int getSetStateFields() {
140.1586 + int mask = 0;
140.1587 + for (int i = 0; i < fields.length; i++) {
140.1588 + if (stamp[i] != UNSET) {
140.1589 + mask |= 1 << i;
140.1590 + }
140.1591 + }
140.1592 + return mask;
140.1593 + }
140.1594 +
140.1595 + /**
140.1596 + * Sets the state of the specified calendar fields to
140.1597 + * <em>computed</em>. This state means that the specified calendar fields
140.1598 + * have valid values that have been set by internal time calculation
140.1599 + * rather than by calling one of the setter methods.
140.1600 + *
140.1601 + * @param fieldMask the field to be marked as computed.
140.1602 + * @exception IndexOutOfBoundsException if the specified
140.1603 + * <code>field</code> is out of range
140.1604 + * (<code>field < 0 || field >= FIELD_COUNT</code>).
140.1605 + * @see #isExternallySet(int)
140.1606 + * @see #selectFields()
140.1607 + */
140.1608 + final void setFieldsComputed(int fieldMask) {
140.1609 + if (fieldMask == ALL_FIELDS) {
140.1610 + for (int i = 0; i < fields.length; i++) {
140.1611 + stamp[i] = COMPUTED;
140.1612 + isSet[i] = true;
140.1613 + }
140.1614 + areFieldsSet = areAllFieldsSet = true;
140.1615 + } else {
140.1616 + for (int i = 0; i < fields.length; i++) {
140.1617 + if ((fieldMask & 1) == 1) {
140.1618 + stamp[i] = COMPUTED;
140.1619 + isSet[i] = true;
140.1620 + } else {
140.1621 + if (areAllFieldsSet && !isSet[i]) {
140.1622 + areAllFieldsSet = false;
140.1623 + }
140.1624 + }
140.1625 + fieldMask >>>= 1;
140.1626 + }
140.1627 + }
140.1628 + }
140.1629 +
140.1630 + /**
140.1631 + * Sets the state of the calendar fields that are <em>not</em> specified
140.1632 + * by <code>fieldMask</code> to <em>unset</em>. If <code>fieldMask</code>
140.1633 + * specifies all the calendar fields, then the state of this
140.1634 + * <code>Calendar</code> becomes that all the calendar fields are in sync
140.1635 + * with the time value (millisecond offset from the Epoch).
140.1636 + *
140.1637 + * @param fieldMask the field mask indicating which calendar fields are in
140.1638 + * sync with the time value.
140.1639 + * @exception IndexOutOfBoundsException if the specified
140.1640 + * <code>field</code> is out of range
140.1641 + * (<code>field < 0 || field >= FIELD_COUNT</code>).
140.1642 + * @see #isExternallySet(int)
140.1643 + * @see #selectFields()
140.1644 + */
140.1645 + final void setFieldsNormalized(int fieldMask) {
140.1646 + if (fieldMask != ALL_FIELDS) {
140.1647 + for (int i = 0; i < fields.length; i++) {
140.1648 + if ((fieldMask & 1) == 0) {
140.1649 + stamp[i] = fields[i] = 0; // UNSET == 0
140.1650 + isSet[i] = false;
140.1651 + }
140.1652 + fieldMask >>= 1;
140.1653 + }
140.1654 + }
140.1655 +
140.1656 + // Some or all of the fields are in sync with the
140.1657 + // milliseconds, but the stamp values are not normalized yet.
140.1658 + areFieldsSet = true;
140.1659 + areAllFieldsSet = false;
140.1660 + }
140.1661 +
140.1662 + /**
140.1663 + * Returns whether the calendar fields are partially in sync with the time
140.1664 + * value or fully in sync but not stamp values are not normalized yet.
140.1665 + */
140.1666 + final boolean isPartiallyNormalized() {
140.1667 + return areFieldsSet && !areAllFieldsSet;
140.1668 + }
140.1669 +
140.1670 + /**
140.1671 + * Returns whether the calendar fields are fully in sync with the time
140.1672 + * value.
140.1673 + */
140.1674 + final boolean isFullyNormalized() {
140.1675 + return areFieldsSet && areAllFieldsSet;
140.1676 + }
140.1677 +
140.1678 + /**
140.1679 + * Marks this Calendar as not sync'd.
140.1680 + */
140.1681 + final void setUnnormalized() {
140.1682 + areFieldsSet = areAllFieldsSet = false;
140.1683 + }
140.1684 +
140.1685 + /**
140.1686 + * Returns whether the specified <code>field</code> is on in the
140.1687 + * <code>fieldMask</code>.
140.1688 + */
140.1689 + static final boolean isFieldSet(int fieldMask, int field) {
140.1690 + return (fieldMask & (1 << field)) != 0;
140.1691 + }
140.1692 +
140.1693 + /**
140.1694 + * Returns a field mask indicating which calendar field values
140.1695 + * to be used to calculate the time value. The calendar fields are
140.1696 + * returned as a bit mask, each bit of which corresponds to a field, i.e.,
140.1697 + * the mask value of <code>field</code> is <code>(1 <<
140.1698 + * field)</code>. For example, 0x26 represents the <code>YEAR</code>,
140.1699 + * <code>MONTH</code>, and <code>DAY_OF_MONTH</code> fields (i.e., 0x26 is
140.1700 + * equal to
140.1701 + * <code>(1<<YEAR)|(1<<MONTH)|(1<<DAY_OF_MONTH))</code>.
140.1702 + *
140.1703 + * <p>This method supports the calendar fields resolution as described in
140.1704 + * the class description. If the bit mask for a given field is on and its
140.1705 + * field has not been set (i.e., <code>isSet(field)</code> is
140.1706 + * <code>false</code>), then the default value of the field has to be
140.1707 + * used, which case means that the field has been selected because the
140.1708 + * selected combination involves the field.
140.1709 + *
140.1710 + * @return a bit mask of selected fields
140.1711 + * @see #isExternallySet(int)
140.1712 + * @see #setInternallySetState(int)
140.1713 + */
140.1714 + final int selectFields() {
140.1715 + // This implementation has been taken from the GregorianCalendar class.
140.1716 +
140.1717 + // The YEAR field must always be used regardless of its SET
140.1718 + // state because YEAR is a mandatory field to determine the date
140.1719 + // and the default value (EPOCH_YEAR) may change through the
140.1720 + // normalization process.
140.1721 + int fieldMask = YEAR_MASK;
140.1722 +
140.1723 + if (stamp[ERA] != UNSET) {
140.1724 + fieldMask |= ERA_MASK;
140.1725 + }
140.1726 + // Find the most recent group of fields specifying the day within
140.1727 + // the year. These may be any of the following combinations:
140.1728 + // MONTH + DAY_OF_MONTH
140.1729 + // MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
140.1730 + // MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
140.1731 + // DAY_OF_YEAR
140.1732 + // WEEK_OF_YEAR + DAY_OF_WEEK
140.1733 + // We look for the most recent of the fields in each group to determine
140.1734 + // the age of the group. For groups involving a week-related field such
140.1735 + // as WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH, or WEEK_OF_YEAR, both the
140.1736 + // week-related field and the DAY_OF_WEEK must be set for the group as a
140.1737 + // whole to be considered. (See bug 4153860 - liu 7/24/98.)
140.1738 + int dowStamp = stamp[DAY_OF_WEEK];
140.1739 + int monthStamp = stamp[MONTH];
140.1740 + int domStamp = stamp[DAY_OF_MONTH];
140.1741 + int womStamp = aggregateStamp(stamp[WEEK_OF_MONTH], dowStamp);
140.1742 + int dowimStamp = aggregateStamp(stamp[DAY_OF_WEEK_IN_MONTH], dowStamp);
140.1743 + int doyStamp = stamp[DAY_OF_YEAR];
140.1744 + int woyStamp = aggregateStamp(stamp[WEEK_OF_YEAR], dowStamp);
140.1745 +
140.1746 + int bestStamp = domStamp;
140.1747 + if (womStamp > bestStamp) {
140.1748 + bestStamp = womStamp;
140.1749 + }
140.1750 + if (dowimStamp > bestStamp) {
140.1751 + bestStamp = dowimStamp;
140.1752 + }
140.1753 + if (doyStamp > bestStamp) {
140.1754 + bestStamp = doyStamp;
140.1755 + }
140.1756 + if (woyStamp > bestStamp) {
140.1757 + bestStamp = woyStamp;
140.1758 + }
140.1759 +
140.1760 + /* No complete combination exists. Look for WEEK_OF_MONTH,
140.1761 + * DAY_OF_WEEK_IN_MONTH, or WEEK_OF_YEAR alone. Treat DAY_OF_WEEK alone
140.1762 + * as DAY_OF_WEEK_IN_MONTH.
140.1763 + */
140.1764 + if (bestStamp == UNSET) {
140.1765 + womStamp = stamp[WEEK_OF_MONTH];
140.1766 + dowimStamp = Math.max(stamp[DAY_OF_WEEK_IN_MONTH], dowStamp);
140.1767 + woyStamp = stamp[WEEK_OF_YEAR];
140.1768 + bestStamp = Math.max(Math.max(womStamp, dowimStamp), woyStamp);
140.1769 +
140.1770 + /* Treat MONTH alone or no fields at all as DAY_OF_MONTH. This may
140.1771 + * result in bestStamp = domStamp = UNSET if no fields are set,
140.1772 + * which indicates DAY_OF_MONTH.
140.1773 + */
140.1774 + if (bestStamp == UNSET) {
140.1775 + bestStamp = domStamp = monthStamp;
140.1776 + }
140.1777 + }
140.1778 +
140.1779 + if (bestStamp == domStamp ||
140.1780 + (bestStamp == womStamp && stamp[WEEK_OF_MONTH] >= stamp[WEEK_OF_YEAR]) ||
140.1781 + (bestStamp == dowimStamp && stamp[DAY_OF_WEEK_IN_MONTH] >= stamp[WEEK_OF_YEAR])) {
140.1782 + fieldMask |= MONTH_MASK;
140.1783 + if (bestStamp == domStamp) {
140.1784 + fieldMask |= DAY_OF_MONTH_MASK;
140.1785 + } else {
140.1786 + assert (bestStamp == womStamp || bestStamp == dowimStamp);
140.1787 + if (dowStamp != UNSET) {
140.1788 + fieldMask |= DAY_OF_WEEK_MASK;
140.1789 + }
140.1790 + if (womStamp == dowimStamp) {
140.1791 + // When they are equal, give the priority to
140.1792 + // WEEK_OF_MONTH for compatibility.
140.1793 + if (stamp[WEEK_OF_MONTH] >= stamp[DAY_OF_WEEK_IN_MONTH]) {
140.1794 + fieldMask |= WEEK_OF_MONTH_MASK;
140.1795 + } else {
140.1796 + fieldMask |= DAY_OF_WEEK_IN_MONTH_MASK;
140.1797 + }
140.1798 + } else {
140.1799 + if (bestStamp == womStamp) {
140.1800 + fieldMask |= WEEK_OF_MONTH_MASK;
140.1801 + } else {
140.1802 + assert (bestStamp == dowimStamp);
140.1803 + if (stamp[DAY_OF_WEEK_IN_MONTH] != UNSET) {
140.1804 + fieldMask |= DAY_OF_WEEK_IN_MONTH_MASK;
140.1805 + }
140.1806 + }
140.1807 + }
140.1808 + }
140.1809 + } else {
140.1810 + assert (bestStamp == doyStamp || bestStamp == woyStamp ||
140.1811 + bestStamp == UNSET);
140.1812 + if (bestStamp == doyStamp) {
140.1813 + fieldMask |= DAY_OF_YEAR_MASK;
140.1814 + } else {
140.1815 + assert (bestStamp == woyStamp);
140.1816 + if (dowStamp != UNSET) {
140.1817 + fieldMask |= DAY_OF_WEEK_MASK;
140.1818 + }
140.1819 + fieldMask |= WEEK_OF_YEAR_MASK;
140.1820 + }
140.1821 + }
140.1822 +
140.1823 + // Find the best set of fields specifying the time of day. There
140.1824 + // are only two possibilities here; the HOUR_OF_DAY or the
140.1825 + // AM_PM and the HOUR.
140.1826 + int hourOfDayStamp = stamp[HOUR_OF_DAY];
140.1827 + int hourStamp = aggregateStamp(stamp[HOUR], stamp[AM_PM]);
140.1828 + bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
140.1829 +
140.1830 + // if bestStamp is still UNSET, then take HOUR or AM_PM. (See 4846659)
140.1831 + if (bestStamp == UNSET) {
140.1832 + bestStamp = Math.max(stamp[HOUR], stamp[AM_PM]);
140.1833 + }
140.1834 +
140.1835 + // Hours
140.1836 + if (bestStamp != UNSET) {
140.1837 + if (bestStamp == hourOfDayStamp) {
140.1838 + fieldMask |= HOUR_OF_DAY_MASK;
140.1839 + } else {
140.1840 + fieldMask |= HOUR_MASK;
140.1841 + if (stamp[AM_PM] != UNSET) {
140.1842 + fieldMask |= AM_PM_MASK;
140.1843 + }
140.1844 + }
140.1845 + }
140.1846 + if (stamp[MINUTE] != UNSET) {
140.1847 + fieldMask |= MINUTE_MASK;
140.1848 + }
140.1849 + if (stamp[SECOND] != UNSET) {
140.1850 + fieldMask |= SECOND_MASK;
140.1851 + }
140.1852 + if (stamp[MILLISECOND] != UNSET) {
140.1853 + fieldMask |= MILLISECOND_MASK;
140.1854 + }
140.1855 + if (stamp[ZONE_OFFSET] >= MINIMUM_USER_STAMP) {
140.1856 + fieldMask |= ZONE_OFFSET_MASK;
140.1857 + }
140.1858 + if (stamp[DST_OFFSET] >= MINIMUM_USER_STAMP) {
140.1859 + fieldMask |= DST_OFFSET_MASK;
140.1860 + }
140.1861 +
140.1862 + return fieldMask;
140.1863 + }
140.1864 +
140.1865 + /**
140.1866 + * Returns the pseudo-time-stamp for two fields, given their
140.1867 + * individual pseudo-time-stamps. If either of the fields
140.1868 + * is unset, then the aggregate is unset. Otherwise, the
140.1869 + * aggregate is the later of the two stamps.
140.1870 + */
140.1871 + private static final int aggregateStamp(int stamp_a, int stamp_b) {
140.1872 + if (stamp_a == UNSET || stamp_b == UNSET) {
140.1873 + return UNSET;
140.1874 + }
140.1875 + return (stamp_a > stamp_b) ? stamp_a : stamp_b;
140.1876 + }
140.1877 +
140.1878 + /**
140.1879 + * Compares this <code>Calendar</code> to the specified
140.1880 + * <code>Object</code>. The result is <code>true</code> if and only if
140.1881 + * the argument is a <code>Calendar</code> object of the same calendar
140.1882 + * system that represents the same time value (millisecond offset from the
140.1883 + * <a href="#Epoch">Epoch</a>) under the same
140.1884 + * <code>Calendar</code> parameters as this object.
140.1885 + *
140.1886 + * <p>The <code>Calendar</code> parameters are the values represented
140.1887 + * by the <code>isLenient</code>, <code>getFirstDayOfWeek</code>,
140.1888 + * <code>getMinimalDaysInFirstWeek</code> and <code>getTimeZone</code>
140.1889 + * methods. If there is any difference in those parameters
140.1890 + * between the two <code>Calendar</code>s, this method returns
140.1891 + * <code>false</code>.
140.1892 + *
140.1893 + * <p>Use the {@link #compareTo(Calendar) compareTo} method to
140.1894 + * compare only the time values.
140.1895 + *
140.1896 + * @param obj the object to compare with.
140.1897 + * @return <code>true</code> if this object is equal to <code>obj</code>;
140.1898 + * <code>false</code> otherwise.
140.1899 + */
140.1900 + public boolean equals(Object obj) {
140.1901 + if (this == obj)
140.1902 + return true;
140.1903 + try {
140.1904 + Calendar that = (Calendar)obj;
140.1905 + return compareTo(getMillisOf(that)) == 0 &&
140.1906 + lenient == that.lenient &&
140.1907 + firstDayOfWeek == that.firstDayOfWeek &&
140.1908 + minimalDaysInFirstWeek == that.minimalDaysInFirstWeek &&
140.1909 + zone.equals(that.zone);
140.1910 + } catch (Exception e) {
140.1911 + // Note: GregorianCalendar.computeTime throws
140.1912 + // IllegalArgumentException if the ERA value is invalid
140.1913 + // even it's in lenient mode.
140.1914 + }
140.1915 + return false;
140.1916 + }
140.1917 +
140.1918 + /**
140.1919 + * Returns a hash code for this calendar.
140.1920 + *
140.1921 + * @return a hash code value for this object.
140.1922 + * @since 1.2
140.1923 + */
140.1924 + public int hashCode() {
140.1925 + // 'otheritems' represents the hash code for the previous versions.
140.1926 + int otheritems = (lenient ? 1 : 0)
140.1927 + | (firstDayOfWeek << 1)
140.1928 + | (minimalDaysInFirstWeek << 4)
140.1929 + | (zone.hashCode() << 7);
140.1930 + long t = getMillisOf(this);
140.1931 + return (int) t ^ (int)(t >> 32) ^ otheritems;
140.1932 + }
140.1933 +
140.1934 + /**
140.1935 + * Returns whether this <code>Calendar</code> represents a time
140.1936 + * before the time represented by the specified
140.1937 + * <code>Object</code>. This method is equivalent to:
140.1938 + * <pre><blockquote>
140.1939 + * compareTo(when) < 0
140.1940 + * </blockquote></pre>
140.1941 + * if and only if <code>when</code> is a <code>Calendar</code>
140.1942 + * instance. Otherwise, the method returns <code>false</code>.
140.1943 + *
140.1944 + * @param when the <code>Object</code> to be compared
140.1945 + * @return <code>true</code> if the time of this
140.1946 + * <code>Calendar</code> is before the time represented by
140.1947 + * <code>when</code>; <code>false</code> otherwise.
140.1948 + * @see #compareTo(Calendar)
140.1949 + */
140.1950 + public boolean before(Object when) {
140.1951 + return when instanceof Calendar
140.1952 + && compareTo((Calendar)when) < 0;
140.1953 + }
140.1954 +
140.1955 + /**
140.1956 + * Returns whether this <code>Calendar</code> represents a time
140.1957 + * after the time represented by the specified
140.1958 + * <code>Object</code>. This method is equivalent to:
140.1959 + * <pre><blockquote>
140.1960 + * compareTo(when) > 0
140.1961 + * </blockquote></pre>
140.1962 + * if and only if <code>when</code> is a <code>Calendar</code>
140.1963 + * instance. Otherwise, the method returns <code>false</code>.
140.1964 + *
140.1965 + * @param when the <code>Object</code> to be compared
140.1966 + * @return <code>true</code> if the time of this <code>Calendar</code> is
140.1967 + * after the time represented by <code>when</code>; <code>false</code>
140.1968 + * otherwise.
140.1969 + * @see #compareTo(Calendar)
140.1970 + */
140.1971 + public boolean after(Object when) {
140.1972 + return when instanceof Calendar
140.1973 + && compareTo((Calendar)when) > 0;
140.1974 + }
140.1975 +
140.1976 + /**
140.1977 + * Compares the time values (millisecond offsets from the <a
140.1978 + * href="#Epoch">Epoch</a>) represented by two
140.1979 + * <code>Calendar</code> objects.
140.1980 + *
140.1981 + * @param anotherCalendar the <code>Calendar</code> to be compared.
140.1982 + * @return the value <code>0</code> if the time represented by the argument
140.1983 + * is equal to the time represented by this <code>Calendar</code>; a value
140.1984 + * less than <code>0</code> if the time of this <code>Calendar</code> is
140.1985 + * before the time represented by the argument; and a value greater than
140.1986 + * <code>0</code> if the time of this <code>Calendar</code> is after the
140.1987 + * time represented by the argument.
140.1988 + * @exception NullPointerException if the specified <code>Calendar</code> is
140.1989 + * <code>null</code>.
140.1990 + * @exception IllegalArgumentException if the time value of the
140.1991 + * specified <code>Calendar</code> object can't be obtained due to
140.1992 + * any invalid calendar values.
140.1993 + * @since 1.5
140.1994 + */
140.1995 + public int compareTo(Calendar anotherCalendar) {
140.1996 + return compareTo(getMillisOf(anotherCalendar));
140.1997 + }
140.1998 +
140.1999 + /**
140.2000 + * Adds or subtracts the specified amount of time to the given calendar field,
140.2001 + * based on the calendar's rules. For example, to subtract 5 days from
140.2002 + * the current time of the calendar, you can achieve it by calling:
140.2003 + * <p><code>add(Calendar.DAY_OF_MONTH, -5)</code>.
140.2004 + *
140.2005 + * @param field the calendar field.
140.2006 + * @param amount the amount of date or time to be added to the field.
140.2007 + * @see #roll(int,int)
140.2008 + * @see #set(int,int)
140.2009 + */
140.2010 + abstract public void add(int field, int amount);
140.2011 +
140.2012 + /**
140.2013 + * Adds or subtracts (up/down) a single unit of time on the given time
140.2014 + * field without changing larger fields. For example, to roll the current
140.2015 + * date up by one day, you can achieve it by calling:
140.2016 + * <p>roll(Calendar.DATE, true).
140.2017 + * When rolling on the year or Calendar.YEAR field, it will roll the year
140.2018 + * value in the range between 1 and the value returned by calling
140.2019 + * <code>getMaximum(Calendar.YEAR)</code>.
140.2020 + * When rolling on the month or Calendar.MONTH field, other fields like
140.2021 + * date might conflict and, need to be changed. For instance,
140.2022 + * rolling the month on the date 01/31/96 will result in 02/29/96.
140.2023 + * When rolling on the hour-in-day or Calendar.HOUR_OF_DAY field, it will
140.2024 + * roll the hour value in the range between 0 and 23, which is zero-based.
140.2025 + *
140.2026 + * @param field the time field.
140.2027 + * @param up indicates if the value of the specified time field is to be
140.2028 + * rolled up or rolled down. Use true if rolling up, false otherwise.
140.2029 + * @see Calendar#add(int,int)
140.2030 + * @see Calendar#set(int,int)
140.2031 + */
140.2032 + abstract public void roll(int field, boolean up);
140.2033 +
140.2034 + /**
140.2035 + * Adds the specified (signed) amount to the specified calendar field
140.2036 + * without changing larger fields. A negative amount means to roll
140.2037 + * down.
140.2038 + *
140.2039 + * <p>NOTE: This default implementation on <code>Calendar</code> just repeatedly calls the
140.2040 + * version of {@link #roll(int,boolean) roll()} that rolls by one unit. This may not
140.2041 + * always do the right thing. For example, if the <code>DAY_OF_MONTH</code> field is 31,
140.2042 + * rolling through February will leave it set to 28. The <code>GregorianCalendar</code>
140.2043 + * version of this function takes care of this problem. Other subclasses
140.2044 + * should also provide overrides of this function that do the right thing.
140.2045 + *
140.2046 + * @param field the calendar field.
140.2047 + * @param amount the signed amount to add to the calendar <code>field</code>.
140.2048 + * @since 1.2
140.2049 + * @see #roll(int,boolean)
140.2050 + * @see #add(int,int)
140.2051 + * @see #set(int,int)
140.2052 + */
140.2053 + public void roll(int field, int amount)
140.2054 + {
140.2055 + while (amount > 0) {
140.2056 + roll(field, true);
140.2057 + amount--;
140.2058 + }
140.2059 + while (amount < 0) {
140.2060 + roll(field, false);
140.2061 + amount++;
140.2062 + }
140.2063 + }
140.2064 +
140.2065 + /**
140.2066 + * Sets the time zone with the given time zone value.
140.2067 + *
140.2068 + * @param value the given time zone.
140.2069 + */
140.2070 + public void setTimeZone(TimeZone value)
140.2071 + {
140.2072 + zone = value;
140.2073 + sharedZone = false;
140.2074 + /* Recompute the fields from the time using the new zone. This also
140.2075 + * works if isTimeSet is false (after a call to set()). In that case
140.2076 + * the time will be computed from the fields using the new zone, then
140.2077 + * the fields will get recomputed from that. Consider the sequence of
140.2078 + * calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
140.2079 + * Is cal set to 1 o'clock EST or 1 o'clock PST? Answer: PST. More
140.2080 + * generally, a call to setTimeZone() affects calls to set() BEFORE AND
140.2081 + * AFTER it up to the next call to complete().
140.2082 + */
140.2083 + areAllFieldsSet = areFieldsSet = false;
140.2084 + }
140.2085 +
140.2086 + /**
140.2087 + * Gets the time zone.
140.2088 + *
140.2089 + * @return the time zone object associated with this calendar.
140.2090 + */
140.2091 + public TimeZone getTimeZone()
140.2092 + {
140.2093 + // If the TimeZone object is shared by other Calendar instances, then
140.2094 + // create a clone.
140.2095 + if (sharedZone) {
140.2096 + zone = (TimeZone) zone.clone();
140.2097 + sharedZone = false;
140.2098 + }
140.2099 + return zone;
140.2100 + }
140.2101 +
140.2102 + /**
140.2103 + * Returns the time zone (without cloning).
140.2104 + */
140.2105 + TimeZone getZone() {
140.2106 + return zone;
140.2107 + }
140.2108 +
140.2109 + /**
140.2110 + * Sets the sharedZone flag to <code>shared</code>.
140.2111 + */
140.2112 + void setZoneShared(boolean shared) {
140.2113 + sharedZone = shared;
140.2114 + }
140.2115 +
140.2116 + /**
140.2117 + * Specifies whether or not date/time interpretation is to be lenient. With
140.2118 + * lenient interpretation, a date such as "February 942, 1996" will be
140.2119 + * treated as being equivalent to the 941st day after February 1, 1996.
140.2120 + * With strict (non-lenient) interpretation, such dates will cause an exception to be
140.2121 + * thrown. The default is lenient.
140.2122 + *
140.2123 + * @param lenient <code>true</code> if the lenient mode is to be turned
140.2124 + * on; <code>false</code> if it is to be turned off.
140.2125 + * @see #isLenient()
140.2126 + * @see java.text.DateFormat#setLenient
140.2127 + */
140.2128 + public void setLenient(boolean lenient)
140.2129 + {
140.2130 + this.lenient = lenient;
140.2131 + }
140.2132 +
140.2133 + /**
140.2134 + * Tells whether date/time interpretation is to be lenient.
140.2135 + *
140.2136 + * @return <code>true</code> if the interpretation mode of this calendar is lenient;
140.2137 + * <code>false</code> otherwise.
140.2138 + * @see #setLenient(boolean)
140.2139 + */
140.2140 + public boolean isLenient()
140.2141 + {
140.2142 + return lenient;
140.2143 + }
140.2144 +
140.2145 + /**
140.2146 + * Sets what the first day of the week is; e.g., <code>SUNDAY</code> in the U.S.,
140.2147 + * <code>MONDAY</code> in France.
140.2148 + *
140.2149 + * @param value the given first day of the week.
140.2150 + * @see #getFirstDayOfWeek()
140.2151 + * @see #getMinimalDaysInFirstWeek()
140.2152 + */
140.2153 + public void setFirstDayOfWeek(int value)
140.2154 + {
140.2155 + if (firstDayOfWeek == value) {
140.2156 + return;
140.2157 + }
140.2158 + firstDayOfWeek = value;
140.2159 + invalidateWeekFields();
140.2160 + }
140.2161 +
140.2162 + /**
140.2163 + * Gets what the first day of the week is; e.g., <code>SUNDAY</code> in the U.S.,
140.2164 + * <code>MONDAY</code> in France.
140.2165 + *
140.2166 + * @return the first day of the week.
140.2167 + * @see #setFirstDayOfWeek(int)
140.2168 + * @see #getMinimalDaysInFirstWeek()
140.2169 + */
140.2170 + public int getFirstDayOfWeek()
140.2171 + {
140.2172 + return firstDayOfWeek;
140.2173 + }
140.2174 +
140.2175 + /**
140.2176 + * Sets what the minimal days required in the first week of the year are;
140.2177 + * For example, if the first week is defined as one that contains the first
140.2178 + * day of the first month of a year, call this method with value 1. If it
140.2179 + * must be a full week, use value 7.
140.2180 + *
140.2181 + * @param value the given minimal days required in the first week
140.2182 + * of the year.
140.2183 + * @see #getMinimalDaysInFirstWeek()
140.2184 + */
140.2185 + public void setMinimalDaysInFirstWeek(int value)
140.2186 + {
140.2187 + if (minimalDaysInFirstWeek == value) {
140.2188 + return;
140.2189 + }
140.2190 + minimalDaysInFirstWeek = value;
140.2191 + invalidateWeekFields();
140.2192 + }
140.2193 +
140.2194 + /**
140.2195 + * Gets what the minimal days required in the first week of the year are;
140.2196 + * e.g., if the first week is defined as one that contains the first day
140.2197 + * of the first month of a year, this method returns 1. If
140.2198 + * the minimal days required must be a full week, this method
140.2199 + * returns 7.
140.2200 + *
140.2201 + * @return the minimal days required in the first week of the year.
140.2202 + * @see #setMinimalDaysInFirstWeek(int)
140.2203 + */
140.2204 + public int getMinimalDaysInFirstWeek()
140.2205 + {
140.2206 + return minimalDaysInFirstWeek;
140.2207 + }
140.2208 +
140.2209 + /**
140.2210 + * Returns whether this {@code Calendar} supports week dates.
140.2211 + *
140.2212 + * <p>The default implementation of this method returns {@code false}.
140.2213 + *
140.2214 + * @return {@code true} if this {@code Calendar} supports week dates;
140.2215 + * {@code false} otherwise.
140.2216 + * @see #getWeekYear()
140.2217 + * @see #setWeekDate(int,int,int)
140.2218 + * @see #getWeeksInWeekYear()
140.2219 + * @since 1.7
140.2220 + */
140.2221 + public boolean isWeekDateSupported() {
140.2222 + return false;
140.2223 + }
140.2224 +
140.2225 + /**
140.2226 + * Returns the week year represented by this {@code Calendar}. The
140.2227 + * week year is in sync with the week cycle. The {@linkplain
140.2228 + * #getFirstDayOfWeek() first day of the first week} is the first
140.2229 + * day of the week year.
140.2230 + *
140.2231 + * <p>The default implementation of this method throws an
140.2232 + * {@link UnsupportedOperationException}.
140.2233 + *
140.2234 + * @return the week year of this {@code Calendar}
140.2235 + * @exception UnsupportedOperationException
140.2236 + * if any week year numbering isn't supported
140.2237 + * in this {@code Calendar}.
140.2238 + * @see #isWeekDateSupported()
140.2239 + * @see #getFirstDayOfWeek()
140.2240 + * @see #getMinimalDaysInFirstWeek()
140.2241 + * @since 1.7
140.2242 + */
140.2243 + public int getWeekYear() {
140.2244 + throw new UnsupportedOperationException();
140.2245 + }
140.2246 +
140.2247 + /**
140.2248 + * Sets the date of this {@code Calendar} with the the given date
140.2249 + * specifiers - week year, week of year, and day of week.
140.2250 + *
140.2251 + * <p>Unlike the {@code set} method, all of the calendar fields
140.2252 + * and {@code time} values are calculated upon return.
140.2253 + *
140.2254 + * <p>If {@code weekOfYear} is out of the valid week-of-year range
140.2255 + * in {@code weekYear}, the {@code weekYear} and {@code
140.2256 + * weekOfYear} values are adjusted in lenient mode, or an {@code
140.2257 + * IllegalArgumentException} is thrown in non-lenient mode.
140.2258 + *
140.2259 + * <p>The default implementation of this method throws an
140.2260 + * {@code UnsupportedOperationException}.
140.2261 + *
140.2262 + * @param weekYear the week year
140.2263 + * @param weekOfYear the week number based on {@code weekYear}
140.2264 + * @param dayOfWeek the day of week value: one of the constants
140.2265 + * for the {@link #DAY_OF_WEEK} field: {@link
140.2266 + * #SUNDAY}, ..., {@link #SATURDAY}.
140.2267 + * @exception IllegalArgumentException
140.2268 + * if any of the given date specifiers is invalid
140.2269 + * or any of the calendar fields are inconsistent
140.2270 + * with the given date specifiers in non-lenient mode
140.2271 + * @exception UnsupportedOperationException
140.2272 + * if any week year numbering isn't supported in this
140.2273 + * {@code Calendar}.
140.2274 + * @see #isWeekDateSupported()
140.2275 + * @see #getFirstDayOfWeek()
140.2276 + * @see #getMinimalDaysInFirstWeek()
140.2277 + * @since 1.7
140.2278 + */
140.2279 + public void setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) {
140.2280 + throw new UnsupportedOperationException();
140.2281 + }
140.2282 +
140.2283 + /**
140.2284 + * Returns the number of weeks in the week year represented by this
140.2285 + * {@code Calendar}.
140.2286 + *
140.2287 + * <p>The default implementation of this method throws an
140.2288 + * {@code UnsupportedOperationException}.
140.2289 + *
140.2290 + * @return the number of weeks in the week year.
140.2291 + * @exception UnsupportedOperationException
140.2292 + * if any week year numbering isn't supported in this
140.2293 + * {@code Calendar}.
140.2294 + * @see #WEEK_OF_YEAR
140.2295 + * @see #isWeekDateSupported()
140.2296 + * @see #getWeekYear()
140.2297 + * @see #getActualMaximum(int)
140.2298 + * @since 1.7
140.2299 + */
140.2300 + public int getWeeksInWeekYear() {
140.2301 + throw new UnsupportedOperationException();
140.2302 + }
140.2303 +
140.2304 + /**
140.2305 + * Returns the minimum value for the given calendar field of this
140.2306 + * <code>Calendar</code> instance. The minimum value is defined as
140.2307 + * the smallest value returned by the {@link #get(int) get} method
140.2308 + * for any possible time value. The minimum value depends on
140.2309 + * calendar system specific parameters of the instance.
140.2310 + *
140.2311 + * @param field the calendar field.
140.2312 + * @return the minimum value for the given calendar field.
140.2313 + * @see #getMaximum(int)
140.2314 + * @see #getGreatestMinimum(int)
140.2315 + * @see #getLeastMaximum(int)
140.2316 + * @see #getActualMinimum(int)
140.2317 + * @see #getActualMaximum(int)
140.2318 + */
140.2319 + abstract public int getMinimum(int field);
140.2320 +
140.2321 + /**
140.2322 + * Returns the maximum value for the given calendar field of this
140.2323 + * <code>Calendar</code> instance. The maximum value is defined as
140.2324 + * the largest value returned by the {@link #get(int) get} method
140.2325 + * for any possible time value. The maximum value depends on
140.2326 + * calendar system specific parameters of the instance.
140.2327 + *
140.2328 + * @param field the calendar field.
140.2329 + * @return the maximum value for the given calendar field.
140.2330 + * @see #getMinimum(int)
140.2331 + * @see #getGreatestMinimum(int)
140.2332 + * @see #getLeastMaximum(int)
140.2333 + * @see #getActualMinimum(int)
140.2334 + * @see #getActualMaximum(int)
140.2335 + */
140.2336 + abstract public int getMaximum(int field);
140.2337 +
140.2338 + /**
140.2339 + * Returns the highest minimum value for the given calendar field
140.2340 + * of this <code>Calendar</code> instance. The highest minimum
140.2341 + * value is defined as the largest value returned by {@link
140.2342 + * #getActualMinimum(int)} for any possible time value. The
140.2343 + * greatest minimum value depends on calendar system specific
140.2344 + * parameters of the instance.
140.2345 + *
140.2346 + * @param field the calendar field.
140.2347 + * @return the highest minimum value for the given calendar field.
140.2348 + * @see #getMinimum(int)
140.2349 + * @see #getMaximum(int)
140.2350 + * @see #getLeastMaximum(int)
140.2351 + * @see #getActualMinimum(int)
140.2352 + * @see #getActualMaximum(int)
140.2353 + */
140.2354 + abstract public int getGreatestMinimum(int field);
140.2355 +
140.2356 + /**
140.2357 + * Returns the lowest maximum value for the given calendar field
140.2358 + * of this <code>Calendar</code> instance. The lowest maximum
140.2359 + * value is defined as the smallest value returned by {@link
140.2360 + * #getActualMaximum(int)} for any possible time value. The least
140.2361 + * maximum value depends on calendar system specific parameters of
140.2362 + * the instance. For example, a <code>Calendar</code> for the
140.2363 + * Gregorian calendar system returns 28 for the
140.2364 + * <code>DAY_OF_MONTH</code> field, because the 28th is the last
140.2365 + * day of the shortest month of this calendar, February in a
140.2366 + * common year.
140.2367 + *
140.2368 + * @param field the calendar field.
140.2369 + * @return the lowest maximum value for the given calendar field.
140.2370 + * @see #getMinimum(int)
140.2371 + * @see #getMaximum(int)
140.2372 + * @see #getGreatestMinimum(int)
140.2373 + * @see #getActualMinimum(int)
140.2374 + * @see #getActualMaximum(int)
140.2375 + */
140.2376 + abstract public int getLeastMaximum(int field);
140.2377 +
140.2378 + /**
140.2379 + * Returns the minimum value that the specified calendar field
140.2380 + * could have, given the time value of this <code>Calendar</code>.
140.2381 + *
140.2382 + * <p>The default implementation of this method uses an iterative
140.2383 + * algorithm to determine the actual minimum value for the
140.2384 + * calendar field. Subclasses should, if possible, override this
140.2385 + * with a more efficient implementation - in many cases, they can
140.2386 + * simply return <code>getMinimum()</code>.
140.2387 + *
140.2388 + * @param field the calendar field
140.2389 + * @return the minimum of the given calendar field for the time
140.2390 + * value of this <code>Calendar</code>
140.2391 + * @see #getMinimum(int)
140.2392 + * @see #getMaximum(int)
140.2393 + * @see #getGreatestMinimum(int)
140.2394 + * @see #getLeastMaximum(int)
140.2395 + * @see #getActualMaximum(int)
140.2396 + * @since 1.2
140.2397 + */
140.2398 + public int getActualMinimum(int field) {
140.2399 + int fieldValue = getGreatestMinimum(field);
140.2400 + int endValue = getMinimum(field);
140.2401 +
140.2402 + // if we know that the minimum value is always the same, just return it
140.2403 + if (fieldValue == endValue) {
140.2404 + return fieldValue;
140.2405 + }
140.2406 +
140.2407 + // clone the calendar so we don't mess with the real one, and set it to
140.2408 + // accept anything for the field values
140.2409 + Calendar work = (Calendar)this.clone();
140.2410 + work.setLenient(true);
140.2411 +
140.2412 + // now try each value from getLeastMaximum() to getMaximum() one by one until
140.2413 + // we get a value that normalizes to another value. The last value that
140.2414 + // normalizes to itself is the actual minimum for the current date
140.2415 + int result = fieldValue;
140.2416 +
140.2417 + do {
140.2418 + work.set(field, fieldValue);
140.2419 + if (work.get(field) != fieldValue) {
140.2420 + break;
140.2421 + } else {
140.2422 + result = fieldValue;
140.2423 + fieldValue--;
140.2424 + }
140.2425 + } while (fieldValue >= endValue);
140.2426 +
140.2427 + return result;
140.2428 + }
140.2429 +
140.2430 + /**
140.2431 + * Returns the maximum value that the specified calendar field
140.2432 + * could have, given the time value of this
140.2433 + * <code>Calendar</code>. For example, the actual maximum value of
140.2434 + * the <code>MONTH</code> field is 12 in some years, and 13 in
140.2435 + * other years in the Hebrew calendar system.
140.2436 + *
140.2437 + * <p>The default implementation of this method uses an iterative
140.2438 + * algorithm to determine the actual maximum value for the
140.2439 + * calendar field. Subclasses should, if possible, override this
140.2440 + * with a more efficient implementation.
140.2441 + *
140.2442 + * @param field the calendar field
140.2443 + * @return the maximum of the given calendar field for the time
140.2444 + * value of this <code>Calendar</code>
140.2445 + * @see #getMinimum(int)
140.2446 + * @see #getMaximum(int)
140.2447 + * @see #getGreatestMinimum(int)
140.2448 + * @see #getLeastMaximum(int)
140.2449 + * @see #getActualMinimum(int)
140.2450 + * @since 1.2
140.2451 + */
140.2452 + public int getActualMaximum(int field) {
140.2453 + int fieldValue = getLeastMaximum(field);
140.2454 + int endValue = getMaximum(field);
140.2455 +
140.2456 + // if we know that the maximum value is always the same, just return it.
140.2457 + if (fieldValue == endValue) {
140.2458 + return fieldValue;
140.2459 + }
140.2460 +
140.2461 + // clone the calendar so we don't mess with the real one, and set it to
140.2462 + // accept anything for the field values.
140.2463 + Calendar work = (Calendar)this.clone();
140.2464 + work.setLenient(true);
140.2465 +
140.2466 + // if we're counting weeks, set the day of the week to Sunday. We know the
140.2467 + // last week of a month or year will contain the first day of the week.
140.2468 + if (field == WEEK_OF_YEAR || field == WEEK_OF_MONTH)
140.2469 + work.set(DAY_OF_WEEK, firstDayOfWeek);
140.2470 +
140.2471 + // now try each value from getLeastMaximum() to getMaximum() one by one until
140.2472 + // we get a value that normalizes to another value. The last value that
140.2473 + // normalizes to itself is the actual maximum for the current date
140.2474 + int result = fieldValue;
140.2475 +
140.2476 + do {
140.2477 + work.set(field, fieldValue);
140.2478 + if (work.get(field) != fieldValue) {
140.2479 + break;
140.2480 + } else {
140.2481 + result = fieldValue;
140.2482 + fieldValue++;
140.2483 + }
140.2484 + } while (fieldValue <= endValue);
140.2485 +
140.2486 + return result;
140.2487 + }
140.2488 +
140.2489 + /**
140.2490 + * Creates and returns a copy of this object.
140.2491 + *
140.2492 + * @return a copy of this object.
140.2493 + */
140.2494 + public Object clone()
140.2495 + {
140.2496 + try {
140.2497 + Calendar other = (Calendar) super.clone();
140.2498 +
140.2499 + other.fields = new int[FIELD_COUNT];
140.2500 + other.isSet = new boolean[FIELD_COUNT];
140.2501 + other.stamp = new int[FIELD_COUNT];
140.2502 + for (int i = 0; i < FIELD_COUNT; i++) {
140.2503 + other.fields[i] = fields[i];
140.2504 + other.stamp[i] = stamp[i];
140.2505 + other.isSet[i] = isSet[i];
140.2506 + }
140.2507 + other.zone = (TimeZone) zone.clone();
140.2508 + return other;
140.2509 + }
140.2510 + catch (CloneNotSupportedException e) {
140.2511 + // this shouldn't happen, since we are Cloneable
140.2512 + throw new InternalError();
140.2513 + }
140.2514 + }
140.2515 +
140.2516 + private static final String[] FIELD_NAME = {
140.2517 + "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH", "DAY_OF_MONTH",
140.2518 + "DAY_OF_YEAR", "DAY_OF_WEEK", "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR",
140.2519 + "HOUR_OF_DAY", "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
140.2520 + "DST_OFFSET"
140.2521 + };
140.2522 +
140.2523 + /**
140.2524 + * Returns the name of the specified calendar field.
140.2525 + *
140.2526 + * @param field the calendar field
140.2527 + * @return the calendar field name
140.2528 + * @exception IndexOutOfBoundsException if <code>field</code> is negative,
140.2529 + * equal to or greater then <code>FIELD_COUNT</code>.
140.2530 + */
140.2531 + static final String getFieldName(int field) {
140.2532 + return FIELD_NAME[field];
140.2533 + }
140.2534 +
140.2535 + /**
140.2536 + * Return a string representation of this calendar. This method
140.2537 + * is intended to be used only for debugging purposes, and the
140.2538 + * format of the returned string may vary between implementations.
140.2539 + * The returned string may be empty but may not be <code>null</code>.
140.2540 + *
140.2541 + * @return a string representation of this calendar.
140.2542 + */
140.2543 + public String toString() {
140.2544 + // NOTE: BuddhistCalendar.toString() interprets the string
140.2545 + // produced by this method so that the Gregorian year number
140.2546 + // is substituted by its B.E. year value. It relies on
140.2547 + // "...,YEAR=<year>,..." or "...,YEAR=?,...".
140.2548 + StringBuilder buffer = new StringBuilder(800);
140.2549 + buffer.append(getClass().getName()).append('[');
140.2550 + appendValue(buffer, "time", isTimeSet, time);
140.2551 + buffer.append(",areFieldsSet=").append(areFieldsSet);
140.2552 + buffer.append(",areAllFieldsSet=").append(areAllFieldsSet);
140.2553 + buffer.append(",lenient=").append(lenient);
140.2554 + buffer.append(",zone=").append(zone);
140.2555 + appendValue(buffer, ",firstDayOfWeek", true, (long) firstDayOfWeek);
140.2556 + appendValue(buffer, ",minimalDaysInFirstWeek", true, (long) minimalDaysInFirstWeek);
140.2557 + for (int i = 0; i < FIELD_COUNT; ++i) {
140.2558 + buffer.append(',');
140.2559 + appendValue(buffer, FIELD_NAME[i], isSet(i), (long) fields[i]);
140.2560 + }
140.2561 + buffer.append(']');
140.2562 + return buffer.toString();
140.2563 + }
140.2564 +
140.2565 + // =======================privates===============================
140.2566 +
140.2567 + private static final void appendValue(StringBuilder sb, String item, boolean valid, long value) {
140.2568 + sb.append(item).append('=');
140.2569 + if (valid) {
140.2570 + sb.append(value);
140.2571 + } else {
140.2572 + sb.append('?');
140.2573 + }
140.2574 + }
140.2575 +
140.2576 + /**
140.2577 + * Both firstDayOfWeek and minimalDaysInFirstWeek are locale-dependent.
140.2578 + * They are used to figure out the week count for a specific date for
140.2579 + * a given locale. These must be set when a Calendar is constructed.
140.2580 + * @param desiredLocale the given locale.
140.2581 + */
140.2582 + private void setWeekCountData(Locale desiredLocale)
140.2583 + {
140.2584 + /* try to get the Locale data from the cache */
140.2585 + int[] data = cachedLocaleData.get(desiredLocale);
140.2586 + if (data == null) { /* cache miss */
140.2587 +// ResourceBundle bundle = LocaleData.getCalendarData(desiredLocale);
140.2588 + data = new int[2];
140.2589 +// data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
140.2590 +// data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
140.2591 + cachedLocaleData.putIfAbsent(desiredLocale, data);
140.2592 + }
140.2593 + firstDayOfWeek = data[0];
140.2594 + minimalDaysInFirstWeek = data[1];
140.2595 + }
140.2596 +
140.2597 + /**
140.2598 + * Recomputes the time and updates the status fields isTimeSet
140.2599 + * and areFieldsSet. Callers should check isTimeSet and only
140.2600 + * call this method if isTimeSet is false.
140.2601 + */
140.2602 + private void updateTime() {
140.2603 + computeTime();
140.2604 + // The areFieldsSet and areAllFieldsSet values are no longer
140.2605 + // controlled here (as of 1.5).
140.2606 + isTimeSet = true;
140.2607 + }
140.2608 +
140.2609 + private int compareTo(long t) {
140.2610 + long thisTime = getMillisOf(this);
140.2611 + return (thisTime > t) ? 1 : (thisTime == t) ? 0 : -1;
140.2612 + }
140.2613 +
140.2614 + private static final long getMillisOf(Calendar calendar) {
140.2615 + if (calendar.isTimeSet) {
140.2616 + return calendar.time;
140.2617 + }
140.2618 + Calendar cal = (Calendar) calendar.clone();
140.2619 + cal.setLenient(true);
140.2620 + return cal.getTimeInMillis();
140.2621 + }
140.2622 +
140.2623 + /**
140.2624 + * Adjusts the stamp[] values before nextStamp overflow. nextStamp
140.2625 + * is set to the next stamp value upon the return.
140.2626 + */
140.2627 + private final void adjustStamp() {
140.2628 + int max = MINIMUM_USER_STAMP;
140.2629 + int newStamp = MINIMUM_USER_STAMP;
140.2630 +
140.2631 + for (;;) {
140.2632 + int min = Integer.MAX_VALUE;
140.2633 + for (int i = 0; i < stamp.length; i++) {
140.2634 + int v = stamp[i];
140.2635 + if (v >= newStamp && min > v) {
140.2636 + min = v;
140.2637 + }
140.2638 + if (max < v) {
140.2639 + max = v;
140.2640 + }
140.2641 + }
140.2642 + if (max != min && min == Integer.MAX_VALUE) {
140.2643 + break;
140.2644 + }
140.2645 + for (int i = 0; i < stamp.length; i++) {
140.2646 + if (stamp[i] == min) {
140.2647 + stamp[i] = newStamp;
140.2648 + }
140.2649 + }
140.2650 + newStamp++;
140.2651 + if (min == max) {
140.2652 + break;
140.2653 + }
140.2654 + }
140.2655 + nextStamp = newStamp;
140.2656 + }
140.2657 +
140.2658 + /**
140.2659 + * Sets the WEEK_OF_MONTH and WEEK_OF_YEAR fields to new values with the
140.2660 + * new parameter value if they have been calculated internally.
140.2661 + */
140.2662 + private void invalidateWeekFields()
140.2663 + {
140.2664 + if (stamp[WEEK_OF_MONTH] != COMPUTED &&
140.2665 + stamp[WEEK_OF_YEAR] != COMPUTED) {
140.2666 + return;
140.2667 + }
140.2668 +
140.2669 + // We have to check the new values of these fields after changing
140.2670 + // firstDayOfWeek and/or minimalDaysInFirstWeek. If the field values
140.2671 + // have been changed, then set the new values. (4822110)
140.2672 + Calendar cal = (Calendar) clone();
140.2673 + cal.setLenient(true);
140.2674 + cal.clear(WEEK_OF_MONTH);
140.2675 + cal.clear(WEEK_OF_YEAR);
140.2676 +
140.2677 + if (stamp[WEEK_OF_MONTH] == COMPUTED) {
140.2678 + int weekOfMonth = cal.get(WEEK_OF_MONTH);
140.2679 + if (fields[WEEK_OF_MONTH] != weekOfMonth) {
140.2680 + fields[WEEK_OF_MONTH] = weekOfMonth;
140.2681 + }
140.2682 + }
140.2683 +
140.2684 + if (stamp[WEEK_OF_YEAR] == COMPUTED) {
140.2685 + int weekOfYear = cal.get(WEEK_OF_YEAR);
140.2686 + if (fields[WEEK_OF_YEAR] != weekOfYear) {
140.2687 + fields[WEEK_OF_YEAR] = weekOfYear;
140.2688 + }
140.2689 + }
140.2690 + }
140.2691 +
140.2692 + /**
140.2693 + * Save the state of this object to a stream (i.e., serialize it).
140.2694 + *
140.2695 + * Ideally, <code>Calendar</code> would only write out its state data and
140.2696 + * the current time, and not write any field data out, such as
140.2697 + * <code>fields[]</code>, <code>isTimeSet</code>, <code>areFieldsSet</code>,
140.2698 + * and <code>isSet[]</code>. <code>nextStamp</code> also should not be part
140.2699 + * of the persistent state. Unfortunately, this didn't happen before JDK 1.1
140.2700 + * shipped. To be compatible with JDK 1.1, we will always have to write out
140.2701 + * the field values and state flags. However, <code>nextStamp</code> can be
140.2702 + * removed from the serialization stream; this will probably happen in the
140.2703 + * near future.
140.2704 + */
140.2705 + private void writeObject(ObjectOutputStream stream)
140.2706 + throws IOException
140.2707 + {
140.2708 + // Try to compute the time correctly, for the future (stream
140.2709 + // version 2) in which we don't write out fields[] or isSet[].
140.2710 + if (!isTimeSet) {
140.2711 + try {
140.2712 + updateTime();
140.2713 + }
140.2714 + catch (IllegalArgumentException e) {}
140.2715 + }
140.2716 +
140.2717 + // If this Calendar has a ZoneInfo, save it and set a
140.2718 + // SimpleTimeZone equivalent (as a single DST schedule) for
140.2719 + // backward compatibility.
140.2720 + TimeZone savedZone = null;
140.2721 +// if (zone instanceof ZoneInfo) {
140.2722 +// SimpleTimeZone stz = ((ZoneInfo)zone).getLastRuleInstance();
140.2723 +// if (stz == null) {
140.2724 +// stz = new SimpleTimeZone(zone.getRawOffset(), zone.getID());
140.2725 +// }
140.2726 +// savedZone = zone;
140.2727 +// zone = stz;
140.2728 +// }
140.2729 +
140.2730 + // Write out the 1.1 FCS object.
140.2731 + stream.defaultWriteObject();
140.2732 +
140.2733 + // Write out the ZoneInfo object
140.2734 + // 4802409: we write out even if it is null, a temporary workaround
140.2735 + // the real fix for bug 4844924 in corba-iiop
140.2736 + stream.writeObject(savedZone);
140.2737 + if (savedZone != null) {
140.2738 + zone = savedZone;
140.2739 + }
140.2740 + }
140.2741 +
140.2742 + /**
140.2743 + * Reconstitutes this object from a stream (i.e., deserialize it).
140.2744 + */
140.2745 + private void readObject(ObjectInputStream stream)
140.2746 + throws IOException, ClassNotFoundException
140.2747 + {
140.2748 + final ObjectInputStream input = stream;
140.2749 + input.defaultReadObject();
140.2750 +
140.2751 + stamp = new int[FIELD_COUNT];
140.2752 +
140.2753 + // Starting with version 2 (not implemented yet), we expect that
140.2754 + // fields[], isSet[], isTimeSet, and areFieldsSet may not be
140.2755 + // streamed out anymore. We expect 'time' to be correct.
140.2756 + if (serialVersionOnStream >= 2)
140.2757 + {
140.2758 + isTimeSet = true;
140.2759 + if (fields == null) fields = new int[FIELD_COUNT];
140.2760 + if (isSet == null) isSet = new boolean[FIELD_COUNT];
140.2761 + }
140.2762 + else if (serialVersionOnStream >= 0)
140.2763 + {
140.2764 + for (int i=0; i<FIELD_COUNT; ++i)
140.2765 + stamp[i] = isSet[i] ? COMPUTED : UNSET;
140.2766 + }
140.2767 +
140.2768 + serialVersionOnStream = currentSerialVersion;
140.2769 +
140.2770 + // If there's a ZoneInfo object, use it for zone.
140.2771 + TimeZone zi = null;
140.2772 +// try {
140.2773 +// zi = AccessController.doPrivileged(
140.2774 +// new PrivilegedExceptionAction<ZoneInfo>() {
140.2775 +// public ZoneInfo run() throws Exception {
140.2776 +// return (ZoneInfo) input.readObject();
140.2777 +// }
140.2778 +// },
140.2779 +// CalendarAccessControlContext.INSTANCE);
140.2780 +// } catch (PrivilegedActionException pae) {
140.2781 +// Exception e = pae.getException();
140.2782 +// if (!(e instanceof OptionalDataException)) {
140.2783 +// if (e instanceof RuntimeException) {
140.2784 +// throw (RuntimeException) e;
140.2785 +// } else if (e instanceof IOException) {
140.2786 +// throw (IOException) e;
140.2787 +// } else if (e instanceof ClassNotFoundException) {
140.2788 +// throw (ClassNotFoundException) e;
140.2789 +// }
140.2790 +// throw new RuntimeException(e);
140.2791 +// }
140.2792 +// }
140.2793 + if (zi != null) {
140.2794 + zone = zi;
140.2795 + }
140.2796 +
140.2797 + // If the deserialized object has a SimpleTimeZone, try to
140.2798 + // replace it with a ZoneInfo equivalent (as of 1.4) in order
140.2799 + // to be compatible with the SimpleTimeZone-based
140.2800 + // implementation as much as possible.
140.2801 + if (zone instanceof SimpleTimeZone) {
140.2802 + String id = zone.getID();
140.2803 + TimeZone tz = TimeZone.getTimeZone(id);
140.2804 + if (tz != null && tz.hasSameRules(zone) && tz.getID().equals(id)) {
140.2805 + zone = tz;
140.2806 + }
140.2807 + }
140.2808 + }
140.2809 +}
141.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
141.2 +++ b/rt/emul/compact/src/main/java/java/util/Currency.java Tue Feb 11 13:31:42 2014 +0100
141.3 @@ -0,0 +1,735 @@
141.4 +/*
141.5 + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
141.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
141.7 + *
141.8 + * This code is free software; you can redistribute it and/or modify it
141.9 + * under the terms of the GNU General Public License version 2 only, as
141.10 + * published by the Free Software Foundation. Oracle designates this
141.11 + * particular file as subject to the "Classpath" exception as provided
141.12 + * by Oracle in the LICENSE file that accompanied this code.
141.13 + *
141.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
141.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
141.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
141.17 + * version 2 for more details (a copy is included in the LICENSE file that
141.18 + * accompanied this code).
141.19 + *
141.20 + * You should have received a copy of the GNU General Public License version
141.21 + * 2 along with this work; if not, write to the Free Software Foundation,
141.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
141.23 + *
141.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
141.25 + * or visit www.oracle.com if you need additional information or have any
141.26 + * questions.
141.27 + */
141.28 +
141.29 +package java.util;
141.30 +
141.31 +import java.io.BufferedInputStream;
141.32 +import java.io.DataInputStream;
141.33 +import java.io.File;
141.34 +import java.io.FileInputStream;
141.35 +import java.io.FileReader;
141.36 +import java.io.IOException;
141.37 +import java.io.Serializable;
141.38 +import java.security.AccessController;
141.39 +import java.security.PrivilegedAction;
141.40 +import java.util.logging.Level;
141.41 +import java.util.logging.Logger;
141.42 +
141.43 +
141.44 +/**
141.45 + * Represents a currency. Currencies are identified by their ISO 4217 currency
141.46 + * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
141.47 + * ISO web site</a> for more information, including a table of
141.48 + * currency codes.
141.49 + * <p>
141.50 + * The class is designed so that there's never more than one
141.51 + * <code>Currency</code> instance for any given currency. Therefore, there's
141.52 + * no public constructor. You obtain a <code>Currency</code> instance using
141.53 + * the <code>getInstance</code> methods.
141.54 + * <p>
141.55 + * Users can supersede the Java runtime currency data by creating a properties
141.56 + * file named <code><JAVA_HOME>/lib/currency.properties</code>. The contents
141.57 + * of the properties file are key/value pairs of the ISO 3166 country codes
141.58 + * and the ISO 4217 currency data respectively. The value part consists of
141.59 + * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
141.60 + * code, and a minor unit. Those three ISO 4217 values are separated by commas.
141.61 + * The lines which start with '#'s are considered comment lines. For example,
141.62 + * <p>
141.63 + * <code>
141.64 + * #Sample currency properties<br>
141.65 + * JP=JPZ,999,0
141.66 + * </code>
141.67 + * <p>
141.68 + * will supersede the currency data for Japan.
141.69 + *
141.70 + * @since 1.4
141.71 + */
141.72 +public final class Currency implements Serializable {
141.73 +
141.74 + private static final long serialVersionUID = -158308464356906721L;
141.75 +
141.76 + /**
141.77 + * ISO 4217 currency code for this currency.
141.78 + *
141.79 + * @serial
141.80 + */
141.81 + private final String currencyCode;
141.82 +
141.83 + /**
141.84 + * Default fraction digits for this currency.
141.85 + * Set from currency data tables.
141.86 + */
141.87 + transient private final int defaultFractionDigits;
141.88 +
141.89 + /**
141.90 + * ISO 4217 numeric code for this currency.
141.91 + * Set from currency data tables.
141.92 + */
141.93 + transient private final int numericCode;
141.94 +
141.95 +
141.96 + // class data: instance map
141.97 +
141.98 + private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
141.99 + private static HashSet<Currency> available;
141.100 +
141.101 +
141.102 + // Class data: currency data obtained from currency.data file.
141.103 + // Purpose:
141.104 + // - determine valid country codes
141.105 + // - determine valid currency codes
141.106 + // - map country codes to currency codes
141.107 + // - obtain default fraction digits for currency codes
141.108 + //
141.109 + // sc = special case; dfd = default fraction digits
141.110 + // Simple countries are those where the country code is a prefix of the
141.111 + // currency code, and there are no known plans to change the currency.
141.112 + //
141.113 + // table formats:
141.114 + // - mainTable:
141.115 + // - maps country code to 32-bit int
141.116 + // - 26*26 entries, corresponding to [A-Z]*[A-Z]
141.117 + // - \u007F -> not valid country
141.118 + // - bits 18-31: unused
141.119 + // - bits 8-17: numeric code (0 to 1023)
141.120 + // - bit 7: 1 - special case, bits 0-4 indicate which one
141.121 + // 0 - simple country, bits 0-4 indicate final char of currency code
141.122 + // - bits 5-6: fraction digits for simple countries, 0 for special cases
141.123 + // - bits 0-4: final char for currency code for simple country, or ID of special case
141.124 + // - special case IDs:
141.125 + // - 0: country has no currency
141.126 + // - other: index into sc* arrays + 1
141.127 + // - scCutOverTimes: cut-over time in millis as returned by
141.128 + // System.currentTimeMillis for special case countries that are changing
141.129 + // currencies; Long.MAX_VALUE for countries that are not changing currencies
141.130 + // - scOldCurrencies: old currencies for special case countries
141.131 + // - scNewCurrencies: new currencies for special case countries that are
141.132 + // changing currencies; null for others
141.133 + // - scOldCurrenciesDFD: default fraction digits for old currencies
141.134 + // - scNewCurrenciesDFD: default fraction digits for new currencies, 0 for
141.135 + // countries that are not changing currencies
141.136 + // - otherCurrencies: concatenation of all currency codes that are not the
141.137 + // main currency of a simple country, separated by "-"
141.138 + // - otherCurrenciesDFD: decimal format digits for currencies in otherCurrencies, same order
141.139 +
141.140 + static int formatVersion;
141.141 + static int dataVersion;
141.142 + static int[] mainTable;
141.143 + static long[] scCutOverTimes;
141.144 + static String[] scOldCurrencies;
141.145 + static String[] scNewCurrencies;
141.146 + static int[] scOldCurrenciesDFD;
141.147 + static int[] scNewCurrenciesDFD;
141.148 + static int[] scOldCurrenciesNumericCode;
141.149 + static int[] scNewCurrenciesNumericCode;
141.150 + static String otherCurrencies;
141.151 + static int[] otherCurrenciesDFD;
141.152 + static int[] otherCurrenciesNumericCode;
141.153 +
141.154 + // handy constants - must match definitions in GenerateCurrencyData
141.155 + // magic number
141.156 + private static final int MAGIC_NUMBER = 0x43757244;
141.157 + // number of characters from A to Z
141.158 + private static final int A_TO_Z = ('Z' - 'A') + 1;
141.159 + // entry for invalid country codes
141.160 + private static final int INVALID_COUNTRY_ENTRY = 0x007F;
141.161 + // entry for countries without currency
141.162 + private static final int COUNTRY_WITHOUT_CURRENCY_ENTRY = 0x0080;
141.163 + // mask for simple case country entries
141.164 + private static final int SIMPLE_CASE_COUNTRY_MASK = 0x0000;
141.165 + // mask for simple case country entry final character
141.166 + private static final int SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK = 0x001F;
141.167 + // mask for simple case country entry default currency digits
141.168 + private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
141.169 + // shift count for simple case country entry default currency digits
141.170 + private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
141.171 + // mask for special case country entries
141.172 + private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
141.173 + // mask for special case country index
141.174 + private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
141.175 + // delta from entry index component in main table to index into special case tables
141.176 + private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
141.177 + // mask for distinguishing simple and special case countries
141.178 + private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
141.179 + // mask for the numeric code of the currency
141.180 + private static final int NUMERIC_CODE_MASK = 0x0003FF00;
141.181 + // shift count for the numeric code of the currency
141.182 + private static final int NUMERIC_CODE_SHIFT = 8;
141.183 +
141.184 + // Currency data format version
141.185 + private static final int VALID_FORMAT_VERSION = 1;
141.186 +
141.187 + static {
141.188 + AccessController.doPrivileged(new PrivilegedAction() {
141.189 + public Object run() {
141.190 + String homeDir = System.getProperty("java.home");
141.191 + try {
141.192 + String dataFile = homeDir + File.separator +
141.193 + "lib" + File.separator + "currency.data";
141.194 + DataInputStream dis = new DataInputStream(
141.195 + new BufferedInputStream(
141.196 + new FileInputStream(dataFile)));
141.197 + if (dis.readInt() != MAGIC_NUMBER) {
141.198 + throw new InternalError("Currency data is possibly corrupted");
141.199 + }
141.200 + formatVersion = dis.readInt();
141.201 + if (formatVersion != VALID_FORMAT_VERSION) {
141.202 + throw new InternalError("Currency data format is incorrect");
141.203 + }
141.204 + dataVersion = dis.readInt();
141.205 + mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
141.206 + int scCount = dis.readInt();
141.207 + scCutOverTimes = readLongArray(dis, scCount);
141.208 + scOldCurrencies = readStringArray(dis, scCount);
141.209 + scNewCurrencies = readStringArray(dis, scCount);
141.210 + scOldCurrenciesDFD = readIntArray(dis, scCount);
141.211 + scNewCurrenciesDFD = readIntArray(dis, scCount);
141.212 + scOldCurrenciesNumericCode = readIntArray(dis, scCount);
141.213 + scNewCurrenciesNumericCode = readIntArray(dis, scCount);
141.214 + int ocCount = dis.readInt();
141.215 + otherCurrencies = dis.readUTF();
141.216 + otherCurrenciesDFD = readIntArray(dis, ocCount);
141.217 + otherCurrenciesNumericCode = readIntArray(dis, ocCount);
141.218 + dis.close();
141.219 + } catch (IOException e) {
141.220 + InternalError ie = new InternalError();
141.221 + ie.initCause(e);
141.222 + throw ie;
141.223 + }
141.224 +
141.225 + // look for the properties file for overrides
141.226 +// try {
141.227 + File propFile = new File(homeDir + File.separator +
141.228 + "lib" + File.separator +
141.229 + "currency.properties");
141.230 +// if (propFile.exists()) {
141.231 +// Properties props = new Properties();
141.232 +// try (FileReader fr = new FileReader(propFile)) {
141.233 +// props.load(fr);
141.234 +// }
141.235 +// Set<String> keys = props.stringPropertyNames();
141.236 +// Pattern propertiesPattern =
141.237 +// Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
141.238 +// for (String key : keys) {
141.239 +// replaceCurrencyData(propertiesPattern,
141.240 +// key.toUpperCase(Locale.ROOT),
141.241 +// props.getProperty(key).toUpperCase(Locale.ROOT));
141.242 +// }
141.243 +// }
141.244 +// } catch (IOException e) {
141.245 +// info("currency.properties is ignored because of an IOException", e);
141.246 +// }
141.247 + return null;
141.248 + }
141.249 + });
141.250 + }
141.251 +
141.252 + /**
141.253 + * Constants for retrieving localized names from the name providers.
141.254 + */
141.255 + private static final int SYMBOL = 0;
141.256 + private static final int DISPLAYNAME = 1;
141.257 +
141.258 +
141.259 + /**
141.260 + * Constructs a <code>Currency</code> instance. The constructor is private
141.261 + * so that we can insure that there's never more than one instance for a
141.262 + * given currency.
141.263 + */
141.264 + private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
141.265 + this.currencyCode = currencyCode;
141.266 + this.defaultFractionDigits = defaultFractionDigits;
141.267 + this.numericCode = numericCode;
141.268 + }
141.269 +
141.270 + /**
141.271 + * Returns the <code>Currency</code> instance for the given currency code.
141.272 + *
141.273 + * @param currencyCode the ISO 4217 code of the currency
141.274 + * @return the <code>Currency</code> instance for the given currency code
141.275 + * @exception NullPointerException if <code>currencyCode</code> is null
141.276 + * @exception IllegalArgumentException if <code>currencyCode</code> is not
141.277 + * a supported ISO 4217 code.
141.278 + */
141.279 + public static Currency getInstance(String currencyCode) {
141.280 + return getInstance(currencyCode, Integer.MIN_VALUE, 0);
141.281 + }
141.282 +
141.283 + private static Currency getInstance(String currencyCode, int defaultFractionDigits,
141.284 + int numericCode) {
141.285 + synchronized (instances) {
141.286 + // Try to look up the currency code in the instances table.
141.287 + // This does the null pointer check as a side effect.
141.288 + // Also, if there already is an entry, the currencyCode must be valid.
141.289 + Currency instance = instances.get(currencyCode);
141.290 + if (instance != null) {
141.291 + return instance;
141.292 + }
141.293 +
141.294 + if (defaultFractionDigits == Integer.MIN_VALUE) {
141.295 + // Currency code not internally generated, need to verify first
141.296 + // A currency code must have 3 characters and exist in the main table
141.297 + // or in the list of other currencies.
141.298 + if (currencyCode.length() != 3) {
141.299 + throw new IllegalArgumentException();
141.300 + }
141.301 + char char1 = currencyCode.charAt(0);
141.302 + char char2 = currencyCode.charAt(1);
141.303 + int tableEntry = getMainTableEntry(char1, char2);
141.304 + if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
141.305 + && tableEntry != INVALID_COUNTRY_ENTRY
141.306 + && currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
141.307 + defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
141.308 + numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
141.309 + } else {
141.310 + // Check for '-' separately so we don't get false hits in the table.
141.311 + if (currencyCode.charAt(2) == '-') {
141.312 + throw new IllegalArgumentException();
141.313 + }
141.314 + int index = otherCurrencies.indexOf(currencyCode);
141.315 + if (index == -1) {
141.316 + throw new IllegalArgumentException();
141.317 + }
141.318 + defaultFractionDigits = otherCurrenciesDFD[index / 4];
141.319 + numericCode = otherCurrenciesNumericCode[index / 4];
141.320 + }
141.321 + }
141.322 +
141.323 + instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
141.324 + instances.put(currencyCode, instance);
141.325 + return instance;
141.326 + }
141.327 + }
141.328 +
141.329 + /**
141.330 + * Returns the <code>Currency</code> instance for the country of the
141.331 + * given locale. The language and variant components of the locale
141.332 + * are ignored. The result may vary over time, as countries change their
141.333 + * currencies. For example, for the original member countries of the
141.334 + * European Monetary Union, the method returns the old national currencies
141.335 + * until December 31, 2001, and the Euro from January 1, 2002, local time
141.336 + * of the respective countries.
141.337 + * <p>
141.338 + * The method returns <code>null</code> for territories that don't
141.339 + * have a currency, such as Antarctica.
141.340 + *
141.341 + * @param locale the locale for whose country a <code>Currency</code>
141.342 + * instance is needed
141.343 + * @return the <code>Currency</code> instance for the country of the given
141.344 + * locale, or null
141.345 + * @exception NullPointerException if <code>locale</code> or its country
141.346 + * code is null
141.347 + * @exception IllegalArgumentException if the country of the given locale
141.348 + * is not a supported ISO 3166 country code.
141.349 + */
141.350 + public static Currency getInstance(Locale locale) {
141.351 + String country = locale.getCountry();
141.352 + if (country == null) {
141.353 + throw new NullPointerException();
141.354 + }
141.355 +
141.356 + if (country.length() != 2) {
141.357 + throw new IllegalArgumentException();
141.358 + }
141.359 +
141.360 + char char1 = country.charAt(0);
141.361 + char char2 = country.charAt(1);
141.362 + int tableEntry = getMainTableEntry(char1, char2);
141.363 + if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
141.364 + && tableEntry != INVALID_COUNTRY_ENTRY) {
141.365 + char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
141.366 + int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
141.367 + int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
141.368 + StringBuffer sb = new StringBuffer(country);
141.369 + sb.append(finalChar);
141.370 + return getInstance(sb.toString(), defaultFractionDigits, numericCode);
141.371 + } else {
141.372 + // special cases
141.373 + if (tableEntry == INVALID_COUNTRY_ENTRY) {
141.374 + throw new IllegalArgumentException();
141.375 + }
141.376 + if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
141.377 + return null;
141.378 + } else {
141.379 + int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
141.380 + if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
141.381 + return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
141.382 + scOldCurrenciesNumericCode[index]);
141.383 + } else {
141.384 + return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
141.385 + scNewCurrenciesNumericCode[index]);
141.386 + }
141.387 + }
141.388 + }
141.389 + }
141.390 +
141.391 + /**
141.392 + * Gets the set of available currencies. The returned set of currencies
141.393 + * contains all of the available currencies, which may include currencies
141.394 + * that represent obsolete ISO 4217 codes. The set can be modified
141.395 + * without affecting the available currencies in the runtime.
141.396 + *
141.397 + * @return the set of available currencies. If there is no currency
141.398 + * available in the runtime, the returned set is empty.
141.399 + * @since 1.7
141.400 + */
141.401 + public static Set<Currency> getAvailableCurrencies() {
141.402 + synchronized(Currency.class) {
141.403 + if (available == null) {
141.404 + available = new HashSet<Currency>(256);
141.405 +
141.406 + // Add simple currencies first
141.407 + for (char c1 = 'A'; c1 <= 'Z'; c1 ++) {
141.408 + for (char c2 = 'A'; c2 <= 'Z'; c2 ++) {
141.409 + int tableEntry = getMainTableEntry(c1, c2);
141.410 + if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
141.411 + && tableEntry != INVALID_COUNTRY_ENTRY) {
141.412 + char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
141.413 + int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
141.414 + int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
141.415 + StringBuilder sb = new StringBuilder();
141.416 + sb.append(c1);
141.417 + sb.append(c2);
141.418 + sb.append(finalChar);
141.419 + available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
141.420 + }
141.421 + }
141.422 + }
141.423 +
141.424 + // Now add other currencies
141.425 + StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
141.426 + while (st.hasMoreElements()) {
141.427 + available.add(getInstance((String)st.nextElement()));
141.428 + }
141.429 + }
141.430 + }
141.431 +
141.432 + return (Set<Currency>) available.clone();
141.433 + }
141.434 +
141.435 + /**
141.436 + * Gets the ISO 4217 currency code of this currency.
141.437 + *
141.438 + * @return the ISO 4217 currency code of this currency.
141.439 + */
141.440 + public String getCurrencyCode() {
141.441 + return currencyCode;
141.442 + }
141.443 +
141.444 + /**
141.445 + * Gets the symbol of this currency for the default locale.
141.446 + * For example, for the US Dollar, the symbol is "$" if the default
141.447 + * locale is the US, while for other locales it may be "US$". If no
141.448 + * symbol can be determined, the ISO 4217 currency code is returned.
141.449 + *
141.450 + * @return the symbol of this currency for the default locale
141.451 + */
141.452 + public String getSymbol() {
141.453 + return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
141.454 + }
141.455 +
141.456 + /**
141.457 + * Gets the symbol of this currency for the specified locale.
141.458 + * For example, for the US Dollar, the symbol is "$" if the specified
141.459 + * locale is the US, while for other locales it may be "US$". If no
141.460 + * symbol can be determined, the ISO 4217 currency code is returned.
141.461 + *
141.462 + * @param locale the locale for which a display name for this currency is
141.463 + * needed
141.464 + * @return the symbol of this currency for the specified locale
141.465 + * @exception NullPointerException if <code>locale</code> is null
141.466 + */
141.467 + public String getSymbol(Locale locale) {
141.468 + try {
141.469 + // Check whether a provider can provide an implementation that's closer
141.470 + // to the requested locale than what the Java runtime itself can provide.
141.471 + /*
141.472 + LocaleServiceProviderPool pool =
141.473 + LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
141.474 +
141.475 + if (pool.hasProviders()) {
141.476 + // Assuming that all the country locales include necessary currency
141.477 + // symbols in the Java runtime's resources, so there is no need to
141.478 + // examine whether Java runtime's currency resource bundle is missing
141.479 + // names. Therefore, no resource bundle is provided for calling this
141.480 + // method.
141.481 + String symbol = pool.getLocalizedObject(
141.482 + CurrencyNameGetter.INSTANCE,
141.483 + locale, (OpenListResourceBundle)null,
141.484 + currencyCode, SYMBOL);
141.485 + if (symbol != null) {
141.486 + return symbol;
141.487 + }
141.488 + }
141.489 + */
141.490 + ResourceBundle bundle = null; //LocaleData.getCurrencyNames(locale);
141.491 + return bundle.getString(currencyCode);
141.492 + } catch (MissingResourceException e) {
141.493 + // use currency code as symbol of last resort
141.494 + return currencyCode;
141.495 + }
141.496 + }
141.497 +
141.498 + /**
141.499 + * Gets the default number of fraction digits used with this currency.
141.500 + * For example, the default number of fraction digits for the Euro is 2,
141.501 + * while for the Japanese Yen it's 0.
141.502 + * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
141.503 + * -1 is returned.
141.504 + *
141.505 + * @return the default number of fraction digits used with this currency
141.506 + */
141.507 + public int getDefaultFractionDigits() {
141.508 + return defaultFractionDigits;
141.509 + }
141.510 +
141.511 + /**
141.512 + * Returns the ISO 4217 numeric code of this currency.
141.513 + *
141.514 + * @return the ISO 4217 numeric code of this currency
141.515 + * @since 1.7
141.516 + */
141.517 + public int getNumericCode() {
141.518 + return numericCode;
141.519 + }
141.520 +
141.521 + /**
141.522 + * Gets the name that is suitable for displaying this currency for
141.523 + * the default locale. If there is no suitable display name found
141.524 + * for the default locale, the ISO 4217 currency code is returned.
141.525 + *
141.526 + * @return the display name of this currency for the default locale
141.527 + * @since 1.7
141.528 + */
141.529 + public String getDisplayName() {
141.530 + return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
141.531 + }
141.532 +
141.533 + /**
141.534 + * Gets the name that is suitable for displaying this currency for
141.535 + * the specified locale. If there is no suitable display name found
141.536 + * for the specified locale, the ISO 4217 currency code is returned.
141.537 + *
141.538 + * @param locale the locale for which a display name for this currency is
141.539 + * needed
141.540 + * @return the display name of this currency for the specified locale
141.541 + * @exception NullPointerException if <code>locale</code> is null
141.542 + * @since 1.7
141.543 + */
141.544 + public String getDisplayName(Locale locale) {
141.545 +// try {
141.546 +// OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
141.547 +// String result = null;
141.548 +// String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
141.549 +//
141.550 +// // Check whether a provider can provide an implementation that's closer
141.551 +// // to the requested locale than what the Java runtime itself can provide.
141.552 +// LocaleServiceProviderPool pool =
141.553 +// LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
141.554 +// if (pool.hasProviders()) {
141.555 +// result = pool.getLocalizedObject(
141.556 +// CurrencyNameGetter.INSTANCE,
141.557 +// locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
141.558 +// }
141.559 +//
141.560 +// if (result == null) {
141.561 +// result = bundle.getString(bundleKey);
141.562 +// }
141.563 +//
141.564 +// if (result != null) {
141.565 +// return result;
141.566 +// }
141.567 +// } catch (MissingResourceException e) {
141.568 +// // fall through
141.569 +// }
141.570 +
141.571 + // use currency code as symbol of last resort
141.572 + return currencyCode;
141.573 + }
141.574 +
141.575 + /**
141.576 + * Returns the ISO 4217 currency code of this currency.
141.577 + *
141.578 + * @return the ISO 4217 currency code of this currency
141.579 + */
141.580 + public String toString() {
141.581 + return currencyCode;
141.582 + }
141.583 +
141.584 + /**
141.585 + * Resolves instances being deserialized to a single instance per currency.
141.586 + */
141.587 + private Object readResolve() {
141.588 + return getInstance(currencyCode);
141.589 + }
141.590 +
141.591 + /**
141.592 + * Gets the main table entry for the country whose country code consists
141.593 + * of char1 and char2.
141.594 + */
141.595 + private static int getMainTableEntry(char char1, char char2) {
141.596 + if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
141.597 + throw new IllegalArgumentException();
141.598 + }
141.599 + return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
141.600 + }
141.601 +
141.602 + /**
141.603 + * Sets the main table entry for the country whose country code consists
141.604 + * of char1 and char2.
141.605 + */
141.606 + private static void setMainTableEntry(char char1, char char2, int entry) {
141.607 + if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
141.608 + throw new IllegalArgumentException();
141.609 + }
141.610 + mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
141.611 + }
141.612 +
141.613 + /**
141.614 + * Obtains a localized currency names from a CurrencyNameProvider
141.615 + * implementation.
141.616 + private static class CurrencyNameGetter
141.617 + implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
141.618 + String> {
141.619 + private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
141.620 +
141.621 + public String getObject(CurrencyNameProvider currencyNameProvider,
141.622 + Locale locale,
141.623 + String key,
141.624 + Object... params) {
141.625 + assert params.length == 1;
141.626 + int type = (Integer)params[0];
141.627 +
141.628 + switch(type) {
141.629 + case SYMBOL:
141.630 + return currencyNameProvider.getSymbol(key, locale);
141.631 + case DISPLAYNAME:
141.632 + return currencyNameProvider.getDisplayName(key, locale);
141.633 + default:
141.634 + assert false; // shouldn't happen
141.635 + }
141.636 +
141.637 + return null;
141.638 + }
141.639 + }
141.640 + */
141.641 +
141.642 + private static int[] readIntArray(DataInputStream dis, int count) throws IOException {
141.643 + int[] ret = new int[count];
141.644 + for (int i = 0; i < count; i++) {
141.645 + ret[i] = dis.readInt();
141.646 + }
141.647 +
141.648 + return ret;
141.649 + }
141.650 +
141.651 + private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
141.652 + long[] ret = new long[count];
141.653 + for (int i = 0; i < count; i++) {
141.654 + ret[i] = dis.readLong();
141.655 + }
141.656 +
141.657 + return ret;
141.658 + }
141.659 +
141.660 + private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
141.661 + String[] ret = new String[count];
141.662 + for (int i = 0; i < count; i++) {
141.663 + ret[i] = dis.readUTF();
141.664 + }
141.665 +
141.666 + return ret;
141.667 + }
141.668 +
141.669 + /**
141.670 + * Replaces currency data found in the currencydata.properties file
141.671 + *
141.672 + * @param pattern regex pattern for the properties
141.673 + * @param ctry country code
141.674 + * @param data currency data. This is a comma separated string that
141.675 + * consists of "three-letter alphabet code", "three-digit numeric code",
141.676 + * and "one-digit (0,1,2, or 3) default fraction digit".
141.677 + * For example, "JPZ,392,0".
141.678 + * @throws
141.679 + private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
141.680 +
141.681 + if (ctry.length() != 2) {
141.682 + // ignore invalid country code
141.683 + String message = new StringBuilder()
141.684 + .append("The entry in currency.properties for ")
141.685 + .append(ctry).append(" is ignored because of the invalid country code.")
141.686 + .toString();
141.687 + info(message, null);
141.688 + return;
141.689 + }
141.690 +
141.691 + Matcher m = pattern.matcher(curdata);
141.692 + if (!m.find()) {
141.693 + // format is not recognized. ignore the data
141.694 + String message = new StringBuilder()
141.695 + .append("The entry in currency.properties for ")
141.696 + .append(ctry)
141.697 + .append(" is ignored because the value format is not recognized.")
141.698 + .toString();
141.699 + info(message, null);
141.700 + return;
141.701 + }
141.702 +
141.703 + String code = m.group(1);
141.704 + int numeric = Integer.parseInt(m.group(2));
141.705 + int fraction = Integer.parseInt(m.group(3));
141.706 + int entry = numeric << NUMERIC_CODE_SHIFT;
141.707 +
141.708 + int index;
141.709 + for (index = 0; index < scOldCurrencies.length; index++) {
141.710 + if (scOldCurrencies[index].equals(code)) {
141.711 + break;
141.712 + }
141.713 + }
141.714 +
141.715 + if (index == scOldCurrencies.length) {
141.716 + // simple case
141.717 + entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
141.718 + (code.charAt(2) - 'A');
141.719 + } else {
141.720 + // special case
141.721 + entry |= SPECIAL_CASE_COUNTRY_MASK |
141.722 + (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
141.723 + }
141.724 + setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
141.725 + }
141.726 + */
141.727 +
141.728 + private static void info(String message, Throwable t) {
141.729 + Logger logger = Logger.getLogger("java.util.Currency");
141.730 + if (logger.isLoggable(Level.INFO)) {
141.731 + if (t != null) {
141.732 + logger.log(Level.INFO, message, t);
141.733 + } else {
141.734 + logger.info(message);
141.735 + }
141.736 + }
141.737 + }
141.738 +}
142.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
142.2 +++ b/rt/emul/compact/src/main/java/java/util/CurrencyData.properties Tue Feb 11 13:31:42 2014 +0100
142.3 @@ -0,0 +1,586 @@
142.4 +#
142.5 +# Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
142.6 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
142.7 +#
142.8 +# This code is free software; you can redistribute it and/or modify it
142.9 +# under the terms of the GNU General Public License version 2 only, as
142.10 +# published by the Free Software Foundation. Oracle designates this
142.11 +# particular file as subject to the "Classpath" exception as provided
142.12 +# by Oracle in the LICENSE file that accompanied this code.
142.13 +#
142.14 +# This code is distributed in the hope that it will be useful, but WITHOUT
142.15 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
142.16 +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
142.17 +# version 2 for more details (a copy is included in the LICENSE file that
142.18 +# accompanied this code).
142.19 +#
142.20 +# You should have received a copy of the GNU General Public License version
142.21 +# 2 along with this work; if not, write to the Free Software Foundation,
142.22 +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
142.23 +#
142.24 +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
142.25 +# or visit www.oracle.com if you need additional information or have any
142.26 +# questions.
142.27 +#
142.28 +
142.29 +formatVersion=1
142.30 +
142.31 +# Version of the currency code information in this class.
142.32 +# It is a serial number that accompanies with each amendment, such as
142.33 +# 'MAxxx.doc'
142.34 +
142.35 +dataVersion=140
142.36 +
142.37 +# List of all valid ISO 4217 currency codes.
142.38 +# To ensure compatibility, do not remove codes.
142.39 +
142.40 +all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036-\
142.41 + AWG533-AYM945-AZM031-AZN944-BAM977-BBD052-BDT050-BEF056-BGL100-BGN975-BHD048-BIF108-\
142.42 + BMD060-BND096-BOB068-BOV984-BRL986-BSD044-BTN064-BWP072-BYB112-BYR974-\
142.43 + BZD084-CAD124-CDF976-CHF756-CLF990-CLP152-CNY156-COP170-CRC188-CSD891-CUP192-\
142.44 + CVE132-CYP196-CZK203-DEM276-DJF262-DKK208-DOP214-DZD012-EEK233-EGP818-\
142.45 + ERN232-ESP724-ETB230-EUR978-FIM246-FJD242-FKP238-FRF250-GBP826-GEL981-\
142.46 + GHC288-GHS936-GIP292-GMD270-GNF324-GRD300-GTQ320-GWP624-GYD328-HKD344-HNL340-\
142.47 + HRK191-HTG332-HUF348-IDR360-IEP372-ILS376-INR356-IQD368-IRR364-ISK352-\
142.48 + ITL380-JMD388-JOD400-JPY392-KES404-KGS417-KHR116-KMF174-KPW408-KRW410-\
142.49 + KWD414-KYD136-KZT398-LAK418-LBP422-LKR144-LRD430-LSL426-LTL440-LUF442-\
142.50 + LVL428-LYD434-MAD504-MDL498-MGA969-MGF450-MKD807-MMK104-MNT496-MOP446-MRO478-\
142.51 + MTL470-MUR480-MVR462-MWK454-MXN484-MXV979-MYR458-MZM508-MZN943-NAD516-NGN566-\
142.52 + NIO558-NLG528-NOK578-NPR524-NZD554-OMR512-PAB590-PEN604-PGK598-PHP608-\
142.53 + PKR586-PLN985-PTE620-PYG600-QAR634-ROL946-RON946-RSD941-RUB643-RUR810-RWF646-SAR682-\
142.54 + SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLL694-SOS706-\
142.55 + SRD968-SRG740-STD678-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TND788-TOP776-\
142.56 + TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-\
142.57 + UYU858-UZS860-VEB862-VEF937-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\
142.58 + XBB956-XBC957-XBD958-XCD951-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\
142.59 + XPT962-XTS963-XXX999-YER886-YUM891-ZAR710-ZMK894-ZWD716-ZWN942
142.60 +
142.61 +
142.62 +# Mappings from ISO 3166 country codes to ISO 4217 currency codes.
142.63 +#
142.64 +# Three forms are used:
142.65 +# Form 1: <country code>=<currency code>
142.66 +# Form 2: <country code>=<currency code 1>;<time stamp>;<currency code 2>
142.67 +# Form 3: <country code>=
142.68 +# Form 1 is used if no future change in currency is known.
142.69 +# Form 2 indicates that before the specified time currency 1 is used, from
142.70 +# the specified time currency 2. The time is given in SimpleDateFormat's
142.71 +# yyyy-MM-dd-HH-mm-ss format in the GMT time zone.
142.72 +# Form 3 indicates the country doesn't have a currency (the entry is still
142.73 +# needed to verify that the country code is valid).
142.74 +#
142.75 +# The table is based on the following web sites:
142.76 +# http://www.din.de/gremien/nas/nabd/iso3166ma/codlstp1/db_en.html
142.77 +# http://www.bsi-global.com/iso4217currency
142.78 +# http://www.cia.gov/cia/publications/factbook/indexgeo.html
142.79 +
142.80 +# AFGHANISTAN
142.81 +AF=AFN
142.82 +# \u00c5LAND ISLANDS
142.83 +AX=EUR
142.84 +# ALBANIA
142.85 +AL=ALL
142.86 +# ALGERIA
142.87 +DZ=DZD
142.88 +# AMERICAN SAMOA
142.89 +AS=USD
142.90 +# ANDORRA
142.91 +AD=EUR
142.92 +# ANGOLA
142.93 +AO=AOA
142.94 +# ANGUILLA
142.95 +AI=XCD
142.96 +# ANTARCTICA
142.97 +AQ=
142.98 +# ANTIGUA AND BARBUDA
142.99 +AG=XCD
142.100 +# ARGENTINA
142.101 +AR=ARS
142.102 +# ARMENIA
142.103 +AM=AMD
142.104 +# ARUBA
142.105 +AW=AWG
142.106 +# AUSTRALIA
142.107 +AU=AUD
142.108 +# AUSTRIA
142.109 +AT=EUR
142.110 +# AZERBAIJAN
142.111 +AZ=AZM;2005-12-31-20-00-00;AZN
142.112 +# BAHAMAS
142.113 +BS=BSD
142.114 +# BAHRAIN
142.115 +BH=BHD
142.116 +# BANGLADESH
142.117 +BD=BDT
142.118 +# BARBADOS
142.119 +BB=BBD
142.120 +# BELARUS
142.121 +BY=BYR
142.122 +# BELGIUM
142.123 +BE=EUR
142.124 +# BELIZE
142.125 +BZ=BZD
142.126 +# BENIN
142.127 +BJ=XOF
142.128 +# BERMUDA
142.129 +BM=BMD
142.130 +# BHUTAN
142.131 +BT=BTN
142.132 +# BOLIVIA
142.133 +BO=BOB
142.134 +# BOSNIA AND HERZEGOVINA
142.135 +BA=BAM
142.136 +# BOTSWANA
142.137 +BW=BWP
142.138 +# BOUVET ISLAND
142.139 +BV=NOK
142.140 +# BRAZIL
142.141 +BR=BRL
142.142 +# BRITISH INDIAN OCEAN TERRITORY
142.143 +IO=USD
142.144 +# BRUNEI DARUSSALAM
142.145 +BN=BND
142.146 +# BULGARIA
142.147 +BG=BGN
142.148 +# BURKINA FASO
142.149 +BF=XOF
142.150 +# BURUNDI
142.151 +BI=BIF
142.152 +# CAMBODIA
142.153 +KH=KHR
142.154 +# CAMEROON
142.155 +CM=XAF
142.156 +# CANADA
142.157 +CA=CAD
142.158 +# CAPE VERDE
142.159 +CV=CVE
142.160 +# CAYMAN ISLANDS
142.161 +KY=KYD
142.162 +# CENTRAL AFRICAN REPUBLIC
142.163 +CF=XAF
142.164 +# CHAD
142.165 +TD=XAF
142.166 +# CHILE
142.167 +CL=CLP
142.168 +# CHINA
142.169 +CN=CNY
142.170 +# CHRISTMAS ISLAND
142.171 +CX=AUD
142.172 +# COCOS (KEELING) ISLANDS
142.173 +CC=AUD
142.174 +# COLOMBIA
142.175 +CO=COP
142.176 +# COMOROS
142.177 +KM=KMF
142.178 +# CONGO
142.179 +CG=XAF
142.180 +# CONGO, THE DEMOCRATIC REPUBLIC OF THE
142.181 +CD=CDF
142.182 +# COOK ISLANDS
142.183 +CK=NZD
142.184 +# COSTA RICA
142.185 +CR=CRC
142.186 +# COTE D'IVOIRE
142.187 +CI=XOF
142.188 +# CROATIA
142.189 +HR=HRK
142.190 +# CUBA
142.191 +CU=CUP
142.192 +# CYPRUS
142.193 +CY=EUR
142.194 +# CZECH REPUBLIC
142.195 +CZ=CZK
142.196 +# DENMARK
142.197 +DK=DKK
142.198 +# DJIBOUTI
142.199 +DJ=DJF
142.200 +# DOMINICA
142.201 +DM=XCD
142.202 +# DOMINICAN REPUBLIC
142.203 +DO=DOP
142.204 +# ECUADOR
142.205 +EC=USD
142.206 +# EGYPT
142.207 +EG=EGP
142.208 +# EL SALVADOR
142.209 +# USD is also legal currency as of 2001/01/01
142.210 +SV=SVC
142.211 +# EQUATORIAL GUINEA
142.212 +GQ=XAF
142.213 +# ERITREA
142.214 +ER=ERN
142.215 +# ESTONIA
142.216 +EE=EEK
142.217 +# ETHIOPIA
142.218 +ET=ETB
142.219 +# FALKLAND ISLANDS (MALVINAS)
142.220 +FK=FKP
142.221 +# FAROE ISLANDS
142.222 +FO=DKK
142.223 +# FIJI
142.224 +FJ=FJD
142.225 +# FINLAND
142.226 +FI=EUR
142.227 +# FRANCE
142.228 +FR=EUR
142.229 +# FRENCH GUIANA
142.230 +GF=EUR
142.231 +# FRENCH POLYNESIA
142.232 +PF=XPF
142.233 +# FRENCH SOUTHERN TERRITORIES
142.234 +TF=EUR
142.235 +# GABON
142.236 +GA=XAF
142.237 +# GAMBIA
142.238 +GM=GMD
142.239 +# GEORGIA
142.240 +GE=GEL
142.241 +# GERMANY
142.242 +DE=EUR
142.243 +# GHANA
142.244 +GH=GHS
142.245 +# GIBRALTAR
142.246 +GI=GIP
142.247 +# GREECE
142.248 +GR=EUR
142.249 +# GREENLAND
142.250 +GL=DKK
142.251 +# GRENADA
142.252 +GD=XCD
142.253 +# GUADELOUPE
142.254 +GP=EUR
142.255 +# GUAM
142.256 +GU=USD
142.257 +# GUATEMALA
142.258 +GT=GTQ
142.259 +# GUERNSEY
142.260 +GG=GBP
142.261 +# GUINEA
142.262 +GN=GNF
142.263 +# GUINEA-BISSAU
142.264 +GW=XOF
142.265 +# GUYANA
142.266 +GY=GYD
142.267 +# HAITI
142.268 +HT=HTG
142.269 +# HEARD ISLAND AND MCDONALD ISLANDS
142.270 +HM=AUD
142.271 +# HOLY SEE (VATICAN CITY STATE)
142.272 +VA=EUR
142.273 +# HONDURAS
142.274 +HN=HNL
142.275 +# HONG KONG
142.276 +HK=HKD
142.277 +# HUNGARY
142.278 +HU=HUF
142.279 +# ICELAND
142.280 +IS=ISK
142.281 +# INDIA
142.282 +IN=INR
142.283 +# INDONESIA
142.284 +ID=IDR
142.285 +# IRAN, ISLAMIC REPUBLIC OF
142.286 +IR=IRR
142.287 +# IRAQ
142.288 +IQ=IQD
142.289 +# IRELAND
142.290 +IE=EUR
142.291 +# ISLE OF MAN
142.292 +IM=GBP
142.293 +# ISRAEL
142.294 +IL=ILS
142.295 +# ITALY
142.296 +IT=EUR
142.297 +# JAMAICA
142.298 +JM=JMD
142.299 +# JAPAN
142.300 +JP=JPY
142.301 +# JERSEY
142.302 +JE=GBP
142.303 +# JORDAN
142.304 +JO=JOD
142.305 +# KAZAKSTAN
142.306 +KZ=KZT
142.307 +# KENYA
142.308 +KE=KES
142.309 +# KIRIBATI
142.310 +KI=AUD
142.311 +# KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF
142.312 +KP=KPW
142.313 +# KOREA, REPUBLIC OF
142.314 +KR=KRW
142.315 +# KUWAIT
142.316 +KW=KWD
142.317 +# KYRGYZSTAN
142.318 +KG=KGS
142.319 +# LAO PEOPLE'S DEMOCRATIC REPUBLIC
142.320 +LA=LAK
142.321 +# LATVIA
142.322 +LV=LVL
142.323 +# LEBANON
142.324 +LB=LBP
142.325 +# LESOTHO
142.326 +LS=LSL
142.327 +# LIBERIA
142.328 +LR=LRD
142.329 +# LIBYAN ARAB JAMAHIRIYA
142.330 +LY=LYD
142.331 +# LIECHTENSTEIN
142.332 +LI=CHF
142.333 +# LITHUANIA
142.334 +LT=LTL
142.335 +# LUXEMBOURG
142.336 +LU=EUR
142.337 +# MACAU
142.338 +MO=MOP
142.339 +# MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF
142.340 +MK=MKD
142.341 +# MADAGASCAR
142.342 +MG=MGA
142.343 +# MALAWI
142.344 +MW=MWK
142.345 +# MALAYSIA
142.346 +MY=MYR
142.347 +# MALDIVES
142.348 +MV=MVR
142.349 +# MALI
142.350 +ML=XOF
142.351 +# MALTA
142.352 +MT=EUR
142.353 +# MARSHALL ISLANDS
142.354 +MH=USD
142.355 +# MARTINIQUE
142.356 +MQ=EUR
142.357 +# MAURITANIA
142.358 +MR=MRO
142.359 +# MAURITIUS
142.360 +MU=MUR
142.361 +# MAYOTTE
142.362 +YT=EUR
142.363 +# MEXICO
142.364 +MX=MXN
142.365 +# MICRONESIA, FEDERATED STATES OF
142.366 +FM=USD
142.367 +# MOLDOVA, REPUBLIC OF
142.368 +MD=MDL
142.369 +# MONACO
142.370 +MC=EUR
142.371 +# MONGOLIA
142.372 +MN=MNT
142.373 +# MONTENEGRO
142.374 +ME=EUR
142.375 +# MONTSERRAT
142.376 +MS=XCD
142.377 +# MOROCCO
142.378 +MA=MAD
142.379 +# MOZAMBIQUE
142.380 +MZ=MZM;2006-06-30-22-00-00;MZN
142.381 +# MYANMAR
142.382 +MM=MMK
142.383 +# NAMIBIA
142.384 +NA=NAD
142.385 +# NAURU
142.386 +NR=AUD
142.387 +# NEPAL
142.388 +NP=NPR
142.389 +# NETHERLANDS
142.390 +NL=EUR
142.391 +# NETHERLANDS ANTILLES
142.392 +AN=ANG
142.393 +# NEW CALEDONIA
142.394 +NC=XPF
142.395 +# NEW ZEALAND
142.396 +NZ=NZD
142.397 +# NICARAGUA
142.398 +NI=NIO
142.399 +# NIGER
142.400 +NE=XOF
142.401 +# NIGERIA
142.402 +NG=NGN
142.403 +# NIUE
142.404 +NU=NZD
142.405 +# NORFOLK ISLAND
142.406 +NF=AUD
142.407 +# NORTHERN MARIANA ISLANDS
142.408 +MP=USD
142.409 +# NORWAY
142.410 +NO=NOK
142.411 +# OMAN
142.412 +OM=OMR
142.413 +# PAKISTAN
142.414 +PK=PKR
142.415 +# PALAU
142.416 +PW=USD
142.417 +# PALESTINIAN TERRITORY, OCCUPIED
142.418 +PS=ILS
142.419 +# PANAMA
142.420 +PA=PAB
142.421 +# PAPUA NEW GUINEA
142.422 +PG=PGK
142.423 +# PARAGUAY
142.424 +PY=PYG
142.425 +# PERU
142.426 +PE=PEN
142.427 +# PHILIPPINES
142.428 +PH=PHP
142.429 +# PITCAIRN
142.430 +PN=NZD
142.431 +# POLAND
142.432 +PL=PLN
142.433 +# PORTUGAL
142.434 +PT=EUR
142.435 +# PUERTO RICO
142.436 +PR=USD
142.437 +# QATAR
142.438 +QA=QAR
142.439 +# REUNION
142.440 +RE=EUR
142.441 +# ROMANIA
142.442 +RO=ROL;2005-06-30-21-00-00;RON
142.443 +# RUSSIAN FEDERATION
142.444 +RU=RUB
142.445 +# RWANDA
142.446 +RW=RWF
142.447 +# SAINT BARTHELEMY
142.448 +BL=EUR
142.449 +# SAINT HELENA
142.450 +SH=SHP
142.451 +# SAINT KITTS AND NEVIS
142.452 +KN=XCD
142.453 +# SAINT LUCIA
142.454 +LC=XCD
142.455 +# SAINT MARTIN
142.456 +MF=EUR
142.457 +# SAINT PIERRE AND MIQUELON
142.458 +PM=EUR
142.459 +# SAINT VINCENT AND THE GRENADINES
142.460 +VC=XCD
142.461 +# SAMOA
142.462 +WS=WST
142.463 +# SAN MARINO
142.464 +SM=EUR
142.465 +# SAO TOME AND PRINCIPE
142.466 +ST=STD
142.467 +# SAUDI ARABIA
142.468 +SA=SAR
142.469 +# SENEGAL
142.470 +SN=XOF
142.471 +# SERBIA
142.472 +RS=RSD
142.473 +# SERBIA AND MONTENEGRO
142.474 +CS=CSD
142.475 +# SEYCHELLES
142.476 +SC=SCR
142.477 +# SIERRA LEONE
142.478 +SL=SLL
142.479 +# SINGAPORE
142.480 +SG=SGD
142.481 +# SLOVAKIA
142.482 +SK=SKK
142.483 +# SLOVENIA
142.484 +SI=EUR
142.485 +# SOLOMON ISLANDS
142.486 +SB=SBD
142.487 +# SOMALIA
142.488 +SO=SOS
142.489 +# SOUTH AFRICA
142.490 +ZA=ZAR
142.491 +# SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS
142.492 +GS=GBP
142.493 +# SPAIN
142.494 +ES=EUR
142.495 +# SRI LANKA
142.496 +LK=LKR
142.497 +# SUDAN
142.498 +SD=SDG
142.499 +# SURINAME
142.500 +SR=SRD
142.501 +# SVALBARD AND JAN MAYEN
142.502 +SJ=NOK
142.503 +# SWAZILAND
142.504 +SZ=SZL
142.505 +# SWEDEN
142.506 +SE=SEK
142.507 +# SWITZERLAND
142.508 +CH=CHF
142.509 +# SYRIAN ARAB REPUBLIC
142.510 +SY=SYP
142.511 +# TAIWAN
142.512 +TW=TWD
142.513 +# TAJIKISTAN
142.514 +TJ=TJS
142.515 +# TANZANIA, UNITED REPUBLIC OF
142.516 +TZ=TZS
142.517 +# THAILAND
142.518 +TH=THB
142.519 +# TIMOR-LESTE
142.520 +TL=USD
142.521 +# TOGO
142.522 +TG=XOF
142.523 +# TOKELAU
142.524 +TK=NZD
142.525 +# TONGA
142.526 +TO=TOP
142.527 +# TRINIDAD AND TOBAGO
142.528 +TT=TTD
142.529 +# TUNISIA
142.530 +TN=TND
142.531 +# TURKEY
142.532 +TR=TRL;2004-12-31-22-00-00;TRY
142.533 +# TURKMENISTAN
142.534 +TM=TMM
142.535 +# TURKS AND CAICOS ISLANDS
142.536 +TC=USD
142.537 +# TUVALU
142.538 +TV=AUD
142.539 +# UGANDA
142.540 +UG=UGX
142.541 +# UKRAINE
142.542 +UA=UAH
142.543 +# UNITED ARAB EMIRATES
142.544 +AE=AED
142.545 +# UNITED KINGDOM
142.546 +GB=GBP
142.547 +# UNITED STATES
142.548 +US=USD
142.549 +# UNITED STATES MINOR OUTLYING ISLANDS
142.550 +UM=USD
142.551 +# URUGUAY
142.552 +UY=UYU
142.553 +# UZBEKISTAN
142.554 +UZ=UZS
142.555 +# VANUATU
142.556 +VU=VUV
142.557 +# VENEZUELA
142.558 +VE=VEB;2008-01-01-04-00-00;VEF
142.559 +# VIET NAM
142.560 +VN=VND
142.561 +# VIRGIN ISLANDS, BRITISH
142.562 +VG=USD
142.563 +# VIRGIN ISLANDS, U.S.
142.564 +VI=USD
142.565 +# WALLIS AND FUTUNA
142.566 +WF=XPF
142.567 +# WESTERN SAHARA
142.568 +EH=MAD
142.569 +# YEMEN
142.570 +YE=YER
142.571 +# ZAMBIA
142.572 +ZM=ZMK
142.573 +# ZIMBABWE
142.574 +ZW=ZWD
142.575 +
142.576 +
142.577 +# List of currencies with 0, 1, OR 3 decimals for minor units, or where there
142.578 +# are no minor units defined. All others use 2 decimals.
142.579 +
142.580 +minor0=\
142.581 + ADP-BEF-BIF-BYB-BYR-CLF-CLP-DJF-ESP-GNF-\
142.582 + GRD-ISK-ITL-JPY-KMF-KRW-LUF-MGF-PYG-PTE-RWF-\
142.583 + TPE-TRL-VUV-XAF-XOF-XPF
142.584 +minor1=
142.585 +minor3=\
142.586 + BHD-IQD-JOD-KWD-LYD-OMR-TND
142.587 +minorUndefined=\
142.588 + XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-\
142.589 + XPT-XTS-XXX
143.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
143.2 +++ b/rt/emul/compact/src/main/java/java/util/Date.java Tue Feb 11 13:31:42 2014 +0100
143.3 @@ -0,0 +1,1430 @@
143.4 +/*
143.5 + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
143.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
143.7 + *
143.8 + * This code is free software; you can redistribute it and/or modify it
143.9 + * under the terms of the GNU General Public License version 2 only, as
143.10 + * published by the Free Software Foundation. Oracle designates this
143.11 + * particular file as subject to the "Classpath" exception as provided
143.12 + * by Oracle in the LICENSE file that accompanied this code.
143.13 + *
143.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
143.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
143.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
143.17 + * version 2 for more details (a copy is included in the LICENSE file that
143.18 + * accompanied this code).
143.19 + *
143.20 + * You should have received a copy of the GNU General Public License version
143.21 + * 2 along with this work; if not, write to the Free Software Foundation,
143.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
143.23 + *
143.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
143.25 + * or visit www.oracle.com if you need additional information or have any
143.26 + * questions.
143.27 + */
143.28 +
143.29 +package java.util;
143.30 +
143.31 +import java.text.DateFormat;
143.32 +import java.io.IOException;
143.33 +import java.io.ObjectOutputStream;
143.34 +import java.io.ObjectInputStream;
143.35 +
143.36 +/**
143.37 + * The class <code>Date</code> represents a specific instant
143.38 + * in time, with millisecond precision.
143.39 + * <p>
143.40 + * Prior to JDK 1.1, the class <code>Date</code> had two additional
143.41 + * functions. It allowed the interpretation of dates as year, month, day, hour,
143.42 + * minute, and second values. It also allowed the formatting and parsing
143.43 + * of date strings. Unfortunately, the API for these functions was not
143.44 + * amenable to internationalization. As of JDK 1.1, the
143.45 + * <code>Calendar</code> class should be used to convert between dates and time
143.46 + * fields and the <code>DateFormat</code> class should be used to format and
143.47 + * parse date strings.
143.48 + * The corresponding methods in <code>Date</code> are deprecated.
143.49 + * <p>
143.50 + * Although the <code>Date</code> class is intended to reflect
143.51 + * coordinated universal time (UTC), it may not do so exactly,
143.52 + * depending on the host environment of the Java Virtual Machine.
143.53 + * Nearly all modern operating systems assume that 1 day =
143.54 + * 24 × 60 × 60 = 86400 seconds
143.55 + * in all cases. In UTC, however, about once every year or two there
143.56 + * is an extra second, called a "leap second." The leap
143.57 + * second is always added as the last second of the day, and always
143.58 + * on December 31 or June 30. For example, the last minute of the
143.59 + * year 1995 was 61 seconds long, thanks to an added leap second.
143.60 + * Most computer clocks are not accurate enough to be able to reflect
143.61 + * the leap-second distinction.
143.62 + * <p>
143.63 + * Some computer standards are defined in terms of Greenwich mean
143.64 + * time (GMT), which is equivalent to universal time (UT). GMT is
143.65 + * the "civil" name for the standard; UT is the
143.66 + * "scientific" name for the same standard. The
143.67 + * distinction between UTC and UT is that UTC is based on an atomic
143.68 + * clock and UT is based on astronomical observations, which for all
143.69 + * practical purposes is an invisibly fine hair to split. Because the
143.70 + * earth's rotation is not uniform (it slows down and speeds up
143.71 + * in complicated ways), UT does not always flow uniformly. Leap
143.72 + * seconds are introduced as needed into UTC so as to keep UTC within
143.73 + * 0.9 seconds of UT1, which is a version of UT with certain
143.74 + * corrections applied. There are other time and date systems as
143.75 + * well; for example, the time scale used by the satellite-based
143.76 + * global positioning system (GPS) is synchronized to UTC but is
143.77 + * <i>not</i> adjusted for leap seconds. An interesting source of
143.78 + * further information is the U.S. Naval Observatory, particularly
143.79 + * the Directorate of Time at:
143.80 + * <blockquote><pre>
143.81 + * <a href=http://tycho.usno.navy.mil>http://tycho.usno.navy.mil</a>
143.82 + * </pre></blockquote>
143.83 + * <p>
143.84 + * and their definitions of "Systems of Time" at:
143.85 + * <blockquote><pre>
143.86 + * <a href=http://tycho.usno.navy.mil/systime.html>http://tycho.usno.navy.mil/systime.html</a>
143.87 + * </pre></blockquote>
143.88 + * <p>
143.89 + * In all methods of class <code>Date</code> that accept or return
143.90 + * year, month, date, hours, minutes, and seconds values, the
143.91 + * following representations are used:
143.92 + * <ul>
143.93 + * <li>A year <i>y</i> is represented by the integer
143.94 + * <i>y</i> <code>- 1900</code>.
143.95 + * <li>A month is represented by an integer from 0 to 11; 0 is January,
143.96 + * 1 is February, and so forth; thus 11 is December.
143.97 + * <li>A date (day of month) is represented by an integer from 1 to 31
143.98 + * in the usual manner.
143.99 + * <li>An hour is represented by an integer from 0 to 23. Thus, the hour
143.100 + * from midnight to 1 a.m. is hour 0, and the hour from noon to 1
143.101 + * p.m. is hour 12.
143.102 + * <li>A minute is represented by an integer from 0 to 59 in the usual manner.
143.103 + * <li>A second is represented by an integer from 0 to 61; the values 60 and
143.104 + * 61 occur only for leap seconds and even then only in Java
143.105 + * implementations that actually track leap seconds correctly. Because
143.106 + * of the manner in which leap seconds are currently introduced, it is
143.107 + * extremely unlikely that two leap seconds will occur in the same
143.108 + * minute, but this specification follows the date and time conventions
143.109 + * for ISO C.
143.110 + * </ul>
143.111 + * <p>
143.112 + * In all cases, arguments given to methods for these purposes need
143.113 + * not fall within the indicated ranges; for example, a date may be
143.114 + * specified as January 32 and is interpreted as meaning February 1.
143.115 + *
143.116 + * @author James Gosling
143.117 + * @author Arthur van Hoff
143.118 + * @author Alan Liu
143.119 + * @see java.text.DateFormat
143.120 + * @see java.util.Calendar
143.121 + * @see java.util.TimeZone
143.122 + * @since JDK1.0
143.123 + */
143.124 +public class Date
143.125 + implements java.io.Serializable, Cloneable, Comparable<Date>
143.126 +{
143.127 + private static final BaseCalendar gcal = new BaseCalendar();
143.128 +
143.129 + private static BaseCalendar jcal;
143.130 +
143.131 + private transient long fastTime;
143.132 +
143.133 + /*
143.134 + * If cdate is null, then fastTime indicates the time in millis.
143.135 + * If cdate.isNormalized() is true, then fastTime and cdate are in
143.136 + * synch. Otherwise, fastTime is ignored, and cdate indicates the
143.137 + * time.
143.138 + */
143.139 + private transient BaseCalendar.Datum cdate;
143.140 +
143.141 + // Initialized just before the value is used. See parse().
143.142 + private static int defaultCenturyStart;
143.143 +
143.144 + /* use serialVersionUID from modified java.util.Date for
143.145 + * interoperability with JDK1.1. The Date was modified to write
143.146 + * and read only the UTC time.
143.147 + */
143.148 + private static final long serialVersionUID = 7523967970034938905L;
143.149 +
143.150 + /**
143.151 + * Allocates a <code>Date</code> object and initializes it so that
143.152 + * it represents the time at which it was allocated, measured to the
143.153 + * nearest millisecond.
143.154 + *
143.155 + * @see java.lang.System#currentTimeMillis()
143.156 + */
143.157 + public Date() {
143.158 + this(System.currentTimeMillis());
143.159 + }
143.160 +
143.161 + /**
143.162 + * Allocates a <code>Date</code> object and initializes it to
143.163 + * represent the specified number of milliseconds since the
143.164 + * standard base time known as "the epoch", namely January 1,
143.165 + * 1970, 00:00:00 GMT.
143.166 + *
143.167 + * @param date the milliseconds since January 1, 1970, 00:00:00 GMT.
143.168 + * @see java.lang.System#currentTimeMillis()
143.169 + */
143.170 + public Date(long date) {
143.171 + fastTime = date;
143.172 + }
143.173 +
143.174 + /**
143.175 + * Allocates a <code>Date</code> object and initializes it so that
143.176 + * it represents midnight, local time, at the beginning of the day
143.177 + * specified by the <code>year</code>, <code>month</code>, and
143.178 + * <code>date</code> arguments.
143.179 + *
143.180 + * @param year the year minus 1900.
143.181 + * @param month the month between 0-11.
143.182 + * @param date the day of the month between 1-31.
143.183 + * @see java.util.Calendar
143.184 + * @deprecated As of JDK version 1.1,
143.185 + * replaced by <code>Calendar.set(year + 1900, month, date)</code>
143.186 + * or <code>GregorianCalendar(year + 1900, month, date)</code>.
143.187 + */
143.188 + @Deprecated
143.189 + public Date(int year, int month, int date) {
143.190 + this(year, month, date, 0, 0, 0);
143.191 + }
143.192 +
143.193 + /**
143.194 + * Allocates a <code>Date</code> object and initializes it so that
143.195 + * it represents the instant at the start of the minute specified by
143.196 + * the <code>year</code>, <code>month</code>, <code>date</code>,
143.197 + * <code>hrs</code>, and <code>min</code> arguments, in the local
143.198 + * time zone.
143.199 + *
143.200 + * @param year the year minus 1900.
143.201 + * @param month the month between 0-11.
143.202 + * @param date the day of the month between 1-31.
143.203 + * @param hrs the hours between 0-23.
143.204 + * @param min the minutes between 0-59.
143.205 + * @see java.util.Calendar
143.206 + * @deprecated As of JDK version 1.1,
143.207 + * replaced by <code>Calendar.set(year + 1900, month, date,
143.208 + * hrs, min)</code> or <code>GregorianCalendar(year + 1900,
143.209 + * month, date, hrs, min)</code>.
143.210 + */
143.211 + @Deprecated
143.212 + public Date(int year, int month, int date, int hrs, int min) {
143.213 + this(year, month, date, hrs, min, 0);
143.214 + }
143.215 +
143.216 + /**
143.217 + * Allocates a <code>Date</code> object and initializes it so that
143.218 + * it represents the instant at the start of the second specified
143.219 + * by the <code>year</code>, <code>month</code>, <code>date</code>,
143.220 + * <code>hrs</code>, <code>min</code>, and <code>sec</code> arguments,
143.221 + * in the local time zone.
143.222 + *
143.223 + * @param year the year minus 1900.
143.224 + * @param month the month between 0-11.
143.225 + * @param date the day of the month between 1-31.
143.226 + * @param hrs the hours between 0-23.
143.227 + * @param min the minutes between 0-59.
143.228 + * @param sec the seconds between 0-59.
143.229 + * @see java.util.Calendar
143.230 + * @deprecated As of JDK version 1.1,
143.231 + * replaced by <code>Calendar.set(year + 1900, month, date,
143.232 + * hrs, min, sec)</code> or <code>GregorianCalendar(year + 1900,
143.233 + * month, date, hrs, min, sec)</code>.
143.234 + */
143.235 + @Deprecated
143.236 + public Date(int year, int month, int date, int hrs, int min, int sec) {
143.237 + int y = year + 1900;
143.238 + // month is 0-based. So we have to normalize month to support Long.MAX_VALUE.
143.239 + if (month >= 12) {
143.240 + y += month / 12;
143.241 + month %= 12;
143.242 + } else if (month < 0) {
143.243 + y += month / 12;
143.244 + month = month % 12;
143.245 + }
143.246 + BaseCalendar cal = getCalendarSystem(y);
143.247 + cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.getDefaultRef());
143.248 + cdate.setNormalizedDate(y, month + 1, date).setTimeOfDay(hrs, min, sec, 0);
143.249 + getTimeImpl();
143.250 + cdate = null;
143.251 + }
143.252 +
143.253 + /**
143.254 + * Allocates a <code>Date</code> object and initializes it so that
143.255 + * it represents the date and time indicated by the string
143.256 + * <code>s</code>, which is interpreted as if by the
143.257 + * {@link Date#parse} method.
143.258 + *
143.259 + * @param s a string representation of the date.
143.260 + * @see java.text.DateFormat
143.261 + * @see java.util.Date#parse(java.lang.String)
143.262 + * @deprecated As of JDK version 1.1,
143.263 + * replaced by <code>DateFormat.parse(String s)</code>.
143.264 + */
143.265 + @Deprecated
143.266 + public Date(String s) {
143.267 + this(parse(s));
143.268 + }
143.269 +
143.270 + /**
143.271 + * Return a copy of this object.
143.272 + */
143.273 + public Object clone() {
143.274 + Date d = null;
143.275 + try {
143.276 + d = (Date)super.clone();
143.277 + if (cdate != null) {
143.278 + d.cdate = (BaseCalendar.Datum) cdate.clone();
143.279 + }
143.280 + } catch (CloneNotSupportedException e) {} // Won't happen
143.281 + return d;
143.282 + }
143.283 +
143.284 + /**
143.285 + * Determines the date and time based on the arguments. The
143.286 + * arguments are interpreted as a year, month, day of the month,
143.287 + * hour of the day, minute within the hour, and second within the
143.288 + * minute, exactly as for the <tt>Date</tt> constructor with six
143.289 + * arguments, except that the arguments are interpreted relative
143.290 + * to UTC rather than to the local time zone. The time indicated is
143.291 + * returned represented as the distance, measured in milliseconds,
143.292 + * of that time from the epoch (00:00:00 GMT on January 1, 1970).
143.293 + *
143.294 + * @param year the year minus 1900.
143.295 + * @param month the month between 0-11.
143.296 + * @param date the day of the month between 1-31.
143.297 + * @param hrs the hours between 0-23.
143.298 + * @param min the minutes between 0-59.
143.299 + * @param sec the seconds between 0-59.
143.300 + * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT for
143.301 + * the date and time specified by the arguments.
143.302 + * @see java.util.Calendar
143.303 + * @deprecated As of JDK version 1.1,
143.304 + * replaced by <code>Calendar.set(year + 1900, month, date,
143.305 + * hrs, min, sec)</code> or <code>GregorianCalendar(year + 1900,
143.306 + * month, date, hrs, min, sec)</code>, using a UTC
143.307 + * <code>TimeZone</code>, followed by <code>Calendar.getTime().getTime()</code>.
143.308 + */
143.309 + @Deprecated
143.310 + public static long UTC(int year, int month, int date,
143.311 + int hrs, int min, int sec) {
143.312 + int y = year + 1900;
143.313 + // month is 0-based. So we have to normalize month to support Long.MAX_VALUE.
143.314 + if (month >= 12) {
143.315 + y += month / 12;
143.316 + month %= 12;
143.317 + } else if (month < 0) {
143.318 + y += month / 12;
143.319 + month = month % 12;
143.320 + }
143.321 + int m = month + 1;
143.322 + BaseCalendar cal = getCalendarSystem(y);
143.323 + BaseCalendar.Datum udate = (BaseCalendar.Datum) cal.newCalendarDate(null);
143.324 + udate.setNormalizedDate(y, m, date).setTimeOfDay(hrs, min, sec, 0);
143.325 +
143.326 + // Use a Date instance to perform normalization. Its fastTime
143.327 + // is the UTC value after the normalization.
143.328 + Date d = new Date(0);
143.329 + d.normalize(udate);
143.330 + return d.fastTime;
143.331 + }
143.332 +
143.333 + /**
143.334 + * Attempts to interpret the string <tt>s</tt> as a representation
143.335 + * of a date and time. If the attempt is successful, the time
143.336 + * indicated is returned represented as the distance, measured in
143.337 + * milliseconds, of that time from the epoch (00:00:00 GMT on
143.338 + * January 1, 1970). If the attempt fails, an
143.339 + * <tt>IllegalArgumentException</tt> is thrown.
143.340 + * <p>
143.341 + * It accepts many syntaxes; in particular, it recognizes the IETF
143.342 + * standard date syntax: "Sat, 12 Aug 1995 13:30:00 GMT". It also
143.343 + * understands the continental U.S. time-zone abbreviations, but for
143.344 + * general use, a time-zone offset should be used: "Sat, 12 Aug 1995
143.345 + * 13:30:00 GMT+0430" (4 hours, 30 minutes west of the Greenwich
143.346 + * meridian). If no time zone is specified, the local time zone is
143.347 + * assumed. GMT and UTC are considered equivalent.
143.348 + * <p>
143.349 + * The string <tt>s</tt> is processed from left to right, looking for
143.350 + * data of interest. Any material in <tt>s</tt> that is within the
143.351 + * ASCII parenthesis characters <tt>(</tt> and <tt>)</tt> is ignored.
143.352 + * Parentheses may be nested. Otherwise, the only characters permitted
143.353 + * within <tt>s</tt> are these ASCII characters:
143.354 + * <blockquote><pre>
143.355 + * abcdefghijklmnopqrstuvwxyz
143.356 + * ABCDEFGHIJKLMNOPQRSTUVWXYZ
143.357 + * 0123456789,+-:/</pre></blockquote>
143.358 + * and whitespace characters.<p>
143.359 + * A consecutive sequence of decimal digits is treated as a decimal
143.360 + * number:<ul>
143.361 + * <li>If a number is preceded by <tt>+</tt> or <tt>-</tt> and a year
143.362 + * has already been recognized, then the number is a time-zone
143.363 + * offset. If the number is less than 24, it is an offset measured
143.364 + * in hours. Otherwise, it is regarded as an offset in minutes,
143.365 + * expressed in 24-hour time format without punctuation. A
143.366 + * preceding <tt>-</tt> means a westward offset. Time zone offsets
143.367 + * are always relative to UTC (Greenwich). Thus, for example,
143.368 + * <tt>-5</tt> occurring in the string would mean "five hours west
143.369 + * of Greenwich" and <tt>+0430</tt> would mean "four hours and
143.370 + * thirty minutes east of Greenwich." It is permitted for the
143.371 + * string to specify <tt>GMT</tt>, <tt>UT</tt>, or <tt>UTC</tt>
143.372 + * redundantly-for example, <tt>GMT-5</tt> or <tt>utc+0430</tt>.
143.373 + * <li>The number is regarded as a year number if one of the
143.374 + * following conditions is true:
143.375 + * <ul>
143.376 + * <li>The number is equal to or greater than 70 and followed by a
143.377 + * space, comma, slash, or end of string
143.378 + * <li>The number is less than 70, and both a month and a day of
143.379 + * the month have already been recognized</li>
143.380 + * </ul>
143.381 + * If the recognized year number is less than 100, it is
143.382 + * interpreted as an abbreviated year relative to a century of
143.383 + * which dates are within 80 years before and 19 years after
143.384 + * the time when the Date class is initialized.
143.385 + * After adjusting the year number, 1900 is subtracted from
143.386 + * it. For example, if the current year is 1999 then years in
143.387 + * the range 19 to 99 are assumed to mean 1919 to 1999, while
143.388 + * years from 0 to 18 are assumed to mean 2000 to 2018. Note
143.389 + * that this is slightly different from the interpretation of
143.390 + * years less than 100 that is used in {@link java.text.SimpleDateFormat}.
143.391 + * <li>If the number is followed by a colon, it is regarded as an hour,
143.392 + * unless an hour has already been recognized, in which case it is
143.393 + * regarded as a minute.
143.394 + * <li>If the number is followed by a slash, it is regarded as a month
143.395 + * (it is decreased by 1 to produce a number in the range <tt>0</tt>
143.396 + * to <tt>11</tt>), unless a month has already been recognized, in
143.397 + * which case it is regarded as a day of the month.
143.398 + * <li>If the number is followed by whitespace, a comma, a hyphen, or
143.399 + * end of string, then if an hour has been recognized but not a
143.400 + * minute, it is regarded as a minute; otherwise, if a minute has
143.401 + * been recognized but not a second, it is regarded as a second;
143.402 + * otherwise, it is regarded as a day of the month. </ul><p>
143.403 + * A consecutive sequence of letters is regarded as a word and treated
143.404 + * as follows:<ul>
143.405 + * <li>A word that matches <tt>AM</tt>, ignoring case, is ignored (but
143.406 + * the parse fails if an hour has not been recognized or is less
143.407 + * than <tt>1</tt> or greater than <tt>12</tt>).
143.408 + * <li>A word that matches <tt>PM</tt>, ignoring case, adds <tt>12</tt>
143.409 + * to the hour (but the parse fails if an hour has not been
143.410 + * recognized or is less than <tt>1</tt> or greater than <tt>12</tt>).
143.411 + * <li>Any word that matches any prefix of <tt>SUNDAY, MONDAY, TUESDAY,
143.412 + * WEDNESDAY, THURSDAY, FRIDAY</tt>, or <tt>SATURDAY</tt>, ignoring
143.413 + * case, is ignored. For example, <tt>sat, Friday, TUE</tt>, and
143.414 + * <tt>Thurs</tt> are ignored.
143.415 + * <li>Otherwise, any word that matches any prefix of <tt>JANUARY,
143.416 + * FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER,
143.417 + * OCTOBER, NOVEMBER</tt>, or <tt>DECEMBER</tt>, ignoring case, and
143.418 + * considering them in the order given here, is recognized as
143.419 + * specifying a month and is converted to a number (<tt>0</tt> to
143.420 + * <tt>11</tt>). For example, <tt>aug, Sept, april</tt>, and
143.421 + * <tt>NOV</tt> are recognized as months. So is <tt>Ma</tt>, which
143.422 + * is recognized as <tt>MARCH</tt>, not <tt>MAY</tt>.
143.423 + * <li>Any word that matches <tt>GMT, UT</tt>, or <tt>UTC</tt>, ignoring
143.424 + * case, is treated as referring to UTC.
143.425 + * <li>Any word that matches <tt>EST, CST, MST</tt>, or <tt>PST</tt>,
143.426 + * ignoring case, is recognized as referring to the time zone in
143.427 + * North America that is five, six, seven, or eight hours west of
143.428 + * Greenwich, respectively. Any word that matches <tt>EDT, CDT,
143.429 + * MDT</tt>, or <tt>PDT</tt>, ignoring case, is recognized as
143.430 + * referring to the same time zone, respectively, during daylight
143.431 + * saving time.</ul><p>
143.432 + * Once the entire string s has been scanned, it is converted to a time
143.433 + * result in one of two ways. If a time zone or time-zone offset has been
143.434 + * recognized, then the year, month, day of month, hour, minute, and
143.435 + * second are interpreted in UTC and then the time-zone offset is
143.436 + * applied. Otherwise, the year, month, day of month, hour, minute, and
143.437 + * second are interpreted in the local time zone.
143.438 + *
143.439 + * @param s a string to be parsed as a date.
143.440 + * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
143.441 + * represented by the string argument.
143.442 + * @see java.text.DateFormat
143.443 + * @deprecated As of JDK version 1.1,
143.444 + * replaced by <code>DateFormat.parse(String s)</code>.
143.445 + */
143.446 + @Deprecated
143.447 + public static long parse(String s) {
143.448 + int year = Integer.MIN_VALUE;
143.449 + int mon = -1;
143.450 + int mday = -1;
143.451 + int hour = -1;
143.452 + int min = -1;
143.453 + int sec = -1;
143.454 + int millis = -1;
143.455 + int c = -1;
143.456 + int i = 0;
143.457 + int n = -1;
143.458 + int wst = -1;
143.459 + int tzoffset = -1;
143.460 + int prevc = 0;
143.461 + syntax:
143.462 + {
143.463 + if (s == null)
143.464 + break syntax;
143.465 + int limit = s.length();
143.466 + while (i < limit) {
143.467 + c = s.charAt(i);
143.468 + i++;
143.469 + if (c <= ' ' || c == ',')
143.470 + continue;
143.471 + if (c == '(') { // skip comments
143.472 + int depth = 1;
143.473 + while (i < limit) {
143.474 + c = s.charAt(i);
143.475 + i++;
143.476 + if (c == '(') depth++;
143.477 + else if (c == ')')
143.478 + if (--depth <= 0)
143.479 + break;
143.480 + }
143.481 + continue;
143.482 + }
143.483 + if ('0' <= c && c <= '9') {
143.484 + n = c - '0';
143.485 + while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
143.486 + n = n * 10 + c - '0';
143.487 + i++;
143.488 + }
143.489 + if (prevc == '+' || prevc == '-' && year != Integer.MIN_VALUE) {
143.490 + // timezone offset
143.491 + if (n < 24)
143.492 + n = n * 60; // EG. "GMT-3"
143.493 + else
143.494 + n = n % 100 + n / 100 * 60; // eg "GMT-0430"
143.495 + if (prevc == '+') // plus means east of GMT
143.496 + n = -n;
143.497 + if (tzoffset != 0 && tzoffset != -1)
143.498 + break syntax;
143.499 + tzoffset = n;
143.500 + } else if (n >= 70)
143.501 + if (year != Integer.MIN_VALUE)
143.502 + break syntax;
143.503 + else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
143.504 + // year = n < 1900 ? n : n - 1900;
143.505 + year = n;
143.506 + else
143.507 + break syntax;
143.508 + else if (c == ':')
143.509 + if (hour < 0)
143.510 + hour = (byte) n;
143.511 + else if (min < 0)
143.512 + min = (byte) n;
143.513 + else
143.514 + break syntax;
143.515 + else if (c == '/')
143.516 + if (mon < 0)
143.517 + mon = (byte) (n - 1);
143.518 + else if (mday < 0)
143.519 + mday = (byte) n;
143.520 + else
143.521 + break syntax;
143.522 + else if (i < limit && c != ',' && c > ' ' && c != '-')
143.523 + break syntax;
143.524 + else if (hour >= 0 && min < 0)
143.525 + min = (byte) n;
143.526 + else if (min >= 0 && sec < 0)
143.527 + sec = (byte) n;
143.528 + else if (mday < 0)
143.529 + mday = (byte) n;
143.530 + // Handle two-digit years < 70 (70-99 handled above).
143.531 + else if (year == Integer.MIN_VALUE && mon >= 0 && mday >= 0)
143.532 + year = n;
143.533 + else
143.534 + break syntax;
143.535 + prevc = 0;
143.536 + } else if (c == '/' || c == ':' || c == '+' || c == '-')
143.537 + prevc = c;
143.538 + else {
143.539 + int st = i - 1;
143.540 + while (i < limit) {
143.541 + c = s.charAt(i);
143.542 + if (!('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'))
143.543 + break;
143.544 + i++;
143.545 + }
143.546 + if (i <= st + 1)
143.547 + break syntax;
143.548 + int k;
143.549 + for (k = wtb.length; --k >= 0;)
143.550 + if (wtb[k].regionMatches(true, 0, s, st, i - st)) {
143.551 + int action = ttb[k];
143.552 + if (action != 0) {
143.553 + if (action == 1) { // pm
143.554 + if (hour > 12 || hour < 1)
143.555 + break syntax;
143.556 + else if (hour < 12)
143.557 + hour += 12;
143.558 + } else if (action == 14) { // am
143.559 + if (hour > 12 || hour < 1)
143.560 + break syntax;
143.561 + else if (hour == 12)
143.562 + hour = 0;
143.563 + } else if (action <= 13) { // month!
143.564 + if (mon < 0)
143.565 + mon = (byte) (action - 2);
143.566 + else
143.567 + break syntax;
143.568 + } else {
143.569 + tzoffset = action - 10000;
143.570 + }
143.571 + }
143.572 + break;
143.573 + }
143.574 + if (k < 0)
143.575 + break syntax;
143.576 + prevc = 0;
143.577 + }
143.578 + }
143.579 + if (year == Integer.MIN_VALUE || mon < 0 || mday < 0)
143.580 + break syntax;
143.581 + // Parse 2-digit years within the correct default century.
143.582 + if (year < 100) {
143.583 + synchronized (Date.class) {
143.584 + if (defaultCenturyStart == 0) {
143.585 + defaultCenturyStart = gcal.getCalendarDate().getYear() - 80;
143.586 + }
143.587 + }
143.588 + year += (defaultCenturyStart / 100) * 100;
143.589 + if (year < defaultCenturyStart) year += 100;
143.590 + }
143.591 + if (sec < 0)
143.592 + sec = 0;
143.593 + if (min < 0)
143.594 + min = 0;
143.595 + if (hour < 0)
143.596 + hour = 0;
143.597 + BaseCalendar cal = getCalendarSystem(year);
143.598 + if (tzoffset == -1) { // no time zone specified, have to use local
143.599 + BaseCalendar.Datum ldate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.getDefaultRef());
143.600 + ldate.setDate(year, mon + 1, mday);
143.601 + ldate.setTimeOfDay(hour, min, sec, 0);
143.602 + return cal.getTime(ldate);
143.603 + }
143.604 + BaseCalendar.Datum udate = (BaseCalendar.Datum) cal.newCalendarDate(null); // no time zone
143.605 + udate.setDate(year, mon + 1, mday);
143.606 + udate.setTimeOfDay(hour, min, sec, 0);
143.607 + return cal.getTime(udate) + tzoffset * (60 * 1000);
143.608 + }
143.609 + // syntax error
143.610 + throw new IllegalArgumentException();
143.611 + }
143.612 + private final static String wtb[] = {
143.613 + "am", "pm",
143.614 + "monday", "tuesday", "wednesday", "thursday", "friday",
143.615 + "saturday", "sunday",
143.616 + "january", "february", "march", "april", "may", "june",
143.617 + "july", "august", "september", "october", "november", "december",
143.618 + "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
143.619 + "mst", "mdt", "pst", "pdt"
143.620 + };
143.621 + private final static int ttb[] = {
143.622 + 14, 1, 0, 0, 0, 0, 0, 0, 0,
143.623 + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
143.624 + 10000 + 0, 10000 + 0, 10000 + 0, // GMT/UT/UTC
143.625 + 10000 + 5 * 60, 10000 + 4 * 60, // EST/EDT
143.626 + 10000 + 6 * 60, 10000 + 5 * 60, // CST/CDT
143.627 + 10000 + 7 * 60, 10000 + 6 * 60, // MST/MDT
143.628 + 10000 + 8 * 60, 10000 + 7 * 60 // PST/PDT
143.629 + };
143.630 +
143.631 + /**
143.632 + * Returns a value that is the result of subtracting 1900 from the
143.633 + * year that contains or begins with the instant in time represented
143.634 + * by this <code>Date</code> object, as interpreted in the local
143.635 + * time zone.
143.636 + *
143.637 + * @return the year represented by this date, minus 1900.
143.638 + * @see java.util.Calendar
143.639 + * @deprecated As of JDK version 1.1,
143.640 + * replaced by <code>Calendar.get(Calendar.YEAR) - 1900</code>.
143.641 + */
143.642 + @Deprecated
143.643 + public int getYear() {
143.644 + return normalize().getYear() - 1900;
143.645 + }
143.646 +
143.647 + /**
143.648 + * Sets the year of this <tt>Date</tt> object to be the specified
143.649 + * value plus 1900. This <code>Date</code> object is modified so
143.650 + * that it represents a point in time within the specified year,
143.651 + * with the month, date, hour, minute, and second the same as
143.652 + * before, as interpreted in the local time zone. (Of course, if
143.653 + * the date was February 29, for example, and the year is set to a
143.654 + * non-leap year, then the new date will be treated as if it were
143.655 + * on March 1.)
143.656 + *
143.657 + * @param year the year value.
143.658 + * @see java.util.Calendar
143.659 + * @deprecated As of JDK version 1.1,
143.660 + * replaced by <code>Calendar.set(Calendar.YEAR, year + 1900)</code>.
143.661 + */
143.662 + @Deprecated
143.663 + public void setYear(int year) {
143.664 + getCalendarDate().setNormalizedYear(year + 1900);
143.665 + }
143.666 +
143.667 + /**
143.668 + * Returns a number representing the month that contains or begins
143.669 + * with the instant in time represented by this <tt>Date</tt> object.
143.670 + * The value returned is between <code>0</code> and <code>11</code>,
143.671 + * with the value <code>0</code> representing January.
143.672 + *
143.673 + * @return the month represented by this date.
143.674 + * @see java.util.Calendar
143.675 + * @deprecated As of JDK version 1.1,
143.676 + * replaced by <code>Calendar.get(Calendar.MONTH)</code>.
143.677 + */
143.678 + @Deprecated
143.679 + public int getMonth() {
143.680 + return normalize().getMonth() - 1; // adjust 1-based to 0-based
143.681 + }
143.682 +
143.683 + /**
143.684 + * Sets the month of this date to the specified value. This
143.685 + * <tt>Date</tt> object is modified so that it represents a point
143.686 + * in time within the specified month, with the year, date, hour,
143.687 + * minute, and second the same as before, as interpreted in the
143.688 + * local time zone. If the date was October 31, for example, and
143.689 + * the month is set to June, then the new date will be treated as
143.690 + * if it were on July 1, because June has only 30 days.
143.691 + *
143.692 + * @param month the month value between 0-11.
143.693 + * @see java.util.Calendar
143.694 + * @deprecated As of JDK version 1.1,
143.695 + * replaced by <code>Calendar.set(Calendar.MONTH, int month)</code>.
143.696 + */
143.697 + @Deprecated
143.698 + public void setMonth(int month) {
143.699 + int y = 0;
143.700 + if (month >= 12) {
143.701 + y = month / 12;
143.702 + month %= 12;
143.703 + } else if (month < 0) {
143.704 + y = month / 12;
143.705 + month = month % 12;
143.706 + }
143.707 + BaseCalendar.Datum d = getCalendarDate();
143.708 + if (y != 0) {
143.709 + d.setNormalizedYear(d.getNormalizedYear() + y);
143.710 + }
143.711 + d.setMonth(month + 1); // adjust 0-based to 1-based month numbering
143.712 + }
143.713 +
143.714 + /**
143.715 + * Returns the day of the month represented by this <tt>Date</tt> object.
143.716 + * The value returned is between <code>1</code> and <code>31</code>
143.717 + * representing the day of the month that contains or begins with the
143.718 + * instant in time represented by this <tt>Date</tt> object, as
143.719 + * interpreted in the local time zone.
143.720 + *
143.721 + * @return the day of the month represented by this date.
143.722 + * @see java.util.Calendar
143.723 + * @deprecated As of JDK version 1.1,
143.724 + * replaced by <code>Calendar.get(Calendar.DAY_OF_MONTH)</code>.
143.725 + * @deprecated
143.726 + */
143.727 + @Deprecated
143.728 + public int getDate() {
143.729 + return normalize().getDayOfMonth();
143.730 + }
143.731 +
143.732 + /**
143.733 + * Sets the day of the month of this <tt>Date</tt> object to the
143.734 + * specified value. This <tt>Date</tt> object is modified so that
143.735 + * it represents a point in time within the specified day of the
143.736 + * month, with the year, month, hour, minute, and second the same
143.737 + * as before, as interpreted in the local time zone. If the date
143.738 + * was April 30, for example, and the date is set to 31, then it
143.739 + * will be treated as if it were on May 1, because April has only
143.740 + * 30 days.
143.741 + *
143.742 + * @param date the day of the month value between 1-31.
143.743 + * @see java.util.Calendar
143.744 + * @deprecated As of JDK version 1.1,
143.745 + * replaced by <code>Calendar.set(Calendar.DAY_OF_MONTH, int date)</code>.
143.746 + */
143.747 + @Deprecated
143.748 + public void setDate(int date) {
143.749 + getCalendarDate().setDayOfMonth(date);
143.750 + }
143.751 +
143.752 + /**
143.753 + * Returns the day of the week represented by this date. The
143.754 + * returned value (<tt>0</tt> = Sunday, <tt>1</tt> = Monday,
143.755 + * <tt>2</tt> = Tuesday, <tt>3</tt> = Wednesday, <tt>4</tt> =
143.756 + * Thursday, <tt>5</tt> = Friday, <tt>6</tt> = Saturday)
143.757 + * represents the day of the week that contains or begins with
143.758 + * the instant in time represented by this <tt>Date</tt> object,
143.759 + * as interpreted in the local time zone.
143.760 + *
143.761 + * @return the day of the week represented by this date.
143.762 + * @see java.util.Calendar
143.763 + * @deprecated As of JDK version 1.1,
143.764 + * replaced by <code>Calendar.get(Calendar.DAY_OF_WEEK)</code>.
143.765 + */
143.766 + @Deprecated
143.767 + public int getDay() {
143.768 + return normalize().getDayOfWeek() - 7;//gcal.SUNDAY;
143.769 + }
143.770 +
143.771 + /**
143.772 + * Returns the hour represented by this <tt>Date</tt> object. The
143.773 + * returned value is a number (<tt>0</tt> through <tt>23</tt>)
143.774 + * representing the hour within the day that contains or begins
143.775 + * with the instant in time represented by this <tt>Date</tt>
143.776 + * object, as interpreted in the local time zone.
143.777 + *
143.778 + * @return the hour represented by this date.
143.779 + * @see java.util.Calendar
143.780 + * @deprecated As of JDK version 1.1,
143.781 + * replaced by <code>Calendar.get(Calendar.HOUR_OF_DAY)</code>.
143.782 + */
143.783 + @Deprecated
143.784 + public int getHours() {
143.785 + return normalize().getHours();
143.786 + }
143.787 +
143.788 + /**
143.789 + * Sets the hour of this <tt>Date</tt> object to the specified value.
143.790 + * This <tt>Date</tt> object is modified so that it represents a point
143.791 + * in time within the specified hour of the day, with the year, month,
143.792 + * date, minute, and second the same as before, as interpreted in the
143.793 + * local time zone.
143.794 + *
143.795 + * @param hours the hour value.
143.796 + * @see java.util.Calendar
143.797 + * @deprecated As of JDK version 1.1,
143.798 + * replaced by <code>Calendar.set(Calendar.HOUR_OF_DAY, int hours)</code>.
143.799 + */
143.800 + @Deprecated
143.801 + public void setHours(int hours) {
143.802 + getCalendarDate().setHours(hours);
143.803 + }
143.804 +
143.805 + /**
143.806 + * Returns the number of minutes past the hour represented by this date,
143.807 + * as interpreted in the local time zone.
143.808 + * The value returned is between <code>0</code> and <code>59</code>.
143.809 + *
143.810 + * @return the number of minutes past the hour represented by this date.
143.811 + * @see java.util.Calendar
143.812 + * @deprecated As of JDK version 1.1,
143.813 + * replaced by <code>Calendar.get(Calendar.MINUTE)</code>.
143.814 + */
143.815 + @Deprecated
143.816 + public int getMinutes() {
143.817 + return normalize().getMinutes();
143.818 + }
143.819 +
143.820 + /**
143.821 + * Sets the minutes of this <tt>Date</tt> object to the specified value.
143.822 + * This <tt>Date</tt> object is modified so that it represents a point
143.823 + * in time within the specified minute of the hour, with the year, month,
143.824 + * date, hour, and second the same as before, as interpreted in the
143.825 + * local time zone.
143.826 + *
143.827 + * @param minutes the value of the minutes.
143.828 + * @see java.util.Calendar
143.829 + * @deprecated As of JDK version 1.1,
143.830 + * replaced by <code>Calendar.set(Calendar.MINUTE, int minutes)</code>.
143.831 + */
143.832 + @Deprecated
143.833 + public void setMinutes(int minutes) {
143.834 + getCalendarDate().setMinutes(minutes);
143.835 + }
143.836 +
143.837 + /**
143.838 + * Returns the number of seconds past the minute represented by this date.
143.839 + * The value returned is between <code>0</code> and <code>61</code>. The
143.840 + * values <code>60</code> and <code>61</code> can only occur on those
143.841 + * Java Virtual Machines that take leap seconds into account.
143.842 + *
143.843 + * @return the number of seconds past the minute represented by this date.
143.844 + * @see java.util.Calendar
143.845 + * @deprecated As of JDK version 1.1,
143.846 + * replaced by <code>Calendar.get(Calendar.SECOND)</code>.
143.847 + */
143.848 + @Deprecated
143.849 + public int getSeconds() {
143.850 + return normalize().getSeconds();
143.851 + }
143.852 +
143.853 + /**
143.854 + * Sets the seconds of this <tt>Date</tt> to the specified value.
143.855 + * This <tt>Date</tt> object is modified so that it represents a
143.856 + * point in time within the specified second of the minute, with
143.857 + * the year, month, date, hour, and minute the same as before, as
143.858 + * interpreted in the local time zone.
143.859 + *
143.860 + * @param seconds the seconds value.
143.861 + * @see java.util.Calendar
143.862 + * @deprecated As of JDK version 1.1,
143.863 + * replaced by <code>Calendar.set(Calendar.SECOND, int seconds)</code>.
143.864 + */
143.865 + @Deprecated
143.866 + public void setSeconds(int seconds) {
143.867 + getCalendarDate().setSeconds(seconds);
143.868 + }
143.869 +
143.870 + /**
143.871 + * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
143.872 + * represented by this <tt>Date</tt> object.
143.873 + *
143.874 + * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
143.875 + * represented by this date.
143.876 + */
143.877 + public long getTime() {
143.878 + return getTimeImpl();
143.879 + }
143.880 +
143.881 + private final long getTimeImpl() {
143.882 + if (cdate != null && !cdate.isNormalized()) {
143.883 + normalize();
143.884 + }
143.885 + return fastTime;
143.886 + }
143.887 +
143.888 + /**
143.889 + * Sets this <code>Date</code> object to represent a point in time that is
143.890 + * <code>time</code> milliseconds after January 1, 1970 00:00:00 GMT.
143.891 + *
143.892 + * @param time the number of milliseconds.
143.893 + */
143.894 + public void setTime(long time) {
143.895 + fastTime = time;
143.896 + cdate = null;
143.897 + }
143.898 +
143.899 + /**
143.900 + * Tests if this date is before the specified date.
143.901 + *
143.902 + * @param when a date.
143.903 + * @return <code>true</code> if and only if the instant of time
143.904 + * represented by this <tt>Date</tt> object is strictly
143.905 + * earlier than the instant represented by <tt>when</tt>;
143.906 + * <code>false</code> otherwise.
143.907 + * @exception NullPointerException if <code>when</code> is null.
143.908 + */
143.909 + public boolean before(Date when) {
143.910 + return getMillisOf(this) < getMillisOf(when);
143.911 + }
143.912 +
143.913 + /**
143.914 + * Tests if this date is after the specified date.
143.915 + *
143.916 + * @param when a date.
143.917 + * @return <code>true</code> if and only if the instant represented
143.918 + * by this <tt>Date</tt> object is strictly later than the
143.919 + * instant represented by <tt>when</tt>;
143.920 + * <code>false</code> otherwise.
143.921 + * @exception NullPointerException if <code>when</code> is null.
143.922 + */
143.923 + public boolean after(Date when) {
143.924 + return getMillisOf(this) > getMillisOf(when);
143.925 + }
143.926 +
143.927 + /**
143.928 + * Compares two dates for equality.
143.929 + * The result is <code>true</code> if and only if the argument is
143.930 + * not <code>null</code> and is a <code>Date</code> object that
143.931 + * represents the same point in time, to the millisecond, as this object.
143.932 + * <p>
143.933 + * Thus, two <code>Date</code> objects are equal if and only if the
143.934 + * <code>getTime</code> method returns the same <code>long</code>
143.935 + * value for both.
143.936 + *
143.937 + * @param obj the object to compare with.
143.938 + * @return <code>true</code> if the objects are the same;
143.939 + * <code>false</code> otherwise.
143.940 + * @see java.util.Date#getTime()
143.941 + */
143.942 + public boolean equals(Object obj) {
143.943 + return obj instanceof Date && getTime() == ((Date) obj).getTime();
143.944 + }
143.945 +
143.946 + /**
143.947 + * Returns the millisecond value of this <code>Date</code> object
143.948 + * without affecting its internal state.
143.949 + */
143.950 + static final long getMillisOf(Date date) {
143.951 + if (date.cdate == null || date.cdate.isNormalized()) {
143.952 + return date.fastTime;
143.953 + }
143.954 + BaseCalendar.Datum d = (BaseCalendar.Datum) date.cdate.clone();
143.955 + return gcal.getTime(d);
143.956 + }
143.957 +
143.958 + /**
143.959 + * Compares two Dates for ordering.
143.960 + *
143.961 + * @param anotherDate the <code>Date</code> to be compared.
143.962 + * @return the value <code>0</code> if the argument Date is equal to
143.963 + * this Date; a value less than <code>0</code> if this Date
143.964 + * is before the Date argument; and a value greater than
143.965 + * <code>0</code> if this Date is after the Date argument.
143.966 + * @since 1.2
143.967 + * @exception NullPointerException if <code>anotherDate</code> is null.
143.968 + */
143.969 + public int compareTo(Date anotherDate) {
143.970 + long thisTime = getMillisOf(this);
143.971 + long anotherTime = getMillisOf(anotherDate);
143.972 + return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
143.973 + }
143.974 +
143.975 + /**
143.976 + * Returns a hash code value for this object. The result is the
143.977 + * exclusive OR of the two halves of the primitive <tt>long</tt>
143.978 + * value returned by the {@link Date#getTime}
143.979 + * method. That is, the hash code is the value of the expression:
143.980 + * <blockquote><pre>
143.981 + * (int)(this.getTime()^(this.getTime() >>> 32))</pre></blockquote>
143.982 + *
143.983 + * @return a hash code value for this object.
143.984 + */
143.985 + public int hashCode() {
143.986 + long ht = this.getTime();
143.987 + return (int) ht ^ (int) (ht >> 32);
143.988 + }
143.989 +
143.990 + /**
143.991 + * Converts this <code>Date</code> object to a <code>String</code>
143.992 + * of the form:
143.993 + * <blockquote><pre>
143.994 + * dow mon dd hh:mm:ss zzz yyyy</pre></blockquote>
143.995 + * where:<ul>
143.996 + * <li><tt>dow</tt> is the day of the week (<tt>Sun, Mon, Tue, Wed,
143.997 + * Thu, Fri, Sat</tt>).
143.998 + * <li><tt>mon</tt> is the month (<tt>Jan, Feb, Mar, Apr, May, Jun,
143.999 + * Jul, Aug, Sep, Oct, Nov, Dec</tt>).
143.1000 + * <li><tt>dd</tt> is the day of the month (<tt>01</tt> through
143.1001 + * <tt>31</tt>), as two decimal digits.
143.1002 + * <li><tt>hh</tt> is the hour of the day (<tt>00</tt> through
143.1003 + * <tt>23</tt>), as two decimal digits.
143.1004 + * <li><tt>mm</tt> is the minute within the hour (<tt>00</tt> through
143.1005 + * <tt>59</tt>), as two decimal digits.
143.1006 + * <li><tt>ss</tt> is the second within the minute (<tt>00</tt> through
143.1007 + * <tt>61</tt>, as two decimal digits.
143.1008 + * <li><tt>zzz</tt> is the time zone (and may reflect daylight saving
143.1009 + * time). Standard time zone abbreviations include those
143.1010 + * recognized by the method <tt>parse</tt>. If time zone
143.1011 + * information is not available, then <tt>zzz</tt> is empty -
143.1012 + * that is, it consists of no characters at all.
143.1013 + * <li><tt>yyyy</tt> is the year, as four decimal digits.
143.1014 + * </ul>
143.1015 + *
143.1016 + * @return a string representation of this date.
143.1017 + * @see java.util.Date#toLocaleString()
143.1018 + * @see java.util.Date#toGMTString()
143.1019 + */
143.1020 + public String toString() {
143.1021 + // "EEE MMM dd HH:mm:ss zzz yyyy";
143.1022 + BaseCalendar.Datum date = normalize();
143.1023 + StringBuilder sb = new StringBuilder(28);
143.1024 + int index = date.getDayOfWeek();
143.1025 + if (index == 7) {
143.1026 + index = 8;
143.1027 + }
143.1028 + convertToAbbr(sb, wtb[index]).append(' '); // EEE
143.1029 + convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
143.1030 +// CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd
143.1031 +//
143.1032 +// CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
143.1033 +// CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
143.1034 +// CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
143.1035 +// TimeZone zi = date.getZone();
143.1036 +// if (zi != null) {
143.1037 +// sb.append(zi.getDisplayName(date.isDaylightTime(), zi.SHORT, Locale.US)); // zzz
143.1038 +// } else {
143.1039 +// sb.append("GMT");
143.1040 +// }
143.1041 + sb.append(' ').append(date.getYear()); // yyyy
143.1042 + return sb.toString();
143.1043 + }
143.1044 +
143.1045 + /**
143.1046 + * Converts the given name to its 3-letter abbreviation (e.g.,
143.1047 + * "monday" -> "Mon") and stored the abbreviation in the given
143.1048 + * <code>StringBuilder</code>.
143.1049 + */
143.1050 + private static final StringBuilder convertToAbbr(StringBuilder sb, String name) {
143.1051 + sb.append(Character.toUpperCase(name.charAt(0)));
143.1052 + sb.append(name.charAt(1)).append(name.charAt(2));
143.1053 + return sb;
143.1054 + }
143.1055 +
143.1056 + /**
143.1057 + * Creates a string representation of this <tt>Date</tt> object in an
143.1058 + * implementation-dependent form. The intent is that the form should
143.1059 + * be familiar to the user of the Java application, wherever it may
143.1060 + * happen to be running. The intent is comparable to that of the
143.1061 + * "<code>%c</code>" format supported by the <code>strftime()</code>
143.1062 + * function of ISO C.
143.1063 + *
143.1064 + * @return a string representation of this date, using the locale
143.1065 + * conventions.
143.1066 + * @see java.text.DateFormat
143.1067 + * @see java.util.Date#toString()
143.1068 + * @see java.util.Date#toGMTString()
143.1069 + * @deprecated As of JDK version 1.1,
143.1070 + * replaced by <code>DateFormat.format(Date date)</code>.
143.1071 + */
143.1072 + @Deprecated
143.1073 + public String toLocaleString() {
143.1074 + DateFormat formatter = DateFormat.getDateTimeInstance();
143.1075 + return formatter.format(this);
143.1076 + }
143.1077 +
143.1078 + /**
143.1079 + * Creates a string representation of this <tt>Date</tt> object of
143.1080 + * the form:
143.1081 + * <blockquote<pre>
143.1082 + * d mon yyyy hh:mm:ss GMT</pre></blockquote>
143.1083 + * where:<ul>
143.1084 + * <li><i>d</i> is the day of the month (<tt>1</tt> through <tt>31</tt>),
143.1085 + * as one or two decimal digits.
143.1086 + * <li><i>mon</i> is the month (<tt>Jan, Feb, Mar, Apr, May, Jun, Jul,
143.1087 + * Aug, Sep, Oct, Nov, Dec</tt>).
143.1088 + * <li><i>yyyy</i> is the year, as four decimal digits.
143.1089 + * <li><i>hh</i> is the hour of the day (<tt>00</tt> through <tt>23</tt>),
143.1090 + * as two decimal digits.
143.1091 + * <li><i>mm</i> is the minute within the hour (<tt>00</tt> through
143.1092 + * <tt>59</tt>), as two decimal digits.
143.1093 + * <li><i>ss</i> is the second within the minute (<tt>00</tt> through
143.1094 + * <tt>61</tt>), as two decimal digits.
143.1095 + * <li><i>GMT</i> is exactly the ASCII letters "<tt>GMT</tt>" to indicate
143.1096 + * Greenwich Mean Time.
143.1097 + * </ul><p>
143.1098 + * The result does not depend on the local time zone.
143.1099 + *
143.1100 + * @return a string representation of this date, using the Internet GMT
143.1101 + * conventions.
143.1102 + * @see java.text.DateFormat
143.1103 + * @see java.util.Date#toString()
143.1104 + * @see java.util.Date#toLocaleString()
143.1105 + * @deprecated As of JDK version 1.1,
143.1106 + * replaced by <code>DateFormat.format(Date date)</code>, using a
143.1107 + * GMT <code>TimeZone</code>.
143.1108 + */
143.1109 + @Deprecated
143.1110 + public String toGMTString() {
143.1111 + // d MMM yyyy HH:mm:ss 'GMT'
143.1112 + long t = getTime();
143.1113 + BaseCalendar cal = getCalendarSystem(t);
143.1114 + StringBuilder sb = new StringBuilder(32);
143.1115 +// BaseCalendar.Datum date =
143.1116 +// (BaseCalendar.Datum) cal.getCalendarDate(getTime(), (TimeZone)null);
143.1117 +// CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 1).append(' '); // d
143.1118 +// convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
143.1119 +// sb.append(date.getYear()).append(' '); // yyyy
143.1120 +// CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
143.1121 +// CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
143.1122 +// CalendarUtils.sprintf0d(sb, date.getSeconds(), 2); // ss
143.1123 + sb.append(" GMT"); // ' GMT'
143.1124 + return sb.toString();
143.1125 + }
143.1126 +
143.1127 + /**
143.1128 + * Returns the offset, measured in minutes, for the local time zone
143.1129 + * relative to UTC that is appropriate for the time represented by
143.1130 + * this <code>Date</code> object.
143.1131 + * <p>
143.1132 + * For example, in Massachusetts, five time zones west of Greenwich:
143.1133 + * <blockquote><pre>
143.1134 + * new Date(96, 1, 14).getTimezoneOffset() returns 300</pre></blockquote>
143.1135 + * because on February 14, 1996, standard time (Eastern Standard Time)
143.1136 + * is in use, which is offset five hours from UTC; but:
143.1137 + * <blockquote><pre>
143.1138 + * new Date(96, 5, 1).getTimezoneOffset() returns 240</pre></blockquote>
143.1139 + * because on June 1, 1996, daylight saving time (Eastern Daylight Time)
143.1140 + * is in use, which is offset only four hours from UTC.<p>
143.1141 + * This method produces the same result as if it computed:
143.1142 + * <blockquote><pre>
143.1143 + * (this.getTime() - UTC(this.getYear(),
143.1144 + * this.getMonth(),
143.1145 + * this.getDate(),
143.1146 + * this.getHours(),
143.1147 + * this.getMinutes(),
143.1148 + * this.getSeconds())) / (60 * 1000)
143.1149 + * </pre></blockquote>
143.1150 + *
143.1151 + * @return the time-zone offset, in minutes, for the current time zone.
143.1152 + * @see java.util.Calendar#ZONE_OFFSET
143.1153 + * @see java.util.Calendar#DST_OFFSET
143.1154 + * @see java.util.TimeZone#getDefault
143.1155 + * @deprecated As of JDK version 1.1,
143.1156 + * replaced by <code>-(Calendar.get(Calendar.ZONE_OFFSET) +
143.1157 + * Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)</code>.
143.1158 + */
143.1159 + @Deprecated
143.1160 + public int getTimezoneOffset() {
143.1161 + int zoneOffset;
143.1162 + if (cdate == null) {
143.1163 + TimeZone tz = TimeZone.getDefaultRef();
143.1164 + zoneOffset = tz.getOffset(fastTime);
143.1165 + } else {
143.1166 + normalize();
143.1167 + zoneOffset = cdate.getZoneOffset();
143.1168 + }
143.1169 + return -zoneOffset/60000; // convert to minutes
143.1170 + }
143.1171 +
143.1172 + private final BaseCalendar.Datum getCalendarDate() {
143.1173 + if (cdate == null) {
143.1174 +// BaseCalendar cal = getCalendarSystem(fastTime);
143.1175 +// cdate = (BaseCalendar.Datum) cal.getCalendarDate(fastTime,
143.1176 +// TimeZone.getDefaultRef());
143.1177 + }
143.1178 + return cdate;
143.1179 + }
143.1180 +
143.1181 + private final BaseCalendar.Datum normalize() {
143.1182 + if (cdate == null) {
143.1183 +// BaseCalendar cal = getCalendarSystem(fastTime);
143.1184 +// cdate = (BaseCalendar.Datum) cal.getCalendarDate(fastTime,
143.1185 +// TimeZone.getDefaultRef());
143.1186 +// return cdate;
143.1187 + }
143.1188 +
143.1189 + // Normalize cdate with the TimeZone in cdate first. This is
143.1190 + // required for the compatible behavior.
143.1191 + if (!cdate.isNormalized()) {
143.1192 + cdate = normalize(cdate);
143.1193 + }
143.1194 +
143.1195 + // If the default TimeZone has changed, then recalculate the
143.1196 + // fields with the new TimeZone.
143.1197 + TimeZone tz = TimeZone.getDefaultRef();
143.1198 + if (tz != cdate.getZone()) {
143.1199 +// cdate.setZone(tz);
143.1200 +// CalendarSystem cal = getCalendarSystem(cdate);
143.1201 +// cal.getCalendarDate(fastTime, cdate);
143.1202 + }
143.1203 + return cdate;
143.1204 + }
143.1205 +
143.1206 + // fastTime and the returned data are in sync upon return.
143.1207 + private final BaseCalendar.Datum normalize(BaseCalendar.Datum date) {
143.1208 + int y = date.getNormalizedYear();
143.1209 + int m = date.getMonth();
143.1210 + int d = date.getDayOfMonth();
143.1211 + int hh = date.getHours();
143.1212 + int mm = date.getMinutes();
143.1213 + int ss = date.getSeconds();
143.1214 + int ms = date.getMillis();
143.1215 + TimeZone tz = date.getZone();
143.1216 +
143.1217 + // If the specified year can't be handled using a long value
143.1218 + // in milliseconds, GregorianCalendar is used for full
143.1219 + // compatibility with underflow and overflow. This is required
143.1220 + // by some JCK tests. The limits are based max year values -
143.1221 + // years that can be represented by max values of d, hh, mm,
143.1222 + // ss and ms. Also, let GregorianCalendar handle the default
143.1223 + // cutover year so that we don't need to worry about the
143.1224 + // transition here.
143.1225 +// if (y == 1582 || y > 280000000 || y < -280000000) {
143.1226 +// if (tz == null) {
143.1227 +// tz = TimeZone.getTimeZone("GMT");
143.1228 +// }
143.1229 +// GregorianCalendar gc = new GregorianCalendar(tz);
143.1230 +// gc.clear();
143.1231 +// gc.set(gc.MILLISECOND, ms);
143.1232 +// gc.set(y, m-1, d, hh, mm, ss);
143.1233 +// fastTime = gc.getTimeInMillis();
143.1234 +// BaseCalendar cal = getCalendarSystem(fastTime);
143.1235 +// date = (BaseCalendar.Datum) cal.getCalendarDate(fastTime, tz);
143.1236 +// return date;
143.1237 +// }
143.1238 +
143.1239 + BaseCalendar cal = getCalendarSystem(y);
143.1240 + if (cal != getCalendarSystem(date)) {
143.1241 + date = (BaseCalendar.Datum) cal.newCalendarDate(tz);
143.1242 + date.setNormalizedDate(y, m, d).setTimeOfDay(hh, mm, ss, ms);
143.1243 + }
143.1244 + // Perform the GregorianCalendar-style normalization.
143.1245 + fastTime = cal.getTime(date);
143.1246 +
143.1247 + // In case the normalized date requires the other calendar
143.1248 + // system, we need to recalculate it using the other one.
143.1249 + BaseCalendar ncal = getCalendarSystem(fastTime);
143.1250 + if (ncal != cal) {
143.1251 + date = (BaseCalendar.Datum) ncal.newCalendarDate(tz);
143.1252 + date.setNormalizedDate(y, m, d).setTimeOfDay(hh, mm, ss, ms);
143.1253 + fastTime = ncal.getTime(date);
143.1254 + }
143.1255 + return date;
143.1256 + }
143.1257 +
143.1258 + /**
143.1259 + * Returns the Gregorian or Julian calendar system to use with the
143.1260 + * given date. Use Gregorian from October 15, 1582.
143.1261 + *
143.1262 + * @param year normalized calendar year (not -1900)
143.1263 + * @return the CalendarSystem to use for the specified date
143.1264 + */
143.1265 + private static final BaseCalendar getCalendarSystem(int year) {
143.1266 + if (year >= 1582) {
143.1267 + return gcal;
143.1268 + }
143.1269 + return getJulianCalendar();
143.1270 + }
143.1271 +
143.1272 + private static final BaseCalendar getCalendarSystem(long utc) {
143.1273 + // Quickly check if the time stamp given by `utc' is the Epoch
143.1274 + // or later. If it's before 1970, we convert the cutover to
143.1275 + // local time to compare.
143.1276 +// if (utc >= 0
143.1277 +// || utc >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER
143.1278 +// - TimeZone.getDefaultRef().getOffset(utc)) {
143.1279 + return gcal;
143.1280 +// }
143.1281 +// return getJulianCalendar();
143.1282 + }
143.1283 +
143.1284 + private static final BaseCalendar getCalendarSystem(BaseCalendar.Datum cdate) {
143.1285 + if (jcal == null) {
143.1286 + return gcal;
143.1287 + }
143.1288 + if (cdate.getEra() != null) {
143.1289 + return jcal;
143.1290 + }
143.1291 + return gcal;
143.1292 + }
143.1293 +
143.1294 + synchronized private static final BaseCalendar getJulianCalendar() {
143.1295 + if (jcal == null) {
143.1296 +// jcal = (BaseCalendar) CalendarSystem.forName("julian");
143.1297 + }
143.1298 + return jcal;
143.1299 + }
143.1300 +
143.1301 + /**
143.1302 + * Save the state of this object to a stream (i.e., serialize it).
143.1303 + *
143.1304 + * @serialData The value returned by <code>getTime()</code>
143.1305 + * is emitted (long). This represents the offset from
143.1306 + * January 1, 1970, 00:00:00 GMT in milliseconds.
143.1307 + */
143.1308 + private void writeObject(ObjectOutputStream s)
143.1309 + throws IOException
143.1310 + {
143.1311 + s.writeLong(getTimeImpl());
143.1312 + }
143.1313 +
143.1314 + /**
143.1315 + * Reconstitute this object from a stream (i.e., deserialize it).
143.1316 + */
143.1317 + private void readObject(ObjectInputStream s)
143.1318 + throws IOException, ClassNotFoundException
143.1319 + {
143.1320 + fastTime = s.readLong();
143.1321 + }
143.1322 +
143.1323 + static final class BaseCalendar {
143.1324 + Datum newCalendarDate(TimeZone t) {
143.1325 + return new Datum();
143.1326 + }
143.1327 +
143.1328 + Datum getNthDayOfWeek(int a, int b, Datum c) {
143.1329 + return new Datum();
143.1330 + }
143.1331 +
143.1332 + Datum getCalendarDate() {
143.1333 + return new Datum();
143.1334 + }
143.1335 +
143.1336 + int getTime(Datum udate) {
143.1337 + return 0;
143.1338 + }
143.1339 +
143.1340 + int getMonthLength(Datum cdate) {
143.1341 + return 0;
143.1342 + }
143.1343 +
143.1344 + void getCalendarDate(long l, Datum cdate) {
143.1345 + }
143.1346 +
143.1347 + static class Datum implements Cloneable {
143.1348 + public Datum clone() {
143.1349 + return new Datum();
143.1350 + }
143.1351 +
143.1352 + Datum setNormalizedDate(int y, int i, int date) {
143.1353 + return this;
143.1354 + }
143.1355 +
143.1356 + void setTimeOfDay(int hrs, int min, int sec, int i) {
143.1357 + }
143.1358 +
143.1359 + int getYear() {
143.1360 + return 0;
143.1361 + }
143.1362 +
143.1363 + void setDate(int year, int i, int mday) {
143.1364 + }
143.1365 +
143.1366 + void setNormalizedYear(int i) {
143.1367 + }
143.1368 +
143.1369 + int getMonth() {
143.1370 + return 0;
143.1371 + }
143.1372 +
143.1373 + int getNormalizedYear() {
143.1374 + return 0;
143.1375 + }
143.1376 +
143.1377 + void setMonth(int i) {
143.1378 + }
143.1379 +
143.1380 + int getDayOfMonth() {
143.1381 + return 0;
143.1382 + }
143.1383 +
143.1384 + void setDayOfMonth(int date) {
143.1385 + }
143.1386 +
143.1387 + int getDayOfWeek() {
143.1388 + return 0;
143.1389 + }
143.1390 +
143.1391 + int getHours() {
143.1392 + return 0;
143.1393 + }
143.1394 +
143.1395 + void setHours(int hours) {
143.1396 + }
143.1397 +
143.1398 + int getMinutes() {
143.1399 + return 0;
143.1400 + }
143.1401 +
143.1402 + void setMinutes(int minutes) {
143.1403 + }
143.1404 +
143.1405 + int getSeconds() {
143.1406 + return 0;
143.1407 + }
143.1408 +
143.1409 + void setSeconds(int seconds) {
143.1410 + }
143.1411 +
143.1412 + boolean isNormalized() {
143.1413 + return false;
143.1414 + }
143.1415 +
143.1416 + Object getEra() {
143.1417 + return this;
143.1418 + }
143.1419 +
143.1420 + int getMillis() {
143.1421 + return 0;
143.1422 + }
143.1423 +
143.1424 + TimeZone getZone() {
143.1425 + return TimeZone.NO_TIMEZONE;
143.1426 + }
143.1427 +
143.1428 + int getZoneOffset() {
143.1429 + return 0;
143.1430 + }
143.1431 + }
143.1432 + }
143.1433 +}
144.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
144.2 +++ b/rt/emul/compact/src/main/java/java/util/EnumMap.java Tue Feb 11 13:31:42 2014 +0100
144.3 @@ -0,0 +1,797 @@
144.4 +/*
144.5 + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
144.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
144.7 + *
144.8 + * This code is free software; you can redistribute it and/or modify it
144.9 + * under the terms of the GNU General Public License version 2 only, as
144.10 + * published by the Free Software Foundation. Oracle designates this
144.11 + * particular file as subject to the "Classpath" exception as provided
144.12 + * by Oracle in the LICENSE file that accompanied this code.
144.13 + *
144.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
144.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
144.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
144.17 + * version 2 for more details (a copy is included in the LICENSE file that
144.18 + * accompanied this code).
144.19 + *
144.20 + * You should have received a copy of the GNU General Public License version
144.21 + * 2 along with this work; if not, write to the Free Software Foundation,
144.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
144.23 + *
144.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
144.25 + * or visit www.oracle.com if you need additional information or have any
144.26 + * questions.
144.27 + */
144.28 +
144.29 +package java.util;
144.30 +
144.31 +import java.util.Map.Entry;
144.32 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
144.33 +
144.34 +/**
144.35 + * A specialized {@link Map} implementation for use with enum type keys. All
144.36 + * of the keys in an enum map must come from a single enum type that is
144.37 + * specified, explicitly or implicitly, when the map is created. Enum maps
144.38 + * are represented internally as arrays. This representation is extremely
144.39 + * compact and efficient.
144.40 + *
144.41 + * <p>Enum maps are maintained in the <i>natural order</i> of their keys
144.42 + * (the order in which the enum constants are declared). This is reflected
144.43 + * in the iterators returned by the collections views ({@link #keySet()},
144.44 + * {@link #entrySet()}, and {@link #values()}).
144.45 + *
144.46 + * <p>Iterators returned by the collection views are <i>weakly consistent</i>:
144.47 + * they will never throw {@link ConcurrentModificationException} and they may
144.48 + * or may not show the effects of any modifications to the map that occur while
144.49 + * the iteration is in progress.
144.50 + *
144.51 + * <p>Null keys are not permitted. Attempts to insert a null key will
144.52 + * throw {@link NullPointerException}. Attempts to test for the
144.53 + * presence of a null key or to remove one will, however, function properly.
144.54 + * Null values are permitted.
144.55 +
144.56 + * <P>Like most collection implementations <tt>EnumMap</tt> is not
144.57 + * synchronized. If multiple threads access an enum map concurrently, and at
144.58 + * least one of the threads modifies the map, it should be synchronized
144.59 + * externally. This is typically accomplished by synchronizing on some
144.60 + * object that naturally encapsulates the enum map. If no such object exists,
144.61 + * the map should be "wrapped" using the {@link Collections#synchronizedMap}
144.62 + * method. This is best done at creation time, to prevent accidental
144.63 + * unsynchronized access:
144.64 + *
144.65 + * <pre>
144.66 + * Map<EnumKey, V> m
144.67 + * = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...));
144.68 + * </pre>
144.69 + *
144.70 + * <p>Implementation note: All basic operations execute in constant time.
144.71 + * They are likely (though not guaranteed) to be faster than their
144.72 + * {@link HashMap} counterparts.
144.73 + *
144.74 + * <p>This class is a member of the
144.75 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
144.76 + * Java Collections Framework</a>.
144.77 + *
144.78 + * @author Josh Bloch
144.79 + * @see EnumSet
144.80 + * @since 1.5
144.81 + */
144.82 +public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
144.83 + implements java.io.Serializable, Cloneable
144.84 +{
144.85 + /**
144.86 + * The <tt>Class</tt> object for the enum type of all the keys of this map.
144.87 + *
144.88 + * @serial
144.89 + */
144.90 + private final Class<K> keyType;
144.91 +
144.92 + /**
144.93 + * All of the values comprising K. (Cached for performance.)
144.94 + */
144.95 + private transient K[] keyUniverse;
144.96 +
144.97 + /**
144.98 + * Array representation of this map. The ith element is the value
144.99 + * to which universe[i] is currently mapped, or null if it isn't
144.100 + * mapped to anything, or NULL if it's mapped to null.
144.101 + */
144.102 + private transient Object[] vals;
144.103 +
144.104 + /**
144.105 + * The number of mappings in this map.
144.106 + */
144.107 + private transient int size = 0;
144.108 +
144.109 + /**
144.110 + * Distinguished non-null value for representing null values.
144.111 + */
144.112 + private static final Object NULL = new Integer(0);
144.113 +
144.114 + private Object maskNull(Object value) {
144.115 + return (value == null ? NULL : value);
144.116 + }
144.117 +
144.118 + private V unmaskNull(Object value) {
144.119 + return (V) (value == NULL ? null : value);
144.120 + }
144.121 +
144.122 + private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0];
144.123 +
144.124 + /**
144.125 + * Creates an empty enum map with the specified key type.
144.126 + *
144.127 + * @param keyType the class object of the key type for this enum map
144.128 + * @throws NullPointerException if <tt>keyType</tt> is null
144.129 + */
144.130 + public EnumMap(Class<K> keyType) {
144.131 + this.keyType = keyType;
144.132 + keyUniverse = getKeyUniverse(keyType);
144.133 + vals = new Object[keyUniverse.length];
144.134 + }
144.135 +
144.136 + /**
144.137 + * Creates an enum map with the same key type as the specified enum
144.138 + * map, initially containing the same mappings (if any).
144.139 + *
144.140 + * @param m the enum map from which to initialize this enum map
144.141 + * @throws NullPointerException if <tt>m</tt> is null
144.142 + */
144.143 + public EnumMap(EnumMap<K, ? extends V> m) {
144.144 + keyType = m.keyType;
144.145 + keyUniverse = m.keyUniverse;
144.146 + vals = m.vals.clone();
144.147 + size = m.size;
144.148 + }
144.149 +
144.150 + /**
144.151 + * Creates an enum map initialized from the specified map. If the
144.152 + * specified map is an <tt>EnumMap</tt> instance, this constructor behaves
144.153 + * identically to {@link #EnumMap(EnumMap)}. Otherwise, the specified map
144.154 + * must contain at least one mapping (in order to determine the new
144.155 + * enum map's key type).
144.156 + *
144.157 + * @param m the map from which to initialize this enum map
144.158 + * @throws IllegalArgumentException if <tt>m</tt> is not an
144.159 + * <tt>EnumMap</tt> instance and contains no mappings
144.160 + * @throws NullPointerException if <tt>m</tt> is null
144.161 + */
144.162 + public EnumMap(Map<K, ? extends V> m) {
144.163 + if (m instanceof EnumMap) {
144.164 + EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
144.165 + keyType = em.keyType;
144.166 + keyUniverse = em.keyUniverse;
144.167 + vals = em.vals.clone();
144.168 + size = em.size;
144.169 + } else {
144.170 + if (m.isEmpty())
144.171 + throw new IllegalArgumentException("Specified map is empty");
144.172 + keyType = m.keySet().iterator().next().getDeclaringClass();
144.173 + keyUniverse = getKeyUniverse(keyType);
144.174 + vals = new Object[keyUniverse.length];
144.175 + putAll(m);
144.176 + }
144.177 + }
144.178 +
144.179 + // Query Operations
144.180 +
144.181 + /**
144.182 + * Returns the number of key-value mappings in this map.
144.183 + *
144.184 + * @return the number of key-value mappings in this map
144.185 + */
144.186 + public int size() {
144.187 + return size;
144.188 + }
144.189 +
144.190 + /**
144.191 + * Returns <tt>true</tt> if this map maps one or more keys to the
144.192 + * specified value.
144.193 + *
144.194 + * @param value the value whose presence in this map is to be tested
144.195 + * @return <tt>true</tt> if this map maps one or more keys to this value
144.196 + */
144.197 + public boolean containsValue(Object value) {
144.198 + value = maskNull(value);
144.199 +
144.200 + for (Object val : vals)
144.201 + if (value.equals(val))
144.202 + return true;
144.203 +
144.204 + return false;
144.205 + }
144.206 +
144.207 + /**
144.208 + * Returns <tt>true</tt> if this map contains a mapping for the specified
144.209 + * key.
144.210 + *
144.211 + * @param key the key whose presence in this map is to be tested
144.212 + * @return <tt>true</tt> if this map contains a mapping for the specified
144.213 + * key
144.214 + */
144.215 + public boolean containsKey(Object key) {
144.216 + return isValidKey(key) && vals[((Enum)key).ordinal()] != null;
144.217 + }
144.218 +
144.219 + private boolean containsMapping(Object key, Object value) {
144.220 + return isValidKey(key) &&
144.221 + maskNull(value).equals(vals[((Enum)key).ordinal()]);
144.222 + }
144.223 +
144.224 + /**
144.225 + * Returns the value to which the specified key is mapped,
144.226 + * or {@code null} if this map contains no mapping for the key.
144.227 + *
144.228 + * <p>More formally, if this map contains a mapping from a key
144.229 + * {@code k} to a value {@code v} such that {@code (key == k)},
144.230 + * then this method returns {@code v}; otherwise it returns
144.231 + * {@code null}. (There can be at most one such mapping.)
144.232 + *
144.233 + * <p>A return value of {@code null} does not <i>necessarily</i>
144.234 + * indicate that the map contains no mapping for the key; it's also
144.235 + * possible that the map explicitly maps the key to {@code null}.
144.236 + * The {@link #containsKey containsKey} operation may be used to
144.237 + * distinguish these two cases.
144.238 + */
144.239 + public V get(Object key) {
144.240 + return (isValidKey(key) ?
144.241 + unmaskNull(vals[((Enum)key).ordinal()]) : null);
144.242 + }
144.243 +
144.244 + // Modification Operations
144.245 +
144.246 + /**
144.247 + * Associates the specified value with the specified key in this map.
144.248 + * If the map previously contained a mapping for this key, the old
144.249 + * value is replaced.
144.250 + *
144.251 + * @param key the key with which the specified value is to be associated
144.252 + * @param value the value to be associated with the specified key
144.253 + *
144.254 + * @return the previous value associated with specified key, or
144.255 + * <tt>null</tt> if there was no mapping for key. (A <tt>null</tt>
144.256 + * return can also indicate that the map previously associated
144.257 + * <tt>null</tt> with the specified key.)
144.258 + * @throws NullPointerException if the specified key is null
144.259 + */
144.260 + public V put(K key, V value) {
144.261 + typeCheck(key);
144.262 +
144.263 + int index = key.ordinal();
144.264 + Object oldValue = vals[index];
144.265 + vals[index] = maskNull(value);
144.266 + if (oldValue == null)
144.267 + size++;
144.268 + return unmaskNull(oldValue);
144.269 + }
144.270 +
144.271 + /**
144.272 + * Removes the mapping for this key from this map if present.
144.273 + *
144.274 + * @param key the key whose mapping is to be removed from the map
144.275 + * @return the previous value associated with specified key, or
144.276 + * <tt>null</tt> if there was no entry for key. (A <tt>null</tt>
144.277 + * return can also indicate that the map previously associated
144.278 + * <tt>null</tt> with the specified key.)
144.279 + */
144.280 + public V remove(Object key) {
144.281 + if (!isValidKey(key))
144.282 + return null;
144.283 + int index = ((Enum)key).ordinal();
144.284 + Object oldValue = vals[index];
144.285 + vals[index] = null;
144.286 + if (oldValue != null)
144.287 + size--;
144.288 + return unmaskNull(oldValue);
144.289 + }
144.290 +
144.291 + private boolean removeMapping(Object key, Object value) {
144.292 + if (!isValidKey(key))
144.293 + return false;
144.294 + int index = ((Enum)key).ordinal();
144.295 + if (maskNull(value).equals(vals[index])) {
144.296 + vals[index] = null;
144.297 + size--;
144.298 + return true;
144.299 + }
144.300 + return false;
144.301 + }
144.302 +
144.303 + /**
144.304 + * Returns true if key is of the proper type to be a key in this
144.305 + * enum map.
144.306 + */
144.307 + private boolean isValidKey(Object key) {
144.308 + if (key == null)
144.309 + return false;
144.310 +
144.311 + // Cheaper than instanceof Enum followed by getDeclaringClass
144.312 + Class keyClass = key.getClass();
144.313 + return keyClass == keyType || keyClass.getSuperclass() == keyType;
144.314 + }
144.315 +
144.316 + // Bulk Operations
144.317 +
144.318 + /**
144.319 + * Copies all of the mappings from the specified map to this map.
144.320 + * These mappings will replace any mappings that this map had for
144.321 + * any of the keys currently in the specified map.
144.322 + *
144.323 + * @param m the mappings to be stored in this map
144.324 + * @throws NullPointerException the specified map is null, or if
144.325 + * one or more keys in the specified map are null
144.326 + */
144.327 + public void putAll(Map<? extends K, ? extends V> m) {
144.328 + if (m instanceof EnumMap) {
144.329 + EnumMap<? extends K, ? extends V> em =
144.330 + (EnumMap<? extends K, ? extends V>)m;
144.331 + if (em.keyType != keyType) {
144.332 + if (em.isEmpty())
144.333 + return;
144.334 + throw new ClassCastException(em.keyType + " != " + keyType);
144.335 + }
144.336 +
144.337 + for (int i = 0; i < keyUniverse.length; i++) {
144.338 + Object emValue = em.vals[i];
144.339 + if (emValue != null) {
144.340 + if (vals[i] == null)
144.341 + size++;
144.342 + vals[i] = emValue;
144.343 + }
144.344 + }
144.345 + } else {
144.346 + super.putAll(m);
144.347 + }
144.348 + }
144.349 +
144.350 + /**
144.351 + * Removes all mappings from this map.
144.352 + */
144.353 + public void clear() {
144.354 + Arrays.fill(vals, null);
144.355 + size = 0;
144.356 + }
144.357 +
144.358 + // Views
144.359 +
144.360 + /**
144.361 + * This field is initialized to contain an instance of the entry set
144.362 + * view the first time this view is requested. The view is stateless,
144.363 + * so there's no reason to create more than one.
144.364 + */
144.365 + private transient Set<Map.Entry<K,V>> entrySet = null;
144.366 +
144.367 + /**
144.368 + * Returns a {@link Set} view of the keys contained in this map.
144.369 + * The returned set obeys the general contract outlined in
144.370 + * {@link Map#keySet()}. The set's iterator will return the keys
144.371 + * in their natural order (the order in which the enum constants
144.372 + * are declared).
144.373 + *
144.374 + * @return a set view of the keys contained in this enum map
144.375 + */
144.376 + public Set<K> keySet() {
144.377 + Set<K> ks = keySet;
144.378 + if (ks != null)
144.379 + return ks;
144.380 + else
144.381 + return keySet = new KeySet();
144.382 + }
144.383 +
144.384 + private class KeySet extends AbstractSet<K> {
144.385 + public Iterator<K> iterator() {
144.386 + return new KeyIterator();
144.387 + }
144.388 + public int size() {
144.389 + return size;
144.390 + }
144.391 + public boolean contains(Object o) {
144.392 + return containsKey(o);
144.393 + }
144.394 + public boolean remove(Object o) {
144.395 + int oldSize = size;
144.396 + EnumMap.this.remove(o);
144.397 + return size != oldSize;
144.398 + }
144.399 + public void clear() {
144.400 + EnumMap.this.clear();
144.401 + }
144.402 + }
144.403 +
144.404 + /**
144.405 + * Returns a {@link Collection} view of the values contained in this map.
144.406 + * The returned collection obeys the general contract outlined in
144.407 + * {@link Map#values()}. The collection's iterator will return the
144.408 + * values in the order their corresponding keys appear in map,
144.409 + * which is their natural order (the order in which the enum constants
144.410 + * are declared).
144.411 + *
144.412 + * @return a collection view of the values contained in this map
144.413 + */
144.414 + public Collection<V> values() {
144.415 + Collection<V> vs = values;
144.416 + if (vs != null)
144.417 + return vs;
144.418 + else
144.419 + return values = new Values();
144.420 + }
144.421 +
144.422 + private class Values extends AbstractCollection<V> {
144.423 + public Iterator<V> iterator() {
144.424 + return new ValueIterator();
144.425 + }
144.426 + public int size() {
144.427 + return size;
144.428 + }
144.429 + public boolean contains(Object o) {
144.430 + return containsValue(o);
144.431 + }
144.432 + public boolean remove(Object o) {
144.433 + o = maskNull(o);
144.434 +
144.435 + for (int i = 0; i < vals.length; i++) {
144.436 + if (o.equals(vals[i])) {
144.437 + vals[i] = null;
144.438 + size--;
144.439 + return true;
144.440 + }
144.441 + }
144.442 + return false;
144.443 + }
144.444 + public void clear() {
144.445 + EnumMap.this.clear();
144.446 + }
144.447 + }
144.448 +
144.449 + /**
144.450 + * Returns a {@link Set} view of the mappings contained in this map.
144.451 + * The returned set obeys the general contract outlined in
144.452 + * {@link Map#keySet()}. The set's iterator will return the
144.453 + * mappings in the order their keys appear in map, which is their
144.454 + * natural order (the order in which the enum constants are declared).
144.455 + *
144.456 + * @return a set view of the mappings contained in this enum map
144.457 + */
144.458 + public Set<Map.Entry<K,V>> entrySet() {
144.459 + Set<Map.Entry<K,V>> es = entrySet;
144.460 + if (es != null)
144.461 + return es;
144.462 + else
144.463 + return entrySet = new EntrySet();
144.464 + }
144.465 +
144.466 + private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
144.467 + public Iterator<Map.Entry<K,V>> iterator() {
144.468 + return new EntryIterator();
144.469 + }
144.470 +
144.471 + public boolean contains(Object o) {
144.472 + if (!(o instanceof Map.Entry))
144.473 + return false;
144.474 + Map.Entry entry = (Map.Entry)o;
144.475 + return containsMapping(entry.getKey(), entry.getValue());
144.476 + }
144.477 + public boolean remove(Object o) {
144.478 + if (!(o instanceof Map.Entry))
144.479 + return false;
144.480 + Map.Entry entry = (Map.Entry)o;
144.481 + return removeMapping(entry.getKey(), entry.getValue());
144.482 + }
144.483 + public int size() {
144.484 + return size;
144.485 + }
144.486 + public void clear() {
144.487 + EnumMap.this.clear();
144.488 + }
144.489 + public Object[] toArray() {
144.490 + return fillEntryArray(new Object[size]);
144.491 + }
144.492 + @SuppressWarnings("unchecked")
144.493 + public <T> T[] toArray(T[] a) {
144.494 + int size = size();
144.495 + if (a.length < size)
144.496 + a = (T[])java.lang.reflect.Array
144.497 + .newInstance(a.getClass().getComponentType(), size);
144.498 + if (a.length > size)
144.499 + a[size] = null;
144.500 + return (T[]) fillEntryArray(a);
144.501 + }
144.502 + private Object[] fillEntryArray(Object[] a) {
144.503 + int j = 0;
144.504 + for (int i = 0; i < vals.length; i++)
144.505 + if (vals[i] != null)
144.506 + a[j++] = new AbstractMap.SimpleEntry<>(
144.507 + keyUniverse[i], unmaskNull(vals[i]));
144.508 + return a;
144.509 + }
144.510 + }
144.511 +
144.512 + private abstract class EnumMapIterator<T> implements Iterator<T> {
144.513 + // Lower bound on index of next element to return
144.514 + int index = 0;
144.515 +
144.516 + // Index of last returned element, or -1 if none
144.517 + int lastReturnedIndex = -1;
144.518 +
144.519 + public boolean hasNext() {
144.520 + while (index < vals.length && vals[index] == null)
144.521 + index++;
144.522 + return index != vals.length;
144.523 + }
144.524 +
144.525 + public void remove() {
144.526 + checkLastReturnedIndex();
144.527 +
144.528 + if (vals[lastReturnedIndex] != null) {
144.529 + vals[lastReturnedIndex] = null;
144.530 + size--;
144.531 + }
144.532 + lastReturnedIndex = -1;
144.533 + }
144.534 +
144.535 + private void checkLastReturnedIndex() {
144.536 + if (lastReturnedIndex < 0)
144.537 + throw new IllegalStateException();
144.538 + }
144.539 + }
144.540 +
144.541 + private class KeyIterator extends EnumMapIterator<K> {
144.542 + public K next() {
144.543 + if (!hasNext())
144.544 + throw new NoSuchElementException();
144.545 + lastReturnedIndex = index++;
144.546 + return keyUniverse[lastReturnedIndex];
144.547 + }
144.548 + }
144.549 +
144.550 + private class ValueIterator extends EnumMapIterator<V> {
144.551 + public V next() {
144.552 + if (!hasNext())
144.553 + throw new NoSuchElementException();
144.554 + lastReturnedIndex = index++;
144.555 + return unmaskNull(vals[lastReturnedIndex]);
144.556 + }
144.557 + }
144.558 +
144.559 + private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
144.560 + private Entry lastReturnedEntry = null;
144.561 +
144.562 + public Map.Entry<K,V> next() {
144.563 + if (!hasNext())
144.564 + throw new NoSuchElementException();
144.565 + lastReturnedEntry = new Entry(index++);
144.566 + return lastReturnedEntry;
144.567 + }
144.568 +
144.569 + public void remove() {
144.570 + lastReturnedIndex =
144.571 + ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
144.572 + super.remove();
144.573 + lastReturnedEntry.index = lastReturnedIndex;
144.574 + lastReturnedEntry = null;
144.575 + }
144.576 +
144.577 + private class Entry implements Map.Entry<K,V> {
144.578 + private int index;
144.579 +
144.580 + private Entry(int index) {
144.581 + this.index = index;
144.582 + }
144.583 +
144.584 + public K getKey() {
144.585 + checkIndexForEntryUse();
144.586 + return keyUniverse[index];
144.587 + }
144.588 +
144.589 + public V getValue() {
144.590 + checkIndexForEntryUse();
144.591 + return unmaskNull(vals[index]);
144.592 + }
144.593 +
144.594 + public V setValue(V value) {
144.595 + checkIndexForEntryUse();
144.596 + V oldValue = unmaskNull(vals[index]);
144.597 + vals[index] = maskNull(value);
144.598 + return oldValue;
144.599 + }
144.600 +
144.601 + public boolean equals(Object o) {
144.602 + if (index < 0)
144.603 + return o == this;
144.604 +
144.605 + if (!(o instanceof Map.Entry))
144.606 + return false;
144.607 +
144.608 + Map.Entry e = (Map.Entry)o;
144.609 + V ourValue = unmaskNull(vals[index]);
144.610 + Object hisValue = e.getValue();
144.611 + return (e.getKey() == keyUniverse[index] &&
144.612 + (ourValue == hisValue ||
144.613 + (ourValue != null && ourValue.equals(hisValue))));
144.614 + }
144.615 +
144.616 + public int hashCode() {
144.617 + if (index < 0)
144.618 + return super.hashCode();
144.619 +
144.620 + return entryHashCode(index);
144.621 + }
144.622 +
144.623 + public String toString() {
144.624 + if (index < 0)
144.625 + return super.toString();
144.626 +
144.627 + return keyUniverse[index] + "="
144.628 + + unmaskNull(vals[index]);
144.629 + }
144.630 +
144.631 + private void checkIndexForEntryUse() {
144.632 + if (index < 0)
144.633 + throw new IllegalStateException("Entry was removed");
144.634 + }
144.635 + }
144.636 + }
144.637 +
144.638 + // Comparison and hashing
144.639 +
144.640 + /**
144.641 + * Compares the specified object with this map for equality. Returns
144.642 + * <tt>true</tt> if the given object is also a map and the two maps
144.643 + * represent the same mappings, as specified in the {@link
144.644 + * Map#equals(Object)} contract.
144.645 + *
144.646 + * @param o the object to be compared for equality with this map
144.647 + * @return <tt>true</tt> if the specified object is equal to this map
144.648 + */
144.649 + public boolean equals(Object o) {
144.650 + if (this == o)
144.651 + return true;
144.652 + if (o instanceof EnumMap)
144.653 + return equals((EnumMap)o);
144.654 + if (!(o instanceof Map))
144.655 + return false;
144.656 +
144.657 + Map<K,V> m = (Map<K,V>)o;
144.658 + if (size != m.size())
144.659 + return false;
144.660 +
144.661 + for (int i = 0; i < keyUniverse.length; i++) {
144.662 + if (null != vals[i]) {
144.663 + K key = keyUniverse[i];
144.664 + V value = unmaskNull(vals[i]);
144.665 + if (null == value) {
144.666 + if (!((null == m.get(key)) && m.containsKey(key)))
144.667 + return false;
144.668 + } else {
144.669 + if (!value.equals(m.get(key)))
144.670 + return false;
144.671 + }
144.672 + }
144.673 + }
144.674 +
144.675 + return true;
144.676 + }
144.677 +
144.678 + private boolean equals(EnumMap em) {
144.679 + if (em.keyType != keyType)
144.680 + return size == 0 && em.size == 0;
144.681 +
144.682 + // Key types match, compare each value
144.683 + for (int i = 0; i < keyUniverse.length; i++) {
144.684 + Object ourValue = vals[i];
144.685 + Object hisValue = em.vals[i];
144.686 + if (hisValue != ourValue &&
144.687 + (hisValue == null || !hisValue.equals(ourValue)))
144.688 + return false;
144.689 + }
144.690 + return true;
144.691 + }
144.692 +
144.693 + /**
144.694 + * Returns the hash code value for this map. The hash code of a map is
144.695 + * defined to be the sum of the hash codes of each entry in the map.
144.696 + */
144.697 + public int hashCode() {
144.698 + int h = 0;
144.699 +
144.700 + for (int i = 0; i < keyUniverse.length; i++) {
144.701 + if (null != vals[i]) {
144.702 + h += entryHashCode(i);
144.703 + }
144.704 + }
144.705 +
144.706 + return h;
144.707 + }
144.708 +
144.709 + private int entryHashCode(int index) {
144.710 + return (keyUniverse[index].hashCode() ^ vals[index].hashCode());
144.711 + }
144.712 +
144.713 + /**
144.714 + * Returns a shallow copy of this enum map. (The values themselves
144.715 + * are not cloned.
144.716 + *
144.717 + * @return a shallow copy of this enum map
144.718 + */
144.719 + public EnumMap<K, V> clone() {
144.720 + EnumMap<K, V> result = null;
144.721 + try {
144.722 + result = (EnumMap<K, V>) super.clone();
144.723 + } catch(CloneNotSupportedException e) {
144.724 + throw new AssertionError();
144.725 + }
144.726 + result.vals = result.vals.clone();
144.727 + return result;
144.728 + }
144.729 +
144.730 + /**
144.731 + * Throws an exception if e is not of the correct type for this enum set.
144.732 + */
144.733 + private void typeCheck(K key) {
144.734 + Class keyClass = key.getClass();
144.735 + if (keyClass != keyType && keyClass.getSuperclass() != keyType)
144.736 + throw new ClassCastException(keyClass + " != " + keyType);
144.737 + }
144.738 +
144.739 + /**
144.740 + * Returns all of the values comprising K.
144.741 + * The result is uncloned, cached, and shared by all callers.
144.742 + */
144.743 + @JavaScriptBody(args = { "enumType" }, body = "return enumType.cnstr.fld_$VALUES;")
144.744 + private static native <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType);
144.745 +
144.746 + private static final long serialVersionUID = 458661240069192865L;
144.747 +
144.748 + /**
144.749 + * Save the state of the <tt>EnumMap</tt> instance to a stream (i.e.,
144.750 + * serialize it).
144.751 + *
144.752 + * @serialData The <i>size</i> of the enum map (the number of key-value
144.753 + * mappings) is emitted (int), followed by the key (Object)
144.754 + * and value (Object) for each key-value mapping represented
144.755 + * by the enum map.
144.756 + */
144.757 + private void writeObject(java.io.ObjectOutputStream s)
144.758 + throws java.io.IOException
144.759 + {
144.760 + // Write out the key type and any hidden stuff
144.761 + s.defaultWriteObject();
144.762 +
144.763 + // Write out size (number of Mappings)
144.764 + s.writeInt(size);
144.765 +
144.766 + // Write out keys and values (alternating)
144.767 + int entriesToBeWritten = size;
144.768 + for (int i = 0; entriesToBeWritten > 0; i++) {
144.769 + if (null != vals[i]) {
144.770 + s.writeObject(keyUniverse[i]);
144.771 + s.writeObject(unmaskNull(vals[i]));
144.772 + entriesToBeWritten--;
144.773 + }
144.774 + }
144.775 + }
144.776 +
144.777 + /**
144.778 + * Reconstitute the <tt>EnumMap</tt> instance from a stream (i.e.,
144.779 + * deserialize it).
144.780 + */
144.781 + private void readObject(java.io.ObjectInputStream s)
144.782 + throws java.io.IOException, ClassNotFoundException
144.783 + {
144.784 + // Read in the key type and any hidden stuff
144.785 + s.defaultReadObject();
144.786 +
144.787 + keyUniverse = getKeyUniverse(keyType);
144.788 + vals = new Object[keyUniverse.length];
144.789 +
144.790 + // Read in size (number of Mappings)
144.791 + int size = s.readInt();
144.792 +
144.793 + // Read the keys and values, and put the mappings in the HashMap
144.794 + for (int i = 0; i < size; i++) {
144.795 + K key = (K) s.readObject();
144.796 + V value = (V) s.readObject();
144.797 + put(key, value);
144.798 + }
144.799 + }
144.800 +}
145.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
145.2 +++ b/rt/emul/compact/src/main/java/java/util/EnumSet.java Tue Feb 11 13:31:42 2014 +0100
145.3 @@ -0,0 +1,441 @@
145.4 +/*
145.5 + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
145.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
145.7 + *
145.8 + * This code is free software; you can redistribute it and/or modify it
145.9 + * under the terms of the GNU General Public License version 2 only, as
145.10 + * published by the Free Software Foundation. Oracle designates this
145.11 + * particular file as subject to the "Classpath" exception as provided
145.12 + * by Oracle in the LICENSE file that accompanied this code.
145.13 + *
145.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
145.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
145.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
145.17 + * version 2 for more details (a copy is included in the LICENSE file that
145.18 + * accompanied this code).
145.19 + *
145.20 + * You should have received a copy of the GNU General Public License version
145.21 + * 2 along with this work; if not, write to the Free Software Foundation,
145.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
145.23 + *
145.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
145.25 + * or visit www.oracle.com if you need additional information or have any
145.26 + * questions.
145.27 + */
145.28 +
145.29 +package java.util;
145.30 +
145.31 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
145.32 +
145.33 +/**
145.34 + * A specialized {@link Set} implementation for use with enum types. All of
145.35 + * the elements in an enum set must come from a single enum type that is
145.36 + * specified, explicitly or implicitly, when the set is created. Enum sets
145.37 + * are represented internally as bit vectors. This representation is
145.38 + * extremely compact and efficient. The space and time performance of this
145.39 + * class should be good enough to allow its use as a high-quality, typesafe
145.40 + * alternative to traditional <tt>int</tt>-based "bit flags." Even bulk
145.41 + * operations (such as <tt>containsAll</tt> and <tt>retainAll</tt>) should
145.42 + * run very quickly if their argument is also an enum set.
145.43 + *
145.44 + * <p>The iterator returned by the <tt>iterator</tt> method traverses the
145.45 + * elements in their <i>natural order</i> (the order in which the enum
145.46 + * constants are declared). The returned iterator is <i>weakly
145.47 + * consistent</i>: it will never throw {@link ConcurrentModificationException}
145.48 + * and it may or may not show the effects of any modifications to the set that
145.49 + * occur while the iteration is in progress.
145.50 + *
145.51 + * <p>Null elements are not permitted. Attempts to insert a null element
145.52 + * will throw {@link NullPointerException}. Attempts to test for the
145.53 + * presence of a null element or to remove one will, however, function
145.54 + * properly.
145.55 + *
145.56 + * <P>Like most collection implementations, <tt>EnumSet</tt> is not
145.57 + * synchronized. If multiple threads access an enum set concurrently, and at
145.58 + * least one of the threads modifies the set, it should be synchronized
145.59 + * externally. This is typically accomplished by synchronizing on some
145.60 + * object that naturally encapsulates the enum set. If no such object exists,
145.61 + * the set should be "wrapped" using the {@link Collections#synchronizedSet}
145.62 + * method. This is best done at creation time, to prevent accidental
145.63 + * unsynchronized access:
145.64 + *
145.65 + * <pre>
145.66 + * Set<MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));
145.67 + * </pre>
145.68 + *
145.69 + * <p>Implementation note: All basic operations execute in constant time.
145.70 + * They are likely (though not guaranteed) to be much faster than their
145.71 + * {@link HashSet} counterparts. Even bulk operations execute in
145.72 + * constant time if their argument is also an enum set.
145.73 + *
145.74 + * <p>This class is a member of the
145.75 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
145.76 + * Java Collections Framework</a>.
145.77 + *
145.78 + * @author Josh Bloch
145.79 + * @since 1.5
145.80 + * @see EnumMap
145.81 + * @serial exclude
145.82 + */
145.83 +public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
145.84 + implements Cloneable, java.io.Serializable
145.85 +{
145.86 + /**
145.87 + * The class of all the elements of this set.
145.88 + */
145.89 + final Class<E> elementType;
145.90 +
145.91 + /**
145.92 + * All of the values comprising T. (Cached for performance.)
145.93 + */
145.94 + final Enum[] universe;
145.95 +
145.96 + private static Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0];
145.97 +
145.98 + EnumSet(Class<E>elementType, Enum[] universe) {
145.99 + this.elementType = elementType;
145.100 + this.universe = universe;
145.101 + }
145.102 +
145.103 + /**
145.104 + * Creates an empty enum set with the specified element type.
145.105 + *
145.106 + * @param elementType the class object of the element type for this enum
145.107 + * set
145.108 + * @throws NullPointerException if <tt>elementType</tt> is null
145.109 + */
145.110 + public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
145.111 + Enum[] universe = getUniverse(elementType);
145.112 + if (universe == null)
145.113 + throw new ClassCastException(elementType + " not an enum");
145.114 +
145.115 + if (universe.length <= 64)
145.116 + return new RegularEnumSet<>(elementType, universe);
145.117 + else
145.118 + return new JumboEnumSet<>(elementType, universe);
145.119 + }
145.120 +
145.121 + /**
145.122 + * Creates an enum set containing all of the elements in the specified
145.123 + * element type.
145.124 + *
145.125 + * @param elementType the class object of the element type for this enum
145.126 + * set
145.127 + * @throws NullPointerException if <tt>elementType</tt> is null
145.128 + */
145.129 + public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
145.130 + EnumSet<E> result = noneOf(elementType);
145.131 + result.addAll();
145.132 + return result;
145.133 + }
145.134 +
145.135 + /**
145.136 + * Adds all of the elements from the appropriate enum type to this enum
145.137 + * set, which is empty prior to the call.
145.138 + */
145.139 + abstract void addAll();
145.140 +
145.141 + /**
145.142 + * Creates an enum set with the same element type as the specified enum
145.143 + * set, initially containing the same elements (if any).
145.144 + *
145.145 + * @param s the enum set from which to initialize this enum set
145.146 + * @throws NullPointerException if <tt>s</tt> is null
145.147 + */
145.148 + public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) {
145.149 + return s.clone();
145.150 + }
145.151 +
145.152 + /**
145.153 + * Creates an enum set initialized from the specified collection. If
145.154 + * the specified collection is an <tt>EnumSet</tt> instance, this static
145.155 + * factory method behaves identically to {@link #copyOf(EnumSet)}.
145.156 + * Otherwise, the specified collection must contain at least one element
145.157 + * (in order to determine the new enum set's element type).
145.158 + *
145.159 + * @param c the collection from which to initialize this enum set
145.160 + * @throws IllegalArgumentException if <tt>c</tt> is not an
145.161 + * <tt>EnumSet</tt> instance and contains no elements
145.162 + * @throws NullPointerException if <tt>c</tt> is null
145.163 + */
145.164 + public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
145.165 + if (c instanceof EnumSet) {
145.166 + return ((EnumSet<E>)c).clone();
145.167 + } else {
145.168 + if (c.isEmpty())
145.169 + throw new IllegalArgumentException("Collection is empty");
145.170 + Iterator<E> i = c.iterator();
145.171 + E first = i.next();
145.172 + EnumSet<E> result = EnumSet.of(first);
145.173 + while (i.hasNext())
145.174 + result.add(i.next());
145.175 + return result;
145.176 + }
145.177 + }
145.178 +
145.179 + /**
145.180 + * Creates an enum set with the same element type as the specified enum
145.181 + * set, initially containing all the elements of this type that are
145.182 + * <i>not</i> contained in the specified set.
145.183 + *
145.184 + * @param s the enum set from whose complement to initialize this enum set
145.185 + * @throws NullPointerException if <tt>s</tt> is null
145.186 + */
145.187 + public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
145.188 + EnumSet<E> result = copyOf(s);
145.189 + result.complement();
145.190 + return result;
145.191 + }
145.192 +
145.193 + /**
145.194 + * Creates an enum set initially containing the specified element.
145.195 + *
145.196 + * Overloadings of this method exist to initialize an enum set with
145.197 + * one through five elements. A sixth overloading is provided that
145.198 + * uses the varargs feature. This overloading may be used to create
145.199 + * an enum set initially containing an arbitrary number of elements, but
145.200 + * is likely to run slower than the overloadings that do not use varargs.
145.201 + *
145.202 + * @param e the element that this set is to contain initially
145.203 + * @throws NullPointerException if <tt>e</tt> is null
145.204 + * @return an enum set initially containing the specified element
145.205 + */
145.206 + public static <E extends Enum<E>> EnumSet<E> of(E e) {
145.207 + EnumSet<E> result = noneOf(e.getDeclaringClass());
145.208 + result.add(e);
145.209 + return result;
145.210 + }
145.211 +
145.212 + /**
145.213 + * Creates an enum set initially containing the specified elements.
145.214 + *
145.215 + * Overloadings of this method exist to initialize an enum set with
145.216 + * one through five elements. A sixth overloading is provided that
145.217 + * uses the varargs feature. This overloading may be used to create
145.218 + * an enum set initially containing an arbitrary number of elements, but
145.219 + * is likely to run slower than the overloadings that do not use varargs.
145.220 + *
145.221 + * @param e1 an element that this set is to contain initially
145.222 + * @param e2 another element that this set is to contain initially
145.223 + * @throws NullPointerException if any parameters are null
145.224 + * @return an enum set initially containing the specified elements
145.225 + */
145.226 + public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
145.227 + EnumSet<E> result = noneOf(e1.getDeclaringClass());
145.228 + result.add(e1);
145.229 + result.add(e2);
145.230 + return result;
145.231 + }
145.232 +
145.233 + /**
145.234 + * Creates an enum set initially containing the specified elements.
145.235 + *
145.236 + * Overloadings of this method exist to initialize an enum set with
145.237 + * one through five elements. A sixth overloading is provided that
145.238 + * uses the varargs feature. This overloading may be used to create
145.239 + * an enum set initially containing an arbitrary number of elements, but
145.240 + * is likely to run slower than the overloadings that do not use varargs.
145.241 + *
145.242 + * @param e1 an element that this set is to contain initially
145.243 + * @param e2 another element that this set is to contain initially
145.244 + * @param e3 another element that this set is to contain initially
145.245 + * @throws NullPointerException if any parameters are null
145.246 + * @return an enum set initially containing the specified elements
145.247 + */
145.248 + public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) {
145.249 + EnumSet<E> result = noneOf(e1.getDeclaringClass());
145.250 + result.add(e1);
145.251 + result.add(e2);
145.252 + result.add(e3);
145.253 + return result;
145.254 + }
145.255 +
145.256 + /**
145.257 + * Creates an enum set initially containing the specified elements.
145.258 + *
145.259 + * Overloadings of this method exist to initialize an enum set with
145.260 + * one through five elements. A sixth overloading is provided that
145.261 + * uses the varargs feature. This overloading may be used to create
145.262 + * an enum set initially containing an arbitrary number of elements, but
145.263 + * is likely to run slower than the overloadings that do not use varargs.
145.264 + *
145.265 + * @param e1 an element that this set is to contain initially
145.266 + * @param e2 another element that this set is to contain initially
145.267 + * @param e3 another element that this set is to contain initially
145.268 + * @param e4 another element that this set is to contain initially
145.269 + * @throws NullPointerException if any parameters are null
145.270 + * @return an enum set initially containing the specified elements
145.271 + */
145.272 + public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) {
145.273 + EnumSet<E> result = noneOf(e1.getDeclaringClass());
145.274 + result.add(e1);
145.275 + result.add(e2);
145.276 + result.add(e3);
145.277 + result.add(e4);
145.278 + return result;
145.279 + }
145.280 +
145.281 + /**
145.282 + * Creates an enum set initially containing the specified elements.
145.283 + *
145.284 + * Overloadings of this method exist to initialize an enum set with
145.285 + * one through five elements. A sixth overloading is provided that
145.286 + * uses the varargs feature. This overloading may be used to create
145.287 + * an enum set initially containing an arbitrary number of elements, but
145.288 + * is likely to run slower than the overloadings that do not use varargs.
145.289 + *
145.290 + * @param e1 an element that this set is to contain initially
145.291 + * @param e2 another element that this set is to contain initially
145.292 + * @param e3 another element that this set is to contain initially
145.293 + * @param e4 another element that this set is to contain initially
145.294 + * @param e5 another element that this set is to contain initially
145.295 + * @throws NullPointerException if any parameters are null
145.296 + * @return an enum set initially containing the specified elements
145.297 + */
145.298 + public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4,
145.299 + E e5)
145.300 + {
145.301 + EnumSet<E> result = noneOf(e1.getDeclaringClass());
145.302 + result.add(e1);
145.303 + result.add(e2);
145.304 + result.add(e3);
145.305 + result.add(e4);
145.306 + result.add(e5);
145.307 + return result;
145.308 + }
145.309 +
145.310 + /**
145.311 + * Creates an enum set initially containing the specified elements.
145.312 + * This factory, whose parameter list uses the varargs feature, may
145.313 + * be used to create an enum set initially containing an arbitrary
145.314 + * number of elements, but it is likely to run slower than the overloadings
145.315 + * that do not use varargs.
145.316 + *
145.317 + * @param first an element that the set is to contain initially
145.318 + * @param rest the remaining elements the set is to contain initially
145.319 + * @throws NullPointerException if any of the specified elements are null,
145.320 + * or if <tt>rest</tt> is null
145.321 + * @return an enum set initially containing the specified elements
145.322 + */
145.323 + @SafeVarargs
145.324 + public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
145.325 + EnumSet<E> result = noneOf(first.getDeclaringClass());
145.326 + result.add(first);
145.327 + for (E e : rest)
145.328 + result.add(e);
145.329 + return result;
145.330 + }
145.331 +
145.332 + /**
145.333 + * Creates an enum set initially containing all of the elements in the
145.334 + * range defined by the two specified endpoints. The returned set will
145.335 + * contain the endpoints themselves, which may be identical but must not
145.336 + * be out of order.
145.337 + *
145.338 + * @param from the first element in the range
145.339 + * @param to the last element in the range
145.340 + * @throws NullPointerException if {@code from} or {@code to} are null
145.341 + * @throws IllegalArgumentException if {@code from.compareTo(to) > 0}
145.342 + * @return an enum set initially containing all of the elements in the
145.343 + * range defined by the two specified endpoints
145.344 + */
145.345 + public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
145.346 + if (from.compareTo(to) > 0)
145.347 + throw new IllegalArgumentException(from + " > " + to);
145.348 + EnumSet<E> result = noneOf(from.getDeclaringClass());
145.349 + result.addRange(from, to);
145.350 + return result;
145.351 + }
145.352 +
145.353 + /**
145.354 + * Adds the specified range to this enum set, which is empty prior
145.355 + * to the call.
145.356 + */
145.357 + abstract void addRange(E from, E to);
145.358 +
145.359 + /**
145.360 + * Returns a copy of this set.
145.361 + *
145.362 + * @return a copy of this set
145.363 + */
145.364 + public EnumSet<E> clone() {
145.365 + try {
145.366 + return (EnumSet<E>) super.clone();
145.367 + } catch(CloneNotSupportedException e) {
145.368 + throw new AssertionError(e);
145.369 + }
145.370 + }
145.371 +
145.372 + /**
145.373 + * Complements the contents of this enum set.
145.374 + */
145.375 + abstract void complement();
145.376 +
145.377 + /**
145.378 + * Throws an exception if e is not of the correct type for this enum set.
145.379 + */
145.380 + final void typeCheck(E e) {
145.381 + Class eClass = e.getClass();
145.382 + if (eClass != elementType && eClass.getSuperclass() != elementType)
145.383 + throw new ClassCastException(eClass + " != " + elementType);
145.384 + }
145.385 +
145.386 + /**
145.387 + * Returns all of the values comprising E.
145.388 + * The result is uncloned, cached, and shared by all callers.
145.389 + */
145.390 + @JavaScriptBody(args = { "enumType" }, body = "return enumType.cnstr.fld_$VALUES;")
145.391 + private static native <E extends Enum<E>> E[] getUniverse(Class<E> elementType);
145.392 +
145.393 + /**
145.394 + * This class is used to serialize all EnumSet instances, regardless of
145.395 + * implementation type. It captures their "logical contents" and they
145.396 + * are reconstructed using public static factories. This is necessary
145.397 + * to ensure that the existence of a particular implementation type is
145.398 + * an implementation detail.
145.399 + *
145.400 + * @serial include
145.401 + */
145.402 + private static class SerializationProxy <E extends Enum<E>>
145.403 + implements java.io.Serializable
145.404 + {
145.405 + /**
145.406 + * The element type of this enum set.
145.407 + *
145.408 + * @serial
145.409 + */
145.410 + private final Class<E> elementType;
145.411 +
145.412 + /**
145.413 + * The elements contained in this enum set.
145.414 + *
145.415 + * @serial
145.416 + */
145.417 + private final Enum[] elements;
145.418 +
145.419 + SerializationProxy(EnumSet<E> set) {
145.420 + elementType = set.elementType;
145.421 + elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY);
145.422 + }
145.423 +
145.424 + private Object readResolve() {
145.425 + EnumSet<E> result = EnumSet.noneOf(elementType);
145.426 + for (Enum e : elements)
145.427 + result.add((E)e);
145.428 + return result;
145.429 + }
145.430 +
145.431 + private static final long serialVersionUID = 362491234563181265L;
145.432 + }
145.433 +
145.434 + Object writeReplace() {
145.435 + return new SerializationProxy<>(this);
145.436 + }
145.437 +
145.438 + // readObject method for the serialization proxy pattern
145.439 + // See Effective Java, Second Ed., Item 78.
145.440 + private void readObject(java.io.ObjectInputStream stream)
145.441 + throws java.io.InvalidObjectException {
145.442 + throw new java.io.InvalidObjectException("Proxy required");
145.443 + }
145.444 +}
146.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
146.2 +++ b/rt/emul/compact/src/main/java/java/util/JumboEnumSet.java Tue Feb 11 13:31:42 2014 +0100
146.3 @@ -0,0 +1,375 @@
146.4 +/*
146.5 + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
146.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
146.7 + *
146.8 + * This code is free software; you can redistribute it and/or modify it
146.9 + * under the terms of the GNU General Public License version 2 only, as
146.10 + * published by the Free Software Foundation. Oracle designates this
146.11 + * particular file as subject to the "Classpath" exception as provided
146.12 + * by Oracle in the LICENSE file that accompanied this code.
146.13 + *
146.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
146.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
146.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
146.17 + * version 2 for more details (a copy is included in the LICENSE file that
146.18 + * accompanied this code).
146.19 + *
146.20 + * You should have received a copy of the GNU General Public License version
146.21 + * 2 along with this work; if not, write to the Free Software Foundation,
146.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
146.23 + *
146.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
146.25 + * or visit www.oracle.com if you need additional information or have any
146.26 + * questions.
146.27 + */
146.28 +
146.29 +package java.util;
146.30 +
146.31 +/**
146.32 + * Private implementation class for EnumSet, for "jumbo" enum types
146.33 + * (i.e., those with more than 64 elements).
146.34 + *
146.35 + * @author Josh Bloch
146.36 + * @since 1.5
146.37 + * @serial exclude
146.38 + */
146.39 +class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
146.40 + private static final long serialVersionUID = 334349849919042784L;
146.41 +
146.42 + /**
146.43 + * Bit vector representation of this set. The ith bit of the jth
146.44 + * element of this array represents the presence of universe[64*j +i]
146.45 + * in this set.
146.46 + */
146.47 + private long elements[];
146.48 +
146.49 + // Redundant - maintained for performance
146.50 + private int size = 0;
146.51 +
146.52 + JumboEnumSet(Class<E>elementType, Enum[] universe) {
146.53 + super(elementType, universe);
146.54 + elements = new long[(universe.length + 63) >>> 6];
146.55 + }
146.56 +
146.57 + void addRange(E from, E to) {
146.58 + int fromIndex = from.ordinal() >>> 6;
146.59 + int toIndex = to.ordinal() >>> 6;
146.60 +
146.61 + if (fromIndex == toIndex) {
146.62 + elements[fromIndex] = (-1L >>> (from.ordinal() - to.ordinal() - 1))
146.63 + << from.ordinal();
146.64 + } else {
146.65 + elements[fromIndex] = (-1L << from.ordinal());
146.66 + for (int i = fromIndex + 1; i < toIndex; i++)
146.67 + elements[i] = -1;
146.68 + elements[toIndex] = -1L >>> (63 - to.ordinal());
146.69 + }
146.70 + size = to.ordinal() - from.ordinal() + 1;
146.71 + }
146.72 +
146.73 + void addAll() {
146.74 + for (int i = 0; i < elements.length; i++)
146.75 + elements[i] = -1;
146.76 + elements[elements.length - 1] >>>= -universe.length;
146.77 + size = universe.length;
146.78 + }
146.79 +
146.80 + void complement() {
146.81 + for (int i = 0; i < elements.length; i++)
146.82 + elements[i] = ~elements[i];
146.83 + elements[elements.length - 1] &= (-1L >>> -universe.length);
146.84 + size = universe.length - size;
146.85 + }
146.86 +
146.87 + /**
146.88 + * Returns an iterator over the elements contained in this set. The
146.89 + * iterator traverses the elements in their <i>natural order</i> (which is
146.90 + * the order in which the enum constants are declared). The returned
146.91 + * Iterator is a "weakly consistent" iterator that will never throw {@link
146.92 + * ConcurrentModificationException}.
146.93 + *
146.94 + * @return an iterator over the elements contained in this set
146.95 + */
146.96 + public Iterator<E> iterator() {
146.97 + return new EnumSetIterator<>();
146.98 + }
146.99 +
146.100 + private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
146.101 + /**
146.102 + * A bit vector representing the elements in the current "word"
146.103 + * of the set not yet returned by this iterator.
146.104 + */
146.105 + long unseen;
146.106 +
146.107 + /**
146.108 + * The index corresponding to unseen in the elements array.
146.109 + */
146.110 + int unseenIndex = 0;
146.111 +
146.112 + /**
146.113 + * The bit representing the last element returned by this iterator
146.114 + * but not removed, or zero if no such element exists.
146.115 + */
146.116 + long lastReturned = 0;
146.117 +
146.118 + /**
146.119 + * The index corresponding to lastReturned in the elements array.
146.120 + */
146.121 + int lastReturnedIndex = 0;
146.122 +
146.123 + EnumSetIterator() {
146.124 + unseen = elements[0];
146.125 + }
146.126 +
146.127 + public boolean hasNext() {
146.128 + while (unseen == 0 && unseenIndex < elements.length - 1)
146.129 + unseen = elements[++unseenIndex];
146.130 + return unseen != 0;
146.131 + }
146.132 +
146.133 + public E next() {
146.134 + if (!hasNext())
146.135 + throw new NoSuchElementException();
146.136 + lastReturned = unseen & -unseen;
146.137 + lastReturnedIndex = unseenIndex;
146.138 + unseen -= lastReturned;
146.139 + return (E) universe[(lastReturnedIndex << 6)
146.140 + + Long.numberOfTrailingZeros(lastReturned)];
146.141 + }
146.142 +
146.143 + public void remove() {
146.144 + if (lastReturned == 0)
146.145 + throw new IllegalStateException();
146.146 + final long oldElements = elements[lastReturnedIndex];
146.147 + elements[lastReturnedIndex] &= ~lastReturned;
146.148 + if (oldElements != elements[lastReturnedIndex]) {
146.149 + size--;
146.150 + }
146.151 + lastReturned = 0;
146.152 + }
146.153 + }
146.154 +
146.155 + /**
146.156 + * Returns the number of elements in this set.
146.157 + *
146.158 + * @return the number of elements in this set
146.159 + */
146.160 + public int size() {
146.161 + return size;
146.162 + }
146.163 +
146.164 + /**
146.165 + * Returns <tt>true</tt> if this set contains no elements.
146.166 + *
146.167 + * @return <tt>true</tt> if this set contains no elements
146.168 + */
146.169 + public boolean isEmpty() {
146.170 + return size == 0;
146.171 + }
146.172 +
146.173 + /**
146.174 + * Returns <tt>true</tt> if this set contains the specified element.
146.175 + *
146.176 + * @param e element to be checked for containment in this collection
146.177 + * @return <tt>true</tt> if this set contains the specified element
146.178 + */
146.179 + public boolean contains(Object e) {
146.180 + if (e == null)
146.181 + return false;
146.182 + Class eClass = e.getClass();
146.183 + if (eClass != elementType && eClass.getSuperclass() != elementType)
146.184 + return false;
146.185 +
146.186 + int eOrdinal = ((Enum)e).ordinal();
146.187 + return (elements[eOrdinal >>> 6] & (1L << eOrdinal)) != 0;
146.188 + }
146.189 +
146.190 + // Modification Operations
146.191 +
146.192 + /**
146.193 + * Adds the specified element to this set if it is not already present.
146.194 + *
146.195 + * @param e element to be added to this set
146.196 + * @return <tt>true</tt> if the set changed as a result of the call
146.197 + *
146.198 + * @throws NullPointerException if <tt>e</tt> is null
146.199 + */
146.200 + public boolean add(E e) {
146.201 + typeCheck(e);
146.202 +
146.203 + int eOrdinal = e.ordinal();
146.204 + int eWordNum = eOrdinal >>> 6;
146.205 +
146.206 + long oldElements = elements[eWordNum];
146.207 + elements[eWordNum] |= (1L << eOrdinal);
146.208 + boolean result = (elements[eWordNum] != oldElements);
146.209 + if (result)
146.210 + size++;
146.211 + return result;
146.212 + }
146.213 +
146.214 + /**
146.215 + * Removes the specified element from this set if it is present.
146.216 + *
146.217 + * @param e element to be removed from this set, if present
146.218 + * @return <tt>true</tt> if the set contained the specified element
146.219 + */
146.220 + public boolean remove(Object e) {
146.221 + if (e == null)
146.222 + return false;
146.223 + Class eClass = e.getClass();
146.224 + if (eClass != elementType && eClass.getSuperclass() != elementType)
146.225 + return false;
146.226 + int eOrdinal = ((Enum)e).ordinal();
146.227 + int eWordNum = eOrdinal >>> 6;
146.228 +
146.229 + long oldElements = elements[eWordNum];
146.230 + elements[eWordNum] &= ~(1L << eOrdinal);
146.231 + boolean result = (elements[eWordNum] != oldElements);
146.232 + if (result)
146.233 + size--;
146.234 + return result;
146.235 + }
146.236 +
146.237 + // Bulk Operations
146.238 +
146.239 + /**
146.240 + * Returns <tt>true</tt> if this set contains all of the elements
146.241 + * in the specified collection.
146.242 + *
146.243 + * @param c collection to be checked for containment in this set
146.244 + * @return <tt>true</tt> if this set contains all of the elements
146.245 + * in the specified collection
146.246 + * @throws NullPointerException if the specified collection is null
146.247 + */
146.248 + public boolean containsAll(Collection<?> c) {
146.249 + if (!(c instanceof JumboEnumSet))
146.250 + return super.containsAll(c);
146.251 +
146.252 + JumboEnumSet es = (JumboEnumSet)c;
146.253 + if (es.elementType != elementType)
146.254 + return es.isEmpty();
146.255 +
146.256 + for (int i = 0; i < elements.length; i++)
146.257 + if ((es.elements[i] & ~elements[i]) != 0)
146.258 + return false;
146.259 + return true;
146.260 + }
146.261 +
146.262 + /**
146.263 + * Adds all of the elements in the specified collection to this set.
146.264 + *
146.265 + * @param c collection whose elements are to be added to this set
146.266 + * @return <tt>true</tt> if this set changed as a result of the call
146.267 + * @throws NullPointerException if the specified collection or any of
146.268 + * its elements are null
146.269 + */
146.270 + public boolean addAll(Collection<? extends E> c) {
146.271 + if (!(c instanceof JumboEnumSet))
146.272 + return super.addAll(c);
146.273 +
146.274 + JumboEnumSet es = (JumboEnumSet)c;
146.275 + if (es.elementType != elementType) {
146.276 + if (es.isEmpty())
146.277 + return false;
146.278 + else
146.279 + throw new ClassCastException(
146.280 + es.elementType + " != " + elementType);
146.281 + }
146.282 +
146.283 + for (int i = 0; i < elements.length; i++)
146.284 + elements[i] |= es.elements[i];
146.285 + return recalculateSize();
146.286 + }
146.287 +
146.288 + /**
146.289 + * Removes from this set all of its elements that are contained in
146.290 + * the specified collection.
146.291 + *
146.292 + * @param c elements to be removed from this set
146.293 + * @return <tt>true</tt> if this set changed as a result of the call
146.294 + * @throws NullPointerException if the specified collection is null
146.295 + */
146.296 + public boolean removeAll(Collection<?> c) {
146.297 + if (!(c instanceof JumboEnumSet))
146.298 + return super.removeAll(c);
146.299 +
146.300 + JumboEnumSet es = (JumboEnumSet)c;
146.301 + if (es.elementType != elementType)
146.302 + return false;
146.303 +
146.304 + for (int i = 0; i < elements.length; i++)
146.305 + elements[i] &= ~es.elements[i];
146.306 + return recalculateSize();
146.307 + }
146.308 +
146.309 + /**
146.310 + * Retains only the elements in this set that are contained in the
146.311 + * specified collection.
146.312 + *
146.313 + * @param c elements to be retained in this set
146.314 + * @return <tt>true</tt> if this set changed as a result of the call
146.315 + * @throws NullPointerException if the specified collection is null
146.316 + */
146.317 + public boolean retainAll(Collection<?> c) {
146.318 + if (!(c instanceof JumboEnumSet))
146.319 + return super.retainAll(c);
146.320 +
146.321 + JumboEnumSet<?> es = (JumboEnumSet<?>)c;
146.322 + if (es.elementType != elementType) {
146.323 + boolean changed = (size != 0);
146.324 + clear();
146.325 + return changed;
146.326 + }
146.327 +
146.328 + for (int i = 0; i < elements.length; i++)
146.329 + elements[i] &= es.elements[i];
146.330 + return recalculateSize();
146.331 + }
146.332 +
146.333 + /**
146.334 + * Removes all of the elements from this set.
146.335 + */
146.336 + public void clear() {
146.337 + Arrays.fill(elements, 0);
146.338 + size = 0;
146.339 + }
146.340 +
146.341 + /**
146.342 + * Compares the specified object with this set for equality. Returns
146.343 + * <tt>true</tt> if the given object is also a set, the two sets have
146.344 + * the same size, and every member of the given set is contained in
146.345 + * this set.
146.346 + *
146.347 + * @param e object to be compared for equality with this set
146.348 + * @return <tt>true</tt> if the specified object is equal to this set
146.349 + */
146.350 + public boolean equals(Object o) {
146.351 + if (!(o instanceof JumboEnumSet))
146.352 + return super.equals(o);
146.353 +
146.354 + JumboEnumSet es = (JumboEnumSet)o;
146.355 + if (es.elementType != elementType)
146.356 + return size == 0 && es.size == 0;
146.357 +
146.358 + return Arrays.equals(es.elements, elements);
146.359 + }
146.360 +
146.361 + /**
146.362 + * Recalculates the size of the set. Returns true if it's changed.
146.363 + */
146.364 + private boolean recalculateSize() {
146.365 + int oldSize = size;
146.366 + size = 0;
146.367 + for (long elt : elements)
146.368 + size += Long.bitCount(elt);
146.369 +
146.370 + return size != oldSize;
146.371 + }
146.372 +
146.373 + public EnumSet<E> clone() {
146.374 + JumboEnumSet<E> result = (JumboEnumSet<E>) super.clone();
146.375 + result.elements = result.elements.clone();
146.376 + return result;
146.377 + }
146.378 +}
147.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
147.2 +++ b/rt/emul/compact/src/main/java/java/util/Locale.java Tue Feb 11 13:31:42 2014 +0100
147.3 @@ -0,0 +1,1012 @@
147.4 +/*
147.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
147.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
147.7 + *
147.8 + * This code is free software; you can redistribute it and/or modify it
147.9 + * under the terms of the GNU General Public License version 2 only, as
147.10 + * published by the Free Software Foundation. Oracle designates this
147.11 + * particular file as subject to the "Classpath" exception as provided
147.12 + * by Oracle in the LICENSE file that accompanied this code.
147.13 + *
147.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
147.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
147.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
147.17 + * version 2 for more details (a copy is included in the LICENSE file that
147.18 + * accompanied this code).
147.19 + *
147.20 + * You should have received a copy of the GNU General Public License version
147.21 + * 2 along with this work; if not, write to the Free Software Foundation,
147.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
147.23 + *
147.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
147.25 + * or visit www.oracle.com if you need additional information or have any
147.26 + * questions.
147.27 + */
147.28 +
147.29 +/*
147.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
147.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
147.32 + *
147.33 + * The original version of this source code and documentation
147.34 + * is copyrighted and owned by Taligent, Inc., a wholly-owned
147.35 + * subsidiary of IBM. These materials are provided under terms
147.36 + * of a License Agreement between Taligent and Sun. This technology
147.37 + * is protected by multiple US and International patents.
147.38 + *
147.39 + * This notice and attribution to Taligent may not be removed.
147.40 + * Taligent is a registered trademark of Taligent, Inc.
147.41 + *
147.42 + */
147.43 +
147.44 +package java.util;
147.45 +
147.46 +import java.io.IOException;
147.47 +import java.io.ObjectInputStream;
147.48 +import java.io.ObjectOutputStream;
147.49 +import java.io.ObjectStreamField;
147.50 +import java.io.Serializable;
147.51 +
147.52 +/**
147.53 + * A <code>Locale</code> object represents a specific geographical, political,
147.54 + * or cultural region. An operation that requires a <code>Locale</code> to perform
147.55 + * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
147.56 + * to tailor information for the user. For example, displaying a number
147.57 + * is a locale-sensitive operation— the number should be formatted
147.58 + * according to the customs and conventions of the user's native country,
147.59 + * region, or culture.
147.60 + *
147.61 + * <p> The <code>Locale</code> class implements identifiers
147.62 + * interchangeable with BCP 47 (IETF BCP 47, "Tags for Identifying
147.63 + * Languages"), with support for the LDML (UTS#35, "Unicode Locale
147.64 + * Data Markup Language") BCP 47-compatible extensions for locale data
147.65 + * exchange.
147.66 + *
147.67 + * <p> A <code>Locale</code> object logically consists of the fields
147.68 + * described below.
147.69 + *
147.70 + * <dl>
147.71 + * <dt><a name="def_language"/><b>language</b></dt>
147.72 + *
147.73 + * <dd>ISO 639 alpha-2 or alpha-3 language code, or registered
147.74 + * language subtags up to 8 alpha letters (for future enhancements).
147.75 + * When a language has both an alpha-2 code and an alpha-3 code, the
147.76 + * alpha-2 code must be used. You can find a full list of valid
147.77 + * language codes in the IANA Language Subtag Registry (search for
147.78 + * "Type: language"). The language field is case insensitive, but
147.79 + * <code>Locale</code> always canonicalizes to lower case.</dd><br>
147.80 + *
147.81 + * <dd>Well-formed language values have the form
147.82 + * <code>[a-zA-Z]{2,8}</code>. Note that this is not the the full
147.83 + * BCP47 language production, since it excludes extlang. They are
147.84 + * not needed since modern three-letter language codes replace
147.85 + * them.</dd><br>
147.86 + *
147.87 + * <dd>Example: "en" (English), "ja" (Japanese), "kok" (Konkani)</dd><br>
147.88 + *
147.89 + * <dt><a name="def_script"/><b>script</b></dt>
147.90 + *
147.91 + * <dd>ISO 15924 alpha-4 script code. You can find a full list of
147.92 + * valid script codes in the IANA Language Subtag Registry (search
147.93 + * for "Type: script"). The script field is case insensitive, but
147.94 + * <code>Locale</code> always canonicalizes to title case (the first
147.95 + * letter is upper case and the rest of the letters are lower
147.96 + * case).</dd><br>
147.97 + *
147.98 + * <dd>Well-formed script values have the form
147.99 + * <code>[a-zA-Z]{4}</code></dd><br>
147.100 + *
147.101 + * <dd>Example: "Latn" (Latin), "Cyrl" (Cyrillic)</dd><br>
147.102 + *
147.103 + * <dt><a name="def_region"/><b>country (region)</b></dt>
147.104 + *
147.105 + * <dd>ISO 3166 alpha-2 country code or UN M.49 numeric-3 area code.
147.106 + * You can find a full list of valid country and region codes in the
147.107 + * IANA Language Subtag Registry (search for "Type: region"). The
147.108 + * country (region) field is case insensitive, but
147.109 + * <code>Locale</code> always canonicalizes to upper case.</dd><br>
147.110 + *
147.111 + * <dd>Well-formed country/region values have
147.112 + * the form <code>[a-zA-Z]{2} | [0-9]{3}</code></dd><br>
147.113 + *
147.114 + * <dd>Example: "US" (United States), "FR" (France), "029"
147.115 + * (Caribbean)</dd><br>
147.116 + *
147.117 + * <dt><a name="def_variant"/><b>variant</b></dt>
147.118 + *
147.119 + * <dd>Any arbitrary value used to indicate a variation of a
147.120 + * <code>Locale</code>. Where there are two or more variant values
147.121 + * each indicating its own semantics, these values should be ordered
147.122 + * by importance, with most important first, separated by
147.123 + * underscore('_'). The variant field is case sensitive.</dd><br>
147.124 + *
147.125 + * <dd>Note: IETF BCP 47 places syntactic restrictions on variant
147.126 + * subtags. Also BCP 47 subtags are strictly used to indicate
147.127 + * additional variations that define a language or its dialects that
147.128 + * are not covered by any combinations of language, script and
147.129 + * region subtags. You can find a full list of valid variant codes
147.130 + * in the IANA Language Subtag Registry (search for "Type: variant").
147.131 + *
147.132 + * <p>However, the variant field in <code>Locale</code> has
147.133 + * historically been used for any kind of variation, not just
147.134 + * language variations. For example, some supported variants
147.135 + * available in Java SE Runtime Environments indicate alternative
147.136 + * cultural behaviors such as calendar type or number script. In
147.137 + * BCP 47 this kind of information, which does not identify the
147.138 + * language, is supported by extension subtags or private use
147.139 + * subtags.</dd><br>
147.140 + *
147.141 + * <dd>Well-formed variant values have the form <code>SUBTAG
147.142 + * (('_'|'-') SUBTAG)*</code> where <code>SUBTAG =
147.143 + * [0-9][0-9a-zA-Z]{3} | [0-9a-zA-Z]{5,8}</code>. (Note: BCP 47 only
147.144 + * uses hyphen ('-') as a delimiter, this is more lenient).</dd><br>
147.145 + *
147.146 + * <dd>Example: "polyton" (Polytonic Greek), "POSIX"</dd><br>
147.147 + *
147.148 + * <dt><a name="def_extensions"/><b>extensions</b></dt>
147.149 + *
147.150 + * <dd>A map from single character keys to string values, indicating
147.151 + * extensions apart from language identification. The extensions in
147.152 + * <code>Locale</code> implement the semantics and syntax of BCP 47
147.153 + * extension subtags and private use subtags. The extensions are
147.154 + * case insensitive, but <code>Locale</code> canonicalizes all
147.155 + * extension keys and values to lower case. Note that extensions
147.156 + * cannot have empty values.</dd><br>
147.157 + *
147.158 + * <dd>Well-formed keys are single characters from the set
147.159 + * <code>[0-9a-zA-Z]</code>. Well-formed values have the form
147.160 + * <code>SUBTAG ('-' SUBTAG)*</code> where for the key 'x'
147.161 + * <code>SUBTAG = [0-9a-zA-Z]{1,8}</code> and for other keys
147.162 + * <code>SUBTAG = [0-9a-zA-Z]{2,8}</code> (that is, 'x' allows
147.163 + * single-character subtags).</dd><br>
147.164 + *
147.165 + * <dd>Example: key="u"/value="ca-japanese" (Japanese Calendar),
147.166 + * key="x"/value="java-1-7"</dd>
147.167 + * </dl>
147.168 + *
147.169 + * <b>Note:</b> Although BCP 47 requires field values to be registered
147.170 + * in the IANA Language Subtag Registry, the <code>Locale</code> class
147.171 + * does not provide any validation features. The <code>Builder</code>
147.172 + * only checks if an individual field satisfies the syntactic
147.173 + * requirement (is well-formed), but does not validate the value
147.174 + * itself. See {@link Builder} for details.
147.175 + *
147.176 + * <h4><a name="def_locale_extension">Unicode locale/language extension</h4>
147.177 + *
147.178 + * <p>UTS#35, "Unicode Locale Data Markup Language" defines optional
147.179 + * attributes and keywords to override or refine the default behavior
147.180 + * associated with a locale. A keyword is represented by a pair of
147.181 + * key and type. For example, "nu-thai" indicates that Thai local
147.182 + * digits (value:"thai") should be used for formatting numbers
147.183 + * (key:"nu").
147.184 + *
147.185 + * <p>The keywords are mapped to a BCP 47 extension value using the
147.186 + * extension key 'u' ({@link #UNICODE_LOCALE_EXTENSION}). The above
147.187 + * example, "nu-thai", becomes the extension "u-nu-thai".code
147.188 + *
147.189 + * <p>Thus, when a <code>Locale</code> object contains Unicode locale
147.190 + * attributes and keywords,
147.191 + * <code>getExtension(UNICODE_LOCALE_EXTENSION)</code> will return a
147.192 + * String representing this information, for example, "nu-thai". The
147.193 + * <code>Locale</code> class also provides {@link
147.194 + * #getUnicodeLocaleAttributes}, {@link #getUnicodeLocaleKeys}, and
147.195 + * {@link #getUnicodeLocaleType} which allow you to access Unicode
147.196 + * locale attributes and key/type pairs directly. When represented as
147.197 + * a string, the Unicode Locale Extension lists attributes
147.198 + * alphabetically, followed by key/type sequences with keys listed
147.199 + * alphabetically (the order of subtags comprising a key's type is
147.200 + * fixed when the type is defined)
147.201 + *
147.202 + * <p>A well-formed locale key has the form
147.203 + * <code>[0-9a-zA-Z]{2}</code>. A well-formed locale type has the
147.204 + * form <code>"" | [0-9a-zA-Z]{3,8} ('-' [0-9a-zA-Z]{3,8})*</code> (it
147.205 + * can be empty, or a series of subtags 3-8 alphanums in length). A
147.206 + * well-formed locale attribute has the form
147.207 + * <code>[0-9a-zA-Z]{3,8}</code> (it is a single subtag with the same
147.208 + * form as a locale type subtag).
147.209 + *
147.210 + * <p>The Unicode locale extension specifies optional behavior in
147.211 + * locale-sensitive services. Although the LDML specification defines
147.212 + * various keys and values, actual locale-sensitive service
147.213 + * implementations in a Java Runtime Environment might not support any
147.214 + * particular Unicode locale attributes or key/type pairs.
147.215 + *
147.216 + * <h4>Creating a Locale</h4>
147.217 + *
147.218 + * <p>There are several different ways to create a <code>Locale</code>
147.219 + * object.
147.220 + *
147.221 + * <h5>Builder</h5>
147.222 + *
147.223 + * <p>Using {@link Builder} you can construct a <code>Locale</code> object
147.224 + * that conforms to BCP 47 syntax.
147.225 + *
147.226 + * <h5>Constructors</h5>
147.227 + *
147.228 + * <p>The <code>Locale</code> class provides three constructors:
147.229 + * <blockquote>
147.230 + * <pre>
147.231 + * {@link #Locale(String language)}
147.232 + * {@link #Locale(String language, String country)}
147.233 + * {@link #Locale(String language, String country, String variant)}
147.234 + * </pre>
147.235 + * </blockquote>
147.236 + * These constructors allow you to create a <code>Locale</code> object
147.237 + * with language, country and variant, but you cannot specify
147.238 + * script or extensions.
147.239 + *
147.240 + * <h5>Factory Methods</h5>
147.241 + *
147.242 + * <p>The method {@link #forLanguageTag} creates a <code>Locale</code>
147.243 + * object for a well-formed BCP 47 language tag.
147.244 + *
147.245 + * <h5>Locale Constants</h5>
147.246 + *
147.247 + * <p>The <code>Locale</code> class provides a number of convenient constants
147.248 + * that you can use to create <code>Locale</code> objects for commonly used
147.249 + * locales. For example, the following creates a <code>Locale</code> object
147.250 + * for the United States:
147.251 + * <blockquote>
147.252 + * <pre>
147.253 + * Locale.US
147.254 + * </pre>
147.255 + * </blockquote>
147.256 + *
147.257 + * <h4>Use of Locale</h4>
147.258 + *
147.259 + * <p>Once you've created a <code>Locale</code> you can query it for information
147.260 + * about itself. Use <code>getCountry</code> to get the country (or region)
147.261 + * code and <code>getLanguage</code> to get the language code.
147.262 + * You can use <code>getDisplayCountry</code> to get the
147.263 + * name of the country suitable for displaying to the user. Similarly,
147.264 + * you can use <code>getDisplayLanguage</code> to get the name of
147.265 + * the language suitable for displaying to the user. Interestingly,
147.266 + * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
147.267 + * and have two versions: one that uses the default locale and one
147.268 + * that uses the locale specified as an argument.
147.269 + *
147.270 + * <p>The Java Platform provides a number of classes that perform locale-sensitive
147.271 + * operations. For example, the <code>NumberFormat</code> class formats
147.272 + * numbers, currency, and percentages in a locale-sensitive manner. Classes
147.273 + * such as <code>NumberFormat</code> have several convenience methods
147.274 + * for creating a default object of that type. For example, the
147.275 + * <code>NumberFormat</code> class provides these three convenience methods
147.276 + * for creating a default <code>NumberFormat</code> object:
147.277 + * <blockquote>
147.278 + * <pre>
147.279 + * NumberFormat.getInstance()
147.280 + * NumberFormat.getCurrencyInstance()
147.281 + * NumberFormat.getPercentInstance()
147.282 + * </pre>
147.283 + * </blockquote>
147.284 + * Each of these methods has two variants; one with an explicit locale
147.285 + * and one without; the latter uses the default locale:
147.286 + * <blockquote>
147.287 + * <pre>
147.288 + * NumberFormat.getInstance(myLocale)
147.289 + * NumberFormat.getCurrencyInstance(myLocale)
147.290 + * NumberFormat.getPercentInstance(myLocale)
147.291 + * </pre>
147.292 + * </blockquote>
147.293 + * A <code>Locale</code> is the mechanism for identifying the kind of object
147.294 + * (<code>NumberFormat</code>) that you would like to get. The locale is
147.295 + * <STRONG>just</STRONG> a mechanism for identifying objects,
147.296 + * <STRONG>not</STRONG> a container for the objects themselves.
147.297 + *
147.298 + * <h4>Compatibility</h4>
147.299 + *
147.300 + * <p>In order to maintain compatibility with existing usage, Locale's
147.301 + * constructors retain their behavior prior to the Java Runtime
147.302 + * Environment version 1.7. The same is largely true for the
147.303 + * <code>toString</code> method. Thus Locale objects can continue to
147.304 + * be used as they were. In particular, clients who parse the output
147.305 + * of toString into language, country, and variant fields can continue
147.306 + * to do so (although this is strongly discouraged), although the
147.307 + * variant field will have additional information in it if script or
147.308 + * extensions are present.
147.309 + *
147.310 + * <p>In addition, BCP 47 imposes syntax restrictions that are not
147.311 + * imposed by Locale's constructors. This means that conversions
147.312 + * between some Locales and BCP 47 language tags cannot be made without
147.313 + * losing information. Thus <code>toLanguageTag</code> cannot
147.314 + * represent the state of locales whose language, country, or variant
147.315 + * do not conform to BCP 47.
147.316 + *
147.317 + * <p>Because of these issues, it is recommended that clients migrate
147.318 + * away from constructing non-conforming locales and use the
147.319 + * <code>forLanguageTag</code> and <code>Locale.Builder</code> APIs instead.
147.320 + * Clients desiring a string representation of the complete locale can
147.321 + * then always rely on <code>toLanguageTag</code> for this purpose.
147.322 + *
147.323 + * <h5><a name="special_cases_constructor"/>Special cases</h5>
147.324 + *
147.325 + * <p>For compatibility reasons, two
147.326 + * non-conforming locales are treated as special cases. These are
147.327 + * <b><tt>ja_JP_JP</tt></b> and <b><tt>th_TH_TH</tt></b>. These are ill-formed
147.328 + * in BCP 47 since the variants are too short. To ease migration to BCP 47,
147.329 + * these are treated specially during construction. These two cases (and only
147.330 + * these) cause a constructor to generate an extension, all other values behave
147.331 + * exactly as they did prior to Java 7.
147.332 + *
147.333 + * <p>Java has used <tt>ja_JP_JP</tt> to represent Japanese as used in
147.334 + * Japan together with the Japanese Imperial calendar. This is now
147.335 + * representable using a Unicode locale extension, by specifying the
147.336 + * Unicode locale key <tt>ca</tt> (for "calendar") and type
147.337 + * <tt>japanese</tt>. When the Locale constructor is called with the
147.338 + * arguments "ja", "JP", "JP", the extension "u-ca-japanese" is
147.339 + * automatically added.
147.340 + *
147.341 + * <p>Java has used <tt>th_TH_TH</tt> to represent Thai as used in
147.342 + * Thailand together with Thai digits. This is also now representable using
147.343 + * a Unicode locale extension, by specifying the Unicode locale key
147.344 + * <tt>nu</tt> (for "number") and value <tt>thai</tt>. When the Locale
147.345 + * constructor is called with the arguments "th", "TH", "TH", the
147.346 + * extension "u-nu-thai" is automatically added.
147.347 + *
147.348 + * <h5>Serialization</h5>
147.349 + *
147.350 + * <p>During serialization, writeObject writes all fields to the output
147.351 + * stream, including extensions.
147.352 + *
147.353 + * <p>During deserialization, readResolve adds extensions as described
147.354 + * in <a href="#special_cases_constructor">Special Cases</a>, only
147.355 + * for the two cases th_TH_TH and ja_JP_JP.
147.356 + *
147.357 + * <h5>Legacy language codes</h5>
147.358 + *
147.359 + * <p>Locale's constructor has always converted three language codes to
147.360 + * their earlier, obsoleted forms: <tt>he</tt> maps to <tt>iw</tt>,
147.361 + * <tt>yi</tt> maps to <tt>ji</tt>, and <tt>id</tt> maps to
147.362 + * <tt>in</tt>. This continues to be the case, in order to not break
147.363 + * backwards compatibility.
147.364 + *
147.365 + * <p>The APIs added in 1.7 map between the old and new language codes,
147.366 + * maintaining the old codes internal to Locale (so that
147.367 + * <code>getLanguage</code> and <code>toString</code> reflect the old
147.368 + * code), but using the new codes in the BCP 47 language tag APIs (so
147.369 + * that <code>toLanguageTag</code> reflects the new one). This
147.370 + * preserves the equivalence between Locales no matter which code or
147.371 + * API is used to construct them. Java's default resource bundle
147.372 + * lookup mechanism also implements this mapping, so that resources
147.373 + * can be named using either convention, see {@link ResourceBundle.Control}.
147.374 + *
147.375 + * <h5>Three-letter language/country(region) codes</h5>
147.376 + *
147.377 + * <p>The Locale constructors have always specified that the language
147.378 + * and the country param be two characters in length, although in
147.379 + * practice they have accepted any length. The specification has now
147.380 + * been relaxed to allow language codes of two to eight characters and
147.381 + * country (region) codes of two to three characters, and in
147.382 + * particular, three-letter language codes and three-digit region
147.383 + * codes as specified in the IANA Language Subtag Registry. For
147.384 + * compatibility, the implementation still does not impose a length
147.385 + * constraint.
147.386 + *
147.387 + * @see Builder
147.388 + * @see ResourceBundle
147.389 + * @see java.text.Format
147.390 + * @see java.text.NumberFormat
147.391 + * @see java.text.Collator
147.392 + * @author Mark Davis
147.393 + * @since 1.1
147.394 + */
147.395 +public final class Locale implements Cloneable, Serializable {
147.396 +
147.397 + /** Useful constant for language.
147.398 + */
147.399 + static public final Locale ENGLISH = createConstant("en", "");
147.400 +
147.401 + /** Useful constant for language.
147.402 + */
147.403 + static public final Locale FRENCH = createConstant("fr", "");
147.404 +
147.405 + /** Useful constant for language.
147.406 + */
147.407 + static public final Locale GERMAN = createConstant("de", "");
147.408 +
147.409 + /** Useful constant for language.
147.410 + */
147.411 + static public final Locale ITALIAN = createConstant("it", "");
147.412 +
147.413 + /** Useful constant for language.
147.414 + */
147.415 + static public final Locale JAPANESE = createConstant("ja", "");
147.416 +
147.417 + /** Useful constant for language.
147.418 + */
147.419 + static public final Locale KOREAN = createConstant("ko", "");
147.420 +
147.421 + /** Useful constant for language.
147.422 + */
147.423 + static public final Locale CHINESE = createConstant("zh", "");
147.424 +
147.425 + /** Useful constant for language.
147.426 + */
147.427 + static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");
147.428 +
147.429 + /** Useful constant for language.
147.430 + */
147.431 + static public final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");
147.432 +
147.433 + /** Useful constant for country.
147.434 + */
147.435 + static public final Locale FRANCE = createConstant("fr", "FR");
147.436 +
147.437 + /** Useful constant for country.
147.438 + */
147.439 + static public final Locale GERMANY = createConstant("de", "DE");
147.440 +
147.441 + /** Useful constant for country.
147.442 + */
147.443 + static public final Locale ITALY = createConstant("it", "IT");
147.444 +
147.445 + /** Useful constant for country.
147.446 + */
147.447 + static public final Locale JAPAN = createConstant("ja", "JP");
147.448 +
147.449 + /** Useful constant for country.
147.450 + */
147.451 + static public final Locale KOREA = createConstant("ko", "KR");
147.452 +
147.453 + /** Useful constant for country.
147.454 + */
147.455 + static public final Locale CHINA = SIMPLIFIED_CHINESE;
147.456 +
147.457 + /** Useful constant for country.
147.458 + */
147.459 + static public final Locale PRC = SIMPLIFIED_CHINESE;
147.460 +
147.461 + /** Useful constant for country.
147.462 + */
147.463 + static public final Locale TAIWAN = TRADITIONAL_CHINESE;
147.464 +
147.465 + /** Useful constant for country.
147.466 + */
147.467 + static public final Locale UK = createConstant("en", "GB");
147.468 +
147.469 + /** Useful constant for country.
147.470 + */
147.471 + static public final Locale US = createConstant("en", "US");
147.472 +
147.473 + /** Useful constant for country.
147.474 + */
147.475 + static public final Locale CANADA = createConstant("en", "CA");
147.476 +
147.477 + /** Useful constant for country.
147.478 + */
147.479 + static public final Locale CANADA_FRENCH = createConstant("fr", "CA");
147.480 +
147.481 + /**
147.482 + * Useful constant for the root locale. The root locale is the locale whose
147.483 + * language, country, and variant are empty ("") strings. This is regarded
147.484 + * as the base locale of all locales, and is used as the language/country
147.485 + * neutral locale for the locale sensitive operations.
147.486 + *
147.487 + * @since 1.6
147.488 + */
147.489 + static public final Locale ROOT = createConstant("", "");
147.490 +
147.491 + /**
147.492 + * The key for the private use extension ('x').
147.493 + *
147.494 + * @see #getExtension(char)
147.495 + * @see Builder#setExtension(char, String)
147.496 + * @since 1.7
147.497 + */
147.498 + static public final char PRIVATE_USE_EXTENSION = 'x';
147.499 +
147.500 + /**
147.501 + * The key for Unicode locale extension ('u').
147.502 + *
147.503 + * @see #getExtension(char)
147.504 + * @see Builder#setExtension(char, String)
147.505 + * @since 1.7
147.506 + */
147.507 + static public final char UNICODE_LOCALE_EXTENSION = 'u';
147.508 +
147.509 + /** serialization ID
147.510 + */
147.511 + static final long serialVersionUID = 9149081749638150636L;
147.512 +
147.513 + /**
147.514 + * Display types for retrieving localized names from the name providers.
147.515 + */
147.516 + private static final int DISPLAY_LANGUAGE = 0;
147.517 + private static final int DISPLAY_COUNTRY = 1;
147.518 + private static final int DISPLAY_VARIANT = 2;
147.519 + private static final int DISPLAY_SCRIPT = 3;
147.520 +
147.521 + static Locale getInstance(String language, String script, String region, String v, Object object) {
147.522 + return new Locale(language, script, region);
147.523 + }
147.524 +
147.525 + static Locale getInstance(String no, String no0, String ny) {
147.526 + return new Locale(no, no0, ny);
147.527 + }
147.528 +
147.529 + private String language;
147.530 + private String country;
147.531 + private String variant;
147.532 +
147.533 +
147.534 + /**
147.535 + * Construct a locale from language, country and variant.
147.536 + * This constructor normalizes the language value to lowercase and
147.537 + * the country value to uppercase.
147.538 + * <p>
147.539 + * <b>Note:</b>
147.540 + * <ul>
147.541 + * <li>ISO 639 is not a stable standard; some of the language codes it defines
147.542 + * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the
147.543 + * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other
147.544 + * API on Locale will return only the OLD codes.
147.545 + * <li>For backward compatibility reasons, this constructor does not make
147.546 + * any syntactic checks on the input.
147.547 + * <li>The two cases ("ja", "JP", "JP") and ("th", "TH", "TH") are handled specially,
147.548 + * see <a href="#special_cases_constructor">Special Cases</a> for more information.
147.549 + * </ul>
147.550 + *
147.551 + * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag
147.552 + * up to 8 characters in length. See the <code>Locale</code> class description about
147.553 + * valid language values.
147.554 + * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code.
147.555 + * See the <code>Locale</code> class description about valid country values.
147.556 + * @param variant Any arbitrary value used to indicate a variation of a <code>Locale</code>.
147.557 + * See the <code>Locale</code> class description for the details.
147.558 + * @exception NullPointerException thrown if any argument is null.
147.559 + */
147.560 + public Locale(String language, String country, String variant) {
147.561 + if (language== null || country == null || variant == null) {
147.562 + throw new NullPointerException();
147.563 + }
147.564 + this.language = language;
147.565 + this.country = country;
147.566 + this.variant = variant;
147.567 + }
147.568 +
147.569 + /**
147.570 + * Construct a locale from language and country.
147.571 + * This constructor normalizes the language value to lowercase and
147.572 + * the country value to uppercase.
147.573 + * <p>
147.574 + * <b>Note:</b>
147.575 + * <ul>
147.576 + * <li>ISO 639 is not a stable standard; some of the language codes it defines
147.577 + * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the
147.578 + * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other
147.579 + * API on Locale will return only the OLD codes.
147.580 + * <li>For backward compatibility reasons, this constructor does not make
147.581 + * any syntactic checks on the input.
147.582 + * </ul>
147.583 + *
147.584 + * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag
147.585 + * up to 8 characters in length. See the <code>Locale</code> class description about
147.586 + * valid language values.
147.587 + * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code.
147.588 + * See the <code>Locale</code> class description about valid country values.
147.589 + * @exception NullPointerException thrown if either argument is null.
147.590 + */
147.591 + public Locale(String language, String country) {
147.592 + this(language, country, "");
147.593 + }
147.594 +
147.595 + /**
147.596 + * Construct a locale from a language code.
147.597 + * This constructor normalizes the language value to lowercase.
147.598 + * <p>
147.599 + * <b>Note:</b>
147.600 + * <ul>
147.601 + * <li>ISO 639 is not a stable standard; some of the language codes it defines
147.602 + * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the
147.603 + * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other
147.604 + * API on Locale will return only the OLD codes.
147.605 + * <li>For backward compatibility reasons, this constructor does not make
147.606 + * any syntactic checks on the input.
147.607 + * </ul>
147.608 + *
147.609 + * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag
147.610 + * up to 8 characters in length. See the <code>Locale</code> class description about
147.611 + * valid language values.
147.612 + * @exception NullPointerException thrown if argument is null.
147.613 + * @since 1.4
147.614 + */
147.615 + public Locale(String language) {
147.616 + this(language, "", "");
147.617 + }
147.618 +
147.619 + /**
147.620 + * This method must be called only for creating the Locale.*
147.621 + * constants due to making shortcuts.
147.622 + */
147.623 + private static Locale createConstant(String lang, String country) {
147.624 + return new Locale(lang, country);
147.625 + }
147.626 +
147.627 + /**
147.628 + * Gets the current value of the default locale for this instance
147.629 + * of the Java Virtual Machine.
147.630 + * <p>
147.631 + * The Java Virtual Machine sets the default locale during startup
147.632 + * based on the host environment. It is used by many locale-sensitive
147.633 + * methods if no locale is explicitly specified.
147.634 + * It can be changed using the
147.635 + * {@link #setDefault(java.util.Locale) setDefault} method.
147.636 + *
147.637 + * @return the default locale for this instance of the Java Virtual Machine
147.638 + */
147.639 + public static Locale getDefault() {
147.640 + return Locale.US;
147.641 + }
147.642 +
147.643 + /**
147.644 + * Gets the current value of the default locale for the specified Category
147.645 + * for this instance of the Java Virtual Machine.
147.646 + * <p>
147.647 + * The Java Virtual Machine sets the default locale during startup based
147.648 + * on the host environment. It is used by many locale-sensitive methods
147.649 + * if no locale is explicitly specified. It can be changed using the
147.650 + * setDefault(Locale.Category, Locale) method.
147.651 + *
147.652 + * @param category - the specified category to get the default locale
147.653 + * @throws NullPointerException - if category is null
147.654 + * @return the default locale for the specified Category for this instance
147.655 + * of the Java Virtual Machine
147.656 + * @see #setDefault(Locale.Category, Locale)
147.657 + * @since 1.7
147.658 + */
147.659 + public static Locale getDefault(Locale.Category category) {
147.660 + return Locale.US;
147.661 + }
147.662 +
147.663 + /**
147.664 + * Sets the default locale for this instance of the Java Virtual Machine.
147.665 + * This does not affect the host locale.
147.666 + * <p>
147.667 + * If there is a security manager, its <code>checkPermission</code>
147.668 + * method is called with a <code>PropertyPermission("user.language", "write")</code>
147.669 + * permission before the default locale is changed.
147.670 + * <p>
147.671 + * The Java Virtual Machine sets the default locale during startup
147.672 + * based on the host environment. It is used by many locale-sensitive
147.673 + * methods if no locale is explicitly specified.
147.674 + * <p>
147.675 + * Since changing the default locale may affect many different areas
147.676 + * of functionality, this method should only be used if the caller
147.677 + * is prepared to reinitialize locale-sensitive code running
147.678 + * within the same Java Virtual Machine.
147.679 + * <p>
147.680 + * By setting the default locale with this method, all of the default
147.681 + * locales for each Category are also set to the specified default locale.
147.682 + *
147.683 + * @throws SecurityException
147.684 + * if a security manager exists and its
147.685 + * <code>checkPermission</code> method doesn't allow the operation.
147.686 + * @throws NullPointerException if <code>newLocale</code> is null
147.687 + * @param newLocale the new default locale
147.688 + * @see SecurityManager#checkPermission
147.689 + * @see java.util.PropertyPermission
147.690 + */
147.691 + public static void setDefault(Locale newLocale) {
147.692 + throw new SecurityException();
147.693 + }
147.694 +
147.695 + /**
147.696 + * Returns an array of all installed locales.
147.697 + * The returned array represents the union of locales supported
147.698 + * by the Java runtime environment and by installed
147.699 + * {@link java.util.spi.LocaleServiceProvider LocaleServiceProvider}
147.700 + * implementations. It must contain at least a <code>Locale</code>
147.701 + * instance equal to {@link java.util.Locale#US Locale.US}.
147.702 + *
147.703 + * @return An array of installed locales.
147.704 + */
147.705 + public static Locale[] getAvailableLocales() {
147.706 + return new Locale[] { Locale.US };
147.707 + }
147.708 +
147.709 + /**
147.710 + * Returns the language code of this Locale.
147.711 + *
147.712 + * <p><b>Note:</b> ISO 639 is not a stable standard— some languages' codes have changed.
147.713 + * Locale's constructor recognizes both the new and the old codes for the languages
147.714 + * whose codes have changed, but this function always returns the old code. If you
147.715 + * want to check for a specific language whose code has changed, don't do
147.716 + * <pre>
147.717 + * if (locale.getLanguage().equals("he")) // BAD!
147.718 + * ...
147.719 + * </pre>
147.720 + * Instead, do
147.721 + * <pre>
147.722 + * if (locale.getLanguage().equals(new Locale("he").getLanguage()))
147.723 + * ...
147.724 + * </pre>
147.725 + * @return The language code, or the empty string if none is defined.
147.726 + * @see #getDisplayLanguage
147.727 + */
147.728 + public String getLanguage() {
147.729 + return language;
147.730 + }
147.731 +
147.732 + /**
147.733 + * Returns the script for this locale, which should
147.734 + * either be the empty string or an ISO 15924 4-letter script
147.735 + * code. The first letter is uppercase and the rest are
147.736 + * lowercase, for example, 'Latn', 'Cyrl'.
147.737 + *
147.738 + * @return The script code, or the empty string if none is defined.
147.739 + * @see #getDisplayScript
147.740 + * @since 1.7
147.741 + */
147.742 + public String getScript() {
147.743 + return "";
147.744 + }
147.745 +
147.746 + /**
147.747 + * Returns the country/region code for this locale, which should
147.748 + * either be the empty string, an uppercase ISO 3166 2-letter code,
147.749 + * or a UN M.49 3-digit code.
147.750 + *
147.751 + * @return The country/region code, or the empty string if none is defined.
147.752 + * @see #getDisplayCountry
147.753 + */
147.754 + public String getCountry() {
147.755 + return country;
147.756 + }
147.757 +
147.758 + /**
147.759 + * Returns the variant code for this locale.
147.760 + *
147.761 + * @return The variant code, or the empty string if none is defined.
147.762 + * @see #getDisplayVariant
147.763 + */
147.764 + public String getVariant() {
147.765 + return variant;
147.766 + }
147.767 +
147.768 + String getRegion() {
147.769 + return getCountry();
147.770 + }
147.771 +
147.772 + /**
147.773 + * Returns the extension (or private use) value associated with
147.774 + * the specified key, or null if there is no extension
147.775 + * associated with the key. To be well-formed, the key must be one
147.776 + * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
147.777 + * for example 'z' and 'Z' represent the same extension.
147.778 + *
147.779 + * @param key the extension key
147.780 + * @return The extension, or null if this locale defines no
147.781 + * extension for the specified key.
147.782 + * @throws IllegalArgumentException if key is not well-formed
147.783 + * @see #PRIVATE_USE_EXTENSION
147.784 + * @see #UNICODE_LOCALE_EXTENSION
147.785 + * @since 1.7
147.786 + */
147.787 + public String getExtension(char key) {
147.788 + return null;
147.789 + }
147.790 +
147.791 + /**
147.792 + * Returns the set of extension keys associated with this locale, or the
147.793 + * empty set if it has no extensions. The returned set is unmodifiable.
147.794 + * The keys will all be lower-case.
147.795 + *
147.796 + * @return The set of extension keys, or the empty set if this locale has
147.797 + * no extensions.
147.798 + * @since 1.7
147.799 + */
147.800 + public Set<Character> getExtensionKeys() {
147.801 + return Collections.emptySet();
147.802 + }
147.803 +
147.804 + /**
147.805 + * Returns the set of unicode locale attributes associated with
147.806 + * this locale, or the empty set if it has no attributes. The
147.807 + * returned set is unmodifiable.
147.808 + *
147.809 + * @return The set of attributes.
147.810 + * @since 1.7
147.811 + */
147.812 + public Set<String> getUnicodeLocaleAttributes() {
147.813 + return Collections.emptySet();
147.814 + }
147.815 +
147.816 + /**
147.817 + * Returns the Unicode locale type associated with the specified Unicode locale key
147.818 + * for this locale. Returns the empty string for keys that are defined with no type.
147.819 + * Returns null if the key is not defined. Keys are case-insensitive. The key must
147.820 + * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
147.821 + * thrown.
147.822 + *
147.823 + * @param key the Unicode locale key
147.824 + * @return The Unicode locale type associated with the key, or null if the
147.825 + * locale does not define the key.
147.826 + * @throws IllegalArgumentException if the key is not well-formed
147.827 + * @throws NullPointerException if <code>key</code> is null
147.828 + * @since 1.7
147.829 + */
147.830 + public String getUnicodeLocaleType(String key) {
147.831 + return null;
147.832 + }
147.833 +
147.834 + /**
147.835 + * Returns the set of Unicode locale keys defined by this locale, or the empty set if
147.836 + * this locale has none. The returned set is immutable. Keys are all lower case.
147.837 + *
147.838 + * @return The set of Unicode locale keys, or the empty set if this locale has
147.839 + * no Unicode locale keywords.
147.840 + * @since 1.7
147.841 + */
147.842 + public Set<String> getUnicodeLocaleKeys() {
147.843 + return Collections.emptySet();
147.844 + }
147.845 +
147.846 + /**
147.847 + * Returns a string representation of this <code>Locale</code>
147.848 + * object, consisting of language, country, variant, script,
147.849 + * and extensions as below:
147.850 + * <p><blockquote>
147.851 + * language + "_" + country + "_" + (variant + "_#" | "#") + script + "-" + extensions
147.852 + * </blockquote>
147.853 + *
147.854 + * Language is always lower case, country is always upper case, script is always title
147.855 + * case, and extensions are always lower case. Extensions and private use subtags
147.856 + * will be in canonical order as explained in {@link #toLanguageTag}.
147.857 + *
147.858 + * <p>When the locale has neither script nor extensions, the result is the same as in
147.859 + * Java 6 and prior.
147.860 + *
147.861 + * <p>If both the language and country fields are missing, this function will return
147.862 + * the empty string, even if the variant, script, or extensions field is present (you
147.863 + * can't have a locale with just a variant, the variant must accompany a well-formed
147.864 + * language or country code).
147.865 + *
147.866 + * <p>If script or extensions are present and variant is missing, no underscore is
147.867 + * added before the "#".
147.868 + *
147.869 + * <p>This behavior is designed to support debugging and to be compatible with
147.870 + * previous uses of <code>toString</code> that expected language, country, and variant
147.871 + * fields only. To represent a Locale as a String for interchange purposes, use
147.872 + * {@link #toLanguageTag}.
147.873 + *
147.874 + * <p>Examples: <ul><tt>
147.875 + * <li>en
147.876 + * <li>de_DE
147.877 + * <li>_GB
147.878 + * <li>en_US_WIN
147.879 + * <li>de__POSIX
147.880 + * <li>zh_CN_#Hans
147.881 + * <li>zh_TW_#Hant-x-java
147.882 + * <li>th_TH_TH_#u-nu-thai</tt></ul>
147.883 + *
147.884 + * @return A string representation of the Locale, for debugging.
147.885 + * @see #getDisplayName
147.886 + * @see #toLanguageTag
147.887 + */
147.888 + @Override
147.889 + public final String toString() {
147.890 + Locale baseLocale = this;
147.891 + boolean l = (baseLocale.getLanguage().length() != 0);
147.892 + boolean s = (baseLocale.getScript().length() != 0);
147.893 + boolean r = (baseLocale.getRegion().length() != 0);
147.894 + boolean v = (baseLocale.getVariant().length() != 0);
147.895 + boolean e = false; //(localeExtensions != null && localeExtensions.getID().length() != 0);
147.896 +
147.897 + StringBuilder result = new StringBuilder(baseLocale.getLanguage());
147.898 + if (r || (l && (v || s || e))) {
147.899 + result.append('_')
147.900 + .append(baseLocale.getRegion()); // This may just append '_'
147.901 + }
147.902 + if (v && (l || r)) {
147.903 + result.append('_')
147.904 + .append(baseLocale.getVariant());
147.905 + }
147.906 +
147.907 + if (s && (l || r)) {
147.908 + result.append("_#")
147.909 + .append(baseLocale.getScript());
147.910 + }
147.911 +
147.912 + if (e && (l || r)) {
147.913 + result.append('_');
147.914 + if (!s) {
147.915 + result.append('#');
147.916 + }
147.917 +// result.append(localeExtensions.getID());
147.918 + }
147.919 +
147.920 + return result.toString();
147.921 + }
147.922 +
147.923 +
147.924 + /**
147.925 + * Overrides Cloneable.
147.926 + */
147.927 + public Object clone()
147.928 + {
147.929 + try {
147.930 + Locale that = (Locale)super.clone();
147.931 + return that;
147.932 + } catch (CloneNotSupportedException e) {
147.933 + throw new InternalError();
147.934 + }
147.935 + }
147.936 +
147.937 + /**
147.938 + * Override hashCode.
147.939 + * Since Locales are often used in hashtables, caches the value
147.940 + * for speed.
147.941 + */
147.942 + @Override
147.943 + public int hashCode() {
147.944 + int hash = 3;
147.945 + hash = 43 * hash + Objects.hashCode(this.language);
147.946 + hash = 43 * hash + Objects.hashCode(this.country);
147.947 + hash = 43 * hash + Objects.hashCode(this.variant);
147.948 + return hash;
147.949 + }
147.950 +
147.951 + // Overrides
147.952 + @Override
147.953 + public boolean equals(Object obj) {
147.954 + if (obj == null) {
147.955 + return false;
147.956 + }
147.957 + if (getClass() != obj.getClass()) {
147.958 + return false;
147.959 + }
147.960 + final Locale other = (Locale) obj;
147.961 + if (!Objects.equals(this.language, other.language)) {
147.962 + return false;
147.963 + }
147.964 + if (!Objects.equals(this.country, other.country)) {
147.965 + return false;
147.966 + }
147.967 + if (!Objects.equals(this.variant, other.variant)) {
147.968 + return false;
147.969 + }
147.970 + return true;
147.971 + }
147.972 +
147.973 + /**
147.974 + * Enum for locale categories. These locale categories are used to get/set
147.975 + * the default locale for the specific functionality represented by the
147.976 + * category.
147.977 + *
147.978 + * @see #getDefault(Locale.Category)
147.979 + * @see #setDefault(Locale.Category, Locale)
147.980 + * @since 1.7
147.981 + */
147.982 + public enum Category {
147.983 +
147.984 + /**
147.985 + * Category used to represent the default locale for
147.986 + * displaying user interfaces.
147.987 + */
147.988 + DISPLAY("user.language.display",
147.989 + "user.script.display",
147.990 + "user.country.display",
147.991 + "user.variant.display"),
147.992 +
147.993 + /**
147.994 + * Category used to represent the default locale for
147.995 + * formatting dates, numbers, and/or currencies.
147.996 + */
147.997 + FORMAT("user.language.format",
147.998 + "user.script.format",
147.999 + "user.country.format",
147.1000 + "user.variant.format");
147.1001 +
147.1002 + Category(String languageKey, String scriptKey, String countryKey, String variantKey) {
147.1003 + this.languageKey = languageKey;
147.1004 + this.scriptKey = scriptKey;
147.1005 + this.countryKey = countryKey;
147.1006 + this.variantKey = variantKey;
147.1007 + }
147.1008 +
147.1009 + final String languageKey;
147.1010 + final String scriptKey;
147.1011 + final String countryKey;
147.1012 + final String variantKey;
147.1013 + }
147.1014 +
147.1015 +}
148.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
148.2 +++ b/rt/emul/compact/src/main/java/java/util/MissingResourceException.java Tue Feb 11 13:31:42 2014 +0100
148.3 @@ -0,0 +1,124 @@
148.4 +/*
148.5 + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
148.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
148.7 + *
148.8 + * This code is free software; you can redistribute it and/or modify it
148.9 + * under the terms of the GNU General Public License version 2 only, as
148.10 + * published by the Free Software Foundation. Oracle designates this
148.11 + * particular file as subject to the "Classpath" exception as provided
148.12 + * by Oracle in the LICENSE file that accompanied this code.
148.13 + *
148.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
148.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
148.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
148.17 + * version 2 for more details (a copy is included in the LICENSE file that
148.18 + * accompanied this code).
148.19 + *
148.20 + * You should have received a copy of the GNU General Public License version
148.21 + * 2 along with this work; if not, write to the Free Software Foundation,
148.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
148.23 + *
148.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
148.25 + * or visit www.oracle.com if you need additional information or have any
148.26 + * questions.
148.27 + */
148.28 +
148.29 +/*
148.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
148.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
148.32 + *
148.33 + * The original version of this source code and documentation
148.34 + * is copyrighted and owned by Taligent, Inc., a wholly-owned
148.35 + * subsidiary of IBM. These materials are provided under terms
148.36 + * of a License Agreement between Taligent and Sun. This technology
148.37 + * is protected by multiple US and International patents.
148.38 + *
148.39 + * This notice and attribution to Taligent may not be removed.
148.40 + * Taligent is a registered trademark of Taligent, Inc.
148.41 + *
148.42 + */
148.43 +
148.44 +package java.util;
148.45 +
148.46 +/**
148.47 + * Signals that a resource is missing.
148.48 + * @see java.lang.Exception
148.49 + * @see ResourceBundle
148.50 + * @author Mark Davis
148.51 + * @since JDK1.1
148.52 + */
148.53 +public
148.54 +class MissingResourceException extends RuntimeException {
148.55 +
148.56 + /**
148.57 + * Constructs a MissingResourceException with the specified information.
148.58 + * A detail message is a String that describes this particular exception.
148.59 + * @param s the detail message
148.60 + * @param className the name of the resource class
148.61 + * @param key the key for the missing resource.
148.62 + */
148.63 + public MissingResourceException(String s, String className, String key) {
148.64 + super(s);
148.65 + this.className = className;
148.66 + this.key = key;
148.67 + }
148.68 +
148.69 + /**
148.70 + * Constructs a <code>MissingResourceException</code> with
148.71 + * <code>message</code>, <code>className</code>, <code>key</code>,
148.72 + * and <code>cause</code>. This constructor is package private for
148.73 + * use by <code>ResourceBundle.getBundle</code>.
148.74 + *
148.75 + * @param message
148.76 + * the detail message
148.77 + * @param className
148.78 + * the name of the resource class
148.79 + * @param key
148.80 + * the key for the missing resource.
148.81 + * @param cause
148.82 + * the cause (which is saved for later retrieval by the
148.83 + * {@link Throwable.getCause()} method). (A null value is
148.84 + * permitted, and indicates that the cause is nonexistent
148.85 + * or unknown.)
148.86 + */
148.87 + MissingResourceException(String message, String className, String key, Throwable cause) {
148.88 + super(message, cause);
148.89 + this.className = className;
148.90 + this.key = key;
148.91 + }
148.92 +
148.93 + /**
148.94 + * Gets parameter passed by constructor.
148.95 + *
148.96 + * @return the name of the resource class
148.97 + */
148.98 + public String getClassName() {
148.99 + return className;
148.100 + }
148.101 +
148.102 + /**
148.103 + * Gets parameter passed by constructor.
148.104 + *
148.105 + * @return the key for the missing resource
148.106 + */
148.107 + public String getKey() {
148.108 + return key;
148.109 + }
148.110 +
148.111 + //============ privates ============
148.112 +
148.113 + // serialization compatibility with JDK1.1
148.114 + private static final long serialVersionUID = -4876345176062000401L;
148.115 +
148.116 + /**
148.117 + * The class name of the resource bundle requested by the user.
148.118 + * @serial
148.119 + */
148.120 + private String className;
148.121 +
148.122 + /**
148.123 + * The name of the specific resource requested by the user.
148.124 + * @serial
148.125 + */
148.126 + private String key;
148.127 +}
149.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
149.2 +++ b/rt/emul/compact/src/main/java/java/util/Properties.java Tue Feb 11 13:31:42 2014 +0100
149.3 @@ -0,0 +1,1113 @@
149.4 +/*
149.5 + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
149.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
149.7 + *
149.8 + * This code is free software; you can redistribute it and/or modify it
149.9 + * under the terms of the GNU General Public License version 2 only, as
149.10 + * published by the Free Software Foundation. Oracle designates this
149.11 + * particular file as subject to the "Classpath" exception as provided
149.12 + * by Oracle in the LICENSE file that accompanied this code.
149.13 + *
149.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
149.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
149.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
149.17 + * version 2 for more details (a copy is included in the LICENSE file that
149.18 + * accompanied this code).
149.19 + *
149.20 + * You should have received a copy of the GNU General Public License version
149.21 + * 2 along with this work; if not, write to the Free Software Foundation,
149.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
149.23 + *
149.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
149.25 + * or visit www.oracle.com if you need additional information or have any
149.26 + * questions.
149.27 + */
149.28 +
149.29 +package java.util;
149.30 +
149.31 +import java.io.IOException;
149.32 +import java.io.PrintStream;
149.33 +import java.io.PrintWriter;
149.34 +import java.io.InputStream;
149.35 +import java.io.OutputStream;
149.36 +import java.io.Reader;
149.37 +import java.io.Writer;
149.38 +import java.io.OutputStreamWriter;
149.39 +import java.io.BufferedWriter;
149.40 +
149.41 +/**
149.42 + * The <code>Properties</code> class represents a persistent set of
149.43 + * properties. The <code>Properties</code> can be saved to a stream
149.44 + * or loaded from a stream. Each key and its corresponding value in
149.45 + * the property list is a string.
149.46 + * <p>
149.47 + * A property list can contain another property list as its
149.48 + * "defaults"; this second property list is searched if
149.49 + * the property key is not found in the original property list.
149.50 + * <p>
149.51 + * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
149.52 + * <code>put</code> and <code>putAll</code> methods can be applied to a
149.53 + * <code>Properties</code> object. Their use is strongly discouraged as they
149.54 + * allow the caller to insert entries whose keys or values are not
149.55 + * <code>Strings</code>. The <code>setProperty</code> method should be used
149.56 + * instead. If the <code>store</code> or <code>save</code> method is called
149.57 + * on a "compromised" <code>Properties</code> object that contains a
149.58 + * non-<code>String</code> key or value, the call will fail. Similarly,
149.59 + * the call to the <code>propertyNames</code> or <code>list</code> method
149.60 + * will fail if it is called on a "compromised" <code>Properties</code>
149.61 + * object that contains a non-<code>String</code> key.
149.62 + *
149.63 + * <p>
149.64 + * The {@link #load(java.io.Reader) load(Reader)} <tt>/</tt>
149.65 + * {@link #store(java.io.Writer, java.lang.String) store(Writer, String)}
149.66 + * methods load and store properties from and to a character based stream
149.67 + * in a simple line-oriented format specified below.
149.68 + *
149.69 + * The {@link #load(java.io.InputStream) load(InputStream)} <tt>/</tt>
149.70 + * {@link #store(java.io.OutputStream, java.lang.String) store(OutputStream, String)}
149.71 + * methods work the same way as the load(Reader)/store(Writer, String) pair, except
149.72 + * the input/output stream is encoded in ISO 8859-1 character encoding.
149.73 + * Characters that cannot be directly represented in this encoding can be written using
149.74 + * Unicode escapes as defined in section 3.3 of
149.75 + * <cite>The Java™ Language Specification</cite>;
149.76 + * only a single 'u' character is allowed in an escape
149.77 + * sequence. The native2ascii tool can be used to convert property files to and
149.78 + * from other character encodings.
149.79 + *
149.80 + * <p> The {@link #loadFromXML(InputStream)} and {@link
149.81 + * #storeToXML(OutputStream, String, String)} methods load and store properties
149.82 + * in a simple XML format. By default the UTF-8 character encoding is used,
149.83 + * however a specific encoding may be specified if required. An XML properties
149.84 + * document has the following DOCTYPE declaration:
149.85 + *
149.86 + * <pre>
149.87 + * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
149.88 + * </pre>
149.89 + * Note that the system URI (http://java.sun.com/dtd/properties.dtd) is
149.90 + * <i>not</i> accessed when exporting or importing properties; it merely
149.91 + * serves as a string to uniquely identify the DTD, which is:
149.92 + * <pre>
149.93 + * <?xml version="1.0" encoding="UTF-8"?>
149.94 + *
149.95 + * <!-- DTD for properties -->
149.96 + *
149.97 + * <!ELEMENT properties ( comment?, entry* ) >
149.98 + *
149.99 + * <!ATTLIST properties version CDATA #FIXED "1.0">
149.100 + *
149.101 + * <!ELEMENT comment (#PCDATA) >
149.102 + *
149.103 + * <!ELEMENT entry (#PCDATA) >
149.104 + *
149.105 + * <!ATTLIST entry key CDATA #REQUIRED>
149.106 + * </pre>
149.107 + *
149.108 + * <p>This class is thread-safe: multiple threads can share a single
149.109 + * <tt>Properties</tt> object without the need for external synchronization.
149.110 + *
149.111 + * @see <a href="../../../technotes/tools/solaris/native2ascii.html">native2ascii tool for Solaris</a>
149.112 + * @see <a href="../../../technotes/tools/windows/native2ascii.html">native2ascii tool for Windows</a>
149.113 + *
149.114 + * @author Arthur van Hoff
149.115 + * @author Michael McCloskey
149.116 + * @author Xueming Shen
149.117 + * @since JDK1.0
149.118 + */
149.119 +public
149.120 +class Properties extends Hashtable<Object,Object> {
149.121 + /**
149.122 + * use serialVersionUID from JDK 1.1.X for interoperability
149.123 + */
149.124 + private static final long serialVersionUID = 4112578634029874840L;
149.125 +
149.126 + /**
149.127 + * A property list that contains default values for any keys not
149.128 + * found in this property list.
149.129 + *
149.130 + * @serial
149.131 + */
149.132 + protected Properties defaults;
149.133 +
149.134 + /**
149.135 + * Creates an empty property list with no default values.
149.136 + */
149.137 + public Properties() {
149.138 + this(null);
149.139 + }
149.140 +
149.141 + /**
149.142 + * Creates an empty property list with the specified defaults.
149.143 + *
149.144 + * @param defaults the defaults.
149.145 + */
149.146 + public Properties(Properties defaults) {
149.147 + this.defaults = defaults;
149.148 + }
149.149 +
149.150 + /**
149.151 + * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for
149.152 + * parallelism with the <tt>getProperty</tt> method. Enforces use of
149.153 + * strings for property keys and values. The value returned is the
149.154 + * result of the <tt>Hashtable</tt> call to <code>put</code>.
149.155 + *
149.156 + * @param key the key to be placed into this property list.
149.157 + * @param value the value corresponding to <tt>key</tt>.
149.158 + * @return the previous value of the specified key in this property
149.159 + * list, or <code>null</code> if it did not have one.
149.160 + * @see #getProperty
149.161 + * @since 1.2
149.162 + */
149.163 + public synchronized Object setProperty(String key, String value) {
149.164 + return put(key, value);
149.165 + }
149.166 +
149.167 +
149.168 + /**
149.169 + * Reads a property list (key and element pairs) from the input
149.170 + * character stream in a simple line-oriented format.
149.171 + * <p>
149.172 + * Properties are processed in terms of lines. There are two
149.173 + * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
149.174 + * A natural line is defined as a line of
149.175 + * characters that is terminated either by a set of line terminator
149.176 + * characters (<code>\n</code> or <code>\r</code> or <code>\r\n</code>)
149.177 + * or by the end of the stream. A natural line may be either a blank line,
149.178 + * a comment line, or hold all or some of a key-element pair. A logical
149.179 + * line holds all the data of a key-element pair, which may be spread
149.180 + * out across several adjacent natural lines by escaping
149.181 + * the line terminator sequence with a backslash character
149.182 + * <code>\</code>. Note that a comment line cannot be extended
149.183 + * in this manner; every natural line that is a comment must have
149.184 + * its own comment indicator, as described below. Lines are read from
149.185 + * input until the end of the stream is reached.
149.186 + *
149.187 + * <p>
149.188 + * A natural line that contains only white space characters is
149.189 + * considered blank and is ignored. A comment line has an ASCII
149.190 + * <code>'#'</code> or <code>'!'</code> as its first non-white
149.191 + * space character; comment lines are also ignored and do not
149.192 + * encode key-element information. In addition to line
149.193 + * terminators, this format considers the characters space
149.194 + * (<code>' '</code>, <code>'\u0020'</code>), tab
149.195 + * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed
149.196 + * (<code>'\f'</code>, <code>'\u000C'</code>) to be white
149.197 + * space.
149.198 + *
149.199 + * <p>
149.200 + * If a logical line is spread across several natural lines, the
149.201 + * backslash escaping the line terminator sequence, the line
149.202 + * terminator sequence, and any white space at the start of the
149.203 + * following line have no affect on the key or element values.
149.204 + * The remainder of the discussion of key and element parsing
149.205 + * (when loading) will assume all the characters constituting
149.206 + * the key and element appear on a single natural line after
149.207 + * line continuation characters have been removed. Note that
149.208 + * it is <i>not</i> sufficient to only examine the character
149.209 + * preceding a line terminator sequence to decide if the line
149.210 + * terminator is escaped; there must be an odd number of
149.211 + * contiguous backslashes for the line terminator to be escaped.
149.212 + * Since the input is processed from left to right, a
149.213 + * non-zero even number of 2<i>n</i> contiguous backslashes
149.214 + * before a line terminator (or elsewhere) encodes <i>n</i>
149.215 + * backslashes after escape processing.
149.216 + *
149.217 + * <p>
149.218 + * The key contains all of the characters in the line starting
149.219 + * with the first non-white space character and up to, but not
149.220 + * including, the first unescaped <code>'='</code>,
149.221 + * <code>':'</code>, or white space character other than a line
149.222 + * terminator. All of these key termination characters may be
149.223 + * included in the key by escaping them with a preceding backslash
149.224 + * character; for example,<p>
149.225 + *
149.226 + * <code>\:\=</code><p>
149.227 + *
149.228 + * would be the two-character key <code>":="</code>. Line
149.229 + * terminator characters can be included using <code>\r</code> and
149.230 + * <code>\n</code> escape sequences. Any white space after the
149.231 + * key is skipped; if the first non-white space character after
149.232 + * the key is <code>'='</code> or <code>':'</code>, then it is
149.233 + * ignored and any white space characters after it are also
149.234 + * skipped. All remaining characters on the line become part of
149.235 + * the associated element string; if there are no remaining
149.236 + * characters, the element is the empty string
149.237 + * <code>""</code>. Once the raw character sequences
149.238 + * constituting the key and element are identified, escape
149.239 + * processing is performed as described above.
149.240 + *
149.241 + * <p>
149.242 + * As an example, each of the following three lines specifies the key
149.243 + * <code>"Truth"</code> and the associated element value
149.244 + * <code>"Beauty"</code>:
149.245 + * <p>
149.246 + * <pre>
149.247 + * Truth = Beauty
149.248 + * Truth:Beauty
149.249 + * Truth :Beauty
149.250 + * </pre>
149.251 + * As another example, the following three lines specify a single
149.252 + * property:
149.253 + * <p>
149.254 + * <pre>
149.255 + * fruits apple, banana, pear, \
149.256 + * cantaloupe, watermelon, \
149.257 + * kiwi, mango
149.258 + * </pre>
149.259 + * The key is <code>"fruits"</code> and the associated element is:
149.260 + * <p>
149.261 + * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
149.262 + * Note that a space appears before each <code>\</code> so that a space
149.263 + * will appear after each comma in the final result; the <code>\</code>,
149.264 + * line terminator, and leading white space on the continuation line are
149.265 + * merely discarded and are <i>not</i> replaced by one or more other
149.266 + * characters.
149.267 + * <p>
149.268 + * As a third example, the line:
149.269 + * <p>
149.270 + * <pre>cheeses
149.271 + * </pre>
149.272 + * specifies that the key is <code>"cheeses"</code> and the associated
149.273 + * element is the empty string <code>""</code>.<p>
149.274 + * <p>
149.275 + *
149.276 + * <a name="unicodeescapes"></a>
149.277 + * Characters in keys and elements can be represented in escape
149.278 + * sequences similar to those used for character and string literals
149.279 + * (see sections 3.3 and 3.10.6 of
149.280 + * <cite>The Java™ Language Specification</cite>).
149.281 + *
149.282 + * The differences from the character escape sequences and Unicode
149.283 + * escapes used for characters and strings are:
149.284 + *
149.285 + * <ul>
149.286 + * <li> Octal escapes are not recognized.
149.287 + *
149.288 + * <li> The character sequence <code>\b</code> does <i>not</i>
149.289 + * represent a backspace character.
149.290 + *
149.291 + * <li> The method does not treat a backslash character,
149.292 + * <code>\</code>, before a non-valid escape character as an
149.293 + * error; the backslash is silently dropped. For example, in a
149.294 + * Java string the sequence <code>"\z"</code> would cause a
149.295 + * compile time error. In contrast, this method silently drops
149.296 + * the backslash. Therefore, this method treats the two character
149.297 + * sequence <code>"\b"</code> as equivalent to the single
149.298 + * character <code>'b'</code>.
149.299 + *
149.300 + * <li> Escapes are not necessary for single and double quotes;
149.301 + * however, by the rule above, single and double quote characters
149.302 + * preceded by a backslash still yield single and double quote
149.303 + * characters, respectively.
149.304 + *
149.305 + * <li> Only a single 'u' character is allowed in a Uniocde escape
149.306 + * sequence.
149.307 + *
149.308 + * </ul>
149.309 + * <p>
149.310 + * The specified stream remains open after this method returns.
149.311 + *
149.312 + * @param reader the input character stream.
149.313 + * @throws IOException if an error occurred when reading from the
149.314 + * input stream.
149.315 + * @throws IllegalArgumentException if a malformed Unicode escape
149.316 + * appears in the input.
149.317 + * @since 1.6
149.318 + */
149.319 + public synchronized void load(Reader reader) throws IOException {
149.320 + load0(new LineReader(reader));
149.321 + }
149.322 +
149.323 + /**
149.324 + * Reads a property list (key and element pairs) from the input
149.325 + * byte stream. The input stream is in a simple line-oriented
149.326 + * format as specified in
149.327 + * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
149.328 + * the ISO 8859-1 character encoding; that is each byte is one Latin1
149.329 + * character. Characters not in Latin1, and certain special characters,
149.330 + * are represented in keys and elements using Unicode escapes as defined in
149.331 + * section 3.3 of
149.332 + * <cite>The Java™ Language Specification</cite>.
149.333 + * <p>
149.334 + * The specified stream remains open after this method returns.
149.335 + *
149.336 + * @param inStream the input stream.
149.337 + * @exception IOException if an error occurred when reading from the
149.338 + * input stream.
149.339 + * @throws IllegalArgumentException if the input stream contains a
149.340 + * malformed Unicode escape sequence.
149.341 + * @since 1.2
149.342 + */
149.343 + public synchronized void load(InputStream inStream) throws IOException {
149.344 + load0(new LineReader(inStream));
149.345 + }
149.346 +
149.347 + private void load0 (LineReader lr) throws IOException {
149.348 + char[] convtBuf = new char[1024];
149.349 + int limit;
149.350 + int keyLen;
149.351 + int valueStart;
149.352 + char c;
149.353 + boolean hasSep;
149.354 + boolean precedingBackslash;
149.355 +
149.356 + while ((limit = lr.readLine()) >= 0) {
149.357 + c = 0;
149.358 + keyLen = 0;
149.359 + valueStart = limit;
149.360 + hasSep = false;
149.361 +
149.362 + //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
149.363 + precedingBackslash = false;
149.364 + while (keyLen < limit) {
149.365 + c = lr.lineBuf[keyLen];
149.366 + //need check if escaped.
149.367 + if ((c == '=' || c == ':') && !precedingBackslash) {
149.368 + valueStart = keyLen + 1;
149.369 + hasSep = true;
149.370 + break;
149.371 + } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
149.372 + valueStart = keyLen + 1;
149.373 + break;
149.374 + }
149.375 + if (c == '\\') {
149.376 + precedingBackslash = !precedingBackslash;
149.377 + } else {
149.378 + precedingBackslash = false;
149.379 + }
149.380 + keyLen++;
149.381 + }
149.382 + while (valueStart < limit) {
149.383 + c = lr.lineBuf[valueStart];
149.384 + if (c != ' ' && c != '\t' && c != '\f') {
149.385 + if (!hasSep && (c == '=' || c == ':')) {
149.386 + hasSep = true;
149.387 + } else {
149.388 + break;
149.389 + }
149.390 + }
149.391 + valueStart++;
149.392 + }
149.393 + String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
149.394 + String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
149.395 + put(key, value);
149.396 + }
149.397 + }
149.398 +
149.399 + /* Read in a "logical line" from an InputStream/Reader, skip all comment
149.400 + * and blank lines and filter out those leading whitespace characters
149.401 + * (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
149.402 + * Method returns the char length of the "logical line" and stores
149.403 + * the line in "lineBuf".
149.404 + */
149.405 + class LineReader {
149.406 + public LineReader(InputStream inStream) {
149.407 + this.inStream = inStream;
149.408 + inByteBuf = new byte[8192];
149.409 + }
149.410 +
149.411 + public LineReader(Reader reader) {
149.412 + this.reader = reader;
149.413 + inCharBuf = new char[8192];
149.414 + }
149.415 +
149.416 + byte[] inByteBuf;
149.417 + char[] inCharBuf;
149.418 + char[] lineBuf = new char[1024];
149.419 + int inLimit = 0;
149.420 + int inOff = 0;
149.421 + InputStream inStream;
149.422 + Reader reader;
149.423 +
149.424 + int readLine() throws IOException {
149.425 + int len = 0;
149.426 + char c = 0;
149.427 +
149.428 + boolean skipWhiteSpace = true;
149.429 + boolean isCommentLine = false;
149.430 + boolean isNewLine = true;
149.431 + boolean appendedLineBegin = false;
149.432 + boolean precedingBackslash = false;
149.433 + boolean skipLF = false;
149.434 +
149.435 + while (true) {
149.436 + if (inOff >= inLimit) {
149.437 + inLimit = (inStream==null)?reader.read(inCharBuf)
149.438 + :inStream.read(inByteBuf);
149.439 + inOff = 0;
149.440 + if (inLimit <= 0) {
149.441 + if (len == 0 || isCommentLine) {
149.442 + return -1;
149.443 + }
149.444 + return len;
149.445 + }
149.446 + }
149.447 + if (inStream != null) {
149.448 + //The line below is equivalent to calling a
149.449 + //ISO8859-1 decoder.
149.450 + c = (char) (0xff & inByteBuf[inOff++]);
149.451 + } else {
149.452 + c = inCharBuf[inOff++];
149.453 + }
149.454 + if (skipLF) {
149.455 + skipLF = false;
149.456 + if (c == '\n') {
149.457 + continue;
149.458 + }
149.459 + }
149.460 + if (skipWhiteSpace) {
149.461 + if (c == ' ' || c == '\t' || c == '\f') {
149.462 + continue;
149.463 + }
149.464 + if (!appendedLineBegin && (c == '\r' || c == '\n')) {
149.465 + continue;
149.466 + }
149.467 + skipWhiteSpace = false;
149.468 + appendedLineBegin = false;
149.469 + }
149.470 + if (isNewLine) {
149.471 + isNewLine = false;
149.472 + if (c == '#' || c == '!') {
149.473 + isCommentLine = true;
149.474 + continue;
149.475 + }
149.476 + }
149.477 +
149.478 + if (c != '\n' && c != '\r') {
149.479 + lineBuf[len++] = c;
149.480 + if (len == lineBuf.length) {
149.481 + int newLength = lineBuf.length * 2;
149.482 + if (newLength < 0) {
149.483 + newLength = Integer.MAX_VALUE;
149.484 + }
149.485 + char[] buf = new char[newLength];
149.486 + System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
149.487 + lineBuf = buf;
149.488 + }
149.489 + //flip the preceding backslash flag
149.490 + if (c == '\\') {
149.491 + precedingBackslash = !precedingBackslash;
149.492 + } else {
149.493 + precedingBackslash = false;
149.494 + }
149.495 + }
149.496 + else {
149.497 + // reached EOL
149.498 + if (isCommentLine || len == 0) {
149.499 + isCommentLine = false;
149.500 + isNewLine = true;
149.501 + skipWhiteSpace = true;
149.502 + len = 0;
149.503 + continue;
149.504 + }
149.505 + if (inOff >= inLimit) {
149.506 + inLimit = (inStream==null)
149.507 + ?reader.read(inCharBuf)
149.508 + :inStream.read(inByteBuf);
149.509 + inOff = 0;
149.510 + if (inLimit <= 0) {
149.511 + return len;
149.512 + }
149.513 + }
149.514 + if (precedingBackslash) {
149.515 + len -= 1;
149.516 + //skip the leading whitespace characters in following line
149.517 + skipWhiteSpace = true;
149.518 + appendedLineBegin = true;
149.519 + precedingBackslash = false;
149.520 + if (c == '\r') {
149.521 + skipLF = true;
149.522 + }
149.523 + } else {
149.524 + return len;
149.525 + }
149.526 + }
149.527 + }
149.528 + }
149.529 + }
149.530 +
149.531 + /*
149.532 + * Converts encoded \uxxxx to unicode chars
149.533 + * and changes special saved chars to their original forms
149.534 + */
149.535 + private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
149.536 + if (convtBuf.length < len) {
149.537 + int newLen = len * 2;
149.538 + if (newLen < 0) {
149.539 + newLen = Integer.MAX_VALUE;
149.540 + }
149.541 + convtBuf = new char[newLen];
149.542 + }
149.543 + char aChar;
149.544 + char[] out = convtBuf;
149.545 + int outLen = 0;
149.546 + int end = off + len;
149.547 +
149.548 + while (off < end) {
149.549 + aChar = in[off++];
149.550 + if (aChar == '\\') {
149.551 + aChar = in[off++];
149.552 + if(aChar == 'u') {
149.553 + // Read the xxxx
149.554 + int value=0;
149.555 + for (int i=0; i<4; i++) {
149.556 + aChar = in[off++];
149.557 + switch (aChar) {
149.558 + case '0': case '1': case '2': case '3': case '4':
149.559 + case '5': case '6': case '7': case '8': case '9':
149.560 + value = (value << 4) + aChar - '0';
149.561 + break;
149.562 + case 'a': case 'b': case 'c':
149.563 + case 'd': case 'e': case 'f':
149.564 + value = (value << 4) + 10 + aChar - 'a';
149.565 + break;
149.566 + case 'A': case 'B': case 'C':
149.567 + case 'D': case 'E': case 'F':
149.568 + value = (value << 4) + 10 + aChar - 'A';
149.569 + break;
149.570 + default:
149.571 + throw new IllegalArgumentException(
149.572 + "Malformed \\uxxxx encoding.");
149.573 + }
149.574 + }
149.575 + out[outLen++] = (char)value;
149.576 + } else {
149.577 + if (aChar == 't') aChar = '\t';
149.578 + else if (aChar == 'r') aChar = '\r';
149.579 + else if (aChar == 'n') aChar = '\n';
149.580 + else if (aChar == 'f') aChar = '\f';
149.581 + out[outLen++] = aChar;
149.582 + }
149.583 + } else {
149.584 + out[outLen++] = aChar;
149.585 + }
149.586 + }
149.587 + return new String (out, 0, outLen);
149.588 + }
149.589 +
149.590 + /*
149.591 + * Converts unicodes to encoded \uxxxx and escapes
149.592 + * special characters with a preceding slash
149.593 + */
149.594 + private String saveConvert(String theString,
149.595 + boolean escapeSpace,
149.596 + boolean escapeUnicode) {
149.597 + int len = theString.length();
149.598 + int bufLen = len * 2;
149.599 + if (bufLen < 0) {
149.600 + bufLen = Integer.MAX_VALUE;
149.601 + }
149.602 + StringBuffer outBuffer = new StringBuffer(bufLen);
149.603 +
149.604 + for(int x=0; x<len; x++) {
149.605 + char aChar = theString.charAt(x);
149.606 + // Handle common case first, selecting largest block that
149.607 + // avoids the specials below
149.608 + if ((aChar > 61) && (aChar < 127)) {
149.609 + if (aChar == '\\') {
149.610 + outBuffer.append('\\'); outBuffer.append('\\');
149.611 + continue;
149.612 + }
149.613 + outBuffer.append(aChar);
149.614 + continue;
149.615 + }
149.616 + switch(aChar) {
149.617 + case ' ':
149.618 + if (x == 0 || escapeSpace)
149.619 + outBuffer.append('\\');
149.620 + outBuffer.append(' ');
149.621 + break;
149.622 + case '\t':outBuffer.append('\\'); outBuffer.append('t');
149.623 + break;
149.624 + case '\n':outBuffer.append('\\'); outBuffer.append('n');
149.625 + break;
149.626 + case '\r':outBuffer.append('\\'); outBuffer.append('r');
149.627 + break;
149.628 + case '\f':outBuffer.append('\\'); outBuffer.append('f');
149.629 + break;
149.630 + case '=': // Fall through
149.631 + case ':': // Fall through
149.632 + case '#': // Fall through
149.633 + case '!':
149.634 + outBuffer.append('\\'); outBuffer.append(aChar);
149.635 + break;
149.636 + default:
149.637 + if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) {
149.638 + outBuffer.append('\\');
149.639 + outBuffer.append('u');
149.640 + outBuffer.append(toHex((aChar >> 12) & 0xF));
149.641 + outBuffer.append(toHex((aChar >> 8) & 0xF));
149.642 + outBuffer.append(toHex((aChar >> 4) & 0xF));
149.643 + outBuffer.append(toHex( aChar & 0xF));
149.644 + } else {
149.645 + outBuffer.append(aChar);
149.646 + }
149.647 + }
149.648 + }
149.649 + return outBuffer.toString();
149.650 + }
149.651 +
149.652 + private static void writeComments(BufferedWriter bw, String comments)
149.653 + throws IOException {
149.654 + bw.write("#");
149.655 + int len = comments.length();
149.656 + int current = 0;
149.657 + int last = 0;
149.658 + char[] uu = new char[6];
149.659 + uu[0] = '\\';
149.660 + uu[1] = 'u';
149.661 + while (current < len) {
149.662 + char c = comments.charAt(current);
149.663 + if (c > '\u00ff' || c == '\n' || c == '\r') {
149.664 + if (last != current)
149.665 + bw.write(comments.substring(last, current));
149.666 + if (c > '\u00ff') {
149.667 + uu[2] = toHex((c >> 12) & 0xf);
149.668 + uu[3] = toHex((c >> 8) & 0xf);
149.669 + uu[4] = toHex((c >> 4) & 0xf);
149.670 + uu[5] = toHex( c & 0xf);
149.671 + bw.write(new String(uu));
149.672 + } else {
149.673 + bw.newLine();
149.674 + if (c == '\r' &&
149.675 + current != len - 1 &&
149.676 + comments.charAt(current + 1) == '\n') {
149.677 + current++;
149.678 + }
149.679 + if (current == len - 1 ||
149.680 + (comments.charAt(current + 1) != '#' &&
149.681 + comments.charAt(current + 1) != '!'))
149.682 + bw.write("#");
149.683 + }
149.684 + last = current + 1;
149.685 + }
149.686 + current++;
149.687 + }
149.688 + if (last != current)
149.689 + bw.write(comments.substring(last, current));
149.690 + bw.newLine();
149.691 + }
149.692 +
149.693 + /**
149.694 + * Calls the <code>store(OutputStream out, String comments)</code> method
149.695 + * and suppresses IOExceptions that were thrown.
149.696 + *
149.697 + * @deprecated This method does not throw an IOException if an I/O error
149.698 + * occurs while saving the property list. The preferred way to save a
149.699 + * properties list is via the <code>store(OutputStream out,
149.700 + * String comments)</code> method or the
149.701 + * <code>storeToXML(OutputStream os, String comment)</code> method.
149.702 + *
149.703 + * @param out an output stream.
149.704 + * @param comments a description of the property list.
149.705 + * @exception ClassCastException if this <code>Properties</code> object
149.706 + * contains any keys or values that are not
149.707 + * <code>Strings</code>.
149.708 + */
149.709 + @Deprecated
149.710 + public void save(OutputStream out, String comments) {
149.711 + try {
149.712 + store(out, comments);
149.713 + } catch (IOException e) {
149.714 + }
149.715 + }
149.716 +
149.717 + /**
149.718 + * Writes this property list (key and element pairs) in this
149.719 + * <code>Properties</code> table to the output character stream in a
149.720 + * format suitable for using the {@link #load(java.io.Reader) load(Reader)}
149.721 + * method.
149.722 + * <p>
149.723 + * Properties from the defaults table of this <code>Properties</code>
149.724 + * table (if any) are <i>not</i> written out by this method.
149.725 + * <p>
149.726 + * If the comments argument is not null, then an ASCII <code>#</code>
149.727 + * character, the comments string, and a line separator are first written
149.728 + * to the output stream. Thus, the <code>comments</code> can serve as an
149.729 + * identifying comment. Any one of a line feed ('\n'), a carriage
149.730 + * return ('\r'), or a carriage return followed immediately by a line feed
149.731 + * in comments is replaced by a line separator generated by the <code>Writer</code>
149.732 + * and if the next character in comments is not character <code>#</code> or
149.733 + * character <code>!</code> then an ASCII <code>#</code> is written out
149.734 + * after that line separator.
149.735 + * <p>
149.736 + * Next, a comment line is always written, consisting of an ASCII
149.737 + * <code>#</code> character, the current date and time (as if produced
149.738 + * by the <code>toString</code> method of <code>Date</code> for the
149.739 + * current time), and a line separator as generated by the <code>Writer</code>.
149.740 + * <p>
149.741 + * Then every entry in this <code>Properties</code> table is
149.742 + * written out, one per line. For each entry the key string is
149.743 + * written, then an ASCII <code>=</code>, then the associated
149.744 + * element string. For the key, all space characters are
149.745 + * written with a preceding <code>\</code> character. For the
149.746 + * element, leading space characters, but not embedded or trailing
149.747 + * space characters, are written with a preceding <code>\</code>
149.748 + * character. The key and element characters <code>#</code>,
149.749 + * <code>!</code>, <code>=</code>, and <code>:</code> are written
149.750 + * with a preceding backslash to ensure that they are properly loaded.
149.751 + * <p>
149.752 + * After the entries have been written, the output stream is flushed.
149.753 + * The output stream remains open after this method returns.
149.754 + * <p>
149.755 + *
149.756 + * @param writer an output character stream writer.
149.757 + * @param comments a description of the property list.
149.758 + * @exception IOException if writing this property list to the specified
149.759 + * output stream throws an <tt>IOException</tt>.
149.760 + * @exception ClassCastException if this <code>Properties</code> object
149.761 + * contains any keys or values that are not <code>Strings</code>.
149.762 + * @exception NullPointerException if <code>writer</code> is null.
149.763 + * @since 1.6
149.764 + */
149.765 + public void store(Writer writer, String comments)
149.766 + throws IOException
149.767 + {
149.768 + store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
149.769 + : new BufferedWriter(writer),
149.770 + comments,
149.771 + false);
149.772 + }
149.773 +
149.774 + /**
149.775 + * Writes this property list (key and element pairs) in this
149.776 + * <code>Properties</code> table to the output stream in a format suitable
149.777 + * for loading into a <code>Properties</code> table using the
149.778 + * {@link #load(InputStream) load(InputStream)} method.
149.779 + * <p>
149.780 + * Properties from the defaults table of this <code>Properties</code>
149.781 + * table (if any) are <i>not</i> written out by this method.
149.782 + * <p>
149.783 + * This method outputs the comments, properties keys and values in
149.784 + * the same format as specified in
149.785 + * {@link #store(java.io.Writer, java.lang.String) store(Writer)},
149.786 + * with the following differences:
149.787 + * <ul>
149.788 + * <li>The stream is written using the ISO 8859-1 character encoding.
149.789 + *
149.790 + * <li>Characters not in Latin-1 in the comments are written as
149.791 + * <code>\u</code><i>xxxx</i> for their appropriate unicode
149.792 + * hexadecimal value <i>xxxx</i>.
149.793 + *
149.794 + * <li>Characters less than <code>\u0020</code> and characters greater
149.795 + * than <code>\u007E</code> in property keys or values are written
149.796 + * as <code>\u</code><i>xxxx</i> for the appropriate hexadecimal
149.797 + * value <i>xxxx</i>.
149.798 + * </ul>
149.799 + * <p>
149.800 + * After the entries have been written, the output stream is flushed.
149.801 + * The output stream remains open after this method returns.
149.802 + * <p>
149.803 + * @param out an output stream.
149.804 + * @param comments a description of the property list.
149.805 + * @exception IOException if writing this property list to the specified
149.806 + * output stream throws an <tt>IOException</tt>.
149.807 + * @exception ClassCastException if this <code>Properties</code> object
149.808 + * contains any keys or values that are not <code>Strings</code>.
149.809 + * @exception NullPointerException if <code>out</code> is null.
149.810 + * @since 1.2
149.811 + */
149.812 + public void store(OutputStream out, String comments)
149.813 + throws IOException
149.814 + {
149.815 + store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
149.816 + comments,
149.817 + true);
149.818 + }
149.819 +
149.820 + private void store0(BufferedWriter bw, String comments, boolean escUnicode)
149.821 + throws IOException
149.822 + {
149.823 + if (comments != null) {
149.824 + writeComments(bw, comments);
149.825 + }
149.826 + bw.write("#" + new Date().toString());
149.827 + bw.newLine();
149.828 + synchronized (this) {
149.829 + for (Enumeration e = keys(); e.hasMoreElements();) {
149.830 + String key = (String)e.nextElement();
149.831 + String val = (String)get(key);
149.832 + key = saveConvert(key, true, escUnicode);
149.833 + /* No need to escape embedded and trailing spaces for value, hence
149.834 + * pass false to flag.
149.835 + */
149.836 + val = saveConvert(val, false, escUnicode);
149.837 + bw.write(key + "=" + val);
149.838 + bw.newLine();
149.839 + }
149.840 + }
149.841 + bw.flush();
149.842 + }
149.843 +
149.844 + /**
149.845 + * Loads all of the properties represented by the XML document on the
149.846 + * specified input stream into this properties table.
149.847 + *
149.848 + * <p>The XML document must have the following DOCTYPE declaration:
149.849 + * <pre>
149.850 + * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
149.851 + * </pre>
149.852 + * Furthermore, the document must satisfy the properties DTD described
149.853 + * above.
149.854 + *
149.855 + * <p>The specified stream is closed after this method returns.
149.856 + *
149.857 + * @param in the input stream from which to read the XML document.
149.858 + * @throws IOException if reading from the specified input stream
149.859 + * results in an <tt>IOException</tt>.
149.860 + * @throws InvalidPropertiesFormatException Data on input stream does not
149.861 + * constitute a valid XML document with the mandated document type.
149.862 + * @throws NullPointerException if <code>in</code> is null.
149.863 + * @see #storeToXML(OutputStream, String, String)
149.864 + * @since 1.5
149.865 + */
149.866 + public synchronized void loadFromXML(InputStream in)
149.867 + throws IOException
149.868 + {
149.869 + if (in == null)
149.870 + throw new NullPointerException();
149.871 + throw new IOException();
149.872 + }
149.873 +
149.874 + /**
149.875 + * Emits an XML document representing all of the properties contained
149.876 + * in this table.
149.877 + *
149.878 + * <p> An invocation of this method of the form <tt>props.storeToXML(os,
149.879 + * comment)</tt> behaves in exactly the same way as the invocation
149.880 + * <tt>props.storeToXML(os, comment, "UTF-8");</tt>.
149.881 + *
149.882 + * @param os the output stream on which to emit the XML document.
149.883 + * @param comment a description of the property list, or <code>null</code>
149.884 + * if no comment is desired.
149.885 + * @throws IOException if writing to the specified output stream
149.886 + * results in an <tt>IOException</tt>.
149.887 + * @throws NullPointerException if <code>os</code> is null.
149.888 + * @throws ClassCastException if this <code>Properties</code> object
149.889 + * contains any keys or values that are not
149.890 + * <code>Strings</code>.
149.891 + * @see #loadFromXML(InputStream)
149.892 + * @since 1.5
149.893 + */
149.894 + public void storeToXML(OutputStream os, String comment)
149.895 + throws IOException
149.896 + {
149.897 + if (os == null)
149.898 + throw new NullPointerException();
149.899 + storeToXML(os, comment, "UTF-8");
149.900 + }
149.901 +
149.902 + /**
149.903 + * Emits an XML document representing all of the properties contained
149.904 + * in this table, using the specified encoding.
149.905 + *
149.906 + * <p>The XML document will have the following DOCTYPE declaration:
149.907 + * <pre>
149.908 + * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
149.909 + * </pre>
149.910 + *
149.911 + *<p>If the specified comment is <code>null</code> then no comment
149.912 + * will be stored in the document.
149.913 + *
149.914 + * <p>The specified stream remains open after this method returns.
149.915 + *
149.916 + * @param os the output stream on which to emit the XML document.
149.917 + * @param comment a description of the property list, or <code>null</code>
149.918 + * if no comment is desired.
149.919 + * @param encoding the name of a supported
149.920 + * <a href="../lang/package-summary.html#charenc">
149.921 + * character encoding</a>
149.922 + *
149.923 + * @throws IOException if writing to the specified output stream
149.924 + * results in an <tt>IOException</tt>.
149.925 + * @throws NullPointerException if <code>os</code> is <code>null</code>,
149.926 + * or if <code>encoding</code> is <code>null</code>.
149.927 + * @throws ClassCastException if this <code>Properties</code> object
149.928 + * contains any keys or values that are not
149.929 + * <code>Strings</code>.
149.930 + * @see #loadFromXML(InputStream)
149.931 + * @since 1.5
149.932 + */
149.933 + public void storeToXML(OutputStream os, String comment, String encoding)
149.934 + throws IOException
149.935 + {
149.936 + if (os == null)
149.937 + throw new NullPointerException();
149.938 + throw new IOException();
149.939 + }
149.940 +
149.941 + /**
149.942 + * Searches for the property with the specified key in this property list.
149.943 + * If the key is not found in this property list, the default property list,
149.944 + * and its defaults, recursively, are then checked. The method returns
149.945 + * <code>null</code> if the property is not found.
149.946 + *
149.947 + * @param key the property key.
149.948 + * @return the value in this property list with the specified key value.
149.949 + * @see #setProperty
149.950 + * @see #defaults
149.951 + */
149.952 + public String getProperty(String key) {
149.953 + Object oval = super.get(key);
149.954 + String sval = (oval instanceof String) ? (String)oval : null;
149.955 + return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
149.956 + }
149.957 +
149.958 + /**
149.959 + * Searches for the property with the specified key in this property list.
149.960 + * If the key is not found in this property list, the default property list,
149.961 + * and its defaults, recursively, are then checked. The method returns the
149.962 + * default value argument if the property is not found.
149.963 + *
149.964 + * @param key the hashtable key.
149.965 + * @param defaultValue a default value.
149.966 + *
149.967 + * @return the value in this property list with the specified key value.
149.968 + * @see #setProperty
149.969 + * @see #defaults
149.970 + */
149.971 + public String getProperty(String key, String defaultValue) {
149.972 + String val = getProperty(key);
149.973 + return (val == null) ? defaultValue : val;
149.974 + }
149.975 +
149.976 + /**
149.977 + * Returns an enumeration of all the keys in this property list,
149.978 + * including distinct keys in the default property list if a key
149.979 + * of the same name has not already been found from the main
149.980 + * properties list.
149.981 + *
149.982 + * @return an enumeration of all the keys in this property list, including
149.983 + * the keys in the default property list.
149.984 + * @throws ClassCastException if any key in this property list
149.985 + * is not a string.
149.986 + * @see java.util.Enumeration
149.987 + * @see java.util.Properties#defaults
149.988 + * @see #stringPropertyNames
149.989 + */
149.990 + public Enumeration<?> propertyNames() {
149.991 + Hashtable h = new Hashtable();
149.992 + enumerate(h);
149.993 + return h.keys();
149.994 + }
149.995 +
149.996 + /**
149.997 + * Returns a set of keys in this property list where
149.998 + * the key and its corresponding value are strings,
149.999 + * including distinct keys in the default property list if a key
149.1000 + * of the same name has not already been found from the main
149.1001 + * properties list. Properties whose key or value is not
149.1002 + * of type <tt>String</tt> are omitted.
149.1003 + * <p>
149.1004 + * The returned set is not backed by the <tt>Properties</tt> object.
149.1005 + * Changes to this <tt>Properties</tt> are not reflected in the set,
149.1006 + * or vice versa.
149.1007 + *
149.1008 + * @return a set of keys in this property list where
149.1009 + * the key and its corresponding value are strings,
149.1010 + * including the keys in the default property list.
149.1011 + * @see java.util.Properties#defaults
149.1012 + * @since 1.6
149.1013 + */
149.1014 + public Set<String> stringPropertyNames() {
149.1015 + Hashtable<String, String> h = new Hashtable<>();
149.1016 + enumerateStringProperties(h);
149.1017 + return h.keySet();
149.1018 + }
149.1019 +
149.1020 + /**
149.1021 + * Prints this property list out to the specified output stream.
149.1022 + * This method is useful for debugging.
149.1023 + *
149.1024 + * @param out an output stream.
149.1025 + * @throws ClassCastException if any key in this property list
149.1026 + * is not a string.
149.1027 + */
149.1028 + public void list(PrintStream out) {
149.1029 + out.println("-- listing properties --");
149.1030 + Hashtable h = new Hashtable();
149.1031 + enumerate(h);
149.1032 + for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
149.1033 + String key = (String)e.nextElement();
149.1034 + String val = (String)h.get(key);
149.1035 + if (val.length() > 40) {
149.1036 + val = val.substring(0, 37) + "...";
149.1037 + }
149.1038 + out.println(key + "=" + val);
149.1039 + }
149.1040 + }
149.1041 +
149.1042 + /**
149.1043 + * Prints this property list out to the specified output stream.
149.1044 + * This method is useful for debugging.
149.1045 + *
149.1046 + * @param out an output stream.
149.1047 + * @throws ClassCastException if any key in this property list
149.1048 + * is not a string.
149.1049 + * @since JDK1.1
149.1050 + */
149.1051 + /*
149.1052 + * Rather than use an anonymous inner class to share common code, this
149.1053 + * method is duplicated in order to ensure that a non-1.1 compiler can
149.1054 + * compile this file.
149.1055 + */
149.1056 + public void list(PrintWriter out) {
149.1057 + out.println("-- listing properties --");
149.1058 + Hashtable h = new Hashtable();
149.1059 + enumerate(h);
149.1060 + for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
149.1061 + String key = (String)e.nextElement();
149.1062 + String val = (String)h.get(key);
149.1063 + if (val.length() > 40) {
149.1064 + val = val.substring(0, 37) + "...";
149.1065 + }
149.1066 + out.println(key + "=" + val);
149.1067 + }
149.1068 + }
149.1069 +
149.1070 + /**
149.1071 + * Enumerates all key/value pairs in the specified hashtable.
149.1072 + * @param h the hashtable
149.1073 + * @throws ClassCastException if any of the property keys
149.1074 + * is not of String type.
149.1075 + */
149.1076 + private synchronized void enumerate(Hashtable h) {
149.1077 + if (defaults != null) {
149.1078 + defaults.enumerate(h);
149.1079 + }
149.1080 + for (Enumeration e = keys() ; e.hasMoreElements() ;) {
149.1081 + String key = (String)e.nextElement();
149.1082 + h.put(key, get(key));
149.1083 + }
149.1084 + }
149.1085 +
149.1086 + /**
149.1087 + * Enumerates all key/value pairs in the specified hashtable
149.1088 + * and omits the property if the key or value is not a string.
149.1089 + * @param h the hashtable
149.1090 + */
149.1091 + private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
149.1092 + if (defaults != null) {
149.1093 + defaults.enumerateStringProperties(h);
149.1094 + }
149.1095 + for (Enumeration e = keys() ; e.hasMoreElements() ;) {
149.1096 + Object k = e.nextElement();
149.1097 + Object v = get(k);
149.1098 + if (k instanceof String && v instanceof String) {
149.1099 + h.put((String) k, (String) v);
149.1100 + }
149.1101 + }
149.1102 + }
149.1103 +
149.1104 + /**
149.1105 + * Convert a nibble to a hex character
149.1106 + * @param nibble the nibble to convert.
149.1107 + */
149.1108 + private static char toHex(int nibble) {
149.1109 + return hexDigit[(nibble & 0xF)];
149.1110 + }
149.1111 +
149.1112 + /** A table of hex digits */
149.1113 + private static final char[] hexDigit = {
149.1114 + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
149.1115 + };
149.1116 +}
150.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
150.2 +++ b/rt/emul/compact/src/main/java/java/util/PropertyResourceBundle.java Tue Feb 11 13:31:42 2014 +0100
150.3 @@ -0,0 +1,242 @@
150.4 +/*
150.5 + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
150.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
150.7 + *
150.8 + * This code is free software; you can redistribute it and/or modify it
150.9 + * under the terms of the GNU General Public License version 2 only, as
150.10 + * published by the Free Software Foundation. Oracle designates this
150.11 + * particular file as subject to the "Classpath" exception as provided
150.12 + * by Oracle in the LICENSE file that accompanied this code.
150.13 + *
150.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
150.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
150.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
150.17 + * version 2 for more details (a copy is included in the LICENSE file that
150.18 + * accompanied this code).
150.19 + *
150.20 + * You should have received a copy of the GNU General Public License version
150.21 + * 2 along with this work; if not, write to the Free Software Foundation,
150.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
150.23 + *
150.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
150.25 + * or visit www.oracle.com if you need additional information or have any
150.26 + * questions.
150.27 + */
150.28 +
150.29 +/*
150.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
150.31 + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
150.32 + *
150.33 + * The original version of this source code and documentation
150.34 + * is copyrighted and owned by Taligent, Inc., a wholly-owned
150.35 + * subsidiary of IBM. These materials are provided under terms
150.36 + * of a License Agreement between Taligent and Sun. This technology
150.37 + * is protected by multiple US and International patents.
150.38 + *
150.39 + * This notice and attribution to Taligent may not be removed.
150.40 + * Taligent is a registered trademark of Taligent, Inc.
150.41 + */
150.42 +
150.43 +package java.util;
150.44 +
150.45 +import java.io.InputStream;
150.46 +import java.io.Reader;
150.47 +import java.io.IOException;
150.48 +
150.49 +/**
150.50 + * <code>PropertyResourceBundle</code> is a concrete subclass of
150.51 + * <code>ResourceBundle</code> that manages resources for a locale
150.52 + * using a set of static strings from a property file. See
150.53 + * {@link ResourceBundle ResourceBundle} for more information about resource
150.54 + * bundles.
150.55 + *
150.56 + * <p>
150.57 + * Unlike other types of resource bundle, you don't subclass
150.58 + * <code>PropertyResourceBundle</code>. Instead, you supply properties
150.59 + * files containing the resource data. <code>ResourceBundle.getBundle</code>
150.60 + * will automatically look for the appropriate properties file and create a
150.61 + * <code>PropertyResourceBundle</code> that refers to it. See
150.62 + * {@link ResourceBundle#getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader) ResourceBundle.getBundle}
150.63 + * for a complete description of the search and instantiation strategy.
150.64 + *
150.65 + * <p>
150.66 + * The following <a name="sample">example</a> shows a member of a resource
150.67 + * bundle family with the base name "MyResources".
150.68 + * The text defines the bundle "MyResources_de",
150.69 + * the German member of the bundle family.
150.70 + * This member is based on <code>PropertyResourceBundle</code>, and the text
150.71 + * therefore is the content of the file "MyResources_de.properties"
150.72 + * (a related <a href="ListResourceBundle.html#sample">example</a> shows
150.73 + * how you can add bundles to this family that are implemented as subclasses
150.74 + * of <code>ListResourceBundle</code>).
150.75 + * The keys in this example are of the form "s1" etc. The actual
150.76 + * keys are entirely up to your choice, so long as they are the same as
150.77 + * the keys you use in your program to retrieve the objects from the bundle.
150.78 + * Keys are case-sensitive.
150.79 + * <blockquote>
150.80 + * <pre>
150.81 + * # MessageFormat pattern
150.82 + * s1=Die Platte \"{1}\" enthält {0}.
150.83 + *
150.84 + * # location of {0} in pattern
150.85 + * s2=1
150.86 + *
150.87 + * # sample disk name
150.88 + * s3=Meine Platte
150.89 + *
150.90 + * # first ChoiceFormat choice
150.91 + * s4=keine Dateien
150.92 + *
150.93 + * # second ChoiceFormat choice
150.94 + * s5=eine Datei
150.95 + *
150.96 + * # third ChoiceFormat choice
150.97 + * s6={0,number} Dateien
150.98 + *
150.99 + * # sample date
150.100 + * s7=3. März 1996
150.101 + * </pre>
150.102 + * </blockquote>
150.103 + *
150.104 + * <p>
150.105 + * <strong>Note:</strong> PropertyResourceBundle can be constructed either
150.106 + * from an InputStream or a Reader, which represents a property file.
150.107 + * Constructing a PropertyResourceBundle instance from an InputStream requires
150.108 + * that the input stream be encoded in ISO-8859-1. In that case, characters
150.109 + * that cannot be represented in ISO-8859-1 encoding must be represented by Unicode Escapes
150.110 + * as defined in section 3.3 of
150.111 + * <cite>The Java™ Language Specification</cite>
150.112 + * whereas the other constructor which takes a Reader does not have that limitation.
150.113 + *
150.114 + * @see ResourceBundle
150.115 + * @see ListResourceBundle
150.116 + * @see Properties
150.117 + * @since JDK1.1
150.118 + */
150.119 +public class PropertyResourceBundle extends ResourceBundle {
150.120 + /**
150.121 + * Creates a property resource bundle from an {@link java.io.InputStream
150.122 + * InputStream}. The property file read with this constructor
150.123 + * must be encoded in ISO-8859-1.
150.124 + *
150.125 + * @param stream an InputStream that represents a property file
150.126 + * to read from.
150.127 + * @throws IOException if an I/O error occurs
150.128 + * @throws NullPointerException if <code>stream</code> is null
150.129 + */
150.130 + public PropertyResourceBundle (InputStream stream) throws IOException {
150.131 + Properties properties = new Properties();
150.132 + properties.load(stream);
150.133 + lookup = new HashMap(properties);
150.134 + }
150.135 +
150.136 + /**
150.137 + * Creates a property resource bundle from a {@link java.io.Reader
150.138 + * Reader}. Unlike the constructor
150.139 + * {@link #PropertyResourceBundle(java.io.InputStream) PropertyResourceBundle(InputStream)},
150.140 + * there is no limitation as to the encoding of the input property file.
150.141 + *
150.142 + * @param reader a Reader that represents a property file to
150.143 + * read from.
150.144 + * @throws IOException if an I/O error occurs
150.145 + * @throws NullPointerException if <code>reader</code> is null
150.146 + * @since 1.6
150.147 + */
150.148 + public PropertyResourceBundle (Reader reader) throws IOException {
150.149 + Properties properties = new Properties();
150.150 + properties.load(reader);
150.151 + lookup = new HashMap(properties);
150.152 + }
150.153 +
150.154 + // Implements java.util.ResourceBundle.handleGetObject; inherits javadoc specification.
150.155 + public Object handleGetObject(String key) {
150.156 + if (key == null) {
150.157 + throw new NullPointerException();
150.158 + }
150.159 + return lookup.get(key);
150.160 + }
150.161 +
150.162 + /**
150.163 + * Returns an <code>Enumeration</code> of the keys contained in
150.164 + * this <code>ResourceBundle</code> and its parent bundles.
150.165 + *
150.166 + * @return an <code>Enumeration</code> of the keys contained in
150.167 + * this <code>ResourceBundle</code> and its parent bundles.
150.168 + * @see #keySet()
150.169 + */
150.170 + public Enumeration<String> getKeys() {
150.171 + ResourceBundle parent = this.parent;
150.172 + return new ResourceBundleEnumeration(lookup.keySet(),
150.173 + (parent != null) ? parent.getKeys() : null);
150.174 + }
150.175 +
150.176 + /**
150.177 + * Returns a <code>Set</code> of the keys contained
150.178 + * <em>only</em> in this <code>ResourceBundle</code>.
150.179 + *
150.180 + * @return a <code>Set</code> of the keys contained only in this
150.181 + * <code>ResourceBundle</code>
150.182 + * @since 1.6
150.183 + * @see #keySet()
150.184 + */
150.185 + protected Set<String> handleKeySet() {
150.186 + return lookup.keySet();
150.187 + }
150.188 +
150.189 + // ==================privates====================
150.190 +
150.191 + private Map<String,Object> lookup;
150.192 +
150.193 +
150.194 + /**
150.195 + * Implements an Enumeration that combines elements from a Set and
150.196 + * an Enumeration. Used by ListResourceBundle and PropertyResourceBundle.
150.197 + */
150.198 + static class ResourceBundleEnumeration implements Enumeration<String> {
150.199 +
150.200 + Set<String> set;
150.201 + Iterator<String> iterator;
150.202 + Enumeration<String> enumeration; // may remain null
150.203 +
150.204 + /**
150.205 + * Constructs a resource bundle enumeration.
150.206 + * @param set an set providing some elements of the enumeration
150.207 + * @param enumeration an enumeration providing more elements of the enumeration.
150.208 + * enumeration may be null.
150.209 + */
150.210 + public ResourceBundleEnumeration(Set<String> set, Enumeration<String> enumeration) {
150.211 + this.set = set;
150.212 + this.iterator = set.iterator();
150.213 + this.enumeration = enumeration;
150.214 + }
150.215 +
150.216 + String next = null;
150.217 +
150.218 + public boolean hasMoreElements() {
150.219 + if (next == null) {
150.220 + if (iterator.hasNext()) {
150.221 + next = iterator.next();
150.222 + } else if (enumeration != null) {
150.223 + while (next == null && enumeration.hasMoreElements()) {
150.224 + next = enumeration.nextElement();
150.225 + if (set.contains(next)) {
150.226 + next = null;
150.227 + }
150.228 + }
150.229 + }
150.230 + }
150.231 + return next != null;
150.232 + }
150.233 +
150.234 + public String nextElement() {
150.235 + if (hasMoreElements()) {
150.236 + String result = next;
150.237 + next = null;
150.238 + return result;
150.239 + } else {
150.240 + throw new NoSuchElementException();
150.241 + }
150.242 + }
150.243 + }
150.244 +
150.245 +}
151.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
151.2 +++ b/rt/emul/compact/src/main/java/java/util/RegularEnumSet.java Tue Feb 11 13:31:42 2014 +0100
151.3 @@ -0,0 +1,303 @@
151.4 +/*
151.5 + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
151.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
151.7 + *
151.8 + * This code is free software; you can redistribute it and/or modify it
151.9 + * under the terms of the GNU General Public License version 2 only, as
151.10 + * published by the Free Software Foundation. Oracle designates this
151.11 + * particular file as subject to the "Classpath" exception as provided
151.12 + * by Oracle in the LICENSE file that accompanied this code.
151.13 + *
151.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
151.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
151.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
151.17 + * version 2 for more details (a copy is included in the LICENSE file that
151.18 + * accompanied this code).
151.19 + *
151.20 + * You should have received a copy of the GNU General Public License version
151.21 + * 2 along with this work; if not, write to the Free Software Foundation,
151.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
151.23 + *
151.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
151.25 + * or visit www.oracle.com if you need additional information or have any
151.26 + * questions.
151.27 + */
151.28 +
151.29 +package java.util;
151.30 +
151.31 +/**
151.32 + * Private implementation class for EnumSet, for "regular sized" enum types
151.33 + * (i.e., those with 64 or fewer enum constants).
151.34 + *
151.35 + * @author Josh Bloch
151.36 + * @since 1.5
151.37 + * @serial exclude
151.38 + */
151.39 +class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
151.40 + private static final long serialVersionUID = 3411599620347842686L;
151.41 + /**
151.42 + * Bit vector representation of this set. The 2^k bit indicates the
151.43 + * presence of universe[k] in this set.
151.44 + */
151.45 + private long elements = 0L;
151.46 +
151.47 + RegularEnumSet(Class<E>elementType, Enum[] universe) {
151.48 + super(elementType, universe);
151.49 + }
151.50 +
151.51 + void addRange(E from, E to) {
151.52 + elements = (-1L >>> (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
151.53 + }
151.54 +
151.55 + void addAll() {
151.56 + if (universe.length != 0)
151.57 + elements = -1L >>> -universe.length;
151.58 + }
151.59 +
151.60 + void complement() {
151.61 + if (universe.length != 0) {
151.62 + elements = ~elements;
151.63 + elements &= -1L >>> -universe.length; // Mask unused bits
151.64 + }
151.65 + }
151.66 +
151.67 + /**
151.68 + * Returns an iterator over the elements contained in this set. The
151.69 + * iterator traverses the elements in their <i>natural order</i> (which is
151.70 + * the order in which the enum constants are declared). The returned
151.71 + * Iterator is a "snapshot" iterator that will never throw {@link
151.72 + * ConcurrentModificationException}; the elements are traversed as they
151.73 + * existed when this call was invoked.
151.74 + *
151.75 + * @return an iterator over the elements contained in this set
151.76 + */
151.77 + public Iterator<E> iterator() {
151.78 + return new EnumSetIterator<>();
151.79 + }
151.80 +
151.81 + private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
151.82 + /**
151.83 + * A bit vector representing the elements in the set not yet
151.84 + * returned by this iterator.
151.85 + */
151.86 + long unseen;
151.87 +
151.88 + /**
151.89 + * The bit representing the last element returned by this iterator
151.90 + * but not removed, or zero if no such element exists.
151.91 + */
151.92 + long lastReturned = 0;
151.93 +
151.94 + EnumSetIterator() {
151.95 + unseen = elements;
151.96 + }
151.97 +
151.98 + public boolean hasNext() {
151.99 + return unseen != 0;
151.100 + }
151.101 +
151.102 + public E next() {
151.103 + if (unseen == 0)
151.104 + throw new NoSuchElementException();
151.105 + lastReturned = unseen & -unseen;
151.106 + unseen -= lastReturned;
151.107 + return (E) universe[Long.numberOfTrailingZeros(lastReturned)];
151.108 + }
151.109 +
151.110 + public void remove() {
151.111 + if (lastReturned == 0)
151.112 + throw new IllegalStateException();
151.113 + elements &= ~lastReturned;
151.114 + lastReturned = 0;
151.115 + }
151.116 + }
151.117 +
151.118 + /**
151.119 + * Returns the number of elements in this set.
151.120 + *
151.121 + * @return the number of elements in this set
151.122 + */
151.123 + public int size() {
151.124 + return Long.bitCount(elements);
151.125 + }
151.126 +
151.127 + /**
151.128 + * Returns <tt>true</tt> if this set contains no elements.
151.129 + *
151.130 + * @return <tt>true</tt> if this set contains no elements
151.131 + */
151.132 + public boolean isEmpty() {
151.133 + return elements == 0;
151.134 + }
151.135 +
151.136 + /**
151.137 + * Returns <tt>true</tt> if this set contains the specified element.
151.138 + *
151.139 + * @param e element to be checked for containment in this collection
151.140 + * @return <tt>true</tt> if this set contains the specified element
151.141 + */
151.142 + public boolean contains(Object e) {
151.143 + if (e == null)
151.144 + return false;
151.145 + Class eClass = e.getClass();
151.146 + if (eClass != elementType && eClass.getSuperclass() != elementType)
151.147 + return false;
151.148 +
151.149 + return (elements & (1L << ((Enum)e).ordinal())) != 0;
151.150 + }
151.151 +
151.152 + // Modification Operations
151.153 +
151.154 + /**
151.155 + * Adds the specified element to this set if it is not already present.
151.156 + *
151.157 + * @param e element to be added to this set
151.158 + * @return <tt>true</tt> if the set changed as a result of the call
151.159 + *
151.160 + * @throws NullPointerException if <tt>e</tt> is null
151.161 + */
151.162 + public boolean add(E e) {
151.163 + typeCheck(e);
151.164 +
151.165 + long oldElements = elements;
151.166 + elements |= (1L << ((Enum)e).ordinal());
151.167 + return elements != oldElements;
151.168 + }
151.169 +
151.170 + /**
151.171 + * Removes the specified element from this set if it is present.
151.172 + *
151.173 + * @param e element to be removed from this set, if present
151.174 + * @return <tt>true</tt> if the set contained the specified element
151.175 + */
151.176 + public boolean remove(Object e) {
151.177 + if (e == null)
151.178 + return false;
151.179 + Class eClass = e.getClass();
151.180 + if (eClass != elementType && eClass.getSuperclass() != elementType)
151.181 + return false;
151.182 +
151.183 + long oldElements = elements;
151.184 + elements &= ~(1L << ((Enum)e).ordinal());
151.185 + return elements != oldElements;
151.186 + }
151.187 +
151.188 + // Bulk Operations
151.189 +
151.190 + /**
151.191 + * Returns <tt>true</tt> if this set contains all of the elements
151.192 + * in the specified collection.
151.193 + *
151.194 + * @param c collection to be checked for containment in this set
151.195 + * @return <tt>true</tt> if this set contains all of the elements
151.196 + * in the specified collection
151.197 + * @throws NullPointerException if the specified collection is null
151.198 + */
151.199 + public boolean containsAll(Collection<?> c) {
151.200 + if (!(c instanceof RegularEnumSet))
151.201 + return super.containsAll(c);
151.202 +
151.203 + RegularEnumSet es = (RegularEnumSet)c;
151.204 + if (es.elementType != elementType)
151.205 + return es.isEmpty();
151.206 +
151.207 + return (es.elements & ~elements) == 0;
151.208 + }
151.209 +
151.210 + /**
151.211 + * Adds all of the elements in the specified collection to this set.
151.212 + *
151.213 + * @param c collection whose elements are to be added to this set
151.214 + * @return <tt>true</tt> if this set changed as a result of the call
151.215 + * @throws NullPointerException if the specified collection or any
151.216 + * of its elements are null
151.217 + */
151.218 + public boolean addAll(Collection<? extends E> c) {
151.219 + if (!(c instanceof RegularEnumSet))
151.220 + return super.addAll(c);
151.221 +
151.222 + RegularEnumSet es = (RegularEnumSet)c;
151.223 + if (es.elementType != elementType) {
151.224 + if (es.isEmpty())
151.225 + return false;
151.226 + else
151.227 + throw new ClassCastException(
151.228 + es.elementType + " != " + elementType);
151.229 + }
151.230 +
151.231 + long oldElements = elements;
151.232 + elements |= es.elements;
151.233 + return elements != oldElements;
151.234 + }
151.235 +
151.236 + /**
151.237 + * Removes from this set all of its elements that are contained in
151.238 + * the specified collection.
151.239 + *
151.240 + * @param c elements to be removed from this set
151.241 + * @return <tt>true</tt> if this set changed as a result of the call
151.242 + * @throws NullPointerException if the specified collection is null
151.243 + */
151.244 + public boolean removeAll(Collection<?> c) {
151.245 + if (!(c instanceof RegularEnumSet))
151.246 + return super.removeAll(c);
151.247 +
151.248 + RegularEnumSet es = (RegularEnumSet)c;
151.249 + if (es.elementType != elementType)
151.250 + return false;
151.251 +
151.252 + long oldElements = elements;
151.253 + elements &= ~es.elements;
151.254 + return elements != oldElements;
151.255 + }
151.256 +
151.257 + /**
151.258 + * Retains only the elements in this set that are contained in the
151.259 + * specified collection.
151.260 + *
151.261 + * @param c elements to be retained in this set
151.262 + * @return <tt>true</tt> if this set changed as a result of the call
151.263 + * @throws NullPointerException if the specified collection is null
151.264 + */
151.265 + public boolean retainAll(Collection<?> c) {
151.266 + if (!(c instanceof RegularEnumSet))
151.267 + return super.retainAll(c);
151.268 +
151.269 + RegularEnumSet<?> es = (RegularEnumSet<?>)c;
151.270 + if (es.elementType != elementType) {
151.271 + boolean changed = (elements != 0);
151.272 + elements = 0;
151.273 + return changed;
151.274 + }
151.275 +
151.276 + long oldElements = elements;
151.277 + elements &= es.elements;
151.278 + return elements != oldElements;
151.279 + }
151.280 +
151.281 + /**
151.282 + * Removes all of the elements from this set.
151.283 + */
151.284 + public void clear() {
151.285 + elements = 0;
151.286 + }
151.287 +
151.288 + /**
151.289 + * Compares the specified object with this set for equality. Returns
151.290 + * <tt>true</tt> if the given object is also a set, the two sets have
151.291 + * the same size, and every member of the given set is contained in
151.292 + * this set.
151.293 + *
151.294 + * @param e object to be compared for equality with this set
151.295 + * @return <tt>true</tt> if the specified object is equal to this set
151.296 + */
151.297 + public boolean equals(Object o) {
151.298 + if (!(o instanceof RegularEnumSet))
151.299 + return super.equals(o);
151.300 +
151.301 + RegularEnumSet es = (RegularEnumSet)o;
151.302 + if (es.elementType != elementType)
151.303 + return elements == 0 && es.elements == 0;
151.304 + return es.elements == elements;
151.305 + }
151.306 +}
152.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
152.2 +++ b/rt/emul/compact/src/main/java/java/util/ResourceBundle.java Tue Feb 11 13:31:42 2014 +0100
152.3 @@ -0,0 +1,2778 @@
152.4 +/*
152.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
152.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
152.7 + *
152.8 + * This code is free software; you can redistribute it and/or modify it
152.9 + * under the terms of the GNU General Public License version 2 only, as
152.10 + * published by the Free Software Foundation. Oracle designates this
152.11 + * particular file as subject to the "Classpath" exception as provided
152.12 + * by Oracle in the LICENSE file that accompanied this code.
152.13 + *
152.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
152.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
152.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
152.17 + * version 2 for more details (a copy is included in the LICENSE file that
152.18 + * accompanied this code).
152.19 + *
152.20 + * You should have received a copy of the GNU General Public License version
152.21 + * 2 along with this work; if not, write to the Free Software Foundation,
152.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
152.23 + *
152.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
152.25 + * or visit www.oracle.com if you need additional information or have any
152.26 + * questions.
152.27 + */
152.28 +
152.29 +/*
152.30 + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
152.31 + * (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
152.32 + *
152.33 + * The original version of this source code and documentation
152.34 + * is copyrighted and owned by Taligent, Inc., a wholly-owned
152.35 + * subsidiary of IBM. These materials are provided under terms
152.36 + * of a License Agreement between Taligent and Sun. This technology
152.37 + * is protected by multiple US and International patents.
152.38 + *
152.39 + * This notice and attribution to Taligent may not be removed.
152.40 + * Taligent is a registered trademark of Taligent, Inc.
152.41 + *
152.42 + */
152.43 +
152.44 +package java.util;
152.45 +
152.46 +import java.io.IOException;
152.47 +import java.io.InputStream;
152.48 +import java.lang.ref.ReferenceQueue;
152.49 +import java.lang.ref.SoftReference;
152.50 +import java.lang.ref.WeakReference;
152.51 +import java.net.URL;
152.52 +
152.53 +
152.54 +/**
152.55 + *
152.56 + * Resource bundles contain locale-specific objects. When your program needs a
152.57 + * locale-specific resource, a <code>String</code> for example, your program can
152.58 + * load it from the resource bundle that is appropriate for the current user's
152.59 + * locale. In this way, you can write program code that is largely independent
152.60 + * of the user's locale isolating most, if not all, of the locale-specific
152.61 + * information in resource bundles.
152.62 + *
152.63 + * <p>
152.64 + * This allows you to write programs that can:
152.65 + * <UL type=SQUARE>
152.66 + * <LI> be easily localized, or translated, into different languages
152.67 + * <LI> handle multiple locales at once
152.68 + * <LI> be easily modified later to support even more locales
152.69 + * </UL>
152.70 + *
152.71 + * <P>
152.72 + * Resource bundles belong to families whose members share a common base
152.73 + * name, but whose names also have additional components that identify
152.74 + * their locales. For example, the base name of a family of resource
152.75 + * bundles might be "MyResources". The family should have a default
152.76 + * resource bundle which simply has the same name as its family -
152.77 + * "MyResources" - and will be used as the bundle of last resort if a
152.78 + * specific locale is not supported. The family can then provide as
152.79 + * many locale-specific members as needed, for example a German one
152.80 + * named "MyResources_de".
152.81 + *
152.82 + * <P>
152.83 + * Each resource bundle in a family contains the same items, but the items have
152.84 + * been translated for the locale represented by that resource bundle.
152.85 + * For example, both "MyResources" and "MyResources_de" may have a
152.86 + * <code>String</code> that's used on a button for canceling operations.
152.87 + * In "MyResources" the <code>String</code> may contain "Cancel" and in
152.88 + * "MyResources_de" it may contain "Abbrechen".
152.89 + *
152.90 + * <P>
152.91 + * If there are different resources for different countries, you
152.92 + * can make specializations: for example, "MyResources_de_CH" contains objects for
152.93 + * the German language (de) in Switzerland (CH). If you want to only
152.94 + * modify some of the resources
152.95 + * in the specialization, you can do so.
152.96 + *
152.97 + * <P>
152.98 + * When your program needs a locale-specific object, it loads
152.99 + * the <code>ResourceBundle</code> class using the
152.100 + * {@link #getBundle(java.lang.String, java.util.Locale) getBundle}
152.101 + * method:
152.102 + * <blockquote>
152.103 + * <pre>
152.104 + * ResourceBundle myResources =
152.105 + * ResourceBundle.getBundle("MyResources", currentLocale);
152.106 + * </pre>
152.107 + * </blockquote>
152.108 + *
152.109 + * <P>
152.110 + * Resource bundles contain key/value pairs. The keys uniquely
152.111 + * identify a locale-specific object in the bundle. Here's an
152.112 + * example of a <code>ListResourceBundle</code> that contains
152.113 + * two key/value pairs:
152.114 + * <blockquote>
152.115 + * <pre>
152.116 + * public class MyResources extends ListResourceBundle {
152.117 + * protected Object[][] getContents() {
152.118 + * return new Object[][] {
152.119 + * // LOCALIZE THE SECOND STRING OF EACH ARRAY (e.g., "OK")
152.120 + * {"OkKey", "OK"},
152.121 + * {"CancelKey", "Cancel"},
152.122 + * // END OF MATERIAL TO LOCALIZE
152.123 + * };
152.124 + * }
152.125 + * }
152.126 + * </pre>
152.127 + * </blockquote>
152.128 + * Keys are always <code>String</code>s.
152.129 + * In this example, the keys are "OkKey" and "CancelKey".
152.130 + * In the above example, the values
152.131 + * are also <code>String</code>s--"OK" and "Cancel"--but
152.132 + * they don't have to be. The values can be any type of object.
152.133 + *
152.134 + * <P>
152.135 + * You retrieve an object from resource bundle using the appropriate
152.136 + * getter method. Because "OkKey" and "CancelKey"
152.137 + * are both strings, you would use <code>getString</code> to retrieve them:
152.138 + * <blockquote>
152.139 + * <pre>
152.140 + * button1 = new Button(myResources.getString("OkKey"));
152.141 + * button2 = new Button(myResources.getString("CancelKey"));
152.142 + * </pre>
152.143 + * </blockquote>
152.144 + * The getter methods all require the key as an argument and return
152.145 + * the object if found. If the object is not found, the getter method
152.146 + * throws a <code>MissingResourceException</code>.
152.147 + *
152.148 + * <P>
152.149 + * Besides <code>getString</code>, <code>ResourceBundle</code> also provides
152.150 + * a method for getting string arrays, <code>getStringArray</code>,
152.151 + * as well as a generic <code>getObject</code> method for any other
152.152 + * type of object. When using <code>getObject</code>, you'll
152.153 + * have to cast the result to the appropriate type. For example:
152.154 + * <blockquote>
152.155 + * <pre>
152.156 + * int[] myIntegers = (int[]) myResources.getObject("intList");
152.157 + * </pre>
152.158 + * </blockquote>
152.159 + *
152.160 + * <P>
152.161 + * The Java Platform provides two subclasses of <code>ResourceBundle</code>,
152.162 + * <code>ListResourceBundle</code> and <code>PropertyResourceBundle</code>,
152.163 + * that provide a fairly simple way to create resources.
152.164 + * As you saw briefly in a previous example, <code>ListResourceBundle</code>
152.165 + * manages its resource as a list of key/value pairs.
152.166 + * <code>PropertyResourceBundle</code> uses a properties file to manage
152.167 + * its resources.
152.168 + *
152.169 + * <p>
152.170 + * If <code>ListResourceBundle</code> or <code>PropertyResourceBundle</code>
152.171 + * do not suit your needs, you can write your own <code>ResourceBundle</code>
152.172 + * subclass. Your subclasses must override two methods: <code>handleGetObject</code>
152.173 + * and <code>getKeys()</code>.
152.174 + *
152.175 + * <h4>ResourceBundle.Control</h4>
152.176 + *
152.177 + * The {@link ResourceBundle.Control} class provides information necessary
152.178 + * to perform the bundle loading process by the <code>getBundle</code>
152.179 + * factory methods that take a <code>ResourceBundle.Control</code>
152.180 + * instance. You can implement your own subclass in order to enable
152.181 + * non-standard resource bundle formats, change the search strategy, or
152.182 + * define caching parameters. Refer to the descriptions of the class and the
152.183 + * {@link #getBundle(String, Locale, ClassLoader, Control) getBundle}
152.184 + * factory method for details.
152.185 + *
152.186 + * <h4>Cache Management</h4>
152.187 + *
152.188 + * Resource bundle instances created by the <code>getBundle</code> factory
152.189 + * methods are cached by default, and the factory methods return the same
152.190 + * resource bundle instance multiple times if it has been
152.191 + * cached. <code>getBundle</code> clients may clear the cache, manage the
152.192 + * lifetime of cached resource bundle instances using time-to-live values,
152.193 + * or specify not to cache resource bundle instances. Refer to the
152.194 + * descriptions of the {@linkplain #getBundle(String, Locale, ClassLoader,
152.195 + * Control) <code>getBundle</code> factory method}, {@link
152.196 + * #clearCache(ClassLoader) clearCache}, {@link
152.197 + * Control#getTimeToLive(String, Locale)
152.198 + * ResourceBundle.Control.getTimeToLive}, and {@link
152.199 + * Control#needsReload(String, Locale, String, ClassLoader, ResourceBundle,
152.200 + * long) ResourceBundle.Control.needsReload} for details.
152.201 + *
152.202 + * <h4>Example</h4>
152.203 + *
152.204 + * The following is a very simple example of a <code>ResourceBundle</code>
152.205 + * subclass, <code>MyResources</code>, that manages two resources (for a larger number of
152.206 + * resources you would probably use a <code>Map</code>).
152.207 + * Notice that you don't need to supply a value if
152.208 + * a "parent-level" <code>ResourceBundle</code> handles the same
152.209 + * key with the same value (as for the okKey below).
152.210 + * <blockquote>
152.211 + * <pre>
152.212 + * // default (English language, United States)
152.213 + * public class MyResources extends ResourceBundle {
152.214 + * public Object handleGetObject(String key) {
152.215 + * if (key.equals("okKey")) return "Ok";
152.216 + * if (key.equals("cancelKey")) return "Cancel";
152.217 + * return null;
152.218 + * }
152.219 + *
152.220 + * public Enumeration<String> getKeys() {
152.221 + * return Collections.enumeration(keySet());
152.222 + * }
152.223 + *
152.224 + * // Overrides handleKeySet() so that the getKeys() implementation
152.225 + * // can rely on the keySet() value.
152.226 + * protected Set<String> handleKeySet() {
152.227 + * return new HashSet<String>(Arrays.asList("okKey", "cancelKey"));
152.228 + * }
152.229 + * }
152.230 + *
152.231 + * // German language
152.232 + * public class MyResources_de extends MyResources {
152.233 + * public Object handleGetObject(String key) {
152.234 + * // don't need okKey, since parent level handles it.
152.235 + * if (key.equals("cancelKey")) return "Abbrechen";
152.236 + * return null;
152.237 + * }
152.238 + *
152.239 + * protected Set<String> handleKeySet() {
152.240 + * return new HashSet<String>(Arrays.asList("cancelKey"));
152.241 + * }
152.242 + * }
152.243 + * </pre>
152.244 + * </blockquote>
152.245 + * You do not have to restrict yourself to using a single family of
152.246 + * <code>ResourceBundle</code>s. For example, you could have a set of bundles for
152.247 + * exception messages, <code>ExceptionResources</code>
152.248 + * (<code>ExceptionResources_fr</code>, <code>ExceptionResources_de</code>, ...),
152.249 + * and one for widgets, <code>WidgetResource</code> (<code>WidgetResources_fr</code>,
152.250 + * <code>WidgetResources_de</code>, ...); breaking up the resources however you like.
152.251 + *
152.252 + * @see ListResourceBundle
152.253 + * @see PropertyResourceBundle
152.254 + * @see MissingResourceException
152.255 + * @since JDK1.1
152.256 + */
152.257 +public abstract class ResourceBundle {
152.258 +
152.259 + /** initial size of the bundle cache */
152.260 + private static final int INITIAL_CACHE_SIZE = 32;
152.261 +
152.262 + /** constant indicating that no resource bundle exists */
152.263 + private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() {
152.264 + public Enumeration<String> getKeys() { return null; }
152.265 + protected Object handleGetObject(String key) { return null; }
152.266 + public String toString() { return "NONEXISTENT_BUNDLE"; }
152.267 + };
152.268 +
152.269 +
152.270 + /**
152.271 + * The cache is a map from cache keys (with bundle base name, locale, and
152.272 + * class loader) to either a resource bundle or NONEXISTENT_BUNDLE wrapped by a
152.273 + * BundleReference.
152.274 + *
152.275 + * The cache is a ConcurrentMap, allowing the cache to be searched
152.276 + * concurrently by multiple threads. This will also allow the cache keys
152.277 + * to be reclaimed along with the ClassLoaders they reference.
152.278 + *
152.279 + * This variable would be better named "cache", but we keep the old
152.280 + * name for compatibility with some workarounds for bug 4212439.
152.281 + */
152.282 + private static final Map<CacheKey, BundleReference> cacheList
152.283 + = new HashMap<>(INITIAL_CACHE_SIZE);
152.284 +
152.285 + /**
152.286 + * Queue for reference objects referring to class loaders or bundles.
152.287 + */
152.288 + private static final ReferenceQueue referenceQueue = new ReferenceQueue();
152.289 +
152.290 + /**
152.291 + * The parent bundle of this bundle.
152.292 + * The parent bundle is searched by {@link #getObject getObject}
152.293 + * when this bundle does not contain a particular resource.
152.294 + */
152.295 + protected ResourceBundle parent = null;
152.296 +
152.297 + /**
152.298 + * The locale for this bundle.
152.299 + */
152.300 + private Locale locale = null;
152.301 +
152.302 + /**
152.303 + * The base bundle name for this bundle.
152.304 + */
152.305 + private String name;
152.306 +
152.307 + /**
152.308 + * The flag indicating this bundle has expired in the cache.
152.309 + */
152.310 + private volatile boolean expired;
152.311 +
152.312 + /**
152.313 + * The back link to the cache key. null if this bundle isn't in
152.314 + * the cache (yet) or has expired.
152.315 + */
152.316 + private volatile CacheKey cacheKey;
152.317 +
152.318 + /**
152.319 + * A Set of the keys contained only in this ResourceBundle.
152.320 + */
152.321 + private volatile Set<String> keySet;
152.322 +
152.323 + /**
152.324 + * Sole constructor. (For invocation by subclass constructors, typically
152.325 + * implicit.)
152.326 + */
152.327 + public ResourceBundle() {
152.328 + }
152.329 +
152.330 + /**
152.331 + * Gets a string for the given key from this resource bundle or one of its parents.
152.332 + * Calling this method is equivalent to calling
152.333 + * <blockquote>
152.334 + * <code>(String) {@link #getObject(java.lang.String) getObject}(key)</code>.
152.335 + * </blockquote>
152.336 + *
152.337 + * @param key the key for the desired string
152.338 + * @exception NullPointerException if <code>key</code> is <code>null</code>
152.339 + * @exception MissingResourceException if no object for the given key can be found
152.340 + * @exception ClassCastException if the object found for the given key is not a string
152.341 + * @return the string for the given key
152.342 + */
152.343 + public final String getString(String key) {
152.344 + return (String) getObject(key);
152.345 + }
152.346 +
152.347 + /**
152.348 + * Gets a string array for the given key from this resource bundle or one of its parents.
152.349 + * Calling this method is equivalent to calling
152.350 + * <blockquote>
152.351 + * <code>(String[]) {@link #getObject(java.lang.String) getObject}(key)</code>.
152.352 + * </blockquote>
152.353 + *
152.354 + * @param key the key for the desired string array
152.355 + * @exception NullPointerException if <code>key</code> is <code>null</code>
152.356 + * @exception MissingResourceException if no object for the given key can be found
152.357 + * @exception ClassCastException if the object found for the given key is not a string array
152.358 + * @return the string array for the given key
152.359 + */
152.360 + public final String[] getStringArray(String key) {
152.361 + return (String[]) getObject(key);
152.362 + }
152.363 +
152.364 + /**
152.365 + * Gets an object for the given key from this resource bundle or one of its parents.
152.366 + * This method first tries to obtain the object from this resource bundle using
152.367 + * {@link #handleGetObject(java.lang.String) handleGetObject}.
152.368 + * If not successful, and the parent resource bundle is not null,
152.369 + * it calls the parent's <code>getObject</code> method.
152.370 + * If still not successful, it throws a MissingResourceException.
152.371 + *
152.372 + * @param key the key for the desired object
152.373 + * @exception NullPointerException if <code>key</code> is <code>null</code>
152.374 + * @exception MissingResourceException if no object for the given key can be found
152.375 + * @return the object for the given key
152.376 + */
152.377 + public final Object getObject(String key) {
152.378 + Object obj = handleGetObject(key);
152.379 + if (obj == null) {
152.380 + if (parent != null) {
152.381 + obj = parent.getObject(key);
152.382 + }
152.383 + if (obj == null)
152.384 + throw new MissingResourceException("Can't find resource for bundle "
152.385 + +this.getClass().getName()
152.386 + +", key "+key,
152.387 + this.getClass().getName(),
152.388 + key);
152.389 + }
152.390 + return obj;
152.391 + }
152.392 +
152.393 + /**
152.394 + * Returns the locale of this resource bundle. This method can be used after a
152.395 + * call to getBundle() to determine whether the resource bundle returned really
152.396 + * corresponds to the requested locale or is a fallback.
152.397 + *
152.398 + * @return the locale of this resource bundle
152.399 + */
152.400 + public Locale getLocale() {
152.401 + return locale;
152.402 + }
152.403 +
152.404 + /**
152.405 + * Sets the parent bundle of this bundle.
152.406 + * The parent bundle is searched by {@link #getObject getObject}
152.407 + * when this bundle does not contain a particular resource.
152.408 + *
152.409 + * @param parent this bundle's parent bundle.
152.410 + */
152.411 + protected void setParent(ResourceBundle parent) {
152.412 + assert parent != NONEXISTENT_BUNDLE;
152.413 + this.parent = parent;
152.414 + }
152.415 +
152.416 + /**
152.417 + * Key used for cached resource bundles. The key checks the base
152.418 + * name, the locale, and the class loader to determine if the
152.419 + * resource is a match to the requested one. The loader may be
152.420 + * null, but the base name and the locale must have a non-null
152.421 + * value.
152.422 + */
152.423 + private static final class CacheKey implements Cloneable {
152.424 + // These three are the actual keys for lookup in Map.
152.425 + private String name;
152.426 + private Locale locale;
152.427 +
152.428 + // bundle format which is necessary for calling
152.429 + // Control.needsReload().
152.430 + private String format;
152.431 +
152.432 + // These time values are in CacheKey so that NONEXISTENT_BUNDLE
152.433 + // doesn't need to be cloned for caching.
152.434 +
152.435 + // The time when the bundle has been loaded
152.436 + private volatile long loadTime;
152.437 +
152.438 + // The time when the bundle expires in the cache, or either
152.439 + // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
152.440 + private volatile long expirationTime;
152.441 +
152.442 + // Placeholder for an error report by a Throwable
152.443 + private Throwable cause;
152.444 +
152.445 + // Hash code value cache to avoid recalculating the hash code
152.446 + // of this instance.
152.447 + private int hashCodeCache;
152.448 +
152.449 + CacheKey(String baseName, Locale locale) {
152.450 + this.name = baseName;
152.451 + this.locale = locale;
152.452 + calculateHashCode();
152.453 + }
152.454 +
152.455 + String getName() {
152.456 + return name;
152.457 + }
152.458 +
152.459 + CacheKey setName(String baseName) {
152.460 + if (!this.name.equals(baseName)) {
152.461 + this.name = baseName;
152.462 + calculateHashCode();
152.463 + }
152.464 + return this;
152.465 + }
152.466 +
152.467 + Locale getLocale() {
152.468 + return locale;
152.469 + }
152.470 +
152.471 + CacheKey setLocale(Locale locale) {
152.472 + if (!this.locale.equals(locale)) {
152.473 + this.locale = locale;
152.474 + calculateHashCode();
152.475 + }
152.476 + return this;
152.477 + }
152.478 +
152.479 + public boolean equals(Object other) {
152.480 + if (this == other) {
152.481 + return true;
152.482 + }
152.483 + try {
152.484 + final CacheKey otherEntry = (CacheKey)other;
152.485 + //quick check to see if they are not equal
152.486 + if (hashCodeCache != otherEntry.hashCodeCache) {
152.487 + return false;
152.488 + }
152.489 + //are the names the same?
152.490 + if (!name.equals(otherEntry.name)) {
152.491 + return false;
152.492 + }
152.493 + // are the locales the same?
152.494 + if (!locale.equals(otherEntry.locale)) {
152.495 + return false;
152.496 + }
152.497 + return true;
152.498 + } catch (NullPointerException e) {
152.499 + } catch (ClassCastException e) {
152.500 + }
152.501 + return false;
152.502 + }
152.503 +
152.504 + public int hashCode() {
152.505 + return hashCodeCache;
152.506 + }
152.507 +
152.508 + private void calculateHashCode() {
152.509 + hashCodeCache = name.hashCode() << 3;
152.510 + hashCodeCache ^= locale.hashCode();
152.511 + }
152.512 +
152.513 + public Object clone() {
152.514 + try {
152.515 + CacheKey clone = (CacheKey) super.clone();
152.516 + // Clear the reference to a Throwable
152.517 + clone.cause = null;
152.518 + return clone;
152.519 + } catch (CloneNotSupportedException e) {
152.520 + //this should never happen
152.521 + throw new InternalError();
152.522 + }
152.523 + }
152.524 +
152.525 + String getFormat() {
152.526 + return format;
152.527 + }
152.528 +
152.529 + void setFormat(String format) {
152.530 + this.format = format;
152.531 + }
152.532 +
152.533 + private void setCause(Throwable cause) {
152.534 + if (this.cause == null) {
152.535 + this.cause = cause;
152.536 + } else {
152.537 + // Override the cause if the previous one is
152.538 + // ClassNotFoundException.
152.539 + if (this.cause instanceof ClassNotFoundException) {
152.540 + this.cause = cause;
152.541 + }
152.542 + }
152.543 + }
152.544 +
152.545 + private Throwable getCause() {
152.546 + return cause;
152.547 + }
152.548 +
152.549 + public String toString() {
152.550 + String l = locale.toString();
152.551 + if (l.length() == 0) {
152.552 + if (locale.getVariant().length() != 0) {
152.553 + l = "__" + locale.getVariant();
152.554 + } else {
152.555 + l = "\"\"";
152.556 + }
152.557 + }
152.558 + return "CacheKey[" + name + ", lc=" + l
152.559 + + "(format=" + format + ")]";
152.560 + }
152.561 + }
152.562 +
152.563 + /**
152.564 + * The common interface to get a CacheKey in LoaderReference and
152.565 + * BundleReference.
152.566 + */
152.567 + private static interface CacheKeyReference {
152.568 + public CacheKey getCacheKey();
152.569 + }
152.570 +
152.571 + /**
152.572 + * References to bundles are soft references so that they can be garbage
152.573 + * collected when they have no hard references.
152.574 + */
152.575 + private static final class BundleReference extends SoftReference<ResourceBundle>
152.576 + implements CacheKeyReference {
152.577 + private CacheKey cacheKey;
152.578 +
152.579 + BundleReference(ResourceBundle referent, ReferenceQueue q, CacheKey key) {
152.580 + super(referent, q);
152.581 + cacheKey = key;
152.582 + }
152.583 +
152.584 + public CacheKey getCacheKey() {
152.585 + return cacheKey;
152.586 + }
152.587 + }
152.588 +
152.589 + /**
152.590 + * Gets a resource bundle using the specified base name, the default locale,
152.591 + * and the caller's class loader. Calling this method is equivalent to calling
152.592 + * <blockquote>
152.593 + * <code>getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader())</code>,
152.594 + * </blockquote>
152.595 + * except that <code>getClassLoader()</code> is run with the security
152.596 + * privileges of <code>ResourceBundle</code>.
152.597 + * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
152.598 + * for a complete description of the search and instantiation strategy.
152.599 + *
152.600 + * @param baseName the base name of the resource bundle, a fully qualified class name
152.601 + * @exception java.lang.NullPointerException
152.602 + * if <code>baseName</code> is <code>null</code>
152.603 + * @exception MissingResourceException
152.604 + * if no resource bundle for the specified base name can be found
152.605 + * @return a resource bundle for the given base name and the default locale
152.606 + */
152.607 + public static final ResourceBundle getBundle(String baseName)
152.608 + {
152.609 + return getBundleImpl(baseName, Locale.getDefault(),
152.610 + Control.INSTANCE);
152.611 + }
152.612 +
152.613 + /**
152.614 + * Returns a resource bundle using the specified base name, the
152.615 + * default locale and the specified control. Calling this method
152.616 + * is equivalent to calling
152.617 + * <pre>
152.618 + * getBundle(baseName, Locale.getDefault(),
152.619 + * this.getClass().getClassLoader(), control),
152.620 + * </pre>
152.621 + * except that <code>getClassLoader()</code> is run with the security
152.622 + * privileges of <code>ResourceBundle</code>. See {@link
152.623 + * #getBundle(String, Locale, ClassLoader, Control) getBundle} for the
152.624 + * complete description of the resource bundle loading process with a
152.625 + * <code>ResourceBundle.Control</code>.
152.626 + *
152.627 + * @param baseName
152.628 + * the base name of the resource bundle, a fully qualified class
152.629 + * name
152.630 + * @param control
152.631 + * the control which gives information for the resource bundle
152.632 + * loading process
152.633 + * @return a resource bundle for the given base name and the default
152.634 + * locale
152.635 + * @exception NullPointerException
152.636 + * if <code>baseName</code> or <code>control</code> is
152.637 + * <code>null</code>
152.638 + * @exception MissingResourceException
152.639 + * if no resource bundle for the specified base name can be found
152.640 + * @exception IllegalArgumentException
152.641 + * if the given <code>control</code> doesn't perform properly
152.642 + * (e.g., <code>control.getCandidateLocales</code> returns null.)
152.643 + * Note that validation of <code>control</code> is performed as
152.644 + * needed.
152.645 + * @since 1.6
152.646 + */
152.647 + public static final ResourceBundle getBundle(String baseName,
152.648 + Control control) {
152.649 + return getBundleImpl(baseName, Locale.getDefault(),
152.650 + /* must determine loader here, else we break stack invariant */
152.651 + control);
152.652 + }
152.653 +
152.654 + /**
152.655 + * Gets a resource bundle using the specified base name and locale,
152.656 + * and the caller's class loader. Calling this method is equivalent to calling
152.657 + * <blockquote>
152.658 + * <code>getBundle(baseName, locale, this.getClass().getClassLoader())</code>,
152.659 + * </blockquote>
152.660 + * except that <code>getClassLoader()</code> is run with the security
152.661 + * privileges of <code>ResourceBundle</code>.
152.662 + * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
152.663 + * for a complete description of the search and instantiation strategy.
152.664 + *
152.665 + * @param baseName
152.666 + * the base name of the resource bundle, a fully qualified class name
152.667 + * @param locale
152.668 + * the locale for which a resource bundle is desired
152.669 + * @exception NullPointerException
152.670 + * if <code>baseName</code> or <code>locale</code> is <code>null</code>
152.671 + * @exception MissingResourceException
152.672 + * if no resource bundle for the specified base name can be found
152.673 + * @return a resource bundle for the given base name and locale
152.674 + */
152.675 + public static final ResourceBundle getBundle(String baseName,
152.676 + Locale locale)
152.677 + {
152.678 + return getBundleImpl(baseName, locale,
152.679 + /* must determine loader here, else we break stack invariant */
152.680 + Control.INSTANCE);
152.681 + }
152.682 +
152.683 + /**
152.684 + * Returns a resource bundle using the specified base name, target
152.685 + * locale and control, and the caller's class loader. Calling this
152.686 + * method is equivalent to calling
152.687 + * <pre>
152.688 + * getBundle(baseName, targetLocale, this.getClass().getClassLoader(),
152.689 + * control),
152.690 + * </pre>
152.691 + * except that <code>getClassLoader()</code> is run with the security
152.692 + * privileges of <code>ResourceBundle</code>. See {@link
152.693 + * #getBundle(String, Locale, ClassLoader, Control) getBundle} for the
152.694 + * complete description of the resource bundle loading process with a
152.695 + * <code>ResourceBundle.Control</code>.
152.696 + *
152.697 + * @param baseName
152.698 + * the base name of the resource bundle, a fully qualified
152.699 + * class name
152.700 + * @param targetLocale
152.701 + * the locale for which a resource bundle is desired
152.702 + * @param control
152.703 + * the control which gives information for the resource
152.704 + * bundle loading process
152.705 + * @return a resource bundle for the given base name and a
152.706 + * <code>Locale</code> in <code>locales</code>
152.707 + * @exception NullPointerException
152.708 + * if <code>baseName</code>, <code>locales</code> or
152.709 + * <code>control</code> is <code>null</code>
152.710 + * @exception MissingResourceException
152.711 + * if no resource bundle for the specified base name in any
152.712 + * of the <code>locales</code> can be found.
152.713 + * @exception IllegalArgumentException
152.714 + * if the given <code>control</code> doesn't perform properly
152.715 + * (e.g., <code>control.getCandidateLocales</code> returns null.)
152.716 + * Note that validation of <code>control</code> is performed as
152.717 + * needed.
152.718 + * @since 1.6
152.719 + */
152.720 + public static final ResourceBundle getBundle(String baseName, Locale targetLocale,
152.721 + Control control) {
152.722 + return getBundleImpl(baseName, targetLocale,
152.723 + /* must determine loader here, else we break stack invariant */
152.724 + control);
152.725 + }
152.726 +
152.727 + /**
152.728 + * Gets a resource bundle using the specified base name, locale, and class
152.729 + * loader.
152.730 + *
152.731 + * <p><a name="default_behavior"/>This method behaves the same as calling
152.732 + * {@link #getBundle(String, Locale, ClassLoader, Control)} passing a
152.733 + * default instance of {@link Control}. The following describes this behavior.
152.734 + *
152.735 + * <p><code>getBundle</code> uses the base name, the specified locale, and
152.736 + * the default locale (obtained from {@link java.util.Locale#getDefault()
152.737 + * Locale.getDefault}) to generate a sequence of <a
152.738 + * name="candidates"><em>candidate bundle names</em></a>. If the specified
152.739 + * locale's language, script, country, and variant are all empty strings,
152.740 + * then the base name is the only candidate bundle name. Otherwise, a list
152.741 + * of candidate locales is generated from the attribute values of the
152.742 + * specified locale (language, script, country and variant) and appended to
152.743 + * the base name. Typically, this will look like the following:
152.744 + *
152.745 + * <pre>
152.746 + * baseName + "_" + language + "_" + script + "_" + country + "_" + variant
152.747 + * baseName + "_" + language + "_" + script + "_" + country
152.748 + * baseName + "_" + language + "_" + script
152.749 + * baseName + "_" + language + "_" + country + "_" + variant
152.750 + * baseName + "_" + language + "_" + country
152.751 + * baseName + "_" + language
152.752 + * </pre>
152.753 + *
152.754 + * <p>Candidate bundle names where the final component is an empty string
152.755 + * are omitted, along with the underscore. For example, if country is an
152.756 + * empty string, the second and the fifth candidate bundle names above
152.757 + * would be omitted. Also, if script is an empty string, the candidate names
152.758 + * including script are omitted. For example, a locale with language "de"
152.759 + * and variant "JAVA" will produce candidate names with base name
152.760 + * "MyResource" below.
152.761 + *
152.762 + * <pre>
152.763 + * MyResource_de__JAVA
152.764 + * MyResource_de
152.765 + * </pre>
152.766 + *
152.767 + * In the case that the variant contains one or more underscores ('_'), a
152.768 + * sequence of bundle names generated by truncating the last underscore and
152.769 + * the part following it is inserted after a candidate bundle name with the
152.770 + * original variant. For example, for a locale with language "en", script
152.771 + * "Latn, country "US" and variant "WINDOWS_VISTA", and bundle base name
152.772 + * "MyResource", the list of candidate bundle names below is generated:
152.773 + *
152.774 + * <pre>
152.775 + * MyResource_en_Latn_US_WINDOWS_VISTA
152.776 + * MyResource_en_Latn_US_WINDOWS
152.777 + * MyResource_en_Latn_US
152.778 + * MyResource_en_Latn
152.779 + * MyResource_en_US_WINDOWS_VISTA
152.780 + * MyResource_en_US_WINDOWS
152.781 + * MyResource_en_US
152.782 + * MyResource_en
152.783 + * </pre>
152.784 + *
152.785 + * <blockquote><b>Note:</b> For some <code>Locale</code>s, the list of
152.786 + * candidate bundle names contains extra names, or the order of bundle names
152.787 + * is slightly modified. See the description of the default implementation
152.788 + * of {@link Control#getCandidateLocales(String, Locale)
152.789 + * getCandidateLocales} for details.</blockquote>
152.790 + *
152.791 + * <p><code>getBundle</code> then iterates over the candidate bundle names
152.792 + * to find the first one for which it can <em>instantiate</em> an actual
152.793 + * resource bundle. It uses the default controls' {@link Control#getFormats
152.794 + * getFormats} method, which generates two bundle names for each generated
152.795 + * name, the first a class name and the second a properties file name. For
152.796 + * each candidate bundle name, it attempts to create a resource bundle:
152.797 + *
152.798 + * <ul><li>First, it attempts to load a class using the generated class name.
152.799 + * If such a class can be found and loaded using the specified class
152.800 + * loader, is assignment compatible with ResourceBundle, is accessible from
152.801 + * ResourceBundle, and can be instantiated, <code>getBundle</code> creates a
152.802 + * new instance of this class and uses it as the <em>result resource
152.803 + * bundle</em>.
152.804 + *
152.805 + * <li>Otherwise, <code>getBundle</code> attempts to locate a property
152.806 + * resource file using the generated properties file name. It generates a
152.807 + * path name from the candidate bundle name by replacing all "." characters
152.808 + * with "/" and appending the string ".properties". It attempts to find a
152.809 + * "resource" with this name using {@link
152.810 + * java.lang.ClassLoader#getResource(java.lang.String)
152.811 + * ClassLoader.getResource}. (Note that a "resource" in the sense of
152.812 + * <code>getResource</code> has nothing to do with the contents of a
152.813 + * resource bundle, it is just a container of data, such as a file.) If it
152.814 + * finds a "resource", it attempts to create a new {@link
152.815 + * PropertyResourceBundle} instance from its contents. If successful, this
152.816 + * instance becomes the <em>result resource bundle</em>. </ul>
152.817 + *
152.818 + * <p>This continues until a result resource bundle is instantiated or the
152.819 + * list of candidate bundle names is exhausted. If no matching resource
152.820 + * bundle is found, the default control's {@link Control#getFallbackLocale
152.821 + * getFallbackLocale} method is called, which returns the current default
152.822 + * locale. A new sequence of candidate locale names is generated using this
152.823 + * locale and and searched again, as above.
152.824 + *
152.825 + * <p>If still no result bundle is found, the base name alone is looked up. If
152.826 + * this still fails, a <code>MissingResourceException</code> is thrown.
152.827 + *
152.828 + * <p><a name="parent_chain"/> Once a result resource bundle has been found,
152.829 + * its <em>parent chain</em> is instantiated. If the result bundle already
152.830 + * has a parent (perhaps because it was returned from a cache) the chain is
152.831 + * complete.
152.832 + *
152.833 + * <p>Otherwise, <code>getBundle</code> examines the remainder of the
152.834 + * candidate locale list that was used during the pass that generated the
152.835 + * result resource bundle. (As before, candidate bundle names where the
152.836 + * final component is an empty string are omitted.) When it comes to the
152.837 + * end of the candidate list, it tries the plain bundle name. With each of the
152.838 + * candidate bundle names it attempts to instantiate a resource bundle (first
152.839 + * looking for a class and then a properties file, as described above).
152.840 + *
152.841 + * <p>Whenever it succeeds, it calls the previously instantiated resource
152.842 + * bundle's {@link #setParent(java.util.ResourceBundle) setParent} method
152.843 + * with the new resource bundle. This continues until the list of names
152.844 + * is exhausted or the current bundle already has a non-null parent.
152.845 + *
152.846 + * <p>Once the parent chain is complete, the bundle is returned.
152.847 + *
152.848 + * <p><b>Note:</b> <code>getBundle</code> caches instantiated resource
152.849 + * bundles and might return the same resource bundle instance multiple times.
152.850 + *
152.851 + * <p><b>Note:</b>The <code>baseName</code> argument should be a fully
152.852 + * qualified class name. However, for compatibility with earlier versions,
152.853 + * Sun's Java SE Runtime Environments do not verify this, and so it is
152.854 + * possible to access <code>PropertyResourceBundle</code>s by specifying a
152.855 + * path name (using "/") instead of a fully qualified class name (using
152.856 + * ".").
152.857 + *
152.858 + * <p><a name="default_behavior_example"/>
152.859 + * <strong>Example:</strong>
152.860 + * <p>
152.861 + * The following class and property files are provided:
152.862 + * <pre>
152.863 + * MyResources.class
152.864 + * MyResources.properties
152.865 + * MyResources_fr.properties
152.866 + * MyResources_fr_CH.class
152.867 + * MyResources_fr_CH.properties
152.868 + * MyResources_en.properties
152.869 + * MyResources_es_ES.class
152.870 + * </pre>
152.871 + *
152.872 + * The contents of all files are valid (that is, public non-abstract
152.873 + * subclasses of <code>ResourceBundle</code> for the ".class" files,
152.874 + * syntactically correct ".properties" files). The default locale is
152.875 + * <code>Locale("en", "GB")</code>.
152.876 + *
152.877 + * <p>Calling <code>getBundle</code> with the locale arguments below will
152.878 + * instantiate resource bundles as follows:
152.879 + *
152.880 + * <table>
152.881 + * <tr><td>Locale("fr", "CH")</td><td>MyResources_fr_CH.class, parent MyResources_fr.properties, parent MyResources.class</td></tr>
152.882 + * <tr><td>Locale("fr", "FR")</td><td>MyResources_fr.properties, parent MyResources.class</td></tr>
152.883 + * <tr><td>Locale("de", "DE")</td><td>MyResources_en.properties, parent MyResources.class</td></tr>
152.884 + * <tr><td>Locale("en", "US")</td><td>MyResources_en.properties, parent MyResources.class</td></tr>
152.885 + * <tr><td>Locale("es", "ES")</td><td>MyResources_es_ES.class, parent MyResources.class</td></tr>
152.886 + * </table>
152.887 + *
152.888 + * <p>The file MyResources_fr_CH.properties is never used because it is
152.889 + * hidden by the MyResources_fr_CH.class. Likewise, MyResources.properties
152.890 + * is also hidden by MyResources.class.
152.891 + *
152.892 + * @param baseName the base name of the resource bundle, a fully qualified class name
152.893 + * @param locale the locale for which a resource bundle is desired
152.894 + * @param loader the class loader from which to load the resource bundle
152.895 + * @return a resource bundle for the given base name and locale
152.896 + * @exception java.lang.NullPointerException
152.897 + * if <code>baseName</code>, <code>locale</code>, or <code>loader</code> is <code>null</code>
152.898 + * @exception MissingResourceException
152.899 + * if no resource bundle for the specified base name can be found
152.900 + * @since 1.2
152.901 + */
152.902 + public static ResourceBundle getBundle(String baseName, Locale locale,
152.903 + ClassLoader loader)
152.904 + {
152.905 + if (loader == null) {
152.906 + throw new NullPointerException();
152.907 + }
152.908 + return getBundleImpl(baseName, locale, Control.INSTANCE);
152.909 + }
152.910 +
152.911 + /**
152.912 + * Returns a resource bundle using the specified base name, target
152.913 + * locale, class loader and control. Unlike the {@linkplain
152.914 + * #getBundle(String, Locale, ClassLoader) <code>getBundle</code>
152.915 + * factory methods with no <code>control</code> argument}, the given
152.916 + * <code>control</code> specifies how to locate and instantiate resource
152.917 + * bundles. Conceptually, the bundle loading process with the given
152.918 + * <code>control</code> is performed in the following steps.
152.919 + *
152.920 + * <p>
152.921 + * <ol>
152.922 + * <li>This factory method looks up the resource bundle in the cache for
152.923 + * the specified <code>baseName</code>, <code>targetLocale</code> and
152.924 + * <code>loader</code>. If the requested resource bundle instance is
152.925 + * found in the cache and the time-to-live periods of the instance and
152.926 + * all of its parent instances have not expired, the instance is returned
152.927 + * to the caller. Otherwise, this factory method proceeds with the
152.928 + * loading process below.</li>
152.929 + *
152.930 + * <li>The {@link ResourceBundle.Control#getFormats(String)
152.931 + * control.getFormats} method is called to get resource bundle formats
152.932 + * to produce bundle or resource names. The strings
152.933 + * <code>"java.class"</code> and <code>"java.properties"</code>
152.934 + * designate class-based and {@linkplain PropertyResourceBundle
152.935 + * property}-based resource bundles, respectively. Other strings
152.936 + * starting with <code>"java."</code> are reserved for future extensions
152.937 + * and must not be used for application-defined formats. Other strings
152.938 + * designate application-defined formats.</li>
152.939 + *
152.940 + * <li>The {@link ResourceBundle.Control#getCandidateLocales(String,
152.941 + * Locale) control.getCandidateLocales} method is called with the target
152.942 + * locale to get a list of <em>candidate <code>Locale</code>s</em> for
152.943 + * which resource bundles are searched.</li>
152.944 + *
152.945 + * <li>The {@link ResourceBundle.Control#newBundle(String, Locale,
152.946 + * String, ClassLoader, boolean) control.newBundle} method is called to
152.947 + * instantiate a <code>ResourceBundle</code> for the base bundle name, a
152.948 + * candidate locale, and a format. (Refer to the note on the cache
152.949 + * lookup below.) This step is iterated over all combinations of the
152.950 + * candidate locales and formats until the <code>newBundle</code> method
152.951 + * returns a <code>ResourceBundle</code> instance or the iteration has
152.952 + * used up all the combinations. For example, if the candidate locales
152.953 + * are <code>Locale("de", "DE")</code>, <code>Locale("de")</code> and
152.954 + * <code>Locale("")</code> and the formats are <code>"java.class"</code>
152.955 + * and <code>"java.properties"</code>, then the following is the
152.956 + * sequence of locale-format combinations to be used to call
152.957 + * <code>control.newBundle</code>.
152.958 + *
152.959 + * <table style="width: 50%; text-align: left; margin-left: 40px;"
152.960 + * border="0" cellpadding="2" cellspacing="2">
152.961 + * <tbody><code>
152.962 + * <tr>
152.963 + * <td
152.964 + * style="vertical-align: top; text-align: left; font-weight: bold; width: 50%;">Locale<br>
152.965 + * </td>
152.966 + * <td
152.967 + * style="vertical-align: top; text-align: left; font-weight: bold; width: 50%;">format<br>
152.968 + * </td>
152.969 + * </tr>
152.970 + * <tr>
152.971 + * <td style="vertical-align: top; width: 50%;">Locale("de", "DE")<br>
152.972 + * </td>
152.973 + * <td style="vertical-align: top; width: 50%;">java.class<br>
152.974 + * </td>
152.975 + * </tr>
152.976 + * <tr>
152.977 + * <td style="vertical-align: top; width: 50%;">Locale("de", "DE")</td>
152.978 + * <td style="vertical-align: top; width: 50%;">java.properties<br>
152.979 + * </td>
152.980 + * </tr>
152.981 + * <tr>
152.982 + * <td style="vertical-align: top; width: 50%;">Locale("de")</td>
152.983 + * <td style="vertical-align: top; width: 50%;">java.class</td>
152.984 + * </tr>
152.985 + * <tr>
152.986 + * <td style="vertical-align: top; width: 50%;">Locale("de")</td>
152.987 + * <td style="vertical-align: top; width: 50%;">java.properties</td>
152.988 + * </tr>
152.989 + * <tr>
152.990 + * <td style="vertical-align: top; width: 50%;">Locale("")<br>
152.991 + * </td>
152.992 + * <td style="vertical-align: top; width: 50%;">java.class</td>
152.993 + * </tr>
152.994 + * <tr>
152.995 + * <td style="vertical-align: top; width: 50%;">Locale("")</td>
152.996 + * <td style="vertical-align: top; width: 50%;">java.properties</td>
152.997 + * </tr>
152.998 + * </code></tbody>
152.999 + * </table>
152.1000 + * </li>
152.1001 + *
152.1002 + * <li>If the previous step has found no resource bundle, proceed to
152.1003 + * Step 6. If a bundle has been found that is a base bundle (a bundle
152.1004 + * for <code>Locale("")</code>), and the candidate locale list only contained
152.1005 + * <code>Locale("")</code>, return the bundle to the caller. If a bundle
152.1006 + * has been found that is a base bundle, but the candidate locale list
152.1007 + * contained locales other than Locale(""), put the bundle on hold and
152.1008 + * proceed to Step 6. If a bundle has been found that is not a base
152.1009 + * bundle, proceed to Step 7.</li>
152.1010 + *
152.1011 + * <li>The {@link ResourceBundle.Control#getFallbackLocale(String,
152.1012 + * Locale) control.getFallbackLocale} method is called to get a fallback
152.1013 + * locale (alternative to the current target locale) to try further
152.1014 + * finding a resource bundle. If the method returns a non-null locale,
152.1015 + * it becomes the next target locale and the loading process starts over
152.1016 + * from Step 3. Otherwise, if a base bundle was found and put on hold in
152.1017 + * a previous Step 5, it is returned to the caller now. Otherwise, a
152.1018 + * MissingResourceException is thrown.</li>
152.1019 + *
152.1020 + * <li>At this point, we have found a resource bundle that's not the
152.1021 + * base bundle. If this bundle set its parent during its instantiation,
152.1022 + * it is returned to the caller. Otherwise, its <a
152.1023 + * href="./ResourceBundle.html#parent_chain">parent chain</a> is
152.1024 + * instantiated based on the list of candidate locales from which it was
152.1025 + * found. Finally, the bundle is returned to the caller.</li>
152.1026 + * </ol>
152.1027 + *
152.1028 + * <p>During the resource bundle loading process above, this factory
152.1029 + * method looks up the cache before calling the {@link
152.1030 + * Control#newBundle(String, Locale, String, ClassLoader, boolean)
152.1031 + * control.newBundle} method. If the time-to-live period of the
152.1032 + * resource bundle found in the cache has expired, the factory method
152.1033 + * calls the {@link ResourceBundle.Control#needsReload(String, Locale,
152.1034 + * String, ClassLoader, ResourceBundle, long) control.needsReload}
152.1035 + * method to determine whether the resource bundle needs to be reloaded.
152.1036 + * If reloading is required, the factory method calls
152.1037 + * <code>control.newBundle</code> to reload the resource bundle. If
152.1038 + * <code>control.newBundle</code> returns <code>null</code>, the factory
152.1039 + * method puts a dummy resource bundle in the cache as a mark of
152.1040 + * nonexistent resource bundles in order to avoid lookup overhead for
152.1041 + * subsequent requests. Such dummy resource bundles are under the same
152.1042 + * expiration control as specified by <code>control</code>.
152.1043 + *
152.1044 + * <p>All resource bundles loaded are cached by default. Refer to
152.1045 + * {@link Control#getTimeToLive(String,Locale)
152.1046 + * control.getTimeToLive} for details.
152.1047 + *
152.1048 + * <p>The following is an example of the bundle loading process with the
152.1049 + * default <code>ResourceBundle.Control</code> implementation.
152.1050 + *
152.1051 + * <p>Conditions:
152.1052 + * <ul>
152.1053 + * <li>Base bundle name: <code>foo.bar.Messages</code>
152.1054 + * <li>Requested <code>Locale</code>: {@link Locale#ITALY}</li>
152.1055 + * <li>Default <code>Locale</code>: {@link Locale#FRENCH}</li>
152.1056 + * <li>Available resource bundles:
152.1057 + * <code>foo/bar/Messages_fr.properties</code> and
152.1058 + * <code>foo/bar/Messages.properties</code></li>
152.1059 + * </ul>
152.1060 + *
152.1061 + * <p>First, <code>getBundle</code> tries loading a resource bundle in
152.1062 + * the following sequence.
152.1063 + *
152.1064 + * <ul>
152.1065 + * <li>class <code>foo.bar.Messages_it_IT</code>
152.1066 + * <li>file <code>foo/bar/Messages_it_IT.properties</code>
152.1067 + * <li>class <code>foo.bar.Messages_it</code></li>
152.1068 + * <li>file <code>foo/bar/Messages_it.properties</code></li>
152.1069 + * <li>class <code>foo.bar.Messages</code></li>
152.1070 + * <li>file <code>foo/bar/Messages.properties</code></li>
152.1071 + * </ul>
152.1072 + *
152.1073 + * <p>At this point, <code>getBundle</code> finds
152.1074 + * <code>foo/bar/Messages.properties</code>, which is put on hold
152.1075 + * because it's the base bundle. <code>getBundle</code> calls {@link
152.1076 + * Control#getFallbackLocale(String, Locale)
152.1077 + * control.getFallbackLocale("foo.bar.Messages", Locale.ITALY)} which
152.1078 + * returns <code>Locale.FRENCH</code>. Next, <code>getBundle</code>
152.1079 + * tries loading a bundle in the following sequence.
152.1080 + *
152.1081 + * <ul>
152.1082 + * <li>class <code>foo.bar.Messages_fr</code></li>
152.1083 + * <li>file <code>foo/bar/Messages_fr.properties</code></li>
152.1084 + * <li>class <code>foo.bar.Messages</code></li>
152.1085 + * <li>file <code>foo/bar/Messages.properties</code></li>
152.1086 + * </ul>
152.1087 + *
152.1088 + * <p><code>getBundle</code> finds
152.1089 + * <code>foo/bar/Messages_fr.properties</code> and creates a
152.1090 + * <code>ResourceBundle</code> instance. Then, <code>getBundle</code>
152.1091 + * sets up its parent chain from the list of the candiate locales. Only
152.1092 + * <code>foo/bar/Messages.properties</code> is found in the list and
152.1093 + * <code>getBundle</code> creates a <code>ResourceBundle</code> instance
152.1094 + * that becomes the parent of the instance for
152.1095 + * <code>foo/bar/Messages_fr.properties</code>.
152.1096 + *
152.1097 + * @param baseName
152.1098 + * the base name of the resource bundle, a fully qualified
152.1099 + * class name
152.1100 + * @param targetLocale
152.1101 + * the locale for which a resource bundle is desired
152.1102 + * @param loader
152.1103 + * the class loader from which to load the resource bundle
152.1104 + * @param control
152.1105 + * the control which gives information for the resource
152.1106 + * bundle loading process
152.1107 + * @return a resource bundle for the given base name and locale
152.1108 + * @exception NullPointerException
152.1109 + * if <code>baseName</code>, <code>targetLocale</code>,
152.1110 + * <code>loader</code>, or <code>control</code> is
152.1111 + * <code>null</code>
152.1112 + * @exception MissingResourceException
152.1113 + * if no resource bundle for the specified base name can be found
152.1114 + * @exception IllegalArgumentException
152.1115 + * if the given <code>control</code> doesn't perform properly
152.1116 + * (e.g., <code>control.getCandidateLocales</code> returns null.)
152.1117 + * Note that validation of <code>control</code> is performed as
152.1118 + * needed.
152.1119 + * @since 1.6
152.1120 + */
152.1121 + public static ResourceBundle getBundle(String baseName, Locale targetLocale,
152.1122 + ClassLoader loader, Control control) {
152.1123 + if (loader == null || control == null) {
152.1124 + throw new NullPointerException();
152.1125 + }
152.1126 + return getBundleImpl(baseName, targetLocale, control);
152.1127 + }
152.1128 +
152.1129 + private static ResourceBundle getBundleImpl(String baseName, Locale locale,
152.1130 + Control control) {
152.1131 + if (locale == null || control == null) {
152.1132 + throw new NullPointerException();
152.1133 + }
152.1134 +
152.1135 + // We create a CacheKey here for use by this call. The base
152.1136 + // name and loader will never change during the bundle loading
152.1137 + // process. We have to make sure that the locale is set before
152.1138 + // using it as a cache key.
152.1139 + CacheKey cacheKey = new CacheKey(baseName, locale);
152.1140 + ResourceBundle bundle = null;
152.1141 +
152.1142 + // Quick lookup of the cache.
152.1143 + BundleReference bundleRef = cacheList.get(cacheKey);
152.1144 + if (bundleRef != null) {
152.1145 + bundle = bundleRef.get();
152.1146 + bundleRef = null;
152.1147 + }
152.1148 +
152.1149 + // If this bundle and all of its parents are valid (not expired),
152.1150 + // then return this bundle. If any of the bundles is expired, we
152.1151 + // don't call control.needsReload here but instead drop into the
152.1152 + // complete loading process below.
152.1153 + if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
152.1154 + return bundle;
152.1155 + }
152.1156 +
152.1157 + // No valid bundle was found in the cache, so we need to load the
152.1158 + // resource bundle and its parents.
152.1159 +
152.1160 + boolean isKnownControl = (control == Control.INSTANCE) ||
152.1161 + (control instanceof SingleFormatControl);
152.1162 + List<String> formats = control.getFormats(baseName);
152.1163 + if (!isKnownControl && !checkList(formats)) {
152.1164 + throw new IllegalArgumentException("Invalid Control: getFormats");
152.1165 + }
152.1166 +
152.1167 + ResourceBundle baseBundle = null;
152.1168 + for (Locale targetLocale = locale;
152.1169 + targetLocale != null;
152.1170 + targetLocale = control.getFallbackLocale(baseName, targetLocale)) {
152.1171 + List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale);
152.1172 + if (!isKnownControl && !checkList(candidateLocales)) {
152.1173 + throw new IllegalArgumentException("Invalid Control: getCandidateLocales");
152.1174 + }
152.1175 +
152.1176 + bundle = findBundle(cacheKey, candidateLocales, formats, 0, control, baseBundle);
152.1177 +
152.1178 + // If the loaded bundle is the base bundle and exactly for the
152.1179 + // requested locale or the only candidate locale, then take the
152.1180 + // bundle as the resulting one. If the loaded bundle is the base
152.1181 + // bundle, it's put on hold until we finish processing all
152.1182 + // fallback locales.
152.1183 + if (isValidBundle(bundle)) {
152.1184 + boolean isBaseBundle = Locale.ROOT.equals(bundle.locale);
152.1185 + if (!isBaseBundle || bundle.locale.equals(locale)
152.1186 + || (candidateLocales.size() == 1
152.1187 + && bundle.locale.equals(candidateLocales.get(0)))) {
152.1188 + break;
152.1189 + }
152.1190 +
152.1191 + // If the base bundle has been loaded, keep the reference in
152.1192 + // baseBundle so that we can avoid any redundant loading in case
152.1193 + // the control specify not to cache bundles.
152.1194 + if (isBaseBundle && baseBundle == null) {
152.1195 + baseBundle = bundle;
152.1196 + }
152.1197 + }
152.1198 + }
152.1199 +
152.1200 + if (bundle == null) {
152.1201 + if (baseBundle == null) {
152.1202 + throwMissingResourceException(baseName, locale, cacheKey.getCause());
152.1203 + }
152.1204 + bundle = baseBundle;
152.1205 + }
152.1206 +
152.1207 + return bundle;
152.1208 + }
152.1209 +
152.1210 + /**
152.1211 + * Checks if the given <code>List</code> is not null, not empty,
152.1212 + * not having null in its elements.
152.1213 + */
152.1214 + private static final boolean checkList(List a) {
152.1215 + boolean valid = (a != null && a.size() != 0);
152.1216 + if (valid) {
152.1217 + int size = a.size();
152.1218 + for (int i = 0; valid && i < size; i++) {
152.1219 + valid = (a.get(i) != null);
152.1220 + }
152.1221 + }
152.1222 + return valid;
152.1223 + }
152.1224 +
152.1225 + private static final ResourceBundle findBundle(CacheKey cacheKey,
152.1226 + List<Locale> candidateLocales,
152.1227 + List<String> formats,
152.1228 + int index,
152.1229 + Control control,
152.1230 + ResourceBundle baseBundle) {
152.1231 + Locale targetLocale = candidateLocales.get(index);
152.1232 + ResourceBundle parent = null;
152.1233 + if (index != candidateLocales.size() - 1) {
152.1234 + parent = findBundle(cacheKey, candidateLocales, formats, index + 1,
152.1235 + control, baseBundle);
152.1236 + } else if (baseBundle != null && Locale.ROOT.equals(targetLocale)) {
152.1237 + return baseBundle;
152.1238 + }
152.1239 +
152.1240 + // Before we do the real loading work, see whether we need to
152.1241 + // do some housekeeping: If references to class loaders or
152.1242 + // resource bundles have been nulled out, remove all related
152.1243 + // information from the cache.
152.1244 + Object ref;
152.1245 + while ((ref = referenceQueue.poll()) != null) {
152.1246 + cacheList.remove(((CacheKeyReference)ref).getCacheKey());
152.1247 + }
152.1248 +
152.1249 + // flag indicating the resource bundle has expired in the cache
152.1250 + boolean expiredBundle = false;
152.1251 +
152.1252 + // First, look up the cache to see if it's in the cache, without
152.1253 + // attempting to load bundle.
152.1254 + cacheKey.setLocale(targetLocale);
152.1255 + ResourceBundle bundle = findBundleInCache(cacheKey, control);
152.1256 + if (isValidBundle(bundle)) {
152.1257 + expiredBundle = bundle.expired;
152.1258 + if (!expiredBundle) {
152.1259 + // If its parent is the one asked for by the candidate
152.1260 + // locales (the runtime lookup path), we can take the cached
152.1261 + // one. (If it's not identical, then we'd have to check the
152.1262 + // parent's parents to be consistent with what's been
152.1263 + // requested.)
152.1264 + if (bundle.parent == parent) {
152.1265 + return bundle;
152.1266 + }
152.1267 + // Otherwise, remove the cached one since we can't keep
152.1268 + // the same bundles having different parents.
152.1269 + BundleReference bundleRef = cacheList.get(cacheKey);
152.1270 + if (bundleRef != null && bundleRef.get() == bundle) {
152.1271 + cacheList.remove(cacheKey);
152.1272 + }
152.1273 + }
152.1274 + }
152.1275 +
152.1276 + if (bundle != NONEXISTENT_BUNDLE) {
152.1277 + CacheKey constKey = (CacheKey) cacheKey.clone();
152.1278 +
152.1279 + try {
152.1280 + bundle = loadBundle(cacheKey, formats, control, expiredBundle);
152.1281 + if (bundle != null) {
152.1282 + if (bundle.parent == null) {
152.1283 + bundle.setParent(parent);
152.1284 + }
152.1285 + bundle.locale = targetLocale;
152.1286 + bundle = putBundleInCache(cacheKey, bundle, control);
152.1287 + return bundle;
152.1288 + }
152.1289 +
152.1290 + // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle
152.1291 + // instance for the locale.
152.1292 + putBundleInCache(cacheKey, NONEXISTENT_BUNDLE, control);
152.1293 + } finally {
152.1294 + if (constKey.getCause() instanceof InterruptedException) {
152.1295 + Thread.currentThread().interrupt();
152.1296 + }
152.1297 + }
152.1298 + }
152.1299 + return parent;
152.1300 + }
152.1301 +
152.1302 + private static final ResourceBundle loadBundle(CacheKey cacheKey,
152.1303 + List<String> formats,
152.1304 + Control control,
152.1305 + boolean reload) {
152.1306 +
152.1307 + // Here we actually load the bundle in the order of formats
152.1308 + // specified by the getFormats() value.
152.1309 + Locale targetLocale = cacheKey.getLocale();
152.1310 +
152.1311 + ResourceBundle bundle = null;
152.1312 + int size = formats.size();
152.1313 + for (int i = 0; i < size; i++) {
152.1314 + String format = formats.get(i);
152.1315 + try {
152.1316 + bundle = control.newBundle(cacheKey.getName(), targetLocale, format,
152.1317 + null, reload);
152.1318 + } catch (LinkageError error) {
152.1319 + // We need to handle the LinkageError case due to
152.1320 + // inconsistent case-sensitivity in ClassLoader.
152.1321 + // See 6572242 for details.
152.1322 + cacheKey.setCause(error);
152.1323 + } catch (Exception cause) {
152.1324 + cacheKey.setCause(cause);
152.1325 + }
152.1326 + if (bundle != null) {
152.1327 + // Set the format in the cache key so that it can be
152.1328 + // used when calling needsReload later.
152.1329 + cacheKey.setFormat(format);
152.1330 + bundle.name = cacheKey.getName();
152.1331 + bundle.locale = targetLocale;
152.1332 + // Bundle provider might reuse instances. So we should make
152.1333 + // sure to clear the expired flag here.
152.1334 + bundle.expired = false;
152.1335 + break;
152.1336 + }
152.1337 + }
152.1338 +
152.1339 + return bundle;
152.1340 + }
152.1341 +
152.1342 + private static final boolean isValidBundle(ResourceBundle bundle) {
152.1343 + return bundle != null && bundle != NONEXISTENT_BUNDLE;
152.1344 + }
152.1345 +
152.1346 + /**
152.1347 + * Determines whether any of resource bundles in the parent chain,
152.1348 + * including the leaf, have expired.
152.1349 + */
152.1350 + private static final boolean hasValidParentChain(ResourceBundle bundle) {
152.1351 + long now = System.currentTimeMillis();
152.1352 + while (bundle != null) {
152.1353 + if (bundle.expired) {
152.1354 + return false;
152.1355 + }
152.1356 + CacheKey key = bundle.cacheKey;
152.1357 + if (key != null) {
152.1358 + long expirationTime = key.expirationTime;
152.1359 + if (expirationTime >= 0 && expirationTime <= now) {
152.1360 + return false;
152.1361 + }
152.1362 + }
152.1363 + bundle = bundle.parent;
152.1364 + }
152.1365 + return true;
152.1366 + }
152.1367 +
152.1368 + /**
152.1369 + * Throw a MissingResourceException with proper message
152.1370 + */
152.1371 + private static final void throwMissingResourceException(String baseName,
152.1372 + Locale locale,
152.1373 + Throwable cause) {
152.1374 + // If the cause is a MissingResourceException, avoid creating
152.1375 + // a long chain. (6355009)
152.1376 + if (cause instanceof MissingResourceException) {
152.1377 + cause = null;
152.1378 + }
152.1379 + throw new MissingResourceException("Can't find bundle for base name "
152.1380 + + baseName + ", locale " + locale,
152.1381 + baseName + "_" + locale, // className
152.1382 + "", // key
152.1383 + cause);
152.1384 + }
152.1385 +
152.1386 + /**
152.1387 + * Finds a bundle in the cache. Any expired bundles are marked as
152.1388 + * `expired' and removed from the cache upon return.
152.1389 + *
152.1390 + * @param cacheKey the key to look up the cache
152.1391 + * @param control the Control to be used for the expiration control
152.1392 + * @return the cached bundle, or null if the bundle is not found in the
152.1393 + * cache or its parent has expired. <code>bundle.expire</code> is true
152.1394 + * upon return if the bundle in the cache has expired.
152.1395 + */
152.1396 + private static final ResourceBundle findBundleInCache(CacheKey cacheKey,
152.1397 + Control control) {
152.1398 + BundleReference bundleRef = cacheList.get(cacheKey);
152.1399 + if (bundleRef == null) {
152.1400 + return null;
152.1401 + }
152.1402 + ResourceBundle bundle = bundleRef.get();
152.1403 + if (bundle == null) {
152.1404 + return null;
152.1405 + }
152.1406 + ResourceBundle p = bundle.parent;
152.1407 + assert p != NONEXISTENT_BUNDLE;
152.1408 + // If the parent has expired, then this one must also expire. We
152.1409 + // check only the immediate parent because the actual loading is
152.1410 + // done from the root (base) to leaf (child) and the purpose of
152.1411 + // checking is to propagate expiration towards the leaf. For
152.1412 + // example, if the requested locale is ja_JP_JP and there are
152.1413 + // bundles for all of the candidates in the cache, we have a list,
152.1414 + //
152.1415 + // base <- ja <- ja_JP <- ja_JP_JP
152.1416 + //
152.1417 + // If ja has expired, then it will reload ja and the list becomes a
152.1418 + // tree.
152.1419 + //
152.1420 + // base <- ja (new)
152.1421 + // " <- ja (expired) <- ja_JP <- ja_JP_JP
152.1422 + //
152.1423 + // When looking up ja_JP in the cache, it finds ja_JP in the cache
152.1424 + // which references to the expired ja. Then, ja_JP is marked as
152.1425 + // expired and removed from the cache. This will be propagated to
152.1426 + // ja_JP_JP.
152.1427 + //
152.1428 + // Now, it's possible, for example, that while loading new ja_JP,
152.1429 + // someone else has started loading the same bundle and finds the
152.1430 + // base bundle has expired. Then, what we get from the first
152.1431 + // getBundle call includes the expired base bundle. However, if
152.1432 + // someone else didn't start its loading, we wouldn't know if the
152.1433 + // base bundle has expired at the end of the loading process. The
152.1434 + // expiration control doesn't guarantee that the returned bundle and
152.1435 + // its parents haven't expired.
152.1436 + //
152.1437 + // We could check the entire parent chain to see if there's any in
152.1438 + // the chain that has expired. But this process may never end. An
152.1439 + // extreme case would be that getTimeToLive returns 0 and
152.1440 + // needsReload always returns true.
152.1441 + if (p != null && p.expired) {
152.1442 + assert bundle != NONEXISTENT_BUNDLE;
152.1443 + bundle.expired = true;
152.1444 + bundle.cacheKey = null;
152.1445 + cacheList.remove(cacheKey);
152.1446 + bundle = null;
152.1447 + } else {
152.1448 + CacheKey key = bundleRef.getCacheKey();
152.1449 + long expirationTime = key.expirationTime;
152.1450 + if (!bundle.expired && expirationTime >= 0 &&
152.1451 + expirationTime <= System.currentTimeMillis()) {
152.1452 + // its TTL period has expired.
152.1453 + if (bundle != NONEXISTENT_BUNDLE) {
152.1454 + // Synchronize here to call needsReload to avoid
152.1455 + // redundant concurrent calls for the same bundle.
152.1456 + synchronized (bundle) {
152.1457 + expirationTime = key.expirationTime;
152.1458 + if (!bundle.expired && expirationTime >= 0 &&
152.1459 + expirationTime <= System.currentTimeMillis()) {
152.1460 + try {
152.1461 + bundle.expired = control.needsReload(key.getName(),
152.1462 + key.getLocale(),
152.1463 + key.getFormat(),
152.1464 + null,
152.1465 + bundle,
152.1466 + key.loadTime);
152.1467 + } catch (Exception e) {
152.1468 + cacheKey.setCause(e);
152.1469 + }
152.1470 + if (bundle.expired) {
152.1471 + // If the bundle needs to be reloaded, then
152.1472 + // remove the bundle from the cache, but
152.1473 + // return the bundle with the expired flag
152.1474 + // on.
152.1475 + bundle.cacheKey = null;
152.1476 + cacheList.remove(cacheKey);
152.1477 + } else {
152.1478 + // Update the expiration control info. and reuse
152.1479 + // the same bundle instance
152.1480 + setExpirationTime(key, control);
152.1481 + }
152.1482 + }
152.1483 + }
152.1484 + } else {
152.1485 + // We just remove NONEXISTENT_BUNDLE from the cache.
152.1486 + cacheList.remove(cacheKey);
152.1487 + bundle = null;
152.1488 + }
152.1489 + }
152.1490 + }
152.1491 + return bundle;
152.1492 + }
152.1493 +
152.1494 + /**
152.1495 + * Put a new bundle in the cache.
152.1496 + *
152.1497 + * @param cacheKey the key for the resource bundle
152.1498 + * @param bundle the resource bundle to be put in the cache
152.1499 + * @return the ResourceBundle for the cacheKey; if someone has put
152.1500 + * the bundle before this call, the one found in the cache is
152.1501 + * returned.
152.1502 + */
152.1503 + private static final ResourceBundle putBundleInCache(CacheKey cacheKey,
152.1504 + ResourceBundle bundle,
152.1505 + Control control) {
152.1506 + setExpirationTime(cacheKey, control);
152.1507 + if (cacheKey.expirationTime != Control.TTL_DONT_CACHE) {
152.1508 + CacheKey key = (CacheKey) cacheKey.clone();
152.1509 + BundleReference bundleRef = new BundleReference(bundle, referenceQueue, key);
152.1510 + bundle.cacheKey = key;
152.1511 +
152.1512 + // Put the bundle in the cache if it's not been in the cache.
152.1513 + BundleReference result = cacheList.put(key, bundleRef);
152.1514 +
152.1515 + // If someone else has put the same bundle in the cache before
152.1516 + // us and it has not expired, we should use the one in the cache.
152.1517 + if (result != null) {
152.1518 + ResourceBundle rb = result.get();
152.1519 + if (rb != null && !rb.expired) {
152.1520 + // Clear the back link to the cache key
152.1521 + bundle.cacheKey = null;
152.1522 + bundle = rb;
152.1523 + // Clear the reference in the BundleReference so that
152.1524 + // it won't be enqueued.
152.1525 + bundleRef.clear();
152.1526 + } else {
152.1527 + // Replace the invalid (garbage collected or expired)
152.1528 + // instance with the valid one.
152.1529 + cacheList.put(key, bundleRef);
152.1530 + }
152.1531 + }
152.1532 + }
152.1533 + return bundle;
152.1534 + }
152.1535 +
152.1536 + private static final void setExpirationTime(CacheKey cacheKey, Control control) {
152.1537 + long ttl = control.getTimeToLive(cacheKey.getName(),
152.1538 + cacheKey.getLocale());
152.1539 + if (ttl >= 0) {
152.1540 + // If any expiration time is specified, set the time to be
152.1541 + // expired in the cache.
152.1542 + long now = System.currentTimeMillis();
152.1543 + cacheKey.loadTime = now;
152.1544 + cacheKey.expirationTime = now + ttl;
152.1545 + } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
152.1546 + cacheKey.expirationTime = ttl;
152.1547 + } else {
152.1548 + throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
152.1549 + }
152.1550 + }
152.1551 +
152.1552 + /**
152.1553 + * Removes all resource bundles from the cache that have been loaded
152.1554 + * using the caller's class loader.
152.1555 + *
152.1556 + * @since 1.6
152.1557 + * @see ResourceBundle.Control#getTimeToLive(String,Locale)
152.1558 + */
152.1559 + public static final void clearCache() {
152.1560 + clearCache(null);
152.1561 + }
152.1562 +
152.1563 + /**
152.1564 + * Removes all resource bundles from the cache that have been loaded
152.1565 + * using the given class loader.
152.1566 + *
152.1567 + * @param loader the class loader
152.1568 + * @exception NullPointerException if <code>loader</code> is null
152.1569 + * @since 1.6
152.1570 + * @see ResourceBundle.Control#getTimeToLive(String,Locale)
152.1571 + */
152.1572 + public static final void clearCache(ClassLoader loader) {
152.1573 + if (loader == null) {
152.1574 + throw new NullPointerException();
152.1575 + }
152.1576 + Set<CacheKey> set = cacheList.keySet();
152.1577 + for (CacheKey key : set) {
152.1578 + set.remove(key);
152.1579 + }
152.1580 + }
152.1581 +
152.1582 + /**
152.1583 + * Gets an object for the given key from this resource bundle.
152.1584 + * Returns null if this resource bundle does not contain an
152.1585 + * object for the given key.
152.1586 + *
152.1587 + * @param key the key for the desired object
152.1588 + * @exception NullPointerException if <code>key</code> is <code>null</code>
152.1589 + * @return the object for the given key, or null
152.1590 + */
152.1591 + protected abstract Object handleGetObject(String key);
152.1592 +
152.1593 + /**
152.1594 + * Returns an enumeration of the keys.
152.1595 + *
152.1596 + * @return an <code>Enumeration</code> of the keys contained in
152.1597 + * this <code>ResourceBundle</code> and its parent bundles.
152.1598 + */
152.1599 + public abstract Enumeration<String> getKeys();
152.1600 +
152.1601 + /**
152.1602 + * Determines whether the given <code>key</code> is contained in
152.1603 + * this <code>ResourceBundle</code> or its parent bundles.
152.1604 + *
152.1605 + * @param key
152.1606 + * the resource <code>key</code>
152.1607 + * @return <code>true</code> if the given <code>key</code> is
152.1608 + * contained in this <code>ResourceBundle</code> or its
152.1609 + * parent bundles; <code>false</code> otherwise.
152.1610 + * @exception NullPointerException
152.1611 + * if <code>key</code> is <code>null</code>
152.1612 + * @since 1.6
152.1613 + */
152.1614 + public boolean containsKey(String key) {
152.1615 + if (key == null) {
152.1616 + throw new NullPointerException();
152.1617 + }
152.1618 + for (ResourceBundle rb = this; rb != null; rb = rb.parent) {
152.1619 + if (rb.handleKeySet().contains(key)) {
152.1620 + return true;
152.1621 + }
152.1622 + }
152.1623 + return false;
152.1624 + }
152.1625 +
152.1626 + /**
152.1627 + * Returns a <code>Set</code> of all keys contained in this
152.1628 + * <code>ResourceBundle</code> and its parent bundles.
152.1629 + *
152.1630 + * @return a <code>Set</code> of all keys contained in this
152.1631 + * <code>ResourceBundle</code> and its parent bundles.
152.1632 + * @since 1.6
152.1633 + */
152.1634 + public Set<String> keySet() {
152.1635 + Set<String> keys = new HashSet<>();
152.1636 + for (ResourceBundle rb = this; rb != null; rb = rb.parent) {
152.1637 + keys.addAll(rb.handleKeySet());
152.1638 + }
152.1639 + return keys;
152.1640 + }
152.1641 +
152.1642 + /**
152.1643 + * Returns a <code>Set</code> of the keys contained <em>only</em>
152.1644 + * in this <code>ResourceBundle</code>.
152.1645 + *
152.1646 + * <p>The default implementation returns a <code>Set</code> of the
152.1647 + * keys returned by the {@link #getKeys() getKeys} method except
152.1648 + * for the ones for which the {@link #handleGetObject(String)
152.1649 + * handleGetObject} method returns <code>null</code>. Once the
152.1650 + * <code>Set</code> has been created, the value is kept in this
152.1651 + * <code>ResourceBundle</code> in order to avoid producing the
152.1652 + * same <code>Set</code> in subsequent calls. Subclasses can
152.1653 + * override this method for faster handling.
152.1654 + *
152.1655 + * @return a <code>Set</code> of the keys contained only in this
152.1656 + * <code>ResourceBundle</code>
152.1657 + * @since 1.6
152.1658 + */
152.1659 + protected Set<String> handleKeySet() {
152.1660 + if (keySet == null) {
152.1661 + synchronized (this) {
152.1662 + if (keySet == null) {
152.1663 + Set<String> keys = new HashSet<>();
152.1664 + Enumeration<String> enumKeys = getKeys();
152.1665 + while (enumKeys.hasMoreElements()) {
152.1666 + String key = enumKeys.nextElement();
152.1667 + if (handleGetObject(key) != null) {
152.1668 + keys.add(key);
152.1669 + }
152.1670 + }
152.1671 + keySet = keys;
152.1672 + }
152.1673 + }
152.1674 + }
152.1675 + return keySet;
152.1676 + }
152.1677 +
152.1678 +
152.1679 +
152.1680 + /**
152.1681 + * <code>ResourceBundle.Control</code> defines a set of callback methods
152.1682 + * that are invoked by the {@link ResourceBundle#getBundle(String,
152.1683 + * Locale, ClassLoader, Control) ResourceBundle.getBundle} factory
152.1684 + * methods during the bundle loading process. In other words, a
152.1685 + * <code>ResourceBundle.Control</code> collaborates with the factory
152.1686 + * methods for loading resource bundles. The default implementation of
152.1687 + * the callback methods provides the information necessary for the
152.1688 + * factory methods to perform the <a
152.1689 + * href="./ResourceBundle.html#default_behavior">default behavior</a>.
152.1690 + *
152.1691 + * <p>In addition to the callback methods, the {@link
152.1692 + * #toBundleName(String, Locale) toBundleName} and {@link
152.1693 + * #toResourceName(String, String) toResourceName} methods are defined
152.1694 + * primarily for convenience in implementing the callback
152.1695 + * methods. However, the <code>toBundleName</code> method could be
152.1696 + * overridden to provide different conventions in the organization and
152.1697 + * packaging of localized resources. The <code>toResourceName</code>
152.1698 + * method is <code>final</code> to avoid use of wrong resource and class
152.1699 + * name separators.
152.1700 + *
152.1701 + * <p>Two factory methods, {@link #getControl(List)} and {@link
152.1702 + * #getNoFallbackControl(List)}, provide
152.1703 + * <code>ResourceBundle.Control</code> instances that implement common
152.1704 + * variations of the default bundle loading process.
152.1705 + *
152.1706 + * <p>The formats returned by the {@link Control#getFormats(String)
152.1707 + * getFormats} method and candidate locales returned by the {@link
152.1708 + * ResourceBundle.Control#getCandidateLocales(String, Locale)
152.1709 + * getCandidateLocales} method must be consistent in all
152.1710 + * <code>ResourceBundle.getBundle</code> invocations for the same base
152.1711 + * bundle. Otherwise, the <code>ResourceBundle.getBundle</code> methods
152.1712 + * may return unintended bundles. For example, if only
152.1713 + * <code>"java.class"</code> is returned by the <code>getFormats</code>
152.1714 + * method for the first call to <code>ResourceBundle.getBundle</code>
152.1715 + * and only <code>"java.properties"</code> for the second call, then the
152.1716 + * second call will return the class-based one that has been cached
152.1717 + * during the first call.
152.1718 + *
152.1719 + * <p>A <code>ResourceBundle.Control</code> instance must be thread-safe
152.1720 + * if it's simultaneously used by multiple threads.
152.1721 + * <code>ResourceBundle.getBundle</code> does not synchronize to call
152.1722 + * the <code>ResourceBundle.Control</code> methods. The default
152.1723 + * implementations of the methods are thread-safe.
152.1724 + *
152.1725 + * <p>Applications can specify <code>ResourceBundle.Control</code>
152.1726 + * instances returned by the <code>getControl</code> factory methods or
152.1727 + * created from a subclass of <code>ResourceBundle.Control</code> to
152.1728 + * customize the bundle loading process. The following are examples of
152.1729 + * changing the default bundle loading process.
152.1730 + *
152.1731 + * <p><b>Example 1</b>
152.1732 + *
152.1733 + * <p>The following code lets <code>ResourceBundle.getBundle</code> look
152.1734 + * up only properties-based resources.
152.1735 + *
152.1736 + * <pre>
152.1737 + * import java.util.*;
152.1738 + * import static java.util.ResourceBundle.Control.*;
152.1739 + * ...
152.1740 + * ResourceBundle bundle =
152.1741 + * ResourceBundle.getBundle("MyResources", new Locale("fr", "CH"),
152.1742 + * ResourceBundle.Control.getControl(FORMAT_PROPERTIES));
152.1743 + * </pre>
152.1744 + *
152.1745 + * Given the resource bundles in the <a
152.1746 + * href="./ResourceBundle.html#default_behavior_example">example</a> in
152.1747 + * the <code>ResourceBundle.getBundle</code> description, this
152.1748 + * <code>ResourceBundle.getBundle</code> call loads
152.1749 + * <code>MyResources_fr_CH.properties</code> whose parent is
152.1750 + * <code>MyResources_fr.properties</code> whose parent is
152.1751 + * <code>MyResources.properties</code>. (<code>MyResources_fr_CH.properties</code>
152.1752 + * is not hidden, but <code>MyResources_fr_CH.class</code> is.)
152.1753 + *
152.1754 + * <p><b>Example 2</b>
152.1755 + *
152.1756 + * <p>The following is an example of loading XML-based bundles
152.1757 + * using {@link Properties#loadFromXML(java.io.InputStream)
152.1758 + * Properties.loadFromXML}.
152.1759 + *
152.1760 + * <pre>
152.1761 + * ResourceBundle rb = ResourceBundle.getBundle("Messages",
152.1762 + * new ResourceBundle.Control() {
152.1763 + * public List<String> getFormats(String baseName) {
152.1764 + * if (baseName == null)
152.1765 + * throw new NullPointerException();
152.1766 + * return Arrays.asList("xml");
152.1767 + * }
152.1768 + * public ResourceBundle newBundle(String baseName,
152.1769 + * Locale locale,
152.1770 + * String format,
152.1771 + * ClassLoader loader,
152.1772 + * boolean reload)
152.1773 + * throws IllegalAccessException,
152.1774 + * InstantiationException,
152.1775 + * IOException {
152.1776 + * if (baseName == null || locale == null
152.1777 + * || format == null || loader == null)
152.1778 + * throw new NullPointerException();
152.1779 + * ResourceBundle bundle = null;
152.1780 + * if (format.equals("xml")) {
152.1781 + * String bundleName = toBundleName(baseName, locale);
152.1782 + * String resourceName = toResourceName(bundleName, format);
152.1783 + * InputStream stream = null;
152.1784 + * if (reload) {
152.1785 + * URL url = loader.getResource(resourceName);
152.1786 + * if (url != null) {
152.1787 + * URLConnection connection = url.openConnection();
152.1788 + * if (connection != null) {
152.1789 + * // Disable caches to get fresh data for
152.1790 + * // reloading.
152.1791 + * connection.setUseCaches(false);
152.1792 + * stream = connection.getInputStream();
152.1793 + * }
152.1794 + * }
152.1795 + * } else {
152.1796 + * stream = loader.getResourceAsStream(resourceName);
152.1797 + * }
152.1798 + * if (stream != null) {
152.1799 + * BufferedInputStream bis = new BufferedInputStream(stream);
152.1800 + * bundle = new XMLResourceBundle(bis);
152.1801 + * bis.close();
152.1802 + * }
152.1803 + * }
152.1804 + * return bundle;
152.1805 + * }
152.1806 + * });
152.1807 + *
152.1808 + * ...
152.1809 + *
152.1810 + * private static class XMLResourceBundle extends ResourceBundle {
152.1811 + * private Properties props;
152.1812 + * XMLResourceBundle(InputStream stream) throws IOException {
152.1813 + * props = new Properties();
152.1814 + * props.loadFromXML(stream);
152.1815 + * }
152.1816 + * protected Object handleGetObject(String key) {
152.1817 + * return props.getProperty(key);
152.1818 + * }
152.1819 + * public Enumeration<String> getKeys() {
152.1820 + * ...
152.1821 + * }
152.1822 + * }
152.1823 + * </pre>
152.1824 + *
152.1825 + * @since 1.6
152.1826 + */
152.1827 + public static class Control {
152.1828 + /**
152.1829 + * The default format <code>List</code>, which contains the strings
152.1830 + * <code>"java.class"</code> and <code>"java.properties"</code>, in
152.1831 + * this order. This <code>List</code> is {@linkplain
152.1832 + * Collections#unmodifiableList(List) unmodifiable}.
152.1833 + *
152.1834 + * @see #getFormats(String)
152.1835 + */
152.1836 + public static final List<String> FORMAT_DEFAULT
152.1837 + = Collections.unmodifiableList(Arrays.asList("java.class",
152.1838 + "java.properties"));
152.1839 +
152.1840 + /**
152.1841 + * The class-only format <code>List</code> containing
152.1842 + * <code>"java.class"</code>. This <code>List</code> is {@linkplain
152.1843 + * Collections#unmodifiableList(List) unmodifiable}.
152.1844 + *
152.1845 + * @see #getFormats(String)
152.1846 + */
152.1847 + public static final List<String> FORMAT_CLASS
152.1848 + = Collections.unmodifiableList(Arrays.asList("java.class"));
152.1849 +
152.1850 + /**
152.1851 + * The properties-only format <code>List</code> containing
152.1852 + * <code>"java.properties"</code>. This <code>List</code> is
152.1853 + * {@linkplain Collections#unmodifiableList(List) unmodifiable}.
152.1854 + *
152.1855 + * @see #getFormats(String)
152.1856 + */
152.1857 + public static final List<String> FORMAT_PROPERTIES
152.1858 + = Collections.unmodifiableList(Arrays.asList("java.properties"));
152.1859 +
152.1860 + /**
152.1861 + * The time-to-live constant for not caching loaded resource bundle
152.1862 + * instances.
152.1863 + *
152.1864 + * @see #getTimeToLive(String, Locale)
152.1865 + */
152.1866 + public static final long TTL_DONT_CACHE = -1;
152.1867 +
152.1868 + /**
152.1869 + * The time-to-live constant for disabling the expiration control
152.1870 + * for loaded resource bundle instances in the cache.
152.1871 + *
152.1872 + * @see #getTimeToLive(String, Locale)
152.1873 + */
152.1874 + public static final long TTL_NO_EXPIRATION_CONTROL = -2;
152.1875 +
152.1876 + private static final Control INSTANCE = new Control();
152.1877 +
152.1878 + /**
152.1879 + * Sole constructor. (For invocation by subclass constructors,
152.1880 + * typically implicit.)
152.1881 + */
152.1882 + protected Control() {
152.1883 + }
152.1884 +
152.1885 + /**
152.1886 + * Returns a <code>ResourceBundle.Control</code> in which the {@link
152.1887 + * #getFormats(String) getFormats} method returns the specified
152.1888 + * <code>formats</code>. The <code>formats</code> must be equal to
152.1889 + * one of {@link Control#FORMAT_PROPERTIES}, {@link
152.1890 + * Control#FORMAT_CLASS} or {@link
152.1891 + * Control#FORMAT_DEFAULT}. <code>ResourceBundle.Control</code>
152.1892 + * instances returned by this method are singletons and thread-safe.
152.1893 + *
152.1894 + * <p>Specifying {@link Control#FORMAT_DEFAULT} is equivalent to
152.1895 + * instantiating the <code>ResourceBundle.Control</code> class,
152.1896 + * except that this method returns a singleton.
152.1897 + *
152.1898 + * @param formats
152.1899 + * the formats to be returned by the
152.1900 + * <code>ResourceBundle.Control.getFormats</code> method
152.1901 + * @return a <code>ResourceBundle.Control</code> supporting the
152.1902 + * specified <code>formats</code>
152.1903 + * @exception NullPointerException
152.1904 + * if <code>formats</code> is <code>null</code>
152.1905 + * @exception IllegalArgumentException
152.1906 + * if <code>formats</code> is unknown
152.1907 + */
152.1908 + public static final Control getControl(List<String> formats) {
152.1909 + if (formats.equals(Control.FORMAT_PROPERTIES)) {
152.1910 + return SingleFormatControl.PROPERTIES_ONLY;
152.1911 + }
152.1912 + if (formats.equals(Control.FORMAT_CLASS)) {
152.1913 + return SingleFormatControl.CLASS_ONLY;
152.1914 + }
152.1915 + if (formats.equals(Control.FORMAT_DEFAULT)) {
152.1916 + return Control.INSTANCE;
152.1917 + }
152.1918 + throw new IllegalArgumentException();
152.1919 + }
152.1920 +
152.1921 + /**
152.1922 + * Returns a <code>ResourceBundle.Control</code> in which the {@link
152.1923 + * #getFormats(String) getFormats} method returns the specified
152.1924 + * <code>formats</code> and the {@link
152.1925 + * Control#getFallbackLocale(String, Locale) getFallbackLocale}
152.1926 + * method returns <code>null</code>. The <code>formats</code> must
152.1927 + * be equal to one of {@link Control#FORMAT_PROPERTIES}, {@link
152.1928 + * Control#FORMAT_CLASS} or {@link Control#FORMAT_DEFAULT}.
152.1929 + * <code>ResourceBundle.Control</code> instances returned by this
152.1930 + * method are singletons and thread-safe.
152.1931 + *
152.1932 + * @param formats
152.1933 + * the formats to be returned by the
152.1934 + * <code>ResourceBundle.Control.getFormats</code> method
152.1935 + * @return a <code>ResourceBundle.Control</code> supporting the
152.1936 + * specified <code>formats</code> with no fallback
152.1937 + * <code>Locale</code> support
152.1938 + * @exception NullPointerException
152.1939 + * if <code>formats</code> is <code>null</code>
152.1940 + * @exception IllegalArgumentException
152.1941 + * if <code>formats</code> is unknown
152.1942 + */
152.1943 + public static final Control getNoFallbackControl(List<String> formats) {
152.1944 + if (formats.equals(Control.FORMAT_DEFAULT)) {
152.1945 + return NoFallbackControl.NO_FALLBACK;
152.1946 + }
152.1947 + if (formats.equals(Control.FORMAT_PROPERTIES)) {
152.1948 + return NoFallbackControl.PROPERTIES_ONLY_NO_FALLBACK;
152.1949 + }
152.1950 + if (formats.equals(Control.FORMAT_CLASS)) {
152.1951 + return NoFallbackControl.CLASS_ONLY_NO_FALLBACK;
152.1952 + }
152.1953 + throw new IllegalArgumentException();
152.1954 + }
152.1955 +
152.1956 + /**
152.1957 + * Returns a <code>List</code> of <code>String</code>s containing
152.1958 + * formats to be used to load resource bundles for the given
152.1959 + * <code>baseName</code>. The <code>ResourceBundle.getBundle</code>
152.1960 + * factory method tries to load resource bundles with formats in the
152.1961 + * order specified by the list. The list returned by this method
152.1962 + * must have at least one <code>String</code>. The predefined
152.1963 + * formats are <code>"java.class"</code> for class-based resource
152.1964 + * bundles and <code>"java.properties"</code> for {@linkplain
152.1965 + * PropertyResourceBundle properties-based} ones. Strings starting
152.1966 + * with <code>"java."</code> are reserved for future extensions and
152.1967 + * must not be used by application-defined formats.
152.1968 + *
152.1969 + * <p>It is not a requirement to return an immutable (unmodifiable)
152.1970 + * <code>List</code>. However, the returned <code>List</code> must
152.1971 + * not be mutated after it has been returned by
152.1972 + * <code>getFormats</code>.
152.1973 + *
152.1974 + * <p>The default implementation returns {@link #FORMAT_DEFAULT} so
152.1975 + * that the <code>ResourceBundle.getBundle</code> factory method
152.1976 + * looks up first class-based resource bundles, then
152.1977 + * properties-based ones.
152.1978 + *
152.1979 + * @param baseName
152.1980 + * the base name of the resource bundle, a fully qualified class
152.1981 + * name
152.1982 + * @return a <code>List</code> of <code>String</code>s containing
152.1983 + * formats for loading resource bundles.
152.1984 + * @exception NullPointerException
152.1985 + * if <code>baseName</code> is null
152.1986 + * @see #FORMAT_DEFAULT
152.1987 + * @see #FORMAT_CLASS
152.1988 + * @see #FORMAT_PROPERTIES
152.1989 + */
152.1990 + public List<String> getFormats(String baseName) {
152.1991 + if (baseName == null) {
152.1992 + throw new NullPointerException();
152.1993 + }
152.1994 + return FORMAT_DEFAULT;
152.1995 + }
152.1996 +
152.1997 + /**
152.1998 + * Returns a <code>List</code> of <code>Locale</code>s as candidate
152.1999 + * locales for <code>baseName</code> and <code>locale</code>. This
152.2000 + * method is called by the <code>ResourceBundle.getBundle</code>
152.2001 + * factory method each time the factory method tries finding a
152.2002 + * resource bundle for a target <code>Locale</code>.
152.2003 + *
152.2004 + * <p>The sequence of the candidate locales also corresponds to the
152.2005 + * runtime resource lookup path (also known as the <I>parent
152.2006 + * chain</I>), if the corresponding resource bundles for the
152.2007 + * candidate locales exist and their parents are not defined by
152.2008 + * loaded resource bundles themselves. The last element of the list
152.2009 + * must be a {@linkplain Locale#ROOT root locale} if it is desired to
152.2010 + * have the base bundle as the terminal of the parent chain.
152.2011 + *
152.2012 + * <p>If the given locale is equal to <code>Locale.ROOT</code> (the
152.2013 + * root locale), a <code>List</code> containing only the root
152.2014 + * <code>Locale</code> must be returned. In this case, the
152.2015 + * <code>ResourceBundle.getBundle</code> factory method loads only
152.2016 + * the base bundle as the resulting resource bundle.
152.2017 + *
152.2018 + * <p>It is not a requirement to return an immutable (unmodifiable)
152.2019 + * <code>List</code>. However, the returned <code>List</code> must not
152.2020 + * be mutated after it has been returned by
152.2021 + * <code>getCandidateLocales</code>.
152.2022 + *
152.2023 + * <p>The default implementation returns a <code>List</code> containing
152.2024 + * <code>Locale</code>s using the rules described below. In the
152.2025 + * description below, <em>L</em>, <em>S</em>, <em>C</em> and <em>V</em>
152.2026 + * respectively represent non-empty language, script, country, and
152.2027 + * variant. For example, [<em>L</em>, <em>C</em>] represents a
152.2028 + * <code>Locale</code> that has non-empty values only for language and
152.2029 + * country. The form <em>L</em>("xx") represents the (non-empty)
152.2030 + * language value is "xx". For all cases, <code>Locale</code>s whose
152.2031 + * final component values are empty strings are omitted.
152.2032 + *
152.2033 + * <ol><li>For an input <code>Locale</code> with an empty script value,
152.2034 + * append candidate <code>Locale</code>s by omitting the final component
152.2035 + * one by one as below:
152.2036 + *
152.2037 + * <ul>
152.2038 + * <li> [<em>L</em>, <em>C</em>, <em>V</em>]
152.2039 + * <li> [<em>L</em>, <em>C</em>]
152.2040 + * <li> [<em>L</em>]
152.2041 + * <li> <code>Locale.ROOT</code>
152.2042 + * </ul>
152.2043 + *
152.2044 + * <li>For an input <code>Locale</code> with a non-empty script value,
152.2045 + * append candidate <code>Locale</code>s by omitting the final component
152.2046 + * up to language, then append candidates generated from the
152.2047 + * <code>Locale</code> with country and variant restored:
152.2048 + *
152.2049 + * <ul>
152.2050 + * <li> [<em>L</em>, <em>S</em>, <em>C</em>, <em>V</em>]
152.2051 + * <li> [<em>L</em>, <em>S</em>, <em>C</em>]
152.2052 + * <li> [<em>L</em>, <em>S</em>]
152.2053 + * <li> [<em>L</em>, <em>C</em>, <em>V</em>]
152.2054 + * <li> [<em>L</em>, <em>C</em>]
152.2055 + * <li> [<em>L</em>]
152.2056 + * <li> <code>Locale.ROOT</code>
152.2057 + * </ul>
152.2058 + *
152.2059 + * <li>For an input <code>Locale</code> with a variant value consisting
152.2060 + * of multiple subtags separated by underscore, generate candidate
152.2061 + * <code>Locale</code>s by omitting the variant subtags one by one, then
152.2062 + * insert them after every occurence of <code> Locale</code>s with the
152.2063 + * full variant value in the original list. For example, if the
152.2064 + * the variant consists of two subtags <em>V1</em> and <em>V2</em>:
152.2065 + *
152.2066 + * <ul>
152.2067 + * <li> [<em>L</em>, <em>S</em>, <em>C</em>, <em>V1</em>, <em>V2</em>]
152.2068 + * <li> [<em>L</em>, <em>S</em>, <em>C</em>, <em>V1</em>]
152.2069 + * <li> [<em>L</em>, <em>S</em>, <em>C</em>]
152.2070 + * <li> [<em>L</em>, <em>S</em>]
152.2071 + * <li> [<em>L</em>, <em>C</em>, <em>V1</em>, <em>V2</em>]
152.2072 + * <li> [<em>L</em>, <em>C</em>, <em>V1</em>]
152.2073 + * <li> [<em>L</em>, <em>C</em>]
152.2074 + * <li> [<em>L</em>]
152.2075 + * <li> <code>Locale.ROOT</code>
152.2076 + * </ul>
152.2077 + *
152.2078 + * <li>Special cases for Chinese. When an input <code>Locale</code> has the
152.2079 + * language "zh" (Chinese) and an empty script value, either "Hans" (Simplified) or
152.2080 + * "Hant" (Traditional) might be supplied, depending on the country.
152.2081 + * When the country is "CN" (China) or "SG" (Singapore), "Hans" is supplied.
152.2082 + * When the country is "HK" (Hong Kong SAR China), "MO" (Macau SAR China),
152.2083 + * or "TW" (Taiwan), "Hant" is supplied. For all other countries or when the country
152.2084 + * is empty, no script is supplied. For example, for <code>Locale("zh", "CN")
152.2085 + * </code>, the candidate list will be:
152.2086 + * <ul>
152.2087 + * <li> [<em>L</em>("zh"), <em>S</em>("Hans"), <em>C</em>("CN")]
152.2088 + * <li> [<em>L</em>("zh"), <em>S</em>("Hans")]
152.2089 + * <li> [<em>L</em>("zh"), <em>C</em>("CN")]
152.2090 + * <li> [<em>L</em>("zh")]
152.2091 + * <li> <code>Locale.ROOT</code>
152.2092 + * </ul>
152.2093 + *
152.2094 + * For <code>Locale("zh", "TW")</code>, the candidate list will be:
152.2095 + * <ul>
152.2096 + * <li> [<em>L</em>("zh"), <em>S</em>("Hant"), <em>C</em>("TW")]
152.2097 + * <li> [<em>L</em>("zh"), <em>S</em>("Hant")]
152.2098 + * <li> [<em>L</em>("zh"), <em>C</em>("TW")]
152.2099 + * <li> [<em>L</em>("zh")]
152.2100 + * <li> <code>Locale.ROOT</code>
152.2101 + * </ul>
152.2102 + *
152.2103 + * <li>Special cases for Norwegian. Both <code>Locale("no", "NO",
152.2104 + * "NY")</code> and <code>Locale("nn", "NO")</code> represent Norwegian
152.2105 + * Nynorsk. When a locale's language is "nn", the standard candidate
152.2106 + * list is generated up to [<em>L</em>("nn")], and then the following
152.2107 + * candidates are added:
152.2108 + *
152.2109 + * <ul><li> [<em>L</em>("no"), <em>C</em>("NO"), <em>V</em>("NY")]
152.2110 + * <li> [<em>L</em>("no"), <em>C</em>("NO")]
152.2111 + * <li> [<em>L</em>("no")]
152.2112 + * <li> <code>Locale.ROOT</code>
152.2113 + * </ul>
152.2114 + *
152.2115 + * If the locale is exactly <code>Locale("no", "NO", "NY")</code>, it is first
152.2116 + * converted to <code>Locale("nn", "NO")</code> and then the above procedure is
152.2117 + * followed.
152.2118 + *
152.2119 + * <p>Also, Java treats the language "no" as a synonym of Norwegian
152.2120 + * Bokmål "nb". Except for the single case <code>Locale("no",
152.2121 + * "NO", "NY")</code> (handled above), when an input <code>Locale</code>
152.2122 + * has language "no" or "nb", candidate <code>Locale</code>s with
152.2123 + * language code "no" and "nb" are interleaved, first using the
152.2124 + * requested language, then using its synonym. For example,
152.2125 + * <code>Locale("nb", "NO", "POSIX")</code> generates the following
152.2126 + * candidate list:
152.2127 + *
152.2128 + * <ul>
152.2129 + * <li> [<em>L</em>("nb"), <em>C</em>("NO"), <em>V</em>("POSIX")]
152.2130 + * <li> [<em>L</em>("no"), <em>C</em>("NO"), <em>V</em>("POSIX")]
152.2131 + * <li> [<em>L</em>("nb"), <em>C</em>("NO")]
152.2132 + * <li> [<em>L</em>("no"), <em>C</em>("NO")]
152.2133 + * <li> [<em>L</em>("nb")]
152.2134 + * <li> [<em>L</em>("no")]
152.2135 + * <li> <code>Locale.ROOT</code>
152.2136 + * </ul>
152.2137 + *
152.2138 + * <code>Locale("no", "NO", "POSIX")</code> would generate the same list
152.2139 + * except that locales with "no" would appear before the corresponding
152.2140 + * locales with "nb".</li>
152.2141 + *
152.2142 + * </li>
152.2143 + * </ol>
152.2144 + *
152.2145 + * <p>The default implementation uses an {@link ArrayList} that
152.2146 + * overriding implementations may modify before returning it to the
152.2147 + * caller. However, a subclass must not modify it after it has
152.2148 + * been returned by <code>getCandidateLocales</code>.
152.2149 + *
152.2150 + * <p>For example, if the given <code>baseName</code> is "Messages"
152.2151 + * and the given <code>locale</code> is
152.2152 + * <code>Locale("ja", "", "XX")</code>, then a
152.2153 + * <code>List</code> of <code>Locale</code>s:
152.2154 + * <pre>
152.2155 + * Locale("ja", "", "XX")
152.2156 + * Locale("ja")
152.2157 + * Locale.ROOT
152.2158 + * </pre>
152.2159 + * is returned. And if the resource bundles for the "ja" and
152.2160 + * "" <code>Locale</code>s are found, then the runtime resource
152.2161 + * lookup path (parent chain) is:
152.2162 + * <pre>
152.2163 + * Messages_ja -> Messages
152.2164 + * </pre>
152.2165 + *
152.2166 + * @param baseName
152.2167 + * the base name of the resource bundle, a fully
152.2168 + * qualified class name
152.2169 + * @param locale
152.2170 + * the locale for which a resource bundle is desired
152.2171 + * @return a <code>List</code> of candidate
152.2172 + * <code>Locale</code>s for the given <code>locale</code>
152.2173 + * @exception NullPointerException
152.2174 + * if <code>baseName</code> or <code>locale</code> is
152.2175 + * <code>null</code>
152.2176 + */
152.2177 + public List<Locale> getCandidateLocales(String baseName, Locale locale) {
152.2178 + if (baseName == null) {
152.2179 + throw new NullPointerException();
152.2180 + }
152.2181 + return new ArrayList<>(CANDIDATES_CACHE.get(locale));
152.2182 + }
152.2183 +
152.2184 + private static final CandidateListCache CANDIDATES_CACHE = new CandidateListCache();
152.2185 +
152.2186 + private static class CandidateListCache {
152.2187 + private Locale prevQuery;
152.2188 + private List<Locale> prevResult;
152.2189 +
152.2190 + public List<Locale> get(Locale l) {
152.2191 + if (prevQuery == l) {
152.2192 + return prevResult;
152.2193 + }
152.2194 + prevResult = createObject(l);
152.2195 + prevQuery = l;
152.2196 + return prevResult;
152.2197 + }
152.2198 +
152.2199 + protected List<Locale> createObject(Locale base) {
152.2200 + String language = base.getLanguage();
152.2201 + String script = base.getScript();
152.2202 + String region = base.getRegion();
152.2203 + String variant = base.getVariant();
152.2204 +
152.2205 + // Special handling for Norwegian
152.2206 + boolean isNorwegianBokmal = false;
152.2207 + boolean isNorwegianNynorsk = false;
152.2208 + if (language.equals("no")) {
152.2209 + if (region.equals("NO") && variant.equals("NY")) {
152.2210 + variant = "";
152.2211 + isNorwegianNynorsk = true;
152.2212 + } else {
152.2213 + isNorwegianBokmal = true;
152.2214 + }
152.2215 + }
152.2216 + if (language.equals("nb") || isNorwegianBokmal) {
152.2217 + List<Locale> tmpList = getDefaultList("nb", script, region, variant);
152.2218 + // Insert a locale replacing "nb" with "no" for every list entry
152.2219 + List<Locale> bokmalList = new LinkedList<>();
152.2220 + for (Locale l : tmpList) {
152.2221 + bokmalList.add(l);
152.2222 + if (l.getLanguage().length() == 0) {
152.2223 + break;
152.2224 + }
152.2225 + bokmalList.add(Locale.getInstance("no", l.getScript(), l.getCountry(),
152.2226 + l.getVariant(), null));
152.2227 + }
152.2228 + return bokmalList;
152.2229 + } else if (language.equals("nn") || isNorwegianNynorsk) {
152.2230 + // Insert no_NO_NY, no_NO, no after nn
152.2231 + List<Locale> nynorskList = getDefaultList("nn", script, region, variant);
152.2232 + int idx = nynorskList.size() - 1;
152.2233 + nynorskList.add(idx++, Locale.getInstance("no", "NO", "NY"));
152.2234 + nynorskList.add(idx++, Locale.getInstance("no", "NO", ""));
152.2235 + nynorskList.add(idx++, Locale.getInstance("no", "", ""));
152.2236 + return nynorskList;
152.2237 + }
152.2238 + // Special handling for Chinese
152.2239 + else if (language.equals("zh")) {
152.2240 + if (script.length() == 0 && region.length() > 0) {
152.2241 + // Supply script for users who want to use zh_Hans/zh_Hant
152.2242 + // as bundle names (recommended for Java7+)
152.2243 + if (region.equals("TW") || region.equals("HK") || region.equals("MO")) {
152.2244 + script = "Hant";
152.2245 + } else if (region.equals("CN") || region.equals("SG")) {
152.2246 + script = "Hans";
152.2247 + }
152.2248 + } else if (script.length() > 0 && region.length() == 0) {
152.2249 + // Supply region(country) for users who still package Chinese
152.2250 + // bundles using old convension.
152.2251 + if (script.equals("Hans")) {
152.2252 + region = "CN";
152.2253 + } else if (script.equals("Hant")) {
152.2254 + region = "TW";
152.2255 + }
152.2256 + }
152.2257 + }
152.2258 +
152.2259 + return getDefaultList(language, script, region, variant);
152.2260 + }
152.2261 +
152.2262 + private static List<Locale> getDefaultList(String language, String script, String region, String variant) {
152.2263 + List<String> variants = null;
152.2264 +
152.2265 + if (variant.length() > 0) {
152.2266 + variants = new LinkedList<>();
152.2267 + int idx = variant.length();
152.2268 + while (idx != -1) {
152.2269 + variants.add(variant.substring(0, idx));
152.2270 + idx = variant.lastIndexOf('_', --idx);
152.2271 + }
152.2272 + }
152.2273 +
152.2274 + List<Locale> list = new LinkedList<>();
152.2275 +
152.2276 + if (variants != null) {
152.2277 + for (String v : variants) {
152.2278 + list.add(Locale.getInstance(language, script, region, v, null));
152.2279 + }
152.2280 + }
152.2281 + if (region.length() > 0) {
152.2282 + list.add(Locale.getInstance(language, script, region, "", null));
152.2283 + }
152.2284 + if (script.length() > 0) {
152.2285 + list.add(Locale.getInstance(language, script, "", "", null));
152.2286 +
152.2287 + // With script, after truncating variant, region and script,
152.2288 + // start over without script.
152.2289 + if (variants != null) {
152.2290 + for (String v : variants) {
152.2291 + list.add(Locale.getInstance(language, "", region, v, null));
152.2292 + }
152.2293 + }
152.2294 + if (region.length() > 0) {
152.2295 + list.add(Locale.getInstance(language, "", region, "", null));
152.2296 + }
152.2297 + }
152.2298 + if (language.length() > 0) {
152.2299 + list.add(Locale.getInstance(language, "", "", "", null));
152.2300 + }
152.2301 + // Add root locale at the end
152.2302 + list.add(Locale.ROOT);
152.2303 +
152.2304 + return list;
152.2305 + }
152.2306 + }
152.2307 +
152.2308 + /**
152.2309 + * Returns a <code>Locale</code> to be used as a fallback locale for
152.2310 + * further resource bundle searches by the
152.2311 + * <code>ResourceBundle.getBundle</code> factory method. This method
152.2312 + * is called from the factory method every time when no resulting
152.2313 + * resource bundle has been found for <code>baseName</code> and
152.2314 + * <code>locale</code>, where locale is either the parameter for
152.2315 + * <code>ResourceBundle.getBundle</code> or the previous fallback
152.2316 + * locale returned by this method.
152.2317 + *
152.2318 + * <p>The method returns <code>null</code> if no further fallback
152.2319 + * search is desired.
152.2320 + *
152.2321 + * <p>The default implementation returns the {@linkplain
152.2322 + * Locale#getDefault() default <code>Locale</code>} if the given
152.2323 + * <code>locale</code> isn't the default one. Otherwise,
152.2324 + * <code>null</code> is returned.
152.2325 + *
152.2326 + * @param baseName
152.2327 + * the base name of the resource bundle, a fully
152.2328 + * qualified class name for which
152.2329 + * <code>ResourceBundle.getBundle</code> has been
152.2330 + * unable to find any resource bundles (except for the
152.2331 + * base bundle)
152.2332 + * @param locale
152.2333 + * the <code>Locale</code> for which
152.2334 + * <code>ResourceBundle.getBundle</code> has been
152.2335 + * unable to find any resource bundles (except for the
152.2336 + * base bundle)
152.2337 + * @return a <code>Locale</code> for the fallback search,
152.2338 + * or <code>null</code> if no further fallback search
152.2339 + * is desired.
152.2340 + * @exception NullPointerException
152.2341 + * if <code>baseName</code> or <code>locale</code>
152.2342 + * is <code>null</code>
152.2343 + */
152.2344 + public Locale getFallbackLocale(String baseName, Locale locale) {
152.2345 + if (baseName == null) {
152.2346 + throw new NullPointerException();
152.2347 + }
152.2348 + Locale defaultLocale = Locale.getDefault();
152.2349 + return locale.equals(defaultLocale) ? null : defaultLocale;
152.2350 + }
152.2351 +
152.2352 + /**
152.2353 + * Instantiates a resource bundle for the given bundle name of the
152.2354 + * given format and locale, using the given class loader if
152.2355 + * necessary. This method returns <code>null</code> if there is no
152.2356 + * resource bundle available for the given parameters. If a resource
152.2357 + * bundle can't be instantiated due to an unexpected error, the
152.2358 + * error must be reported by throwing an <code>Error</code> or
152.2359 + * <code>Exception</code> rather than simply returning
152.2360 + * <code>null</code>.
152.2361 + *
152.2362 + * <p>If the <code>reload</code> flag is <code>true</code>, it
152.2363 + * indicates that this method is being called because the previously
152.2364 + * loaded resource bundle has expired.
152.2365 + *
152.2366 + * <p>The default implementation instantiates a
152.2367 + * <code>ResourceBundle</code> as follows.
152.2368 + *
152.2369 + * <ul>
152.2370 + *
152.2371 + * <li>The bundle name is obtained by calling {@link
152.2372 + * #toBundleName(String, Locale) toBundleName(baseName,
152.2373 + * locale)}.</li>
152.2374 + *
152.2375 + * <li>If <code>format</code> is <code>"java.class"</code>, the
152.2376 + * {@link Class} specified by the bundle name is loaded by calling
152.2377 + * {@link ClassLoader#loadClass(String)}. Then, a
152.2378 + * <code>ResourceBundle</code> is instantiated by calling {@link
152.2379 + * Class#newInstance()}. Note that the <code>reload</code> flag is
152.2380 + * ignored for loading class-based resource bundles in this default
152.2381 + * implementation.</li>
152.2382 + *
152.2383 + * <li>If <code>format</code> is <code>"java.properties"</code>,
152.2384 + * {@link #toResourceName(String, String) toResourceName(bundlename,
152.2385 + * "properties")} is called to get the resource name.
152.2386 + * If <code>reload</code> is <code>true</code>, {@link
152.2387 + * ClassLoader#getResource(String) load.getResource} is called
152.2388 + * to get a {@link URL} for creating a {@link
152.2389 + * URLConnection}. This <code>URLConnection</code> is used to
152.2390 + * {@linkplain URLConnection#setUseCaches(boolean) disable the
152.2391 + * caches} of the underlying resource loading layers,
152.2392 + * and to {@linkplain URLConnection#getInputStream() get an
152.2393 + * <code>InputStream</code>}.
152.2394 + * Otherwise, {@link ClassLoader#getResourceAsStream(String)
152.2395 + * loader.getResourceAsStream} is called to get an {@link
152.2396 + * InputStream}. Then, a {@link
152.2397 + * PropertyResourceBundle} is constructed with the
152.2398 + * <code>InputStream</code>.</li>
152.2399 + *
152.2400 + * <li>If <code>format</code> is neither <code>"java.class"</code>
152.2401 + * nor <code>"java.properties"</code>, an
152.2402 + * <code>IllegalArgumentException</code> is thrown.</li>
152.2403 + *
152.2404 + * </ul>
152.2405 + *
152.2406 + * @param baseName
152.2407 + * the base bundle name of the resource bundle, a fully
152.2408 + * qualified class name
152.2409 + * @param locale
152.2410 + * the locale for which the resource bundle should be
152.2411 + * instantiated
152.2412 + * @param format
152.2413 + * the resource bundle format to be loaded
152.2414 + * @param loader
152.2415 + * the <code>ClassLoader</code> to use to load the bundle
152.2416 + * @param reload
152.2417 + * the flag to indicate bundle reloading; <code>true</code>
152.2418 + * if reloading an expired resource bundle,
152.2419 + * <code>false</code> otherwise
152.2420 + * @return the resource bundle instance,
152.2421 + * or <code>null</code> if none could be found.
152.2422 + * @exception NullPointerException
152.2423 + * if <code>bundleName</code>, <code>locale</code>,
152.2424 + * <code>format</code>, or <code>loader</code> is
152.2425 + * <code>null</code>, or if <code>null</code> is returned by
152.2426 + * {@link #toBundleName(String, Locale) toBundleName}
152.2427 + * @exception IllegalArgumentException
152.2428 + * if <code>format</code> is unknown, or if the resource
152.2429 + * found for the given parameters contains malformed data.
152.2430 + * @exception ClassCastException
152.2431 + * if the loaded class cannot be cast to <code>ResourceBundle</code>
152.2432 + * @exception IllegalAccessException
152.2433 + * if the class or its nullary constructor is not
152.2434 + * accessible.
152.2435 + * @exception InstantiationException
152.2436 + * if the instantiation of a class fails for some other
152.2437 + * reason.
152.2438 + * @exception ExceptionInInitializerError
152.2439 + * if the initialization provoked by this method fails.
152.2440 + * @exception SecurityException
152.2441 + * If a security manager is present and creation of new
152.2442 + * instances is denied. See {@link Class#newInstance()}
152.2443 + * for details.
152.2444 + * @exception IOException
152.2445 + * if an error occurred when reading resources using
152.2446 + * any I/O operations
152.2447 + */
152.2448 + public ResourceBundle newBundle(String baseName, Locale locale, String format,
152.2449 + ClassLoader loader, boolean reload)
152.2450 + throws IllegalAccessException, InstantiationException, IOException {
152.2451 + String bundleName = toBundleName(baseName, locale);
152.2452 + ResourceBundle bundle = null;
152.2453 + if (format.equals("java.class")) {
152.2454 + try {
152.2455 + Class<? extends ResourceBundle> bundleClass
152.2456 + = (Class<? extends ResourceBundle>)(loader != null ?
152.2457 + loader.loadClass(bundleName) :
152.2458 + Class.forName(bundleName));
152.2459 +
152.2460 + // If the class isn't a ResourceBundle subclass, throw a
152.2461 + // ClassCastException.
152.2462 + if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
152.2463 + bundle = bundleClass.newInstance();
152.2464 + } else {
152.2465 + throw new ClassCastException(bundleClass.getName()
152.2466 + + " cannot be cast to ResourceBundle");
152.2467 + }
152.2468 + } catch (ClassNotFoundException e) {
152.2469 + }
152.2470 + } else if (format.equals("java.properties")) {
152.2471 + final String resourceName = toResourceName(bundleName, "properties");
152.2472 + final ClassLoader classLoader = loader;
152.2473 + final boolean reloadFlag = reload;
152.2474 + InputStream stream = classLoader != null ? classLoader.getResourceAsStream(resourceName) :
152.2475 + ResourceBundle.class.getResourceAsStream("/" + resourceName);
152.2476 + if (stream != null) {
152.2477 + try {
152.2478 + bundle = new PropertyResourceBundle(stream);
152.2479 + } finally {
152.2480 + stream.close();
152.2481 + }
152.2482 + }
152.2483 + } else {
152.2484 + throw new IllegalArgumentException("unknown format: " + format);
152.2485 + }
152.2486 + return bundle;
152.2487 + }
152.2488 +
152.2489 + /**
152.2490 + * Returns the time-to-live (TTL) value for resource bundles that
152.2491 + * are loaded under this
152.2492 + * <code>ResourceBundle.Control</code>. Positive time-to-live values
152.2493 + * specify the number of milliseconds a bundle can remain in the
152.2494 + * cache without being validated against the source data from which
152.2495 + * it was constructed. The value 0 indicates that a bundle must be
152.2496 + * validated each time it is retrieved from the cache. {@link
152.2497 + * #TTL_DONT_CACHE} specifies that loaded resource bundles are not
152.2498 + * put in the cache. {@link #TTL_NO_EXPIRATION_CONTROL} specifies
152.2499 + * that loaded resource bundles are put in the cache with no
152.2500 + * expiration control.
152.2501 + *
152.2502 + * <p>The expiration affects only the bundle loading process by the
152.2503 + * <code>ResourceBundle.getBundle</code> factory method. That is,
152.2504 + * if the factory method finds a resource bundle in the cache that
152.2505 + * has expired, the factory method calls the {@link
152.2506 + * #needsReload(String, Locale, String, ClassLoader, ResourceBundle,
152.2507 + * long) needsReload} method to determine whether the resource
152.2508 + * bundle needs to be reloaded. If <code>needsReload</code> returns
152.2509 + * <code>true</code>, the cached resource bundle instance is removed
152.2510 + * from the cache. Otherwise, the instance stays in the cache,
152.2511 + * updated with the new TTL value returned by this method.
152.2512 + *
152.2513 + * <p>All cached resource bundles are subject to removal from the
152.2514 + * cache due to memory constraints of the runtime environment.
152.2515 + * Returning a large positive value doesn't mean to lock loaded
152.2516 + * resource bundles in the cache.
152.2517 + *
152.2518 + * <p>The default implementation returns {@link #TTL_NO_EXPIRATION_CONTROL}.
152.2519 + *
152.2520 + * @param baseName
152.2521 + * the base name of the resource bundle for which the
152.2522 + * expiration value is specified.
152.2523 + * @param locale
152.2524 + * the locale of the resource bundle for which the
152.2525 + * expiration value is specified.
152.2526 + * @return the time (0 or a positive millisecond offset from the
152.2527 + * cached time) to get loaded bundles expired in the cache,
152.2528 + * {@link #TTL_NO_EXPIRATION_CONTROL} to disable the
152.2529 + * expiration control, or {@link #TTL_DONT_CACHE} to disable
152.2530 + * caching.
152.2531 + * @exception NullPointerException
152.2532 + * if <code>baseName</code> or <code>locale</code> is
152.2533 + * <code>null</code>
152.2534 + */
152.2535 + public long getTimeToLive(String baseName, Locale locale) {
152.2536 + if (baseName == null || locale == null) {
152.2537 + throw new NullPointerException();
152.2538 + }
152.2539 + return TTL_NO_EXPIRATION_CONTROL;
152.2540 + }
152.2541 +
152.2542 + /**
152.2543 + * Determines if the expired <code>bundle</code> in the cache needs
152.2544 + * to be reloaded based on the loading time given by
152.2545 + * <code>loadTime</code> or some other criteria. The method returns
152.2546 + * <code>true</code> if reloading is required; <code>false</code>
152.2547 + * otherwise. <code>loadTime</code> is a millisecond offset since
152.2548 + * the <a href="Calendar.html#Epoch"> <code>Calendar</code>
152.2549 + * Epoch</a>.
152.2550 + *
152.2551 + * The calling <code>ResourceBundle.getBundle</code> factory method
152.2552 + * calls this method on the <code>ResourceBundle.Control</code>
152.2553 + * instance used for its current invocation, not on the instance
152.2554 + * used in the invocation that originally loaded the resource
152.2555 + * bundle.
152.2556 + *
152.2557 + * <p>The default implementation compares <code>loadTime</code> and
152.2558 + * the last modified time of the source data of the resource
152.2559 + * bundle. If it's determined that the source data has been modified
152.2560 + * since <code>loadTime</code>, <code>true</code> is
152.2561 + * returned. Otherwise, <code>false</code> is returned. This
152.2562 + * implementation assumes that the given <code>format</code> is the
152.2563 + * same string as its file suffix if it's not one of the default
152.2564 + * formats, <code>"java.class"</code> or
152.2565 + * <code>"java.properties"</code>.
152.2566 + *
152.2567 + * @param baseName
152.2568 + * the base bundle name of the resource bundle, a
152.2569 + * fully qualified class name
152.2570 + * @param locale
152.2571 + * the locale for which the resource bundle
152.2572 + * should be instantiated
152.2573 + * @param format
152.2574 + * the resource bundle format to be loaded
152.2575 + * @param loader
152.2576 + * the <code>ClassLoader</code> to use to load the bundle
152.2577 + * @param bundle
152.2578 + * the resource bundle instance that has been expired
152.2579 + * in the cache
152.2580 + * @param loadTime
152.2581 + * the time when <code>bundle</code> was loaded and put
152.2582 + * in the cache
152.2583 + * @return <code>true</code> if the expired bundle needs to be
152.2584 + * reloaded; <code>false</code> otherwise.
152.2585 + * @exception NullPointerException
152.2586 + * if <code>baseName</code>, <code>locale</code>,
152.2587 + * <code>format</code>, <code>loader</code>, or
152.2588 + * <code>bundle</code> is <code>null</code>
152.2589 + */
152.2590 + public boolean needsReload(String baseName, Locale locale,
152.2591 + String format, ClassLoader loader,
152.2592 + ResourceBundle bundle, long loadTime) {
152.2593 + if (bundle == null) {
152.2594 + throw new NullPointerException();
152.2595 + }
152.2596 + if (format.equals("java.class") || format.equals("java.properties")) {
152.2597 + format = format.substring(5);
152.2598 + }
152.2599 + boolean result = false;
152.2600 + try {
152.2601 +/*
152.2602 + String resourceName = toResourceName(toBundleName(baseName, locale), format);
152.2603 + URL url = loader.getResource(resourceName);
152.2604 + if (url != null) {
152.2605 + long lastModified = 0;
152.2606 + URLConnection connection = url.openConnection();
152.2607 + if (connection != null) {
152.2608 + // disable caches to get the correct data
152.2609 + connection.setUseCaches(false);
152.2610 + if (connection instanceof JarURLConnection) {
152.2611 + JarEntry ent = ((JarURLConnection)connection).getJarEntry();
152.2612 + if (ent != null) {
152.2613 + lastModified = ent.getTime();
152.2614 + if (lastModified == -1) {
152.2615 + lastModified = 0;
152.2616 + }
152.2617 + }
152.2618 + } else {
152.2619 + lastModified = connection.getLastModified();
152.2620 + }
152.2621 + }
152.2622 + result = lastModified >= loadTime;
152.2623 + }
152.2624 + */
152.2625 + } catch (NullPointerException npe) {
152.2626 + throw npe;
152.2627 + } catch (Exception e) {
152.2628 + // ignore other exceptions
152.2629 + }
152.2630 + return result;
152.2631 + }
152.2632 +
152.2633 + /**
152.2634 + * Converts the given <code>baseName</code> and <code>locale</code>
152.2635 + * to the bundle name. This method is called from the default
152.2636 + * implementation of the {@link #newBundle(String, Locale, String,
152.2637 + * ClassLoader, boolean) newBundle} and {@link #needsReload(String,
152.2638 + * Locale, String, ClassLoader, ResourceBundle, long) needsReload}
152.2639 + * methods.
152.2640 + *
152.2641 + * <p>This implementation returns the following value:
152.2642 + * <pre>
152.2643 + * baseName + "_" + language + "_" + script + "_" + country + "_" + variant
152.2644 + * </pre>
152.2645 + * where <code>language</code>, <code>script</code>, <code>country</code>,
152.2646 + * and <code>variant</code> are the language, script, country, and variant
152.2647 + * values of <code>locale</code>, respectively. Final component values that
152.2648 + * are empty Strings are omitted along with the preceding '_'. When the
152.2649 + * script is empty, the script value is ommitted along with the preceding '_'.
152.2650 + * If all of the values are empty strings, then <code>baseName</code>
152.2651 + * is returned.
152.2652 + *
152.2653 + * <p>For example, if <code>baseName</code> is
152.2654 + * <code>"baseName"</code> and <code>locale</code> is
152.2655 + * <code>Locale("ja", "", "XX")</code>, then
152.2656 + * <code>"baseName_ja_ _XX"</code> is returned. If the given
152.2657 + * locale is <code>Locale("en")</code>, then
152.2658 + * <code>"baseName_en"</code> is returned.
152.2659 + *
152.2660 + * <p>Overriding this method allows applications to use different
152.2661 + * conventions in the organization and packaging of localized
152.2662 + * resources.
152.2663 + *
152.2664 + * @param baseName
152.2665 + * the base name of the resource bundle, a fully
152.2666 + * qualified class name
152.2667 + * @param locale
152.2668 + * the locale for which a resource bundle should be
152.2669 + * loaded
152.2670 + * @return the bundle name for the resource bundle
152.2671 + * @exception NullPointerException
152.2672 + * if <code>baseName</code> or <code>locale</code>
152.2673 + * is <code>null</code>
152.2674 + */
152.2675 + public String toBundleName(String baseName, Locale locale) {
152.2676 + if (locale == Locale.ROOT) {
152.2677 + return baseName;
152.2678 + }
152.2679 +
152.2680 + String language = locale.getLanguage();
152.2681 + String script = locale.getScript();
152.2682 + String country = locale.getCountry();
152.2683 + String variant = locale.getVariant();
152.2684 +
152.2685 + if (language == "" && country == "" && variant == "") {
152.2686 + return baseName;
152.2687 + }
152.2688 +
152.2689 + StringBuilder sb = new StringBuilder(baseName);
152.2690 + sb.append('_');
152.2691 + if (script != "") {
152.2692 + if (variant != "") {
152.2693 + sb.append(language).append('_').append(script).append('_').append(country).append('_').append(variant);
152.2694 + } else if (country != "") {
152.2695 + sb.append(language).append('_').append(script).append('_').append(country);
152.2696 + } else {
152.2697 + sb.append(language).append('_').append(script);
152.2698 + }
152.2699 + } else {
152.2700 + if (variant != "") {
152.2701 + sb.append(language).append('_').append(country).append('_').append(variant);
152.2702 + } else if (country != "") {
152.2703 + sb.append(language).append('_').append(country);
152.2704 + } else {
152.2705 + sb.append(language);
152.2706 + }
152.2707 + }
152.2708 + return sb.toString();
152.2709 +
152.2710 + }
152.2711 +
152.2712 + /**
152.2713 + * Converts the given <code>bundleName</code> to the form required
152.2714 + * by the {@link ClassLoader#getResource ClassLoader.getResource}
152.2715 + * method by replacing all occurrences of <code>'.'</code> in
152.2716 + * <code>bundleName</code> with <code>'/'</code> and appending a
152.2717 + * <code>'.'</code> and the given file <code>suffix</code>. For
152.2718 + * example, if <code>bundleName</code> is
152.2719 + * <code>"foo.bar.MyResources_ja_JP"</code> and <code>suffix</code>
152.2720 + * is <code>"properties"</code>, then
152.2721 + * <code>"foo/bar/MyResources_ja_JP.properties"</code> is returned.
152.2722 + *
152.2723 + * @param bundleName
152.2724 + * the bundle name
152.2725 + * @param suffix
152.2726 + * the file type suffix
152.2727 + * @return the converted resource name
152.2728 + * @exception NullPointerException
152.2729 + * if <code>bundleName</code> or <code>suffix</code>
152.2730 + * is <code>null</code>
152.2731 + */
152.2732 + public final String toResourceName(String bundleName, String suffix) {
152.2733 + StringBuilder sb = new StringBuilder(bundleName.length() + 1 + suffix.length());
152.2734 + sb.append(bundleName.replace('.', '/')).append('.').append(suffix);
152.2735 + return sb.toString();
152.2736 + }
152.2737 + }
152.2738 +
152.2739 + private static class SingleFormatControl extends Control {
152.2740 + private static final Control PROPERTIES_ONLY
152.2741 + = new SingleFormatControl(FORMAT_PROPERTIES);
152.2742 +
152.2743 + private static final Control CLASS_ONLY
152.2744 + = new SingleFormatControl(FORMAT_CLASS);
152.2745 +
152.2746 + private final List<String> formats;
152.2747 +
152.2748 + protected SingleFormatControl(List<String> formats) {
152.2749 + this.formats = formats;
152.2750 + }
152.2751 +
152.2752 + public List<String> getFormats(String baseName) {
152.2753 + if (baseName == null) {
152.2754 + throw new NullPointerException();
152.2755 + }
152.2756 + return formats;
152.2757 + }
152.2758 + }
152.2759 +
152.2760 + private static final class NoFallbackControl extends SingleFormatControl {
152.2761 + private static final Control NO_FALLBACK
152.2762 + = new NoFallbackControl(FORMAT_DEFAULT);
152.2763 +
152.2764 + private static final Control PROPERTIES_ONLY_NO_FALLBACK
152.2765 + = new NoFallbackControl(FORMAT_PROPERTIES);
152.2766 +
152.2767 + private static final Control CLASS_ONLY_NO_FALLBACK
152.2768 + = new NoFallbackControl(FORMAT_CLASS);
152.2769 +
152.2770 + protected NoFallbackControl(List<String> formats) {
152.2771 + super(formats);
152.2772 + }
152.2773 +
152.2774 + public Locale getFallbackLocale(String baseName, Locale locale) {
152.2775 + if (baseName == null || locale == null) {
152.2776 + throw new NullPointerException();
152.2777 + }
152.2778 + return null;
152.2779 + }
152.2780 + }
152.2781 +}
153.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
153.2 +++ b/rt/emul/compact/src/main/java/java/util/SimpleTimeZone.java Tue Feb 11 13:31:42 2014 +0100
153.3 @@ -0,0 +1,1737 @@
153.4 +/*
153.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
153.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
153.7 + *
153.8 + * This code is free software; you can redistribute it and/or modify it
153.9 + * under the terms of the GNU General Public License version 2 only, as
153.10 + * published by the Free Software Foundation. Oracle designates this
153.11 + * particular file as subject to the "Classpath" exception as provided
153.12 + * by Oracle in the LICENSE file that accompanied this code.
153.13 + *
153.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
153.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
153.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
153.17 + * version 2 for more details (a copy is included in the LICENSE file that
153.18 + * accompanied this code).
153.19 + *
153.20 + * You should have received a copy of the GNU General Public License version
153.21 + * 2 along with this work; if not, write to the Free Software Foundation,
153.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
153.23 + *
153.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
153.25 + * or visit www.oracle.com if you need additional information or have any
153.26 + * questions.
153.27 + */
153.28 +
153.29 +/*
153.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
153.31 + * (C) Copyright IBM Corp. 1996 - All Rights Reserved
153.32 + *
153.33 + * The original version of this source code and documentation is copyrighted
153.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
153.35 + * materials are provided under terms of a License Agreement between Taligent
153.36 + * and Sun. This technology is protected by multiple US and International
153.37 + * patents. This notice and attribution to Taligent may not be removed.
153.38 + * Taligent is a registered trademark of Taligent, Inc.
153.39 + *
153.40 + */
153.41 +
153.42 +package java.util;
153.43 +
153.44 +import java.io.ObjectInputStream;
153.45 +import java.io.ObjectOutputStream;
153.46 +import java.io.IOException;
153.47 +import java.util.Date.BaseCalendar;
153.48 +
153.49 +/**
153.50 + * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
153.51 + * that represents a time zone for use with a Gregorian calendar.
153.52 + * The class holds an offset from GMT, called <em>raw offset</em>, and start
153.53 + * and end rules for a daylight saving time schedule. Since it only holds
153.54 + * single values for each, it cannot handle historical changes in the offset
153.55 + * from GMT and the daylight saving schedule, except that the {@link
153.56 + * #setStartYear setStartYear} method can specify the year when the daylight
153.57 + * saving time schedule starts in effect.
153.58 + * <p>
153.59 + * To construct a <code>SimpleTimeZone</code> with a daylight saving time
153.60 + * schedule, the schedule can be described with a set of rules,
153.61 + * <em>start-rule</em> and <em>end-rule</em>. A day when daylight saving time
153.62 + * starts or ends is specified by a combination of <em>month</em>,
153.63 + * <em>day-of-month</em>, and <em>day-of-week</em> values. The <em>month</em>
153.64 + * value is represented by a Calendar {@link Calendar#MONTH MONTH} field
153.65 + * value, such as {@link Calendar#MARCH}. The <em>day-of-week</em> value is
153.66 + * represented by a Calendar {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value,
153.67 + * such as {@link Calendar#SUNDAY SUNDAY}. The meanings of value combinations
153.68 + * are as follows.
153.69 + *
153.70 + * <ul>
153.71 + * <li><b>Exact day of month</b><br>
153.72 + * To specify an exact day of month, set the <em>month</em> and
153.73 + * <em>day-of-month</em> to an exact value, and <em>day-of-week</em> to zero. For
153.74 + * example, to specify March 1, set the <em>month</em> to {@link Calendar#MARCH
153.75 + * MARCH}, <em>day-of-month</em> to 1, and <em>day-of-week</em> to 0.</li>
153.76 + *
153.77 + * <li><b>Day of week on or after day of month</b><br>
153.78 + * To specify a day of week on or after an exact day of month, set the
153.79 + * <em>month</em> to an exact month value, <em>day-of-month</em> to the day on
153.80 + * or after which the rule is applied, and <em>day-of-week</em> to a negative {@link
153.81 + * Calendar#DAY_OF_WEEK DAY_OF_WEEK} field value. For example, to specify the
153.82 + * second Sunday of April, set <em>month</em> to {@link Calendar#APRIL APRIL},
153.83 + * <em>day-of-month</em> to 8, and <em>day-of-week</em> to <code>-</code>{@link
153.84 + * Calendar#SUNDAY SUNDAY}.</li>
153.85 + *
153.86 + * <li><b>Day of week on or before day of month</b><br>
153.87 + * To specify a day of the week on or before an exact day of the month, set
153.88 + * <em>day-of-month</em> and <em>day-of-week</em> to a negative value. For
153.89 + * example, to specify the last Wednesday on or before the 21st of March, set
153.90 + * <em>month</em> to {@link Calendar#MARCH MARCH}, <em>day-of-month</em> is -21
153.91 + * and <em>day-of-week</em> is <code>-</code>{@link Calendar#WEDNESDAY WEDNESDAY}. </li>
153.92 + *
153.93 + * <li><b>Last day-of-week of month</b><br>
153.94 + * To specify, the last day-of-week of the month, set <em>day-of-week</em> to a
153.95 + * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value and <em>day-of-month</em> to
153.96 + * -1. For example, to specify the last Sunday of October, set <em>month</em>
153.97 + * to {@link Calendar#OCTOBER OCTOBER}, <em>day-of-week</em> to {@link
153.98 + * Calendar#SUNDAY SUNDAY} and <em>day-of-month</em> to -1. </li>
153.99 + *
153.100 + * </ul>
153.101 + * The time of the day at which daylight saving time starts or ends is
153.102 + * specified by a millisecond value within the day. There are three kinds of
153.103 + * <em>mode</em>s to specify the time: {@link #WALL_TIME}, {@link
153.104 + * #STANDARD_TIME} and {@link #UTC_TIME}. For example, if daylight
153.105 + * saving time ends
153.106 + * at 2:00 am in the wall clock time, it can be specified by 7200000
153.107 + * milliseconds in the {@link #WALL_TIME} mode. In this case, the wall clock time
153.108 + * for an <em>end-rule</em> means the same thing as the daylight time.
153.109 + * <p>
153.110 + * The following are examples of parameters for constructing time zone objects.
153.111 + * <pre><code>
153.112 + * // Base GMT offset: -8:00
153.113 + * // DST starts: at 2:00am in standard time
153.114 + * // on the first Sunday in April
153.115 + * // DST ends: at 2:00am in daylight time
153.116 + * // on the last Sunday in October
153.117 + * // Save: 1 hour
153.118 + * SimpleTimeZone(-28800000,
153.119 + * "America/Los_Angeles",
153.120 + * Calendar.APRIL, 1, -Calendar.SUNDAY,
153.121 + * 7200000,
153.122 + * Calendar.OCTOBER, -1, Calendar.SUNDAY,
153.123 + * 7200000,
153.124 + * 3600000)
153.125 + *
153.126 + * // Base GMT offset: +1:00
153.127 + * // DST starts: at 1:00am in UTC time
153.128 + * // on the last Sunday in March
153.129 + * // DST ends: at 1:00am in UTC time
153.130 + * // on the last Sunday in October
153.131 + * // Save: 1 hour
153.132 + * SimpleTimeZone(3600000,
153.133 + * "Europe/Paris",
153.134 + * Calendar.MARCH, -1, Calendar.SUNDAY,
153.135 + * 3600000, SimpleTimeZone.UTC_TIME,
153.136 + * Calendar.OCTOBER, -1, Calendar.SUNDAY,
153.137 + * 3600000, SimpleTimeZone.UTC_TIME,
153.138 + * 3600000)
153.139 + * </code></pre>
153.140 + * These parameter rules are also applicable to the set rule methods, such as
153.141 + * <code>setStartRule</code>.
153.142 + *
153.143 + * @since 1.1
153.144 + * @see Calendar
153.145 + * @see GregorianCalendar
153.146 + * @see TimeZone
153.147 + * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
153.148 + */
153.149 +
153.150 +public class SimpleTimeZone extends TimeZone {
153.151 + /**
153.152 + * Constructs a SimpleTimeZone with the given base time zone offset from GMT
153.153 + * and time zone ID with no daylight saving time schedule.
153.154 + *
153.155 + * @param rawOffset The base time zone offset in milliseconds to GMT.
153.156 + * @param ID The time zone name that is given to this instance.
153.157 + */
153.158 + public SimpleTimeZone(int rawOffset, String ID)
153.159 + {
153.160 + this.rawOffset = rawOffset;
153.161 + setID (ID);
153.162 + dstSavings = millisPerHour; // In case user sets rules later
153.163 + }
153.164 +
153.165 + /**
153.166 + * Constructs a SimpleTimeZone with the given base time zone offset from
153.167 + * GMT, time zone ID, and rules for starting and ending the daylight
153.168 + * time.
153.169 + * Both <code>startTime</code> and <code>endTime</code> are specified to be
153.170 + * represented in the wall clock time. The amount of daylight saving is
153.171 + * assumed to be 3600000 milliseconds (i.e., one hour). This constructor is
153.172 + * equivalent to:
153.173 + * <pre><code>
153.174 + * SimpleTimeZone(rawOffset,
153.175 + * ID,
153.176 + * startMonth,
153.177 + * startDay,
153.178 + * startDayOfWeek,
153.179 + * startTime,
153.180 + * SimpleTimeZone.{@link #WALL_TIME},
153.181 + * endMonth,
153.182 + * endDay,
153.183 + * endDayOfWeek,
153.184 + * endTime,
153.185 + * SimpleTimeZone.{@link #WALL_TIME},
153.186 + * 3600000)
153.187 + * </code></pre>
153.188 + *
153.189 + * @param rawOffset The given base time zone offset from GMT.
153.190 + * @param ID The time zone ID which is given to this object.
153.191 + * @param startMonth The daylight saving time starting month. Month is
153.192 + * a {@link Calendar#MONTH MONTH} field value (0-based. e.g., 0
153.193 + * for January).
153.194 + * @param startDay The day of the month on which the daylight saving time starts.
153.195 + * See the class description for the special cases of this parameter.
153.196 + * @param startDayOfWeek The daylight saving time starting day-of-week.
153.197 + * See the class description for the special cases of this parameter.
153.198 + * @param startTime The daylight saving time starting time in local wall clock
153.199 + * time (in milliseconds within the day), which is local
153.200 + * standard time in this case.
153.201 + * @param endMonth The daylight saving time ending month. Month is
153.202 + * a {@link Calendar#MONTH MONTH} field
153.203 + * value (0-based. e.g., 9 for October).
153.204 + * @param endDay The day of the month on which the daylight saving time ends.
153.205 + * See the class description for the special cases of this parameter.
153.206 + * @param endDayOfWeek The daylight saving time ending day-of-week.
153.207 + * See the class description for the special cases of this parameter.
153.208 + * @param endTime The daylight saving ending time in local wall clock time,
153.209 + * (in milliseconds within the day) which is local daylight
153.210 + * time in this case.
153.211 + * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
153.212 + * parameters are out of range for the start or end rule
153.213 + */
153.214 + public SimpleTimeZone(int rawOffset, String ID,
153.215 + int startMonth, int startDay, int startDayOfWeek, int startTime,
153.216 + int endMonth, int endDay, int endDayOfWeek, int endTime)
153.217 + {
153.218 + this(rawOffset, ID,
153.219 + startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
153.220 + endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
153.221 + millisPerHour);
153.222 + }
153.223 +
153.224 + /**
153.225 + * Constructs a SimpleTimeZone with the given base time zone offset from
153.226 + * GMT, time zone ID, and rules for starting and ending the daylight
153.227 + * time.
153.228 + * Both <code>startTime</code> and <code>endTime</code> are assumed to be
153.229 + * represented in the wall clock time. This constructor is equivalent to:
153.230 + * <pre><code>
153.231 + * SimpleTimeZone(rawOffset,
153.232 + * ID,
153.233 + * startMonth,
153.234 + * startDay,
153.235 + * startDayOfWeek,
153.236 + * startTime,
153.237 + * SimpleTimeZone.{@link #WALL_TIME},
153.238 + * endMonth,
153.239 + * endDay,
153.240 + * endDayOfWeek,
153.241 + * endTime,
153.242 + * SimpleTimeZone.{@link #WALL_TIME},
153.243 + * dstSavings)
153.244 + * </code></pre>
153.245 + *
153.246 + * @param rawOffset The given base time zone offset from GMT.
153.247 + * @param ID The time zone ID which is given to this object.
153.248 + * @param startMonth The daylight saving time starting month. Month is
153.249 + * a {@link Calendar#MONTH MONTH} field
153.250 + * value (0-based. e.g., 0 for January).
153.251 + * @param startDay The day of the month on which the daylight saving time starts.
153.252 + * See the class description for the special cases of this parameter.
153.253 + * @param startDayOfWeek The daylight saving time starting day-of-week.
153.254 + * See the class description for the special cases of this parameter.
153.255 + * @param startTime The daylight saving time starting time in local wall clock
153.256 + * time, which is local standard time in this case.
153.257 + * @param endMonth The daylight saving time ending month. Month is
153.258 + * a {@link Calendar#MONTH MONTH} field
153.259 + * value (0-based. e.g., 9 for October).
153.260 + * @param endDay The day of the month on which the daylight saving time ends.
153.261 + * See the class description for the special cases of this parameter.
153.262 + * @param endDayOfWeek The daylight saving time ending day-of-week.
153.263 + * See the class description for the special cases of this parameter.
153.264 + * @param endTime The daylight saving ending time in local wall clock time,
153.265 + * which is local daylight time in this case.
153.266 + * @param dstSavings The amount of time in milliseconds saved during
153.267 + * daylight saving time.
153.268 + * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
153.269 + * parameters are out of range for the start or end rule
153.270 + * @since 1.2
153.271 + */
153.272 + public SimpleTimeZone(int rawOffset, String ID,
153.273 + int startMonth, int startDay, int startDayOfWeek, int startTime,
153.274 + int endMonth, int endDay, int endDayOfWeek, int endTime,
153.275 + int dstSavings)
153.276 + {
153.277 + this(rawOffset, ID,
153.278 + startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
153.279 + endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
153.280 + dstSavings);
153.281 + }
153.282 +
153.283 + /**
153.284 + * Constructs a SimpleTimeZone with the given base time zone offset from
153.285 + * GMT, time zone ID, and rules for starting and ending the daylight
153.286 + * time.
153.287 + * This constructor takes the full set of the start and end rules
153.288 + * parameters, including modes of <code>startTime</code> and
153.289 + * <code>endTime</code>. The mode specifies either {@link #WALL_TIME wall
153.290 + * time} or {@link #STANDARD_TIME standard time} or {@link #UTC_TIME UTC
153.291 + * time}.
153.292 + *
153.293 + * @param rawOffset The given base time zone offset from GMT.
153.294 + * @param ID The time zone ID which is given to this object.
153.295 + * @param startMonth The daylight saving time starting month. Month is
153.296 + * a {@link Calendar#MONTH MONTH} field
153.297 + * value (0-based. e.g., 0 for January).
153.298 + * @param startDay The day of the month on which the daylight saving time starts.
153.299 + * See the class description for the special cases of this parameter.
153.300 + * @param startDayOfWeek The daylight saving time starting day-of-week.
153.301 + * See the class description for the special cases of this parameter.
153.302 + * @param startTime The daylight saving time starting time in the time mode
153.303 + * specified by <code>startTimeMode</code>.
153.304 + * @param startTimeMode The mode of the start time specified by startTime.
153.305 + * @param endMonth The daylight saving time ending month. Month is
153.306 + * a {@link Calendar#MONTH MONTH} field
153.307 + * value (0-based. e.g., 9 for October).
153.308 + * @param endDay The day of the month on which the daylight saving time ends.
153.309 + * See the class description for the special cases of this parameter.
153.310 + * @param endDayOfWeek The daylight saving time ending day-of-week.
153.311 + * See the class description for the special cases of this parameter.
153.312 + * @param endTime The daylight saving ending time in time time mode
153.313 + * specified by <code>endTimeMode</code>.
153.314 + * @param endTimeMode The mode of the end time specified by endTime
153.315 + * @param dstSavings The amount of time in milliseconds saved during
153.316 + * daylight saving time.
153.317 + *
153.318 + * @exception IllegalArgumentException if the month, day, dayOfWeek, time more, or
153.319 + * time parameters are out of range for the start or end rule, or if a time mode
153.320 + * value is invalid.
153.321 + *
153.322 + * @see #WALL_TIME
153.323 + * @see #STANDARD_TIME
153.324 + * @see #UTC_TIME
153.325 + *
153.326 + * @since 1.4
153.327 + */
153.328 + public SimpleTimeZone(int rawOffset, String ID,
153.329 + int startMonth, int startDay, int startDayOfWeek,
153.330 + int startTime, int startTimeMode,
153.331 + int endMonth, int endDay, int endDayOfWeek,
153.332 + int endTime, int endTimeMode,
153.333 + int dstSavings) {
153.334 +
153.335 + setID(ID);
153.336 + this.rawOffset = rawOffset;
153.337 + this.startMonth = startMonth;
153.338 + this.startDay = startDay;
153.339 + this.startDayOfWeek = startDayOfWeek;
153.340 + this.startTime = startTime;
153.341 + this.startTimeMode = startTimeMode;
153.342 + this.endMonth = endMonth;
153.343 + this.endDay = endDay;
153.344 + this.endDayOfWeek = endDayOfWeek;
153.345 + this.endTime = endTime;
153.346 + this.endTimeMode = endTimeMode;
153.347 + this.dstSavings = dstSavings;
153.348 +
153.349 + // this.useDaylight is set by decodeRules
153.350 + decodeRules();
153.351 + if (dstSavings <= 0) {
153.352 + throw new IllegalArgumentException("Illegal daylight saving value: " + dstSavings);
153.353 + }
153.354 + }
153.355 +
153.356 + /**
153.357 + * Sets the daylight saving time starting year.
153.358 + *
153.359 + * @param year The daylight saving starting year.
153.360 + */
153.361 + public void setStartYear(int year)
153.362 + {
153.363 + startYear = year;
153.364 + invalidateCache();
153.365 + }
153.366 +
153.367 + /**
153.368 + * Sets the daylight saving time start rule. For example, if daylight saving
153.369 + * time starts on the first Sunday in April at 2 am in local wall clock
153.370 + * time, you can set the start rule by calling:
153.371 + * <pre><code>setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);</code></pre>
153.372 + *
153.373 + * @param startMonth The daylight saving time starting month. Month is
153.374 + * a {@link Calendar#MONTH MONTH} field
153.375 + * value (0-based. e.g., 0 for January).
153.376 + * @param startDay The day of the month on which the daylight saving time starts.
153.377 + * See the class description for the special cases of this parameter.
153.378 + * @param startDayOfWeek The daylight saving time starting day-of-week.
153.379 + * See the class description for the special cases of this parameter.
153.380 + * @param startTime The daylight saving time starting time in local wall clock
153.381 + * time, which is local standard time in this case.
153.382 + * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
153.383 + * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
153.384 + */
153.385 + public void setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)
153.386 + {
153.387 + this.startMonth = startMonth;
153.388 + this.startDay = startDay;
153.389 + this.startDayOfWeek = startDayOfWeek;
153.390 + this.startTime = startTime;
153.391 + startTimeMode = WALL_TIME;
153.392 + decodeStartRule();
153.393 + invalidateCache();
153.394 + }
153.395 +
153.396 + /**
153.397 + * Sets the daylight saving time start rule to a fixed date within a month.
153.398 + * This method is equivalent to:
153.399 + * <pre><code>setStartRule(startMonth, startDay, 0, startTime)</code></pre>
153.400 + *
153.401 + * @param startMonth The daylight saving time starting month. Month is
153.402 + * a {@link Calendar#MONTH MONTH} field
153.403 + * value (0-based. e.g., 0 for January).
153.404 + * @param startDay The day of the month on which the daylight saving time starts.
153.405 + * @param startTime The daylight saving time starting time in local wall clock
153.406 + * time, which is local standard time in this case.
153.407 + * See the class description for the special cases of this parameter.
153.408 + * @exception IllegalArgumentException if the <code>startMonth</code>,
153.409 + * <code>startDayOfMonth</code>, or <code>startTime</code> parameters are out of range
153.410 + * @since 1.2
153.411 + */
153.412 + public void setStartRule(int startMonth, int startDay, int startTime) {
153.413 + setStartRule(startMonth, startDay, 0, startTime);
153.414 + }
153.415 +
153.416 + /**
153.417 + * Sets the daylight saving time start rule to a weekday before or after the given date within
153.418 + * a month, e.g., the first Monday on or after the 8th.
153.419 + *
153.420 + * @param startMonth The daylight saving time starting month. Month is
153.421 + * a {@link Calendar#MONTH MONTH} field
153.422 + * value (0-based. e.g., 0 for January).
153.423 + * @param startDay The day of the month on which the daylight saving time starts.
153.424 + * @param startDayOfWeek The daylight saving time starting day-of-week.
153.425 + * @param startTime The daylight saving time starting time in local wall clock
153.426 + * time, which is local standard time in this case.
153.427 + * @param after If true, this rule selects the first <code>dayOfWeek</code> on or
153.428 + * <em>after</em> <code>dayOfMonth</code>. If false, this rule
153.429 + * selects the last <code>dayOfWeek</code> on or <em>before</em>
153.430 + * <code>dayOfMonth</code>.
153.431 + * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
153.432 + * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
153.433 + * @since 1.2
153.434 + */
153.435 + public void setStartRule(int startMonth, int startDay, int startDayOfWeek,
153.436 + int startTime, boolean after)
153.437 + {
153.438 + // TODO: this method doesn't check the initial values of dayOfMonth or dayOfWeek.
153.439 + if (after) {
153.440 + setStartRule(startMonth, startDay, -startDayOfWeek, startTime);
153.441 + } else {
153.442 + setStartRule(startMonth, -startDay, -startDayOfWeek, startTime);
153.443 + }
153.444 + }
153.445 +
153.446 + /**
153.447 + * Sets the daylight saving time end rule. For example, if daylight saving time
153.448 + * ends on the last Sunday in October at 2 am in wall clock time,
153.449 + * you can set the end rule by calling:
153.450 + * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code>
153.451 + *
153.452 + * @param endMonth The daylight saving time ending month. Month is
153.453 + * a {@link Calendar#MONTH MONTH} field
153.454 + * value (0-based. e.g., 9 for October).
153.455 + * @param endDay The day of the month on which the daylight saving time ends.
153.456 + * See the class description for the special cases of this parameter.
153.457 + * @param endDayOfWeek The daylight saving time ending day-of-week.
153.458 + * See the class description for the special cases of this parameter.
153.459 + * @param endTime The daylight saving ending time in local wall clock time,
153.460 + * (in milliseconds within the day) which is local daylight
153.461 + * time in this case.
153.462 + * @exception IllegalArgumentException if the <code>endMonth</code>, <code>endDay</code>,
153.463 + * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
153.464 + */
153.465 + public void setEndRule(int endMonth, int endDay, int endDayOfWeek,
153.466 + int endTime)
153.467 + {
153.468 + this.endMonth = endMonth;
153.469 + this.endDay = endDay;
153.470 + this.endDayOfWeek = endDayOfWeek;
153.471 + this.endTime = endTime;
153.472 + this.endTimeMode = WALL_TIME;
153.473 + decodeEndRule();
153.474 + invalidateCache();
153.475 + }
153.476 +
153.477 + /**
153.478 + * Sets the daylight saving time end rule to a fixed date within a month.
153.479 + * This method is equivalent to:
153.480 + * <pre><code>setEndRule(endMonth, endDay, 0, endTime)</code></pre>
153.481 + *
153.482 + * @param endMonth The daylight saving time ending month. Month is
153.483 + * a {@link Calendar#MONTH MONTH} field
153.484 + * value (0-based. e.g., 9 for October).
153.485 + * @param endDay The day of the month on which the daylight saving time ends.
153.486 + * @param endTime The daylight saving ending time in local wall clock time,
153.487 + * (in milliseconds within the day) which is local daylight
153.488 + * time in this case.
153.489 + * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
153.490 + * or <code>endTime</code> parameters are out of range
153.491 + * @since 1.2
153.492 + */
153.493 + public void setEndRule(int endMonth, int endDay, int endTime)
153.494 + {
153.495 + setEndRule(endMonth, endDay, 0, endTime);
153.496 + }
153.497 +
153.498 + /**
153.499 + * Sets the daylight saving time end rule to a weekday before or after the given date within
153.500 + * a month, e.g., the first Monday on or after the 8th.
153.501 + *
153.502 + * @param endMonth The daylight saving time ending month. Month is
153.503 + * a {@link Calendar#MONTH MONTH} field
153.504 + * value (0-based. e.g., 9 for October).
153.505 + * @param endDay The day of the month on which the daylight saving time ends.
153.506 + * @param endDayOfWeek The daylight saving time ending day-of-week.
153.507 + * @param endTime The daylight saving ending time in local wall clock time,
153.508 + * (in milliseconds within the day) which is local daylight
153.509 + * time in this case.
153.510 + * @param after If true, this rule selects the first <code>endDayOfWeek</code> on
153.511 + * or <em>after</em> <code>endDay</code>. If false, this rule
153.512 + * selects the last <code>endDayOfWeek</code> on or before
153.513 + * <code>endDay</code> of the month.
153.514 + * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
153.515 + * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
153.516 + * @since 1.2
153.517 + */
153.518 + public void setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)
153.519 + {
153.520 + if (after) {
153.521 + setEndRule(endMonth, endDay, -endDayOfWeek, endTime);
153.522 + } else {
153.523 + setEndRule(endMonth, -endDay, -endDayOfWeek, endTime);
153.524 + }
153.525 + }
153.526 +
153.527 + /**
153.528 + * Returns the offset of this time zone from UTC at the given
153.529 + * time. If daylight saving time is in effect at the given time,
153.530 + * the offset value is adjusted with the amount of daylight
153.531 + * saving.
153.532 + *
153.533 + * @param date the time at which the time zone offset is found
153.534 + * @return the amount of time in milliseconds to add to UTC to get
153.535 + * local time.
153.536 + * @since 1.4
153.537 + */
153.538 + public int getOffset(long date) {
153.539 + return getOffsets(date, null);
153.540 + }
153.541 +
153.542 + /**
153.543 + * @see TimeZone#getOffsets
153.544 + */
153.545 + int getOffsets(long date, int[] offsets) {
153.546 + int offset = rawOffset;
153.547 +
153.548 + computeOffset:
153.549 + if (useDaylight) {
153.550 + synchronized (this) {
153.551 + if (cacheStart != 0) {
153.552 + if (date >= cacheStart && date < cacheEnd) {
153.553 + offset += dstSavings;
153.554 + break computeOffset;
153.555 + }
153.556 + }
153.557 + }
153.558 + BaseCalendar cal = gcal;
153.559 +// date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ?
153.560 +// gcal : (BaseCalendar) CalendarSystem.forName("julian");
153.561 + BaseCalendar.Datum cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
153.562 + // Get the year in local time
153.563 + cal.getCalendarDate(date + rawOffset, cdate);
153.564 + int year = cdate.getNormalizedYear();
153.565 + if (year >= startYear) {
153.566 + // Clear time elements for the transition calculations
153.567 + cdate.setTimeOfDay(0, 0, 0, 0);
153.568 + offset = getOffset(cal, cdate, year, date);
153.569 + }
153.570 + }
153.571 +
153.572 + if (offsets != null) {
153.573 + offsets[0] = rawOffset;
153.574 + offsets[1] = offset - rawOffset;
153.575 + }
153.576 + return offset;
153.577 + }
153.578 +
153.579 + /**
153.580 + * Returns the difference in milliseconds between local time and
153.581 + * UTC, taking into account both the raw offset and the effect of
153.582 + * daylight saving, for the specified date and time. This method
153.583 + * assumes that the start and end month are distinct. It also
153.584 + * uses a default {@link GregorianCalendar} object as its
153.585 + * underlying calendar, such as for determining leap years. Do
153.586 + * not use the result of this method with a calendar other than a
153.587 + * default <code>GregorianCalendar</code>.
153.588 + *
153.589 + * <p><em>Note: In general, clients should use
153.590 + * <code>Calendar.get(ZONE_OFFSET) + Calendar.get(DST_OFFSET)</code>
153.591 + * instead of calling this method.</em>
153.592 + *
153.593 + * @param era The era of the given date.
153.594 + * @param year The year in the given date.
153.595 + * @param month The month in the given date. Month is 0-based. e.g.,
153.596 + * 0 for January.
153.597 + * @param day The day-in-month of the given date.
153.598 + * @param dayOfWeek The day-of-week of the given date.
153.599 + * @param millis The milliseconds in day in <em>standard</em> local time.
153.600 + * @return The milliseconds to add to UTC to get local time.
153.601 + * @exception IllegalArgumentException the <code>era</code>,
153.602 + * <code>month</code>, <code>day</code>, <code>dayOfWeek</code>,
153.603 + * or <code>millis</code> parameters are out of range
153.604 + */
153.605 + public int getOffset(int era, int year, int month, int day, int dayOfWeek,
153.606 + int millis)
153.607 + {
153.608 + if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
153.609 + throw new IllegalArgumentException("Illegal era " + era);
153.610 + }
153.611 +
153.612 + int y = year;
153.613 + if (era == GregorianCalendar.BC) {
153.614 + // adjust y with the GregorianCalendar-style year numbering.
153.615 + y = 1 - y;
153.616 + }
153.617 +
153.618 + // If the year isn't representable with the 64-bit long
153.619 + // integer in milliseconds, convert the year to an
153.620 + // equivalent year. This is required to pass some JCK test cases
153.621 + // which are actually useless though because the specified years
153.622 + // can't be supported by the Java time system.
153.623 + if (y >= 292278994) {
153.624 + y = 2800 + y % 2800;
153.625 + } else if (y <= -292269054) {
153.626 + // y %= 28 also produces an equivalent year, but positive
153.627 + // year numbers would be convenient to use the UNIX cal
153.628 + // command.
153.629 + y = (int) (long) y % 28;
153.630 + }
153.631 +
153.632 + // convert year to its 1-based month value
153.633 + int m = month + 1;
153.634 +
153.635 + // First, calculate time as a Gregorian date.
153.636 + BaseCalendar cal = gcal;
153.637 + BaseCalendar.Datum cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
153.638 + cdate.setDate(y, m, day);
153.639 + long time = cal.getTime(cdate); // normalize cdate
153.640 + time += millis - rawOffset; // UTC time
153.641 +
153.642 + // If the time value represents a time before the default
153.643 + // Gregorian cutover, recalculate time using the Julian
153.644 + // calendar system. For the Julian calendar system, the
153.645 + // normalized year numbering is ..., -2 (BCE 2), -1 (BCE 1),
153.646 + // 1, 2 ... which is different from the GregorianCalendar
153.647 + // style year numbering (..., -1, 0 (BCE 1), 1, 2, ...).
153.648 +// if (time < GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER) {
153.649 +// cal = (BaseCalendar) CalendarSystem.forName("julian");
153.650 +// cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
153.651 +// cdate.setNormalizedDate(y, m, day);
153.652 +// time = cal.getTime(cdate) + millis - rawOffset;
153.653 +// }
153.654 +
153.655 + if ((cdate.getNormalizedYear() != y)
153.656 + || (cdate.getMonth() != m)
153.657 + || (cdate.getDayOfMonth() != day)
153.658 + // The validation should be cdate.getDayOfWeek() ==
153.659 + // dayOfWeek. However, we don't check dayOfWeek for
153.660 + // compatibility.
153.661 + || (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
153.662 + || (millis < 0 || millis >= (24*60*60*1000))) {
153.663 + throw new IllegalArgumentException();
153.664 + }
153.665 +
153.666 + if (!useDaylight || year < startYear || era != GregorianCalendar.CE) {
153.667 + return rawOffset;
153.668 + }
153.669 +
153.670 + return getOffset(cal, cdate, y, time);
153.671 + }
153.672 +
153.673 + private int getOffset(BaseCalendar cal, BaseCalendar.Datum cdate, int year, long time) {
153.674 + synchronized (this) {
153.675 + if (cacheStart != 0) {
153.676 + if (time >= cacheStart && time < cacheEnd) {
153.677 + return rawOffset + dstSavings;
153.678 + }
153.679 + if (year == cacheYear) {
153.680 + return rawOffset;
153.681 + }
153.682 + }
153.683 + }
153.684 +
153.685 + long start = getStart(cal, cdate, year);
153.686 + long end = getEnd(cal, cdate, year);
153.687 + int offset = rawOffset;
153.688 + if (start <= end) {
153.689 + if (time >= start && time < end) {
153.690 + offset += dstSavings;
153.691 + }
153.692 + synchronized (this) {
153.693 + cacheYear = year;
153.694 + cacheStart = start;
153.695 + cacheEnd = end;
153.696 + }
153.697 + } else {
153.698 + if (time < end) {
153.699 + // TODO: support Gregorian cutover. The previous year
153.700 + // may be in the other calendar system.
153.701 + start = getStart(cal, cdate, year - 1);
153.702 + if (time >= start) {
153.703 + offset += dstSavings;
153.704 + }
153.705 + } else if (time >= start) {
153.706 + // TODO: support Gregorian cutover. The next year
153.707 + // may be in the other calendar system.
153.708 + end = getEnd(cal, cdate, year + 1);
153.709 + if (time < end) {
153.710 + offset += dstSavings;
153.711 + }
153.712 + }
153.713 + if (start <= end) {
153.714 + synchronized (this) {
153.715 + // The start and end transitions are in multiple years.
153.716 + cacheYear = (long) startYear - 1;
153.717 + cacheStart = start;
153.718 + cacheEnd = end;
153.719 + }
153.720 + }
153.721 + }
153.722 + return offset;
153.723 + }
153.724 +
153.725 + private long getStart(BaseCalendar cal, BaseCalendar.Datum cdate, int year) {
153.726 + int time = startTime;
153.727 + if (startTimeMode != UTC_TIME) {
153.728 + time -= rawOffset;
153.729 + }
153.730 + return getTransition(cal, cdate, startMode, year, startMonth, startDay,
153.731 + startDayOfWeek, time);
153.732 + }
153.733 +
153.734 + private long getEnd(BaseCalendar cal, BaseCalendar.Datum cdate, int year) {
153.735 + int time = endTime;
153.736 + if (endTimeMode != UTC_TIME) {
153.737 + time -= rawOffset;
153.738 + }
153.739 + if (endTimeMode == WALL_TIME) {
153.740 + time -= dstSavings;
153.741 + }
153.742 + return getTransition(cal, cdate, endMode, year, endMonth, endDay,
153.743 + endDayOfWeek, time);
153.744 + }
153.745 +
153.746 + private long getTransition(BaseCalendar cal, BaseCalendar.Datum cdate,
153.747 + int mode, int year, int month, int dayOfMonth,
153.748 + int dayOfWeek, int timeOfDay) {
153.749 + cdate.setNormalizedYear(year);
153.750 + cdate.setMonth(month + 1);
153.751 + switch (mode) {
153.752 + case DOM_MODE:
153.753 + cdate.setDayOfMonth(dayOfMonth);
153.754 + break;
153.755 +
153.756 + case DOW_IN_MONTH_MODE:
153.757 + cdate.setDayOfMonth(1);
153.758 + if (dayOfMonth < 0) {
153.759 + cdate.setDayOfMonth(cal.getMonthLength(cdate));
153.760 + }
153.761 + cdate = (BaseCalendar.Datum) cal.getNthDayOfWeek(dayOfMonth, dayOfWeek, cdate);
153.762 + break;
153.763 +
153.764 + case DOW_GE_DOM_MODE:
153.765 + cdate.setDayOfMonth(dayOfMonth);
153.766 + cdate = (BaseCalendar.Datum) cal.getNthDayOfWeek(1, dayOfWeek, cdate);
153.767 + break;
153.768 +
153.769 + case DOW_LE_DOM_MODE:
153.770 + cdate.setDayOfMonth(dayOfMonth);
153.771 + cdate = (BaseCalendar.Datum) cal.getNthDayOfWeek(-1, dayOfWeek, cdate);
153.772 + break;
153.773 + }
153.774 + return cal.getTime(cdate) + timeOfDay;
153.775 + }
153.776 +
153.777 + /**
153.778 + * Gets the GMT offset for this time zone.
153.779 + * @return the GMT offset value in milliseconds
153.780 + * @see #setRawOffset
153.781 + */
153.782 + public int getRawOffset()
153.783 + {
153.784 + // The given date will be taken into account while
153.785 + // we have the historical time zone data in place.
153.786 + return rawOffset;
153.787 + }
153.788 +
153.789 + /**
153.790 + * Sets the base time zone offset to GMT.
153.791 + * This is the offset to add to UTC to get local time.
153.792 + * @see #getRawOffset
153.793 + */
153.794 + public void setRawOffset(int offsetMillis)
153.795 + {
153.796 + this.rawOffset = offsetMillis;
153.797 + }
153.798 +
153.799 + /**
153.800 + * Sets the amount of time in milliseconds that the clock is advanced
153.801 + * during daylight saving time.
153.802 + * @param millisSavedDuringDST the number of milliseconds the time is
153.803 + * advanced with respect to standard time when the daylight saving time rules
153.804 + * are in effect. A positive number, typically one hour (3600000).
153.805 + * @see #getDSTSavings
153.806 + * @since 1.2
153.807 + */
153.808 + public void setDSTSavings(int millisSavedDuringDST) {
153.809 + if (millisSavedDuringDST <= 0) {
153.810 + throw new IllegalArgumentException("Illegal daylight saving value: "
153.811 + + millisSavedDuringDST);
153.812 + }
153.813 + dstSavings = millisSavedDuringDST;
153.814 + }
153.815 +
153.816 + /**
153.817 + * Returns the amount of time in milliseconds that the clock is
153.818 + * advanced during daylight saving time.
153.819 + *
153.820 + * @return the number of milliseconds the time is advanced with
153.821 + * respect to standard time when the daylight saving rules are in
153.822 + * effect, or 0 (zero) if this time zone doesn't observe daylight
153.823 + * saving time.
153.824 + *
153.825 + * @see #setDSTSavings
153.826 + * @since 1.2
153.827 + */
153.828 + public int getDSTSavings() {
153.829 + return useDaylight ? dstSavings : 0;
153.830 + }
153.831 +
153.832 + /**
153.833 + * Queries if this time zone uses daylight saving time.
153.834 + * @return true if this time zone uses daylight saving time;
153.835 + * false otherwise.
153.836 + */
153.837 + public boolean useDaylightTime()
153.838 + {
153.839 + return useDaylight;
153.840 + }
153.841 +
153.842 + /**
153.843 + * Returns {@code true} if this {@code SimpleTimeZone} observes
153.844 + * Daylight Saving Time. This method is equivalent to {@link
153.845 + * #useDaylightTime()}.
153.846 + *
153.847 + * @return {@code true} if this {@code SimpleTimeZone} observes
153.848 + * Daylight Saving Time; {@code false} otherwise.
153.849 + * @since 1.7
153.850 + */
153.851 + @Override
153.852 + public boolean observesDaylightTime() {
153.853 + return useDaylightTime();
153.854 + }
153.855 +
153.856 + /**
153.857 + * Queries if the given date is in daylight saving time.
153.858 + * @return true if daylight saving time is in effective at the
153.859 + * given date; false otherwise.
153.860 + */
153.861 + public boolean inDaylightTime(Date date)
153.862 + {
153.863 + return (getOffset(date.getTime()) != rawOffset);
153.864 + }
153.865 +
153.866 + /**
153.867 + * Returns a clone of this <code>SimpleTimeZone</code> instance.
153.868 + * @return a clone of this instance.
153.869 + */
153.870 + public Object clone()
153.871 + {
153.872 + return super.clone();
153.873 + }
153.874 +
153.875 + /**
153.876 + * Generates the hash code for the SimpleDateFormat object.
153.877 + * @return the hash code for this object
153.878 + */
153.879 + public synchronized int hashCode()
153.880 + {
153.881 + return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
153.882 + endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
153.883 + }
153.884 +
153.885 + /**
153.886 + * Compares the equality of two <code>SimpleTimeZone</code> objects.
153.887 + *
153.888 + * @param obj The <code>SimpleTimeZone</code> object to be compared with.
153.889 + * @return True if the given <code>obj</code> is the same as this
153.890 + * <code>SimpleTimeZone</code> object; false otherwise.
153.891 + */
153.892 + public boolean equals(Object obj)
153.893 + {
153.894 + if (this == obj) {
153.895 + return true;
153.896 + }
153.897 + if (!(obj instanceof SimpleTimeZone)) {
153.898 + return false;
153.899 + }
153.900 +
153.901 + SimpleTimeZone that = (SimpleTimeZone) obj;
153.902 +
153.903 + return getID().equals(that.getID()) &&
153.904 + hasSameRules(that);
153.905 + }
153.906 +
153.907 + /**
153.908 + * Returns <code>true</code> if this zone has the same rules and offset as another zone.
153.909 + * @param other the TimeZone object to be compared with
153.910 + * @return <code>true</code> if the given zone is a SimpleTimeZone and has the
153.911 + * same rules and offset as this one
153.912 + * @since 1.2
153.913 + */
153.914 + public boolean hasSameRules(TimeZone other) {
153.915 + if (this == other) {
153.916 + return true;
153.917 + }
153.918 + if (!(other instanceof SimpleTimeZone)) {
153.919 + return false;
153.920 + }
153.921 + SimpleTimeZone that = (SimpleTimeZone) other;
153.922 + return rawOffset == that.rawOffset &&
153.923 + useDaylight == that.useDaylight &&
153.924 + (!useDaylight
153.925 + // Only check rules if using DST
153.926 + || (dstSavings == that.dstSavings &&
153.927 + startMode == that.startMode &&
153.928 + startMonth == that.startMonth &&
153.929 + startDay == that.startDay &&
153.930 + startDayOfWeek == that.startDayOfWeek &&
153.931 + startTime == that.startTime &&
153.932 + startTimeMode == that.startTimeMode &&
153.933 + endMode == that.endMode &&
153.934 + endMonth == that.endMonth &&
153.935 + endDay == that.endDay &&
153.936 + endDayOfWeek == that.endDayOfWeek &&
153.937 + endTime == that.endTime &&
153.938 + endTimeMode == that.endTimeMode &&
153.939 + startYear == that.startYear));
153.940 + }
153.941 +
153.942 + /**
153.943 + * Returns a string representation of this time zone.
153.944 + * @return a string representation of this time zone.
153.945 + */
153.946 + public String toString() {
153.947 + return getClass().getName() +
153.948 + "[id=" + getID() +
153.949 + ",offset=" + rawOffset +
153.950 + ",dstSavings=" + dstSavings +
153.951 + ",useDaylight=" + useDaylight +
153.952 + ",startYear=" + startYear +
153.953 + ",startMode=" + startMode +
153.954 + ",startMonth=" + startMonth +
153.955 + ",startDay=" + startDay +
153.956 + ",startDayOfWeek=" + startDayOfWeek +
153.957 + ",startTime=" + startTime +
153.958 + ",startTimeMode=" + startTimeMode +
153.959 + ",endMode=" + endMode +
153.960 + ",endMonth=" + endMonth +
153.961 + ",endDay=" + endDay +
153.962 + ",endDayOfWeek=" + endDayOfWeek +
153.963 + ",endTime=" + endTime +
153.964 + ",endTimeMode=" + endTimeMode + ']';
153.965 + }
153.966 +
153.967 + // =======================privates===============================
153.968 +
153.969 + /**
153.970 + * The month in which daylight saving time starts. This value must be
153.971 + * between <code>Calendar.JANUARY</code> and
153.972 + * <code>Calendar.DECEMBER</code> inclusive. This value must not equal
153.973 + * <code>endMonth</code>.
153.974 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.975 + * @serial
153.976 + */
153.977 + private int startMonth;
153.978 +
153.979 + /**
153.980 + * This field has two possible interpretations:
153.981 + * <dl>
153.982 + * <dt><code>startMode == DOW_IN_MONTH</code></dt>
153.983 + * <dd>
153.984 + * <code>startDay</code> indicates the day of the month of
153.985 + * <code>startMonth</code> on which daylight
153.986 + * saving time starts, from 1 to 28, 30, or 31, depending on the
153.987 + * <code>startMonth</code>.
153.988 + * </dd>
153.989 + * <dt><code>startMode != DOW_IN_MONTH</code></dt>
153.990 + * <dd>
153.991 + * <code>startDay</code> indicates which <code>startDayOfWeek</code> in the
153.992 + * month <code>startMonth</code> daylight
153.993 + * saving time starts on. For example, a value of +1 and a
153.994 + * <code>startDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
153.995 + * first Sunday of <code>startMonth</code>. Likewise, +2 would indicate the
153.996 + * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
153.997 + * </dd>
153.998 + * </dl>
153.999 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1000 + * @serial
153.1001 + */
153.1002 + private int startDay;
153.1003 +
153.1004 + /**
153.1005 + * The day of the week on which daylight saving time starts. This value
153.1006 + * must be between <code>Calendar.SUNDAY</code> and
153.1007 + * <code>Calendar.SATURDAY</code> inclusive.
153.1008 + * <p>If <code>useDaylight</code> is false or
153.1009 + * <code>startMode == DAY_OF_MONTH</code>, this value is ignored.
153.1010 + * @serial
153.1011 + */
153.1012 + private int startDayOfWeek;
153.1013 +
153.1014 + /**
153.1015 + * The time in milliseconds after midnight at which daylight saving
153.1016 + * time starts. This value is expressed as wall time, standard time,
153.1017 + * or UTC time, depending on the setting of <code>startTimeMode</code>.
153.1018 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1019 + * @serial
153.1020 + */
153.1021 + private int startTime;
153.1022 +
153.1023 + /**
153.1024 + * The format of startTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME.
153.1025 + * @serial
153.1026 + * @since 1.3
153.1027 + */
153.1028 + private int startTimeMode;
153.1029 +
153.1030 + /**
153.1031 + * The month in which daylight saving time ends. This value must be
153.1032 + * between <code>Calendar.JANUARY</code> and
153.1033 + * <code>Calendar.UNDECIMBER</code>. This value must not equal
153.1034 + * <code>startMonth</code>.
153.1035 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1036 + * @serial
153.1037 + */
153.1038 + private int endMonth;
153.1039 +
153.1040 + /**
153.1041 + * This field has two possible interpretations:
153.1042 + * <dl>
153.1043 + * <dt><code>endMode == DOW_IN_MONTH</code></dt>
153.1044 + * <dd>
153.1045 + * <code>endDay</code> indicates the day of the month of
153.1046 + * <code>endMonth</code> on which daylight
153.1047 + * saving time ends, from 1 to 28, 30, or 31, depending on the
153.1048 + * <code>endMonth</code>.
153.1049 + * </dd>
153.1050 + * <dt><code>endMode != DOW_IN_MONTH</code></dt>
153.1051 + * <dd>
153.1052 + * <code>endDay</code> indicates which <code>endDayOfWeek</code> in th
153.1053 + * month <code>endMonth</code> daylight
153.1054 + * saving time ends on. For example, a value of +1 and a
153.1055 + * <code>endDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
153.1056 + * first Sunday of <code>endMonth</code>. Likewise, +2 would indicate the
153.1057 + * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
153.1058 + * </dd>
153.1059 + * </dl>
153.1060 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1061 + * @serial
153.1062 + */
153.1063 + private int endDay;
153.1064 +
153.1065 + /**
153.1066 + * The day of the week on which daylight saving time ends. This value
153.1067 + * must be between <code>Calendar.SUNDAY</code> and
153.1068 + * <code>Calendar.SATURDAY</code> inclusive.
153.1069 + * <p>If <code>useDaylight</code> is false or
153.1070 + * <code>endMode == DAY_OF_MONTH</code>, this value is ignored.
153.1071 + * @serial
153.1072 + */
153.1073 + private int endDayOfWeek;
153.1074 +
153.1075 + /**
153.1076 + * The time in milliseconds after midnight at which daylight saving
153.1077 + * time ends. This value is expressed as wall time, standard time,
153.1078 + * or UTC time, depending on the setting of <code>endTimeMode</code>.
153.1079 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1080 + * @serial
153.1081 + */
153.1082 + private int endTime;
153.1083 +
153.1084 + /**
153.1085 + * The format of endTime, either <code>WALL_TIME</code>,
153.1086 + * <code>STANDARD_TIME</code>, or <code>UTC_TIME</code>.
153.1087 + * @serial
153.1088 + * @since 1.3
153.1089 + */
153.1090 + private int endTimeMode;
153.1091 +
153.1092 + /**
153.1093 + * The year in which daylight saving time is first observed. This is an {@link GregorianCalendar#AD AD}
153.1094 + * value. If this value is less than 1 then daylight saving time is observed
153.1095 + * for all <code>AD</code> years.
153.1096 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1097 + * @serial
153.1098 + */
153.1099 + private int startYear;
153.1100 +
153.1101 + /**
153.1102 + * The offset in milliseconds between this zone and GMT. Negative offsets
153.1103 + * are to the west of Greenwich. To obtain local <em>standard</em> time,
153.1104 + * add the offset to GMT time. To obtain local wall time it may also be
153.1105 + * necessary to add <code>dstSavings</code>.
153.1106 + * @serial
153.1107 + */
153.1108 + private int rawOffset;
153.1109 +
153.1110 + /**
153.1111 + * A boolean value which is true if and only if this zone uses daylight
153.1112 + * saving time. If this value is false, several other fields are ignored.
153.1113 + * @serial
153.1114 + */
153.1115 + private boolean useDaylight=false; // indicate if this time zone uses DST
153.1116 +
153.1117 + private static final int millisPerHour = 60*60*1000;
153.1118 + private static final int millisPerDay = 24*millisPerHour;
153.1119 +
153.1120 + /**
153.1121 + * This field was serialized in JDK 1.1, so we have to keep it that way
153.1122 + * to maintain serialization compatibility. However, there's no need to
153.1123 + * recreate the array each time we create a new time zone.
153.1124 + * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30,
153.1125 + * 31, 31, 30, 31, 30, 31}. This is ignored as of the Java 2 platform v1.2, however, it must
153.1126 + * be streamed out for compatibility with JDK 1.1.
153.1127 + */
153.1128 + private final byte monthLength[] = staticMonthLength;
153.1129 + private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
153.1130 + private final static byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
153.1131 +
153.1132 + /**
153.1133 + * Variables specifying the mode of the start rule. Takes the following
153.1134 + * values:
153.1135 + * <dl>
153.1136 + * <dt><code>DOM_MODE</code></dt>
153.1137 + * <dd>
153.1138 + * Exact day of week; e.g., March 1.
153.1139 + * </dd>
153.1140 + * <dt><code>DOW_IN_MONTH_MODE</code></dt>
153.1141 + * <dd>
153.1142 + * Day of week in month; e.g., last Sunday in March.
153.1143 + * </dd>
153.1144 + * <dt><code>DOW_GE_DOM_MODE</code></dt>
153.1145 + * <dd>
153.1146 + * Day of week after day of month; e.g., Sunday on or after March 15.
153.1147 + * </dd>
153.1148 + * <dt><code>DOW_LE_DOM_MODE</code></dt>
153.1149 + * <dd>
153.1150 + * Day of week before day of month; e.g., Sunday on or before March 15.
153.1151 + * </dd>
153.1152 + * </dl>
153.1153 + * The setting of this field affects the interpretation of the
153.1154 + * <code>startDay</code> field.
153.1155 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1156 + * @serial
153.1157 + * @since 1.1.4
153.1158 + */
153.1159 + private int startMode;
153.1160 +
153.1161 + /**
153.1162 + * Variables specifying the mode of the end rule. Takes the following
153.1163 + * values:
153.1164 + * <dl>
153.1165 + * <dt><code>DOM_MODE</code></dt>
153.1166 + * <dd>
153.1167 + * Exact day of week; e.g., March 1.
153.1168 + * </dd>
153.1169 + * <dt><code>DOW_IN_MONTH_MODE</code></dt>
153.1170 + * <dd>
153.1171 + * Day of week in month; e.g., last Sunday in March.
153.1172 + * </dd>
153.1173 + * <dt><code>DOW_GE_DOM_MODE</code></dt>
153.1174 + * <dd>
153.1175 + * Day of week after day of month; e.g., Sunday on or after March 15.
153.1176 + * </dd>
153.1177 + * <dt><code>DOW_LE_DOM_MODE</code></dt>
153.1178 + * <dd>
153.1179 + * Day of week before day of month; e.g., Sunday on or before March 15.
153.1180 + * </dd>
153.1181 + * </dl>
153.1182 + * The setting of this field affects the interpretation of the
153.1183 + * <code>endDay</code> field.
153.1184 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1185 + * @serial
153.1186 + * @since 1.1.4
153.1187 + */
153.1188 + private int endMode;
153.1189 +
153.1190 + /**
153.1191 + * A positive value indicating the amount of time saved during DST in
153.1192 + * milliseconds.
153.1193 + * Typically one hour (3600000); sometimes 30 minutes (1800000).
153.1194 + * <p>If <code>useDaylight</code> is false, this value is ignored.
153.1195 + * @serial
153.1196 + * @since 1.1.4
153.1197 + */
153.1198 + private int dstSavings;
153.1199 +
153.1200 + private static final BaseCalendar gcal = new BaseCalendar();//CalendarSystem.getGregorianCalendar();
153.1201 +
153.1202 + /**
153.1203 + * Cache values representing a single period of daylight saving
153.1204 + * time. When the cache values are valid, cacheStart is the start
153.1205 + * time (inclusive) of daylight saving time and cacheEnd is the
153.1206 + * end time (exclusive).
153.1207 + *
153.1208 + * cacheYear has a year value if both cacheStart and cacheEnd are
153.1209 + * in the same year. cacheYear is set to startYear - 1 if
153.1210 + * cacheStart and cacheEnd are in different years. cacheStart is 0
153.1211 + * if the cache values are void. cacheYear is a long to support
153.1212 + * Integer.MIN_VALUE - 1 (JCK requirement).
153.1213 + */
153.1214 + private transient long cacheYear;
153.1215 + private transient long cacheStart;
153.1216 + private transient long cacheEnd;
153.1217 +
153.1218 + /**
153.1219 + * Constants specifying values of startMode and endMode.
153.1220 + */
153.1221 + private static final int DOM_MODE = 1; // Exact day of month, "Mar 1"
153.1222 + private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
153.1223 + private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15"
153.1224 + private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21"
153.1225 +
153.1226 + /**
153.1227 + * Constant for a mode of start or end time specified as wall clock
153.1228 + * time. Wall clock time is standard time for the onset rule, and
153.1229 + * daylight time for the end rule.
153.1230 + * @since 1.4
153.1231 + */
153.1232 + public static final int WALL_TIME = 0; // Zero for backward compatibility
153.1233 +
153.1234 + /**
153.1235 + * Constant for a mode of start or end time specified as standard time.
153.1236 + * @since 1.4
153.1237 + */
153.1238 + public static final int STANDARD_TIME = 1;
153.1239 +
153.1240 + /**
153.1241 + * Constant for a mode of start or end time specified as UTC. European
153.1242 + * Union rules are specified as UTC time, for example.
153.1243 + * @since 1.4
153.1244 + */
153.1245 + public static final int UTC_TIME = 2;
153.1246 +
153.1247 + // Proclaim compatibility with 1.1
153.1248 + static final long serialVersionUID = -403250971215465050L;
153.1249 +
153.1250 + // the internal serial version which says which version was written
153.1251 + // - 0 (default) for version up to JDK 1.1.3
153.1252 + // - 1 for version from JDK 1.1.4, which includes 3 new fields
153.1253 + // - 2 for JDK 1.3, which includes 2 new fields
153.1254 + static final int currentSerialVersion = 2;
153.1255 +
153.1256 + /**
153.1257 + * The version of the serialized data on the stream. Possible values:
153.1258 + * <dl>
153.1259 + * <dt><b>0</b> or not present on stream</dt>
153.1260 + * <dd>
153.1261 + * JDK 1.1.3 or earlier.
153.1262 + * </dd>
153.1263 + * <dt><b>1</b></dt>
153.1264 + * <dd>
153.1265 + * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>,
153.1266 + * <code>endMode</code>, and <code>dstSavings</code>.
153.1267 + * </dd>
153.1268 + * <dt><b>2</b></dt>
153.1269 + * <dd>
153.1270 + * JDK 1.3 or later. Includes two new fields: <code>startTimeMode</code>
153.1271 + * and <code>endTimeMode</code>.
153.1272 + * </dd>
153.1273 + * </dl>
153.1274 + * When streaming out this class, the most recent format
153.1275 + * and the highest allowable <code>serialVersionOnStream</code>
153.1276 + * is written.
153.1277 + * @serial
153.1278 + * @since 1.1.4
153.1279 + */
153.1280 + private int serialVersionOnStream = currentSerialVersion;
153.1281 +
153.1282 + synchronized private void invalidateCache() {
153.1283 + cacheYear = startYear - 1;
153.1284 + cacheStart = cacheEnd = 0;
153.1285 + }
153.1286 +
153.1287 + //----------------------------------------------------------------------
153.1288 + // Rule representation
153.1289 + //
153.1290 + // We represent the following flavors of rules:
153.1291 + // 5 the fifth of the month
153.1292 + // lastSun the last Sunday in the month
153.1293 + // lastMon the last Monday in the month
153.1294 + // Sun>=8 first Sunday on or after the eighth
153.1295 + // Sun<=25 last Sunday on or before the 25th
153.1296 + // This is further complicated by the fact that we need to remain
153.1297 + // backward compatible with the 1.1 FCS. Finally, we need to minimize
153.1298 + // API changes. In order to satisfy these requirements, we support
153.1299 + // three representation systems, and we translate between them.
153.1300 + //
153.1301 + // INTERNAL REPRESENTATION
153.1302 + // This is the format SimpleTimeZone objects take after construction or
153.1303 + // streaming in is complete. Rules are represented directly, using an
153.1304 + // unencoded format. We will discuss the start rule only below; the end
153.1305 + // rule is analogous.
153.1306 + // startMode Takes on enumerated values DAY_OF_MONTH,
153.1307 + // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
153.1308 + // startDay The day of the month, or for DOW_IN_MONTH mode, a
153.1309 + // value indicating which DOW, such as +1 for first,
153.1310 + // +2 for second, -1 for last, etc.
153.1311 + // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
153.1312 + //
153.1313 + // ENCODED REPRESENTATION
153.1314 + // This is the format accepted by the constructor and by setStartRule()
153.1315 + // and setEndRule(). It uses various combinations of positive, negative,
153.1316 + // and zero values to encode the different rules. This representation
153.1317 + // allows us to specify all the different rule flavors without altering
153.1318 + // the API.
153.1319 + // MODE startMonth startDay startDayOfWeek
153.1320 + // DOW_IN_MONTH_MODE >=0 !=0 >0
153.1321 + // DOM_MODE >=0 >0 ==0
153.1322 + // DOW_GE_DOM_MODE >=0 >0 <0
153.1323 + // DOW_LE_DOM_MODE >=0 <0 <0
153.1324 + // (no DST) don't care ==0 don't care
153.1325 + //
153.1326 + // STREAMED REPRESENTATION
153.1327 + // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
153.1328 + // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
153.1329 + // flag useDaylight. When we stream an object out, we translate into an
153.1330 + // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
153.1331 + // and used by 1.1 code. Following that, we write out the full
153.1332 + // representation separately so that contemporary code can recognize and
153.1333 + // parse it. The full representation is written in a "packed" format,
153.1334 + // consisting of a version number, a length, and an array of bytes. Future
153.1335 + // versions of this class may specify different versions. If they wish to
153.1336 + // include additional data, they should do so by storing them after the
153.1337 + // packed representation below.
153.1338 + //----------------------------------------------------------------------
153.1339 +
153.1340 + /**
153.1341 + * Given a set of encoded rules in startDay and startDayOfMonth, decode
153.1342 + * them and set the startMode appropriately. Do the same for endDay and
153.1343 + * endDayOfMonth. Upon entry, the day of week variables may be zero or
153.1344 + * negative, in order to indicate special modes. The day of month
153.1345 + * variables may also be negative. Upon exit, the mode variables will be
153.1346 + * set, and the day of week and day of month variables will be positive.
153.1347 + * This method also recognizes a startDay or endDay of zero as indicating
153.1348 + * no DST.
153.1349 + */
153.1350 + private void decodeRules()
153.1351 + {
153.1352 + decodeStartRule();
153.1353 + decodeEndRule();
153.1354 + }
153.1355 +
153.1356 + /**
153.1357 + * Decode the start rule and validate the parameters. The parameters are
153.1358 + * expected to be in encoded form, which represents the various rule modes
153.1359 + * by negating or zeroing certain values. Representation formats are:
153.1360 + * <p>
153.1361 + * <pre>
153.1362 + * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
153.1363 + * ------------ ----- -------- -------- ----------
153.1364 + * month 0..11 same same same don't care
153.1365 + * day -5..5 1..31 1..31 -1..-31 0
153.1366 + * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
153.1367 + * time 0..ONEDAY same same same don't care
153.1368 + * </pre>
153.1369 + * The range for month does not include UNDECIMBER since this class is
153.1370 + * really specific to GregorianCalendar, which does not use that month.
153.1371 + * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
153.1372 + * end rule is an exclusive limit point. That is, the range of times that
153.1373 + * are in DST include those >= the start and < the end. For this reason,
153.1374 + * it should be possible to specify an end of ONEDAY in order to include the
153.1375 + * entire day. Although this is equivalent to time 0 of the following day,
153.1376 + * it's not always possible to specify that, for example, on December 31.
153.1377 + * While arguably the start range should still be 0..ONEDAY-1, we keep
153.1378 + * the start and end ranges the same for consistency.
153.1379 + */
153.1380 + private void decodeStartRule() {
153.1381 + useDaylight = (startDay != 0) && (endDay != 0);
153.1382 + if (startDay != 0) {
153.1383 + if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
153.1384 + throw new IllegalArgumentException(
153.1385 + "Illegal start month " + startMonth);
153.1386 + }
153.1387 + if (startTime < 0 || startTime > millisPerDay) {
153.1388 + throw new IllegalArgumentException(
153.1389 + "Illegal start time " + startTime);
153.1390 + }
153.1391 + if (startDayOfWeek == 0) {
153.1392 + startMode = DOM_MODE;
153.1393 + } else {
153.1394 + if (startDayOfWeek > 0) {
153.1395 + startMode = DOW_IN_MONTH_MODE;
153.1396 + } else {
153.1397 + startDayOfWeek = -startDayOfWeek;
153.1398 + if (startDay > 0) {
153.1399 + startMode = DOW_GE_DOM_MODE;
153.1400 + } else {
153.1401 + startDay = -startDay;
153.1402 + startMode = DOW_LE_DOM_MODE;
153.1403 + }
153.1404 + }
153.1405 + if (startDayOfWeek > Calendar.SATURDAY) {
153.1406 + throw new IllegalArgumentException(
153.1407 + "Illegal start day of week " + startDayOfWeek);
153.1408 + }
153.1409 + }
153.1410 + if (startMode == DOW_IN_MONTH_MODE) {
153.1411 + if (startDay < -5 || startDay > 5) {
153.1412 + throw new IllegalArgumentException(
153.1413 + "Illegal start day of week in month " + startDay);
153.1414 + }
153.1415 + } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
153.1416 + throw new IllegalArgumentException(
153.1417 + "Illegal start day " + startDay);
153.1418 + }
153.1419 + }
153.1420 + }
153.1421 +
153.1422 + /**
153.1423 + * Decode the end rule and validate the parameters. This method is exactly
153.1424 + * analogous to decodeStartRule().
153.1425 + * @see decodeStartRule
153.1426 + */
153.1427 + private void decodeEndRule() {
153.1428 + useDaylight = (startDay != 0) && (endDay != 0);
153.1429 + if (endDay != 0) {
153.1430 + if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
153.1431 + throw new IllegalArgumentException(
153.1432 + "Illegal end month " + endMonth);
153.1433 + }
153.1434 + if (endTime < 0 || endTime > millisPerDay) {
153.1435 + throw new IllegalArgumentException(
153.1436 + "Illegal end time " + endTime);
153.1437 + }
153.1438 + if (endDayOfWeek == 0) {
153.1439 + endMode = DOM_MODE;
153.1440 + } else {
153.1441 + if (endDayOfWeek > 0) {
153.1442 + endMode = DOW_IN_MONTH_MODE;
153.1443 + } else {
153.1444 + endDayOfWeek = -endDayOfWeek;
153.1445 + if (endDay > 0) {
153.1446 + endMode = DOW_GE_DOM_MODE;
153.1447 + } else {
153.1448 + endDay = -endDay;
153.1449 + endMode = DOW_LE_DOM_MODE;
153.1450 + }
153.1451 + }
153.1452 + if (endDayOfWeek > Calendar.SATURDAY) {
153.1453 + throw new IllegalArgumentException(
153.1454 + "Illegal end day of week " + endDayOfWeek);
153.1455 + }
153.1456 + }
153.1457 + if (endMode == DOW_IN_MONTH_MODE) {
153.1458 + if (endDay < -5 || endDay > 5) {
153.1459 + throw new IllegalArgumentException(
153.1460 + "Illegal end day of week in month " + endDay);
153.1461 + }
153.1462 + } else if (endDay < 1 || endDay > staticMonthLength[endMonth]) {
153.1463 + throw new IllegalArgumentException(
153.1464 + "Illegal end day " + endDay);
153.1465 + }
153.1466 + }
153.1467 + }
153.1468 +
153.1469 + /**
153.1470 + * Make rules compatible to 1.1 FCS code. Since 1.1 FCS code only understands
153.1471 + * day-of-week-in-month rules, we must modify other modes of rules to their
153.1472 + * approximate equivalent in 1.1 FCS terms. This method is used when streaming
153.1473 + * out objects of this class. After it is called, the rules will be modified,
153.1474 + * with a possible loss of information. startMode and endMode will NOT be
153.1475 + * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
153.1476 + * since the rule modification is only intended to be temporary.
153.1477 + */
153.1478 + private void makeRulesCompatible()
153.1479 + {
153.1480 + switch (startMode) {
153.1481 + case DOM_MODE:
153.1482 + startDay = 1 + (startDay / 7);
153.1483 + startDayOfWeek = Calendar.SUNDAY;
153.1484 + break;
153.1485 +
153.1486 + case DOW_GE_DOM_MODE:
153.1487 + // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
153.1488 + // that is, Sun>=1 == firstSun.
153.1489 + if (startDay != 1) {
153.1490 + startDay = 1 + (startDay / 7);
153.1491 + }
153.1492 + break;
153.1493 +
153.1494 + case DOW_LE_DOM_MODE:
153.1495 + if (startDay >= 30) {
153.1496 + startDay = -1;
153.1497 + } else {
153.1498 + startDay = 1 + (startDay / 7);
153.1499 + }
153.1500 + break;
153.1501 + }
153.1502 +
153.1503 + switch (endMode) {
153.1504 + case DOM_MODE:
153.1505 + endDay = 1 + (endDay / 7);
153.1506 + endDayOfWeek = Calendar.SUNDAY;
153.1507 + break;
153.1508 +
153.1509 + case DOW_GE_DOM_MODE:
153.1510 + // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
153.1511 + // that is, Sun>=1 == firstSun.
153.1512 + if (endDay != 1) {
153.1513 + endDay = 1 + (endDay / 7);
153.1514 + }
153.1515 + break;
153.1516 +
153.1517 + case DOW_LE_DOM_MODE:
153.1518 + if (endDay >= 30) {
153.1519 + endDay = -1;
153.1520 + } else {
153.1521 + endDay = 1 + (endDay / 7);
153.1522 + }
153.1523 + break;
153.1524 + }
153.1525 +
153.1526 + /*
153.1527 + * Adjust the start and end times to wall time. This works perfectly
153.1528 + * well unless it pushes into the next or previous day. If that
153.1529 + * happens, we attempt to adjust the day rule somewhat crudely. The day
153.1530 + * rules have been forced into DOW_IN_MONTH mode already, so we change
153.1531 + * the day of week to move forward or back by a day. It's possible to
153.1532 + * make a more refined adjustment of the original rules first, but in
153.1533 + * most cases this extra effort will go to waste once we adjust the day
153.1534 + * rules anyway.
153.1535 + */
153.1536 + switch (startTimeMode) {
153.1537 + case UTC_TIME:
153.1538 + startTime += rawOffset;
153.1539 + break;
153.1540 + }
153.1541 + while (startTime < 0) {
153.1542 + startTime += millisPerDay;
153.1543 + startDayOfWeek = 1 + ((startDayOfWeek+5) % 7); // Back 1 day
153.1544 + }
153.1545 + while (startTime >= millisPerDay) {
153.1546 + startTime -= millisPerDay;
153.1547 + startDayOfWeek = 1 + (startDayOfWeek % 7); // Forward 1 day
153.1548 + }
153.1549 +
153.1550 + switch (endTimeMode) {
153.1551 + case UTC_TIME:
153.1552 + endTime += rawOffset + dstSavings;
153.1553 + break;
153.1554 + case STANDARD_TIME:
153.1555 + endTime += dstSavings;
153.1556 + }
153.1557 + while (endTime < 0) {
153.1558 + endTime += millisPerDay;
153.1559 + endDayOfWeek = 1 + ((endDayOfWeek+5) % 7); // Back 1 day
153.1560 + }
153.1561 + while (endTime >= millisPerDay) {
153.1562 + endTime -= millisPerDay;
153.1563 + endDayOfWeek = 1 + (endDayOfWeek % 7); // Forward 1 day
153.1564 + }
153.1565 + }
153.1566 +
153.1567 + /**
153.1568 + * Pack the start and end rules into an array of bytes. Only pack
153.1569 + * data which is not preserved by makeRulesCompatible.
153.1570 + */
153.1571 + private byte[] packRules()
153.1572 + {
153.1573 + byte[] rules = new byte[6];
153.1574 + rules[0] = (byte)startDay;
153.1575 + rules[1] = (byte)startDayOfWeek;
153.1576 + rules[2] = (byte)endDay;
153.1577 + rules[3] = (byte)endDayOfWeek;
153.1578 +
153.1579 + // As of serial version 2, include time modes
153.1580 + rules[4] = (byte)startTimeMode;
153.1581 + rules[5] = (byte)endTimeMode;
153.1582 +
153.1583 + return rules;
153.1584 + }
153.1585 +
153.1586 + /**
153.1587 + * Given an array of bytes produced by packRules, interpret them
153.1588 + * as the start and end rules.
153.1589 + */
153.1590 + private void unpackRules(byte[] rules)
153.1591 + {
153.1592 + startDay = rules[0];
153.1593 + startDayOfWeek = rules[1];
153.1594 + endDay = rules[2];
153.1595 + endDayOfWeek = rules[3];
153.1596 +
153.1597 + // As of serial version 2, include time modes
153.1598 + if (rules.length >= 6) {
153.1599 + startTimeMode = rules[4];
153.1600 + endTimeMode = rules[5];
153.1601 + }
153.1602 + }
153.1603 +
153.1604 + /**
153.1605 + * Pack the start and end times into an array of bytes. This is required
153.1606 + * as of serial version 2.
153.1607 + */
153.1608 + private int[] packTimes() {
153.1609 + int[] times = new int[2];
153.1610 + times[0] = startTime;
153.1611 + times[1] = endTime;
153.1612 + return times;
153.1613 + }
153.1614 +
153.1615 + /**
153.1616 + * Unpack the start and end times from an array of bytes. This is required
153.1617 + * as of serial version 2.
153.1618 + */
153.1619 + private void unpackTimes(int[] times) {
153.1620 + startTime = times[0];
153.1621 + endTime = times[1];
153.1622 + }
153.1623 +
153.1624 + /**
153.1625 + * Save the state of this object to a stream (i.e., serialize it).
153.1626 + *
153.1627 + * @serialData We write out two formats, a JDK 1.1 compatible format, using
153.1628 + * <code>DOW_IN_MONTH_MODE</code> rules, in the required section, followed
153.1629 + * by the full rules, in packed format, in the optional section. The
153.1630 + * optional section will be ignored by JDK 1.1 code upon stream in.
153.1631 + * <p> Contents of the optional section: The length of a byte array is
153.1632 + * emitted (int); this is 4 as of this release. The byte array of the given
153.1633 + * length is emitted. The contents of the byte array are the true values of
153.1634 + * the fields <code>startDay</code>, <code>startDayOfWeek</code>,
153.1635 + * <code>endDay</code>, and <code>endDayOfWeek</code>. The values of these
153.1636 + * fields in the required section are approximate values suited to the rule
153.1637 + * mode <code>DOW_IN_MONTH_MODE</code>, which is the only mode recognized by
153.1638 + * JDK 1.1.
153.1639 + */
153.1640 + private void writeObject(ObjectOutputStream stream)
153.1641 + throws IOException
153.1642 + {
153.1643 + // Construct a binary rule
153.1644 + byte[] rules = packRules();
153.1645 + int[] times = packTimes();
153.1646 +
153.1647 + // Convert to 1.1 FCS rules. This step may cause us to lose information.
153.1648 + makeRulesCompatible();
153.1649 +
153.1650 + // Write out the 1.1 FCS rules
153.1651 + stream.defaultWriteObject();
153.1652 +
153.1653 + // Write out the binary rules in the optional data area of the stream.
153.1654 + stream.writeInt(rules.length);
153.1655 + stream.write(rules);
153.1656 + stream.writeObject(times);
153.1657 +
153.1658 + // Recover the original rules. This recovers the information lost
153.1659 + // by makeRulesCompatible.
153.1660 + unpackRules(rules);
153.1661 + unpackTimes(times);
153.1662 + }
153.1663 +
153.1664 + /**
153.1665 + * Reconstitute this object from a stream (i.e., deserialize it).
153.1666 + *
153.1667 + * We handle both JDK 1.1
153.1668 + * binary formats and full formats with a packed byte array.
153.1669 + */
153.1670 + private void readObject(ObjectInputStream stream)
153.1671 + throws IOException, ClassNotFoundException
153.1672 + {
153.1673 + stream.defaultReadObject();
153.1674 +
153.1675 + if (serialVersionOnStream < 1) {
153.1676 + // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
153.1677 + // startDayOfWeek and endDayOfWeek were usually uninitialized. We can't do
153.1678 + // too much, so we assume SUNDAY, which actually works most of the time.
153.1679 + if (startDayOfWeek == 0) {
153.1680 + startDayOfWeek = Calendar.SUNDAY;
153.1681 + }
153.1682 + if (endDayOfWeek == 0) {
153.1683 + endDayOfWeek = Calendar.SUNDAY;
153.1684 + }
153.1685 +
153.1686 + // The variables dstSavings, startMode, and endMode are post-1.1, so they
153.1687 + // won't be present if we're reading from a 1.1 stream. Fix them up.
153.1688 + startMode = endMode = DOW_IN_MONTH_MODE;
153.1689 + dstSavings = millisPerHour;
153.1690 + } else {
153.1691 + // For 1.1.4, in addition to the 3 new instance variables, we also
153.1692 + // store the actual rules (which have not be made compatible with 1.1)
153.1693 + // in the optional area. Read them in here and parse them.
153.1694 + int length = stream.readInt();
153.1695 + byte[] rules = new byte[length];
153.1696 + stream.readFully(rules);
153.1697 + unpackRules(rules);
153.1698 + }
153.1699 +
153.1700 + if (serialVersionOnStream >= 2) {
153.1701 + int[] times = (int[]) stream.readObject();
153.1702 + unpackTimes(times);
153.1703 + }
153.1704 +
153.1705 + serialVersionOnStream = currentSerialVersion;
153.1706 + }
153.1707 +
153.1708 + static final class GregorianCalendar {
153.1709 + public static final int BC = 0;
153.1710 +
153.1711 + /**
153.1712 + * Value of the {@link #ERA} field indicating the period before the
153.1713 + * common era, the same value as {@link #BC}.
153.1714 + *
153.1715 + * @see #CE
153.1716 + */
153.1717 + static final int BCE = 0;
153.1718 +
153.1719 + /**
153.1720 + * Value of the <code>ERA</code> field indicating the common era (Anno
153.1721 + * Domini), also known as CE. The sequence of years at the transition
153.1722 + * from <code>BC</code> to <code>AD</code> is ..., 2 BC, 1 BC, 1 AD, 2
153.1723 + * AD,...
153.1724 + *
153.1725 + * @see #ERA
153.1726 + */
153.1727 + public static final int AD = 1;
153.1728 +
153.1729 + // The default value of gregorianCutover.
153.1730 + static final long DEFAULT_GREGORIAN_CUTOVER = -12219292800000L;
153.1731 + /**
153.1732 + * Value of the {@link #ERA} field indicating
153.1733 + * the common era, the same value as {@link #AD}.
153.1734 + *
153.1735 + * @see #BCE
153.1736 + */
153.1737 + static final int CE = 1;
153.1738 +
153.1739 + }
153.1740 +}
154.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
154.2 +++ b/rt/emul/compact/src/main/java/java/util/TimeZone.java Tue Feb 11 13:31:42 2014 +0100
154.3 @@ -0,0 +1,715 @@
154.4 +/*
154.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
154.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
154.7 + *
154.8 + * This code is free software; you can redistribute it and/or modify it
154.9 + * under the terms of the GNU General Public License version 2 only, as
154.10 + * published by the Free Software Foundation. Oracle designates this
154.11 + * particular file as subject to the "Classpath" exception as provided
154.12 + * by Oracle in the LICENSE file that accompanied this code.
154.13 + *
154.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
154.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
154.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
154.17 + * version 2 for more details (a copy is included in the LICENSE file that
154.18 + * accompanied this code).
154.19 + *
154.20 + * You should have received a copy of the GNU General Public License version
154.21 + * 2 along with this work; if not, write to the Free Software Foundation,
154.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
154.23 + *
154.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
154.25 + * or visit www.oracle.com if you need additional information or have any
154.26 + * questions.
154.27 + */
154.28 +
154.29 +/*
154.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
154.31 + * (C) Copyright IBM Corp. 1996 - All Rights Reserved
154.32 + *
154.33 + * The original version of this source code and documentation is copyrighted
154.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
154.35 + * materials are provided under terms of a License Agreement between Taligent
154.36 + * and Sun. This technology is protected by multiple US and International
154.37 + * patents. This notice and attribution to Taligent may not be removed.
154.38 + * Taligent is a registered trademark of Taligent, Inc.
154.39 + *
154.40 + */
154.41 +
154.42 +package java.util;
154.43 +
154.44 +import java.io.Serializable;
154.45 +import java.lang.ref.SoftReference;
154.46 +import java.security.AccessController;
154.47 +import java.security.PrivilegedAction;
154.48 +import java.util.concurrent.ConcurrentHashMap;
154.49 +
154.50 +/**
154.51 + * <code>TimeZone</code> represents a time zone offset, and also figures out daylight
154.52 + * savings.
154.53 + *
154.54 + * <p>
154.55 + * Typically, you get a <code>TimeZone</code> using <code>getDefault</code>
154.56 + * which creates a <code>TimeZone</code> based on the time zone where the program
154.57 + * is running. For example, for a program running in Japan, <code>getDefault</code>
154.58 + * creates a <code>TimeZone</code> object based on Japanese Standard Time.
154.59 + *
154.60 + * <p>
154.61 + * You can also get a <code>TimeZone</code> using <code>getTimeZone</code>
154.62 + * along with a time zone ID. For instance, the time zone ID for the
154.63 + * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a
154.64 + * U.S. Pacific Time <code>TimeZone</code> object with:
154.65 + * <blockquote><pre>
154.66 + * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
154.67 + * </pre></blockquote>
154.68 + * You can use the <code>getAvailableIDs</code> method to iterate through
154.69 + * all the supported time zone IDs. You can then choose a
154.70 + * supported ID to get a <code>TimeZone</code>.
154.71 + * If the time zone you want is not represented by one of the
154.72 + * supported IDs, then a custom time zone ID can be specified to
154.73 + * produce a TimeZone. The syntax of a custom time zone ID is:
154.74 + *
154.75 + * <blockquote><pre>
154.76 + * <a name="CustomID"><i>CustomID:</i></a>
154.77 + * <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
154.78 + * <code>GMT</code> <i>Sign</i> <i>Hours</i> <i>Minutes</i>
154.79 + * <code>GMT</code> <i>Sign</i> <i>Hours</i>
154.80 + * <i>Sign:</i> one of
154.81 + * <code>+ -</code>
154.82 + * <i>Hours:</i>
154.83 + * <i>Digit</i>
154.84 + * <i>Digit</i> <i>Digit</i>
154.85 + * <i>Minutes:</i>
154.86 + * <i>Digit</i> <i>Digit</i>
154.87 + * <i>Digit:</i> one of
154.88 + * <code>0 1 2 3 4 5 6 7 8 9</code>
154.89 + * </pre></blockquote>
154.90 + *
154.91 + * <i>Hours</i> must be between 0 to 23 and <i>Minutes</i> must be
154.92 + * between 00 to 59. For example, "GMT+10" and "GMT+0010" mean ten
154.93 + * hours and ten minutes ahead of GMT, respectively.
154.94 + * <p>
154.95 + * The format is locale independent and digits must be taken from the
154.96 + * Basic Latin block of the Unicode standard. No daylight saving time
154.97 + * transition schedule can be specified with a custom time zone ID. If
154.98 + * the specified string doesn't match the syntax, <code>"GMT"</code>
154.99 + * is used.
154.100 + * <p>
154.101 + * When creating a <code>TimeZone</code>, the specified custom time
154.102 + * zone ID is normalized in the following syntax:
154.103 + * <blockquote><pre>
154.104 + * <a name="NormalizedCustomID"><i>NormalizedCustomID:</i></a>
154.105 + * <code>GMT</code> <i>Sign</i> <i>TwoDigitHours</i> <code>:</code> <i>Minutes</i>
154.106 + * <i>Sign:</i> one of
154.107 + * <code>+ -</code>
154.108 + * <i>TwoDigitHours:</i>
154.109 + * <i>Digit</i> <i>Digit</i>
154.110 + * <i>Minutes:</i>
154.111 + * <i>Digit</i> <i>Digit</i>
154.112 + * <i>Digit:</i> one of
154.113 + * <code>0 1 2 3 4 5 6 7 8 9</code>
154.114 + * </pre></blockquote>
154.115 + * For example, TimeZone.getTimeZone("GMT-8").getID() returns "GMT-08:00".
154.116 + *
154.117 + * <h4>Three-letter time zone IDs</h4>
154.118 + *
154.119 + * For compatibility with JDK 1.1.x, some other three-letter time zone IDs
154.120 + * (such as "PST", "CTT", "AST") are also supported. However, <strong>their
154.121 + * use is deprecated</strong> because the same abbreviation is often used
154.122 + * for multiple time zones (for example, "CST" could be U.S. "Central Standard
154.123 + * Time" and "China Standard Time"), and the Java platform can then only
154.124 + * recognize one of them.
154.125 + *
154.126 + *
154.127 + * @see Calendar
154.128 + * @see GregorianCalendar
154.129 + * @see SimpleTimeZone
154.130 + * @author Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu
154.131 + * @since JDK1.1
154.132 + */
154.133 +abstract public class TimeZone implements Serializable, Cloneable {
154.134 + /**
154.135 + * Sole constructor. (For invocation by subclass constructors, typically
154.136 + * implicit.)
154.137 + */
154.138 + public TimeZone() {
154.139 + }
154.140 +
154.141 + /**
154.142 + * A style specifier for <code>getDisplayName()</code> indicating
154.143 + * a short name, such as "PST."
154.144 + * @see #LONG
154.145 + * @since 1.2
154.146 + */
154.147 + public static final int SHORT = 0;
154.148 +
154.149 + /**
154.150 + * A style specifier for <code>getDisplayName()</code> indicating
154.151 + * a long name, such as "Pacific Standard Time."
154.152 + * @see #SHORT
154.153 + * @since 1.2
154.154 + */
154.155 + public static final int LONG = 1;
154.156 +
154.157 + // Constants used internally; unit is milliseconds
154.158 + private static final int ONE_MINUTE = 60*1000;
154.159 + private static final int ONE_HOUR = 60*ONE_MINUTE;
154.160 + private static final int ONE_DAY = 24*ONE_HOUR;
154.161 +
154.162 + // Proclaim serialization compatibility with JDK 1.1
154.163 + static final long serialVersionUID = 3581463369166924961L;
154.164 +
154.165 + /**
154.166 + * Gets the time zone offset, for current date, modified in case of
154.167 + * daylight savings. This is the offset to add to UTC to get local time.
154.168 + * <p>
154.169 + * This method returns a historically correct offset if an
154.170 + * underlying <code>TimeZone</code> implementation subclass
154.171 + * supports historical Daylight Saving Time schedule and GMT
154.172 + * offset changes.
154.173 + *
154.174 + * @param era the era of the given date.
154.175 + * @param year the year in the given date.
154.176 + * @param month the month in the given date.
154.177 + * Month is 0-based. e.g., 0 for January.
154.178 + * @param day the day-in-month of the given date.
154.179 + * @param dayOfWeek the day-of-week of the given date.
154.180 + * @param milliseconds the milliseconds in day in <em>standard</em>
154.181 + * local time.
154.182 + *
154.183 + * @return the offset in milliseconds to add to GMT to get local time.
154.184 + *
154.185 + * @see Calendar#ZONE_OFFSET
154.186 + * @see Calendar#DST_OFFSET
154.187 + */
154.188 + public abstract int getOffset(int era, int year, int month, int day,
154.189 + int dayOfWeek, int milliseconds);
154.190 +
154.191 + /**
154.192 + * Returns the offset of this time zone from UTC at the specified
154.193 + * date. If Daylight Saving Time is in effect at the specified
154.194 + * date, the offset value is adjusted with the amount of daylight
154.195 + * saving.
154.196 + * <p>
154.197 + * This method returns a historically correct offset value if an
154.198 + * underlying TimeZone implementation subclass supports historical
154.199 + * Daylight Saving Time schedule and GMT offset changes.
154.200 + *
154.201 + * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT
154.202 + * @return the amount of time in milliseconds to add to UTC to get local time.
154.203 + *
154.204 + * @see Calendar#ZONE_OFFSET
154.205 + * @see Calendar#DST_OFFSET
154.206 + * @since 1.4
154.207 + */
154.208 + public int getOffset(long date) {
154.209 + if (inDaylightTime(new Date(date))) {
154.210 + return getRawOffset() + getDSTSavings();
154.211 + }
154.212 + return getRawOffset();
154.213 + }
154.214 +
154.215 + /**
154.216 + * Gets the raw GMT offset and the amount of daylight saving of this
154.217 + * time zone at the given time.
154.218 + * @param date the milliseconds (since January 1, 1970,
154.219 + * 00:00:00.000 GMT) at which the time zone offset and daylight
154.220 + * saving amount are found
154.221 + * @param offset an array of int where the raw GMT offset
154.222 + * (offset[0]) and daylight saving amount (offset[1]) are stored,
154.223 + * or null if those values are not needed. The method assumes that
154.224 + * the length of the given array is two or larger.
154.225 + * @return the total amount of the raw GMT offset and daylight
154.226 + * saving at the specified date.
154.227 + *
154.228 + * @see Calendar#ZONE_OFFSET
154.229 + * @see Calendar#DST_OFFSET
154.230 + */
154.231 + int getOffsets(long date, int[] offsets) {
154.232 + int rawoffset = getRawOffset();
154.233 + int dstoffset = 0;
154.234 + if (inDaylightTime(new Date(date))) {
154.235 + dstoffset = getDSTSavings();
154.236 + }
154.237 + if (offsets != null) {
154.238 + offsets[0] = rawoffset;
154.239 + offsets[1] = dstoffset;
154.240 + }
154.241 + return rawoffset + dstoffset;
154.242 + }
154.243 +
154.244 + /**
154.245 + * Sets the base time zone offset to GMT.
154.246 + * This is the offset to add to UTC to get local time.
154.247 + * <p>
154.248 + * If an underlying <code>TimeZone</code> implementation subclass
154.249 + * supports historical GMT offset changes, the specified GMT
154.250 + * offset is set as the latest GMT offset and the difference from
154.251 + * the known latest GMT offset value is used to adjust all
154.252 + * historical GMT offset values.
154.253 + *
154.254 + * @param offsetMillis the given base time zone offset to GMT.
154.255 + */
154.256 + abstract public void setRawOffset(int offsetMillis);
154.257 +
154.258 + /**
154.259 + * Returns the amount of time in milliseconds to add to UTC to get
154.260 + * standard time in this time zone. Because this value is not
154.261 + * affected by daylight saving time, it is called <I>raw
154.262 + * offset</I>.
154.263 + * <p>
154.264 + * If an underlying <code>TimeZone</code> implementation subclass
154.265 + * supports historical GMT offset changes, the method returns the
154.266 + * raw offset value of the current date. In Honolulu, for example,
154.267 + * its raw offset changed from GMT-10:30 to GMT-10:00 in 1947, and
154.268 + * this method always returns -36000000 milliseconds (i.e., -10
154.269 + * hours).
154.270 + *
154.271 + * @return the amount of raw offset time in milliseconds to add to UTC.
154.272 + * @see Calendar#ZONE_OFFSET
154.273 + */
154.274 + public abstract int getRawOffset();
154.275 +
154.276 + /**
154.277 + * Gets the ID of this time zone.
154.278 + * @return the ID of this time zone.
154.279 + */
154.280 + public String getID()
154.281 + {
154.282 + return ID;
154.283 + }
154.284 +
154.285 + /**
154.286 + * Sets the time zone ID. This does not change any other data in
154.287 + * the time zone object.
154.288 + * @param ID the new time zone ID.
154.289 + */
154.290 + public void setID(String ID)
154.291 + {
154.292 + if (ID == null) {
154.293 + throw new NullPointerException();
154.294 + }
154.295 + this.ID = ID;
154.296 + }
154.297 +
154.298 + /**
154.299 + * Returns a long standard time name of this {@code TimeZone} suitable for
154.300 + * presentation to the user in the default locale.
154.301 + *
154.302 + * <p>This method is equivalent to:
154.303 + * <pre><blockquote>
154.304 + * getDisplayName(false, {@link #LONG},
154.305 + * Locale.getDefault({@link Locale.Category#DISPLAY}))
154.306 + * </blockquote></pre>
154.307 + *
154.308 + * @return the human-readable name of this time zone in the default locale.
154.309 + * @since 1.2
154.310 + * @see #getDisplayName(boolean, int, Locale)
154.311 + * @see Locale#getDefault(Locale.Category)
154.312 + * @see Locale.Category
154.313 + */
154.314 + public final String getDisplayName() {
154.315 + return getDisplayName(false, LONG,
154.316 + Locale.getDefault(Locale.Category.DISPLAY));
154.317 + }
154.318 +
154.319 + /**
154.320 + * Returns a long standard time name of this {@code TimeZone} suitable for
154.321 + * presentation to the user in the specified {@code locale}.
154.322 + *
154.323 + * <p>This method is equivalent to:
154.324 + * <pre><blockquote>
154.325 + * getDisplayName(false, {@link #LONG}, locale)
154.326 + * </blockquote></pre>
154.327 + *
154.328 + * @param locale the locale in which to supply the display name.
154.329 + * @return the human-readable name of this time zone in the given locale.
154.330 + * @exception NullPointerException if {@code locale} is {@code null}.
154.331 + * @since 1.2
154.332 + * @see #getDisplayName(boolean, int, Locale)
154.333 + */
154.334 + public final String getDisplayName(Locale locale) {
154.335 + return getDisplayName(false, LONG, locale);
154.336 + }
154.337 +
154.338 + /**
154.339 + * Returns a name in the specified {@code style} of this {@code TimeZone}
154.340 + * suitable for presentation to the user in the default locale. If the
154.341 + * specified {@code daylight} is {@code true}, a Daylight Saving Time name
154.342 + * is returned (even if this {@code TimeZone} doesn't observe Daylight Saving
154.343 + * Time). Otherwise, a Standard Time name is returned.
154.344 + *
154.345 + * <p>This method is equivalent to:
154.346 + * <pre><blockquote>
154.347 + * getDisplayName(daylight, style,
154.348 + * Locale.getDefault({@link Locale.Category#DISPLAY}))
154.349 + * </blockquote></pre>
154.350 + *
154.351 + * @param daylight {@code true} specifying a Daylight Saving Time name, or
154.352 + * {@code false} specifying a Standard Time name
154.353 + * @param style either {@link #LONG} or {@link #SHORT}
154.354 + * @return the human-readable name of this time zone in the default locale.
154.355 + * @exception IllegalArgumentException if {@code style} is invalid.
154.356 + * @since 1.2
154.357 + * @see #getDisplayName(boolean, int, Locale)
154.358 + * @see Locale#getDefault(Locale.Category)
154.359 + * @see Locale.Category
154.360 + * @see java.text.DateFormatSymbols#getZoneStrings()
154.361 + */
154.362 + public final String getDisplayName(boolean daylight, int style) {
154.363 + return getDisplayName(daylight, style,
154.364 + Locale.getDefault(Locale.Category.DISPLAY));
154.365 + }
154.366 +
154.367 + /**
154.368 + * Returns a name in the specified {@code style} of this {@code TimeZone}
154.369 + * suitable for presentation to the user in the specified {@code
154.370 + * locale}. If the specified {@code daylight} is {@code true}, a Daylight
154.371 + * Saving Time name is returned (even if this {@code TimeZone} doesn't
154.372 + * observe Daylight Saving Time). Otherwise, a Standard Time name is
154.373 + * returned.
154.374 + *
154.375 + * <p>When looking up a time zone name, the {@linkplain
154.376 + * ResourceBundle.Control#getCandidateLocales(String,Locale) default
154.377 + * <code>Locale</code> search path of <code>ResourceBundle</code>} derived
154.378 + * from the specified {@code locale} is used. (No {@linkplain
154.379 + * ResourceBundle.Control#getFallbackLocale(String,Locale) fallback
154.380 + * <code>Locale</code>} search is performed.) If a time zone name in any
154.381 + * {@code Locale} of the search path, including {@link Locale#ROOT}, is
154.382 + * found, the name is returned. Otherwise, a string in the
154.383 + * <a href="#NormalizedCustomID">normalized custom ID format</a> is returned.
154.384 + *
154.385 + * @param daylight {@code true} specifying a Daylight Saving Time name, or
154.386 + * {@code false} specifying a Standard Time name
154.387 + * @param style either {@link #LONG} or {@link #SHORT}
154.388 + * @param locale the locale in which to supply the display name.
154.389 + * @return the human-readable name of this time zone in the given locale.
154.390 + * @exception IllegalArgumentException if {@code style} is invalid.
154.391 + * @exception NullPointerException if {@code locale} is {@code null}.
154.392 + * @since 1.2
154.393 + * @see java.text.DateFormatSymbols#getZoneStrings()
154.394 + */
154.395 + public String getDisplayName(boolean daylight, int style, Locale locale) {
154.396 + if (style != SHORT && style != LONG) {
154.397 + throw new IllegalArgumentException("Illegal style: " + style);
154.398 + }
154.399 +
154.400 + String id = getID();
154.401 + String[] names = getDisplayNames(id, locale);
154.402 + if (names == null) {
154.403 + if (id.startsWith("GMT")) {
154.404 + char sign = id.charAt(3);
154.405 + if (sign == '+' || sign == '-') {
154.406 + return id;
154.407 + }
154.408 + }
154.409 + int offset = getRawOffset();
154.410 + if (daylight) {
154.411 + offset += getDSTSavings();
154.412 + }
154.413 + // return ZoneInfoFile.toCustomID(offset);
154.414 + }
154.415 +
154.416 + int index = daylight ? 3 : 1;
154.417 + if (style == SHORT) {
154.418 + index++;
154.419 + }
154.420 + return names[index];
154.421 + }
154.422 +
154.423 + private static class DisplayNames {
154.424 + // Cache for managing display names per timezone per locale
154.425 + // The structure is:
154.426 + // Map(key=id, value=SoftReference(Map(key=locale, value=displaynames)))
154.427 + private static final Map<String, SoftReference<Map<Locale, String[]>>> CACHE =
154.428 + new ConcurrentHashMap<String, SoftReference<Map<Locale, String[]>>>();
154.429 + }
154.430 +
154.431 + private static final String[] getDisplayNames(String id, Locale locale) {
154.432 + Map<String, SoftReference<Map<Locale, String[]>>> displayNames = DisplayNames.CACHE;
154.433 +
154.434 + SoftReference<Map<Locale, String[]>> ref = displayNames.get(id);
154.435 + if (ref != null) {
154.436 + Map<Locale, String[]> perLocale = ref.get();
154.437 + if (perLocale != null) {
154.438 + String[] names = perLocale.get(locale);
154.439 + if (names != null) {
154.440 + return names;
154.441 + }
154.442 + names = null; // TimeZoneNameUtility.retrieveDisplayNames(id, locale);
154.443 + if (names != null) {
154.444 + perLocale.put(locale, names);
154.445 + }
154.446 + return names;
154.447 + }
154.448 + }
154.449 +
154.450 + String[] names = null; // TimeZoneNameUtility.retrieveDisplayNames(id, locale);
154.451 + if (names != null) {
154.452 + Map<Locale, String[]> perLocale = new ConcurrentHashMap<Locale, String[]>();
154.453 + perLocale.put(locale, names);
154.454 + ref = new SoftReference<Map<Locale, String[]>>(perLocale);
154.455 + displayNames.put(id, ref);
154.456 + }
154.457 + return names;
154.458 + }
154.459 +
154.460 + /**
154.461 + * Returns the amount of time to be added to local standard time
154.462 + * to get local wall clock time.
154.463 + *
154.464 + * <p>The default implementation returns 3600000 milliseconds
154.465 + * (i.e., one hour) if a call to {@link #useDaylightTime()}
154.466 + * returns {@code true}. Otherwise, 0 (zero) is returned.
154.467 + *
154.468 + * <p>If an underlying {@code TimeZone} implementation subclass
154.469 + * supports historical and future Daylight Saving Time schedule
154.470 + * changes, this method returns the amount of saving time of the
154.471 + * last known Daylight Saving Time rule that can be a future
154.472 + * prediction.
154.473 + *
154.474 + * <p>If the amount of saving time at any given time stamp is
154.475 + * required, construct a {@link Calendar} with this {@code
154.476 + * TimeZone} and the time stamp, and call {@link Calendar#get(int)
154.477 + * Calendar.get}{@code (}{@link Calendar#DST_OFFSET}{@code )}.
154.478 + *
154.479 + * @return the amount of saving time in milliseconds
154.480 + * @since 1.4
154.481 + * @see #inDaylightTime(Date)
154.482 + * @see #getOffset(long)
154.483 + * @see #getOffset(int,int,int,int,int,int)
154.484 + * @see Calendar#ZONE_OFFSET
154.485 + */
154.486 + public int getDSTSavings() {
154.487 + if (useDaylightTime()) {
154.488 + return 3600000;
154.489 + }
154.490 + return 0;
154.491 + }
154.492 +
154.493 + /**
154.494 + * Queries if this {@code TimeZone} uses Daylight Saving Time.
154.495 + *
154.496 + * <p>If an underlying {@code TimeZone} implementation subclass
154.497 + * supports historical and future Daylight Saving Time schedule
154.498 + * changes, this method refers to the last known Daylight Saving Time
154.499 + * rule that can be a future prediction and may not be the same as
154.500 + * the current rule. Consider calling {@link #observesDaylightTime()}
154.501 + * if the current rule should also be taken into account.
154.502 + *
154.503 + * @return {@code true} if this {@code TimeZone} uses Daylight Saving Time,
154.504 + * {@code false}, otherwise.
154.505 + * @see #inDaylightTime(Date)
154.506 + * @see Calendar#DST_OFFSET
154.507 + */
154.508 + public abstract boolean useDaylightTime();
154.509 +
154.510 + /**
154.511 + * Returns {@code true} if this {@code TimeZone} is currently in
154.512 + * Daylight Saving Time, or if a transition from Standard Time to
154.513 + * Daylight Saving Time occurs at any future time.
154.514 + *
154.515 + * <p>The default implementation returns {@code true} if
154.516 + * {@code useDaylightTime()} or {@code inDaylightTime(new Date())}
154.517 + * returns {@code true}.
154.518 + *
154.519 + * @return {@code true} if this {@code TimeZone} is currently in
154.520 + * Daylight Saving Time, or if a transition from Standard Time to
154.521 + * Daylight Saving Time occurs at any future time; {@code false}
154.522 + * otherwise.
154.523 + * @since 1.7
154.524 + * @see #useDaylightTime()
154.525 + * @see #inDaylightTime(Date)
154.526 + * @see Calendar#DST_OFFSET
154.527 + */
154.528 + public boolean observesDaylightTime() {
154.529 + return useDaylightTime() || inDaylightTime(new Date());
154.530 + }
154.531 +
154.532 + /**
154.533 + * Queries if the given {@code date} is in Daylight Saving Time in
154.534 + * this time zone.
154.535 + *
154.536 + * @param date the given Date.
154.537 + * @return {@code true} if the given date is in Daylight Saving Time,
154.538 + * {@code false}, otherwise.
154.539 + */
154.540 + abstract public boolean inDaylightTime(Date date);
154.541 +
154.542 + /**
154.543 + * Gets the <code>TimeZone</code> for the given ID.
154.544 + *
154.545 + * @param ID the ID for a <code>TimeZone</code>, either an abbreviation
154.546 + * such as "PST", a full name such as "America/Los_Angeles", or a custom
154.547 + * ID such as "GMT-8:00". Note that the support of abbreviations is
154.548 + * for JDK 1.1.x compatibility only and full names should be used.
154.549 + *
154.550 + * @return the specified <code>TimeZone</code>, or the GMT zone if the given ID
154.551 + * cannot be understood.
154.552 + */
154.553 + public static synchronized TimeZone getTimeZone(String ID) {
154.554 + return getTimeZone(ID, true);
154.555 + }
154.556 +
154.557 + private static TimeZone getTimeZone(String ID, boolean fallback) {
154.558 +// TimeZone tz = ZoneInfo.getTimeZone(ID);
154.559 +// if (tz == null) {
154.560 +// tz = parseCustomTimeZone(ID);
154.561 +// if (tz == null && fallback) {
154.562 +// tz = new ZoneInfo(GMT_ID, 0);
154.563 +// }
154.564 +// }
154.565 +// return tz;
154.566 + return TimeZone.NO_TIMEZONE;
154.567 + }
154.568 +
154.569 + /**
154.570 + * Gets the available IDs according to the given time zone offset in milliseconds.
154.571 + *
154.572 + * @param rawOffset the given time zone GMT offset in milliseconds.
154.573 + * @return an array of IDs, where the time zone for that ID has
154.574 + * the specified GMT offset. For example, "America/Phoenix" and "America/Denver"
154.575 + * both have GMT-07:00, but differ in daylight saving behavior.
154.576 + * @see #getRawOffset()
154.577 + */
154.578 + public static synchronized String[] getAvailableIDs(int rawOffset) {
154.579 + return new String[0];//ZoneInfo.getAvailableIDs(rawOffset);
154.580 + }
154.581 +
154.582 + /**
154.583 + * Gets all the available IDs supported.
154.584 + * @return an array of IDs.
154.585 + */
154.586 + public static synchronized String[] getAvailableIDs() {
154.587 + return new String[0];//return ZoneInfo.getAvailableIDs();
154.588 + }
154.589 +
154.590 + /**
154.591 + * Gets the platform defined TimeZone ID.
154.592 + **/
154.593 + private static native String getSystemTimeZoneID(String javaHome,
154.594 + String country);
154.595 +
154.596 + /**
154.597 + * Gets the custom time zone ID based on the GMT offset of the
154.598 + * platform. (e.g., "GMT+08:00")
154.599 + */
154.600 + private static native String getSystemGMTOffsetID();
154.601 +
154.602 + /**
154.603 + * Gets the default <code>TimeZone</code> for this host.
154.604 + * The source of the default <code>TimeZone</code>
154.605 + * may vary with implementation.
154.606 + * @return a default <code>TimeZone</code>.
154.607 + * @see #setDefault
154.608 + */
154.609 + public static TimeZone getDefault() {
154.610 + return (TimeZone) getDefaultRef().clone();
154.611 + }
154.612 +
154.613 + /**
154.614 + * Returns the reference to the default TimeZone object. This
154.615 + * method doesn't create a clone.
154.616 + */
154.617 + static TimeZone getDefaultRef() {
154.618 + TimeZone defaultZone = null;//defaultZoneTL.get();
154.619 + if (defaultZone == null) {
154.620 + defaultZone = defaultTimeZone;
154.621 + if (defaultZone == null) {
154.622 + // Need to initialize the default time zone.
154.623 + defaultZone = TimeZone.NO_TIMEZONE;
154.624 + assert defaultZone != null;
154.625 + }
154.626 + }
154.627 + // Don't clone here.
154.628 + return defaultZone;
154.629 + }
154.630 +
154.631 + private static boolean hasPermission() {
154.632 + boolean hasPermission = false;
154.633 + return hasPermission;
154.634 + }
154.635 +
154.636 + /**
154.637 + * Sets the <code>TimeZone</code> that is
154.638 + * returned by the <code>getDefault</code> method. If <code>zone</code>
154.639 + * is null, reset the default to the value it had originally when the
154.640 + * VM first started.
154.641 + * @param zone the new default time zone
154.642 + * @see #getDefault
154.643 + */
154.644 + public static void setDefault(TimeZone zone)
154.645 + {
154.646 + if (hasPermission()) {
154.647 + synchronized (TimeZone.class) {
154.648 + defaultTimeZone = zone;
154.649 + // defaultZoneTL.set(null);
154.650 + }
154.651 + } else {
154.652 + //defaultZoneTL.set(zone);
154.653 + }
154.654 + }
154.655 +
154.656 + /**
154.657 + * Returns true if this zone has the same rule and offset as another zone.
154.658 + * That is, if this zone differs only in ID, if at all. Returns false
154.659 + * if the other zone is null.
154.660 + * @param other the <code>TimeZone</code> object to be compared with
154.661 + * @return true if the other zone is not null and is the same as this one,
154.662 + * with the possible exception of the ID
154.663 + * @since 1.2
154.664 + */
154.665 + public boolean hasSameRules(TimeZone other) {
154.666 + return other != null && getRawOffset() == other.getRawOffset() &&
154.667 + useDaylightTime() == other.useDaylightTime();
154.668 + }
154.669 +
154.670 + /**
154.671 + * Creates a copy of this <code>TimeZone</code>.
154.672 + *
154.673 + * @return a clone of this <code>TimeZone</code>
154.674 + */
154.675 + public Object clone()
154.676 + {
154.677 + try {
154.678 + TimeZone other = (TimeZone) super.clone();
154.679 + other.ID = ID;
154.680 + return other;
154.681 + } catch (CloneNotSupportedException e) {
154.682 + throw new InternalError();
154.683 + }
154.684 + }
154.685 +
154.686 + /**
154.687 + * The null constant as a TimeZone.
154.688 + */
154.689 + static final TimeZone NO_TIMEZONE = null;
154.690 +
154.691 + // =======================privates===============================
154.692 +
154.693 + /**
154.694 + * The string identifier of this <code>TimeZone</code>. This is a
154.695 + * programmatic identifier used internally to look up <code>TimeZone</code>
154.696 + * objects from the system table and also to map them to their localized
154.697 + * display names. <code>ID</code> values are unique in the system
154.698 + * table but may not be for dynamically created zones.
154.699 + * @serial
154.700 + */
154.701 + private String ID;
154.702 + private static volatile TimeZone defaultTimeZone;
154.703 +
154.704 + static final String GMT_ID = "GMT";
154.705 + private static final int GMT_ID_LENGTH = 3;
154.706 +
154.707 + /**
154.708 + * Parses a custom time zone identifier and returns a corresponding zone.
154.709 + * This method doesn't support the RFC 822 time zone format. (e.g., +hhmm)
154.710 + *
154.711 + * @param id a string of the <a href="#CustomID">custom ID form</a>.
154.712 + * @return a newly created TimeZone with the given offset and
154.713 + * no daylight saving time, or null if the id cannot be parsed.
154.714 + */
154.715 + private static final TimeZone parseCustomTimeZone(String id) {
154.716 + return null;
154.717 + }
154.718 +}
155.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
155.2 +++ b/rt/emul/compact/src/main/java/java/util/Timer.java Tue Feb 11 13:31:42 2014 +0100
155.3 @@ -0,0 +1,731 @@
155.4 +/*
155.5 + * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
155.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
155.7 + *
155.8 + * This code is free software; you can redistribute it and/or modify it
155.9 + * under the terms of the GNU General Public License version 2 only, as
155.10 + * published by the Free Software Foundation. Oracle designates this
155.11 + * particular file as subject to the "Classpath" exception as provided
155.12 + * by Oracle in the LICENSE file that accompanied this code.
155.13 + *
155.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
155.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
155.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
155.17 + * version 2 for more details (a copy is included in the LICENSE file that
155.18 + * accompanied this code).
155.19 + *
155.20 + * You should have received a copy of the GNU General Public License version
155.21 + * 2 along with this work; if not, write to the Free Software Foundation,
155.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
155.23 + *
155.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
155.25 + * or visit www.oracle.com if you need additional information or have any
155.26 + * questions.
155.27 + */
155.28 +
155.29 +package java.util;
155.30 +import java.util.Date;
155.31 +import java.util.concurrent.atomic.AtomicInteger;
155.32 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
155.33 +
155.34 +/**
155.35 + * A facility for threads to schedule tasks for future execution in a
155.36 + * background thread. Tasks may be scheduled for one-time execution, or for
155.37 + * repeated execution at regular intervals.
155.38 + *
155.39 + * <p>Corresponding to each <tt>Timer</tt> object is a single background
155.40 + * thread that is used to execute all of the timer's tasks, sequentially.
155.41 + * Timer tasks should complete quickly. If a timer task takes excessive time
155.42 + * to complete, it "hogs" the timer's task execution thread. This can, in
155.43 + * turn, delay the execution of subsequent tasks, which may "bunch up" and
155.44 + * execute in rapid succession when (and if) the offending task finally
155.45 + * completes.
155.46 + *
155.47 + * <p>After the last live reference to a <tt>Timer</tt> object goes away
155.48 + * <i>and</i> all outstanding tasks have completed execution, the timer's task
155.49 + * execution thread terminates gracefully (and becomes subject to garbage
155.50 + * collection). However, this can take arbitrarily long to occur. By
155.51 + * default, the task execution thread does not run as a <i>daemon thread</i>,
155.52 + * so it is capable of keeping an application from terminating. If a caller
155.53 + * wants to terminate a timer's task execution thread rapidly, the caller
155.54 + * should invoke the timer's <tt>cancel</tt> method.
155.55 + *
155.56 + * <p>If the timer's task execution thread terminates unexpectedly, for
155.57 + * example, because its <tt>stop</tt> method is invoked, any further
155.58 + * attempt to schedule a task on the timer will result in an
155.59 + * <tt>IllegalStateException</tt>, as if the timer's <tt>cancel</tt>
155.60 + * method had been invoked.
155.61 + *
155.62 + * <p>This class is thread-safe: multiple threads can share a single
155.63 + * <tt>Timer</tt> object without the need for external synchronization.
155.64 + *
155.65 + * <p>This class does <i>not</i> offer real-time guarantees: it schedules
155.66 + * tasks using the <tt>Object.wait(long)</tt> method.
155.67 + *
155.68 + * <p>Java 5.0 introduced the {@code java.util.concurrent} package and
155.69 + * one of the concurrency utilities therein is the {@link
155.70 + * java.util.concurrent.ScheduledThreadPoolExecutor
155.71 + * ScheduledThreadPoolExecutor} which is a thread pool for repeatedly
155.72 + * executing tasks at a given rate or delay. It is effectively a more
155.73 + * versatile replacement for the {@code Timer}/{@code TimerTask}
155.74 + * combination, as it allows multiple service threads, accepts various
155.75 + * time units, and doesn't require subclassing {@code TimerTask} (just
155.76 + * implement {@code Runnable}). Configuring {@code
155.77 + * ScheduledThreadPoolExecutor} with one thread makes it equivalent to
155.78 + * {@code Timer}.
155.79 + *
155.80 + * <p>Implementation note: This class scales to large numbers of concurrently
155.81 + * scheduled tasks (thousands should present no problem). Internally,
155.82 + * it uses a binary heap to represent its task queue, so the cost to schedule
155.83 + * a task is O(log n), where n is the number of concurrently scheduled tasks.
155.84 + *
155.85 + * <p>Implementation note: All constructors start a timer thread.
155.86 + *
155.87 + * @author Josh Bloch
155.88 + * @see TimerTask
155.89 + * @see Object#wait(long)
155.90 + * @since 1.3
155.91 + */
155.92 +
155.93 +public class Timer {
155.94 + /**
155.95 + * The timer task queue. This data structure is shared with the timer
155.96 + * thread. The timer produces tasks, via its various schedule calls,
155.97 + * and the timer thread consumes, executing timer tasks as appropriate,
155.98 + * and removing them from the queue when they're obsolete.
155.99 + */
155.100 + private final TaskQueue queue = new TaskQueue();
155.101 +
155.102 + /**
155.103 + * The timer thread.
155.104 + */
155.105 + private final TimerThread thread = new TimerThread(queue);
155.106 +
155.107 + /**
155.108 + * This object causes the timer's task execution thread to exit
155.109 + * gracefully when there are no live references to the Timer object and no
155.110 + * tasks in the timer queue. It is used in preference to a finalizer on
155.111 + * Timer as such a finalizer would be susceptible to a subclass's
155.112 + * finalizer forgetting to call it.
155.113 + */
155.114 + private final Object threadReaper = new Object() {
155.115 + protected void finalize() throws Throwable {
155.116 + synchronized(queue) {
155.117 + thread.newTasksMayBeScheduled = false;
155.118 + thread.notifyQueue(1); // In case queue is empty.
155.119 + }
155.120 + }
155.121 + };
155.122 +
155.123 + /**
155.124 + * This ID is used to generate thread names.
155.125 + */
155.126 + private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
155.127 + private static int serialNumber() {
155.128 + return nextSerialNumber.getAndIncrement();
155.129 + }
155.130 +
155.131 + /**
155.132 + * Creates a new timer. The associated thread does <i>not</i>
155.133 + * {@linkplain Thread#setDaemon run as a daemon}.
155.134 + */
155.135 + public Timer() {
155.136 + this("Timer-" + serialNumber());
155.137 + }
155.138 +
155.139 + /**
155.140 + * Creates a new timer whose associated thread may be specified to
155.141 + * {@linkplain Thread#setDaemon run as a daemon}.
155.142 + * A daemon thread is called for if the timer will be used to
155.143 + * schedule repeating "maintenance activities", which must be
155.144 + * performed as long as the application is running, but should not
155.145 + * prolong the lifetime of the application.
155.146 + *
155.147 + * @param isDaemon true if the associated thread should run as a daemon.
155.148 + */
155.149 + public Timer(boolean isDaemon) {
155.150 + this("Timer-" + serialNumber(), isDaemon);
155.151 + }
155.152 +
155.153 + /**
155.154 + * Creates a new timer whose associated thread has the specified name.
155.155 + * The associated thread does <i>not</i>
155.156 + * {@linkplain Thread#setDaemon run as a daemon}.
155.157 + *
155.158 + * @param name the name of the associated thread
155.159 + * @throws NullPointerException if {@code name} is null
155.160 + * @since 1.5
155.161 + */
155.162 + public Timer(String name) {
155.163 + }
155.164 +
155.165 + /**
155.166 + * Creates a new timer whose associated thread has the specified name,
155.167 + * and may be specified to
155.168 + * {@linkplain Thread#setDaemon run as a daemon}.
155.169 + *
155.170 + * @param name the name of the associated thread
155.171 + * @param isDaemon true if the associated thread should run as a daemon
155.172 + * @throws NullPointerException if {@code name} is null
155.173 + * @since 1.5
155.174 + */
155.175 + public Timer(String name, boolean isDaemon) {
155.176 + }
155.177 +
155.178 + /**
155.179 + * Schedules the specified task for execution after the specified delay.
155.180 + *
155.181 + * @param task task to be scheduled.
155.182 + * @param delay delay in milliseconds before task is to be executed.
155.183 + * @throws IllegalArgumentException if <tt>delay</tt> is negative, or
155.184 + * <tt>delay + System.currentTimeMillis()</tt> is negative.
155.185 + * @throws IllegalStateException if task was already scheduled or
155.186 + * cancelled, timer was cancelled, or timer thread terminated.
155.187 + * @throws NullPointerException if {@code task} is null
155.188 + */
155.189 + public void schedule(TimerTask task, long delay) {
155.190 + if (delay < 0)
155.191 + throw new IllegalArgumentException("Negative delay.");
155.192 + sched(task, System.currentTimeMillis()+delay, 0);
155.193 + }
155.194 +
155.195 + /**
155.196 + * Schedules the specified task for execution at the specified time. If
155.197 + * the time is in the past, the task is scheduled for immediate execution.
155.198 + *
155.199 + * @param task task to be scheduled.
155.200 + * @param time time at which task is to be executed.
155.201 + * @throws IllegalArgumentException if <tt>time.getTime()</tt> is negative.
155.202 + * @throws IllegalStateException if task was already scheduled or
155.203 + * cancelled, timer was cancelled, or timer thread terminated.
155.204 + * @throws NullPointerException if {@code task} or {@code time} is null
155.205 + */
155.206 + public void schedule(TimerTask task, Date time) {
155.207 + sched(task, time.getTime(), 0);
155.208 + }
155.209 +
155.210 + /**
155.211 + * Schedules the specified task for repeated <i>fixed-delay execution</i>,
155.212 + * beginning after the specified delay. Subsequent executions take place
155.213 + * at approximately regular intervals separated by the specified period.
155.214 + *
155.215 + * <p>In fixed-delay execution, each execution is scheduled relative to
155.216 + * the actual execution time of the previous execution. If an execution
155.217 + * is delayed for any reason (such as garbage collection or other
155.218 + * background activity), subsequent executions will be delayed as well.
155.219 + * In the long run, the frequency of execution will generally be slightly
155.220 + * lower than the reciprocal of the specified period (assuming the system
155.221 + * clock underlying <tt>Object.wait(long)</tt> is accurate).
155.222 + *
155.223 + * <p>Fixed-delay execution is appropriate for recurring activities
155.224 + * that require "smoothness." In other words, it is appropriate for
155.225 + * activities where it is more important to keep the frequency accurate
155.226 + * in the short run than in the long run. This includes most animation
155.227 + * tasks, such as blinking a cursor at regular intervals. It also includes
155.228 + * tasks wherein regular activity is performed in response to human
155.229 + * input, such as automatically repeating a character as long as a key
155.230 + * is held down.
155.231 + *
155.232 + * @param task task to be scheduled.
155.233 + * @param delay delay in milliseconds before task is to be executed.
155.234 + * @param period time in milliseconds between successive task executions.
155.235 + * @throws IllegalArgumentException if {@code delay < 0}, or
155.236 + * {@code delay + System.currentTimeMillis() < 0}, or
155.237 + * {@code period <= 0}
155.238 + * @throws IllegalStateException if task was already scheduled or
155.239 + * cancelled, timer was cancelled, or timer thread terminated.
155.240 + * @throws NullPointerException if {@code task} is null
155.241 + */
155.242 + public void schedule(TimerTask task, long delay, long period) {
155.243 + if (delay < 0)
155.244 + throw new IllegalArgumentException("Negative delay.");
155.245 + if (period <= 0)
155.246 + throw new IllegalArgumentException("Non-positive period.");
155.247 + sched(task, System.currentTimeMillis()+delay, -period);
155.248 + }
155.249 +
155.250 + /**
155.251 + * Schedules the specified task for repeated <i>fixed-delay execution</i>,
155.252 + * beginning at the specified time. Subsequent executions take place at
155.253 + * approximately regular intervals, separated by the specified period.
155.254 + *
155.255 + * <p>In fixed-delay execution, each execution is scheduled relative to
155.256 + * the actual execution time of the previous execution. If an execution
155.257 + * is delayed for any reason (such as garbage collection or other
155.258 + * background activity), subsequent executions will be delayed as well.
155.259 + * In the long run, the frequency of execution will generally be slightly
155.260 + * lower than the reciprocal of the specified period (assuming the system
155.261 + * clock underlying <tt>Object.wait(long)</tt> is accurate). As a
155.262 + * consequence of the above, if the scheduled first time is in the past,
155.263 + * it is scheduled for immediate execution.
155.264 + *
155.265 + * <p>Fixed-delay execution is appropriate for recurring activities
155.266 + * that require "smoothness." In other words, it is appropriate for
155.267 + * activities where it is more important to keep the frequency accurate
155.268 + * in the short run than in the long run. This includes most animation
155.269 + * tasks, such as blinking a cursor at regular intervals. It also includes
155.270 + * tasks wherein regular activity is performed in response to human
155.271 + * input, such as automatically repeating a character as long as a key
155.272 + * is held down.
155.273 + *
155.274 + * @param task task to be scheduled.
155.275 + * @param firstTime First time at which task is to be executed.
155.276 + * @param period time in milliseconds between successive task executions.
155.277 + * @throws IllegalArgumentException if {@code firstTime.getTime() < 0}, or
155.278 + * {@code period <= 0}
155.279 + * @throws IllegalStateException if task was already scheduled or
155.280 + * cancelled, timer was cancelled, or timer thread terminated.
155.281 + * @throws NullPointerException if {@code task} or {@code firstTime} is null
155.282 + */
155.283 + public void schedule(TimerTask task, Date firstTime, long period) {
155.284 + if (period <= 0)
155.285 + throw new IllegalArgumentException("Non-positive period.");
155.286 + sched(task, firstTime.getTime(), -period);
155.287 + }
155.288 +
155.289 + /**
155.290 + * Schedules the specified task for repeated <i>fixed-rate execution</i>,
155.291 + * beginning after the specified delay. Subsequent executions take place
155.292 + * at approximately regular intervals, separated by the specified period.
155.293 + *
155.294 + * <p>In fixed-rate execution, each execution is scheduled relative to the
155.295 + * scheduled execution time of the initial execution. If an execution is
155.296 + * delayed for any reason (such as garbage collection or other background
155.297 + * activity), two or more executions will occur in rapid succession to
155.298 + * "catch up." In the long run, the frequency of execution will be
155.299 + * exactly the reciprocal of the specified period (assuming the system
155.300 + * clock underlying <tt>Object.wait(long)</tt> is accurate).
155.301 + *
155.302 + * <p>Fixed-rate execution is appropriate for recurring activities that
155.303 + * are sensitive to <i>absolute</i> time, such as ringing a chime every
155.304 + * hour on the hour, or running scheduled maintenance every day at a
155.305 + * particular time. It is also appropriate for recurring activities
155.306 + * where the total time to perform a fixed number of executions is
155.307 + * important, such as a countdown timer that ticks once every second for
155.308 + * ten seconds. Finally, fixed-rate execution is appropriate for
155.309 + * scheduling multiple repeating timer tasks that must remain synchronized
155.310 + * with respect to one another.
155.311 + *
155.312 + * @param task task to be scheduled.
155.313 + * @param delay delay in milliseconds before task is to be executed.
155.314 + * @param period time in milliseconds between successive task executions.
155.315 + * @throws IllegalArgumentException if {@code delay < 0}, or
155.316 + * {@code delay + System.currentTimeMillis() < 0}, or
155.317 + * {@code period <= 0}
155.318 + * @throws IllegalStateException if task was already scheduled or
155.319 + * cancelled, timer was cancelled, or timer thread terminated.
155.320 + * @throws NullPointerException if {@code task} is null
155.321 + */
155.322 + public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
155.323 + if (delay < 0)
155.324 + throw new IllegalArgumentException("Negative delay.");
155.325 + if (period <= 0)
155.326 + throw new IllegalArgumentException("Non-positive period.");
155.327 + sched(task, System.currentTimeMillis()+delay, period);
155.328 + }
155.329 +
155.330 + /**
155.331 + * Schedules the specified task for repeated <i>fixed-rate execution</i>,
155.332 + * beginning at the specified time. Subsequent executions take place at
155.333 + * approximately regular intervals, separated by the specified period.
155.334 + *
155.335 + * <p>In fixed-rate execution, each execution is scheduled relative to the
155.336 + * scheduled execution time of the initial execution. If an execution is
155.337 + * delayed for any reason (such as garbage collection or other background
155.338 + * activity), two or more executions will occur in rapid succession to
155.339 + * "catch up." In the long run, the frequency of execution will be
155.340 + * exactly the reciprocal of the specified period (assuming the system
155.341 + * clock underlying <tt>Object.wait(long)</tt> is accurate). As a
155.342 + * consequence of the above, if the scheduled first time is in the past,
155.343 + * then any "missed" executions will be scheduled for immediate "catch up"
155.344 + * execution.
155.345 + *
155.346 + * <p>Fixed-rate execution is appropriate for recurring activities that
155.347 + * are sensitive to <i>absolute</i> time, such as ringing a chime every
155.348 + * hour on the hour, or running scheduled maintenance every day at a
155.349 + * particular time. It is also appropriate for recurring activities
155.350 + * where the total time to perform a fixed number of executions is
155.351 + * important, such as a countdown timer that ticks once every second for
155.352 + * ten seconds. Finally, fixed-rate execution is appropriate for
155.353 + * scheduling multiple repeating timer tasks that must remain synchronized
155.354 + * with respect to one another.
155.355 + *
155.356 + * @param task task to be scheduled.
155.357 + * @param firstTime First time at which task is to be executed.
155.358 + * @param period time in milliseconds between successive task executions.
155.359 + * @throws IllegalArgumentException if {@code firstTime.getTime() < 0} or
155.360 + * {@code period <= 0}
155.361 + * @throws IllegalStateException if task was already scheduled or
155.362 + * cancelled, timer was cancelled, or timer thread terminated.
155.363 + * @throws NullPointerException if {@code task} or {@code firstTime} is null
155.364 + */
155.365 + public void scheduleAtFixedRate(TimerTask task, Date firstTime,
155.366 + long period) {
155.367 + if (period <= 0)
155.368 + throw new IllegalArgumentException("Non-positive period.");
155.369 + sched(task, firstTime.getTime(), period);
155.370 + }
155.371 +
155.372 + /**
155.373 + * Schedule the specified timer task for execution at the specified
155.374 + * time with the specified period, in milliseconds. If period is
155.375 + * positive, the task is scheduled for repeated execution; if period is
155.376 + * zero, the task is scheduled for one-time execution. Time is specified
155.377 + * in Date.getTime() format. This method checks timer state, task state,
155.378 + * and initial execution time, but not period.
155.379 + *
155.380 + * @throws IllegalArgumentException if <tt>time</tt> is negative.
155.381 + * @throws IllegalStateException if task was already scheduled or
155.382 + * cancelled, timer was cancelled, or timer thread terminated.
155.383 + * @throws NullPointerException if {@code task} is null
155.384 + */
155.385 + private void sched(TimerTask task, long time, long period) {
155.386 + if (time < 0)
155.387 + throw new IllegalArgumentException("Illegal execution time.");
155.388 +
155.389 + // Constrain value of period sufficiently to prevent numeric
155.390 + // overflow while still being effectively infinitely large.
155.391 + if (Math.abs(period) > (Long.MAX_VALUE >> 1))
155.392 + period >>= 1;
155.393 +
155.394 + synchronized(queue) {
155.395 + if (!thread.newTasksMayBeScheduled)
155.396 + throw new IllegalStateException("Timer already cancelled.");
155.397 +
155.398 + synchronized(task.lock) {
155.399 + if (task.state != TimerTask.VIRGIN)
155.400 + throw new IllegalStateException(
155.401 + "Task already scheduled or cancelled");
155.402 + task.nextExecutionTime = time;
155.403 + task.period = period;
155.404 + task.state = TimerTask.SCHEDULED;
155.405 + }
155.406 +
155.407 + queue.add(task);
155.408 + if (queue.getMin() == task)
155.409 + thread.notifyQueue(1);
155.410 + }
155.411 + }
155.412 +
155.413 + /**
155.414 + * Terminates this timer, discarding any currently scheduled tasks.
155.415 + * Does not interfere with a currently executing task (if it exists).
155.416 + * Once a timer has been terminated, its execution thread terminates
155.417 + * gracefully, and no more tasks may be scheduled on it.
155.418 + *
155.419 + * <p>Note that calling this method from within the run method of a
155.420 + * timer task that was invoked by this timer absolutely guarantees that
155.421 + * the ongoing task execution is the last task execution that will ever
155.422 + * be performed by this timer.
155.423 + *
155.424 + * <p>This method may be called repeatedly; the second and subsequent
155.425 + * calls have no effect.
155.426 + */
155.427 + public void cancel() {
155.428 + synchronized(queue) {
155.429 + thread.newTasksMayBeScheduled = false;
155.430 + queue.clear();
155.431 + thread.notifyQueue(1); // In case queue was already empty.
155.432 + }
155.433 + }
155.434 +
155.435 + /**
155.436 + * Removes all cancelled tasks from this timer's task queue. <i>Calling
155.437 + * this method has no effect on the behavior of the timer</i>, but
155.438 + * eliminates the references to the cancelled tasks from the queue.
155.439 + * If there are no external references to these tasks, they become
155.440 + * eligible for garbage collection.
155.441 + *
155.442 + * <p>Most programs will have no need to call this method.
155.443 + * It is designed for use by the rare application that cancels a large
155.444 + * number of tasks. Calling this method trades time for space: the
155.445 + * runtime of the method may be proportional to n + c log n, where n
155.446 + * is the number of tasks in the queue and c is the number of cancelled
155.447 + * tasks.
155.448 + *
155.449 + * <p>Note that it is permissible to call this method from within a
155.450 + * a task scheduled on this timer.
155.451 + *
155.452 + * @return the number of tasks removed from the queue.
155.453 + * @since 1.5
155.454 + */
155.455 + public int purge() {
155.456 + int result = 0;
155.457 +
155.458 + synchronized(queue) {
155.459 + for (int i = queue.size(); i > 0; i--) {
155.460 + if (queue.get(i).state == TimerTask.CANCELLED) {
155.461 + queue.quickRemove(i);
155.462 + result++;
155.463 + }
155.464 + }
155.465 +
155.466 + if (result != 0)
155.467 + queue.heapify();
155.468 + }
155.469 +
155.470 + return result;
155.471 + }
155.472 +}
155.473 +
155.474 +/**
155.475 + * This "helper class" implements the timer's task execution thread, which
155.476 + * waits for tasks on the timer queue, executions them when they fire,
155.477 + * reschedules repeating tasks, and removes cancelled tasks and spent
155.478 + * non-repeating tasks from the queue.
155.479 + */
155.480 +class TimerThread implements Runnable {
155.481 + /**
155.482 + * This flag is set to false by the reaper to inform us that there
155.483 + * are no more live references to our Timer object. Once this flag
155.484 + * is true and there are no more tasks in our queue, there is no
155.485 + * work left for us to do, so we terminate gracefully. Note that
155.486 + * this field is protected by queue's monitor!
155.487 + */
155.488 + boolean newTasksMayBeScheduled = true;
155.489 +
155.490 + /**
155.491 + * Our Timer's queue. We store this reference in preference to
155.492 + * a reference to the Timer so the reference graph remains acyclic.
155.493 + * Otherwise, the Timer would never be garbage-collected and this
155.494 + * thread would never go away.
155.495 + */
155.496 + private TaskQueue queue;
155.497 +
155.498 + TimerThread(TaskQueue queue) {
155.499 + this.queue = queue;
155.500 + }
155.501 +
155.502 + void notifyQueue(int delay) {
155.503 + if (delay < 1) {
155.504 + delay = 1;
155.505 + }
155.506 + setTimeout(delay, this);
155.507 + }
155.508 +
155.509 + @JavaScriptBody(args = { "delay", "r" }, body = "window.setTimeout(function() { r.run__V(); }, delay);")
155.510 + private static native void setTimeout(int delay, Runnable r);
155.511 +
155.512 + public void run() {
155.513 + mainLoop(1);
155.514 +// try {
155.515 +// mainLoop(0);
155.516 +// } finally {
155.517 +// // Someone killed this Thread, behave as if Timer cancelled
155.518 +// synchronized(queue) {
155.519 +// newTasksMayBeScheduled = false;
155.520 +// queue.clear(); // Eliminate obsolete references
155.521 +// }
155.522 +// }
155.523 + }
155.524 +
155.525 + /**
155.526 + * The main timer loop. (See class comment.)
155.527 + */
155.528 + private void mainLoop(int inc) {
155.529 + for (int i = 0; i < 1; i += inc) {
155.530 + try {
155.531 + TimerTask task;
155.532 + boolean taskFired;
155.533 + synchronized(queue) {
155.534 + // Wait for queue to become non-empty
155.535 + while (queue.isEmpty() && newTasksMayBeScheduled)
155.536 + break;
155.537 + if (queue.isEmpty())
155.538 + break; // Queue is empty and will forever remain; die
155.539 +
155.540 + // Queue nonempty; look at first evt and do the right thing
155.541 + long currentTime, executionTime;
155.542 + task = queue.getMin();
155.543 + synchronized(task.lock) {
155.544 + if (task.state == TimerTask.CANCELLED) {
155.545 + queue.removeMin();
155.546 + continue; // No action required, poll queue again
155.547 + }
155.548 + currentTime = System.currentTimeMillis();
155.549 + executionTime = task.nextExecutionTime;
155.550 + if (taskFired = (executionTime<=currentTime)) {
155.551 + if (task.period == 0) { // Non-repeating, remove
155.552 + queue.removeMin();
155.553 + task.state = TimerTask.EXECUTED;
155.554 + } else { // Repeating task, reschedule
155.555 + queue.rescheduleMin(
155.556 + task.period<0 ? currentTime - task.period
155.557 + : executionTime + task.period);
155.558 + }
155.559 + }
155.560 + }
155.561 + if (!taskFired) {
155.562 + // Task hasn't yet fired; wait
155.563 + notifyQueue((int)(executionTime - currentTime));
155.564 + return;
155.565 + }
155.566 + }
155.567 + if (taskFired) // Task fired; run it, holding no locks
155.568 + task.run();
155.569 + } catch(Exception e) {
155.570 + e.printStackTrace();
155.571 + }
155.572 + }
155.573 + }
155.574 +}
155.575 +
155.576 +/**
155.577 + * This class represents a timer task queue: a priority queue of TimerTasks,
155.578 + * ordered on nextExecutionTime. Each Timer object has one of these, which it
155.579 + * shares with its TimerThread. Internally this class uses a heap, which
155.580 + * offers log(n) performance for the add, removeMin and rescheduleMin
155.581 + * operations, and constant time performance for the getMin operation.
155.582 + */
155.583 +class TaskQueue {
155.584 + /**
155.585 + * Priority queue represented as a balanced binary heap: the two children
155.586 + * of queue[n] are queue[2*n] and queue[2*n+1]. The priority queue is
155.587 + * ordered on the nextExecutionTime field: The TimerTask with the lowest
155.588 + * nextExecutionTime is in queue[1] (assuming the queue is nonempty). For
155.589 + * each node n in the heap, and each descendant of n, d,
155.590 + * n.nextExecutionTime <= d.nextExecutionTime.
155.591 + */
155.592 + private TimerTask[] queue = new TimerTask[128];
155.593 +
155.594 + /**
155.595 + * The number of tasks in the priority queue. (The tasks are stored in
155.596 + * queue[1] up to queue[size]).
155.597 + */
155.598 + private int size = 0;
155.599 +
155.600 + /**
155.601 + * Returns the number of tasks currently on the queue.
155.602 + */
155.603 + int size() {
155.604 + return size;
155.605 + }
155.606 +
155.607 + /**
155.608 + * Adds a new task to the priority queue.
155.609 + */
155.610 + void add(TimerTask task) {
155.611 + // Grow backing store if necessary
155.612 + if (size + 1 == queue.length)
155.613 + queue = Arrays.copyOf(queue, 2*queue.length);
155.614 +
155.615 + queue[++size] = task;
155.616 + fixUp(size);
155.617 + }
155.618 +
155.619 + /**
155.620 + * Return the "head task" of the priority queue. (The head task is an
155.621 + * task with the lowest nextExecutionTime.)
155.622 + */
155.623 + TimerTask getMin() {
155.624 + return queue[1];
155.625 + }
155.626 +
155.627 + /**
155.628 + * Return the ith task in the priority queue, where i ranges from 1 (the
155.629 + * head task, which is returned by getMin) to the number of tasks on the
155.630 + * queue, inclusive.
155.631 + */
155.632 + TimerTask get(int i) {
155.633 + return queue[i];
155.634 + }
155.635 +
155.636 + /**
155.637 + * Remove the head task from the priority queue.
155.638 + */
155.639 + void removeMin() {
155.640 + queue[1] = queue[size];
155.641 + queue[size--] = null; // Drop extra reference to prevent memory leak
155.642 + fixDown(1);
155.643 + }
155.644 +
155.645 + /**
155.646 + * Removes the ith element from queue without regard for maintaining
155.647 + * the heap invariant. Recall that queue is one-based, so
155.648 + * 1 <= i <= size.
155.649 + */
155.650 + void quickRemove(int i) {
155.651 + assert i <= size;
155.652 +
155.653 + queue[i] = queue[size];
155.654 + queue[size--] = null; // Drop extra ref to prevent memory leak
155.655 + }
155.656 +
155.657 + /**
155.658 + * Sets the nextExecutionTime associated with the head task to the
155.659 + * specified value, and adjusts priority queue accordingly.
155.660 + */
155.661 + void rescheduleMin(long newTime) {
155.662 + queue[1].nextExecutionTime = newTime;
155.663 + fixDown(1);
155.664 + }
155.665 +
155.666 + /**
155.667 + * Returns true if the priority queue contains no elements.
155.668 + */
155.669 + boolean isEmpty() {
155.670 + return size==0;
155.671 + }
155.672 +
155.673 + /**
155.674 + * Removes all elements from the priority queue.
155.675 + */
155.676 + void clear() {
155.677 + // Null out task references to prevent memory leak
155.678 + for (int i=1; i<=size; i++)
155.679 + queue[i] = null;
155.680 +
155.681 + size = 0;
155.682 + }
155.683 +
155.684 + /**
155.685 + * Establishes the heap invariant (described above) assuming the heap
155.686 + * satisfies the invariant except possibly for the leaf-node indexed by k
155.687 + * (which may have a nextExecutionTime less than its parent's).
155.688 + *
155.689 + * This method functions by "promoting" queue[k] up the hierarchy
155.690 + * (by swapping it with its parent) repeatedly until queue[k]'s
155.691 + * nextExecutionTime is greater than or equal to that of its parent.
155.692 + */
155.693 + private void fixUp(int k) {
155.694 + while (k > 1) {
155.695 + int j = k >> 1;
155.696 + if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
155.697 + break;
155.698 + TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
155.699 + k = j;
155.700 + }
155.701 + }
155.702 +
155.703 + /**
155.704 + * Establishes the heap invariant (described above) in the subtree
155.705 + * rooted at k, which is assumed to satisfy the heap invariant except
155.706 + * possibly for node k itself (which may have a nextExecutionTime greater
155.707 + * than its children's).
155.708 + *
155.709 + * This method functions by "demoting" queue[k] down the hierarchy
155.710 + * (by swapping it with its smaller child) repeatedly until queue[k]'s
155.711 + * nextExecutionTime is less than or equal to those of its children.
155.712 + */
155.713 + private void fixDown(int k) {
155.714 + int j;
155.715 + while ((j = k << 1) <= size && j > 0) {
155.716 + if (j < size &&
155.717 + queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
155.718 + j++; // j indexes smallest kid
155.719 + if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
155.720 + break;
155.721 + TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
155.722 + k = j;
155.723 + }
155.724 + }
155.725 +
155.726 + /**
155.727 + * Establishes the heap invariant (described above) in the entire tree,
155.728 + * assuming nothing about the order of the elements prior to the call.
155.729 + */
155.730 + void heapify() {
155.731 + for (int i = size/2; i >= 1; i--)
155.732 + fixDown(i);
155.733 + }
155.734 +}
156.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
156.2 +++ b/rt/emul/compact/src/main/java/java/util/TimerTask.java Tue Feb 11 13:31:42 2014 +0100
156.3 @@ -0,0 +1,158 @@
156.4 +/*
156.5 + * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved.
156.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
156.7 + *
156.8 + * This code is free software; you can redistribute it and/or modify it
156.9 + * under the terms of the GNU General Public License version 2 only, as
156.10 + * published by the Free Software Foundation. Oracle designates this
156.11 + * particular file as subject to the "Classpath" exception as provided
156.12 + * by Oracle in the LICENSE file that accompanied this code.
156.13 + *
156.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
156.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
156.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
156.17 + * version 2 for more details (a copy is included in the LICENSE file that
156.18 + * accompanied this code).
156.19 + *
156.20 + * You should have received a copy of the GNU General Public License version
156.21 + * 2 along with this work; if not, write to the Free Software Foundation,
156.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
156.23 + *
156.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
156.25 + * or visit www.oracle.com if you need additional information or have any
156.26 + * questions.
156.27 + */
156.28 +
156.29 +package java.util;
156.30 +
156.31 +/**
156.32 + * A task that can be scheduled for one-time or repeated execution by a Timer.
156.33 + *
156.34 + * @author Josh Bloch
156.35 + * @see Timer
156.36 + * @since 1.3
156.37 + */
156.38 +
156.39 +public abstract class TimerTask implements Runnable {
156.40 + /**
156.41 + * This object is used to control access to the TimerTask internals.
156.42 + */
156.43 + final Object lock = new Object();
156.44 +
156.45 + /**
156.46 + * The state of this task, chosen from the constants below.
156.47 + */
156.48 + int state = VIRGIN;
156.49 +
156.50 + /**
156.51 + * This task has not yet been scheduled.
156.52 + */
156.53 + static final int VIRGIN = 0;
156.54 +
156.55 + /**
156.56 + * This task is scheduled for execution. If it is a non-repeating task,
156.57 + * it has not yet been executed.
156.58 + */
156.59 + static final int SCHEDULED = 1;
156.60 +
156.61 + /**
156.62 + * This non-repeating task has already executed (or is currently
156.63 + * executing) and has not been cancelled.
156.64 + */
156.65 + static final int EXECUTED = 2;
156.66 +
156.67 + /**
156.68 + * This task has been cancelled (with a call to TimerTask.cancel).
156.69 + */
156.70 + static final int CANCELLED = 3;
156.71 +
156.72 + /**
156.73 + * Next execution time for this task in the format returned by
156.74 + * System.currentTimeMillis, assuming this task is scheduled for execution.
156.75 + * For repeating tasks, this field is updated prior to each task execution.
156.76 + */
156.77 + long nextExecutionTime;
156.78 +
156.79 + /**
156.80 + * Period in milliseconds for repeating tasks. A positive value indicates
156.81 + * fixed-rate execution. A negative value indicates fixed-delay execution.
156.82 + * A value of 0 indicates a non-repeating task.
156.83 + */
156.84 + long period = 0;
156.85 +
156.86 + /**
156.87 + * Creates a new timer task.
156.88 + */
156.89 + protected TimerTask() {
156.90 + }
156.91 +
156.92 + /**
156.93 + * The action to be performed by this timer task.
156.94 + */
156.95 + public abstract void run();
156.96 +
156.97 + /**
156.98 + * Cancels this timer task. If the task has been scheduled for one-time
156.99 + * execution and has not yet run, or has not yet been scheduled, it will
156.100 + * never run. If the task has been scheduled for repeated execution, it
156.101 + * will never run again. (If the task is running when this call occurs,
156.102 + * the task will run to completion, but will never run again.)
156.103 + *
156.104 + * <p>Note that calling this method from within the <tt>run</tt> method of
156.105 + * a repeating timer task absolutely guarantees that the timer task will
156.106 + * not run again.
156.107 + *
156.108 + * <p>This method may be called repeatedly; the second and subsequent
156.109 + * calls have no effect.
156.110 + *
156.111 + * @return true if this task is scheduled for one-time execution and has
156.112 + * not yet run, or this task is scheduled for repeated execution.
156.113 + * Returns false if the task was scheduled for one-time execution
156.114 + * and has already run, or if the task was never scheduled, or if
156.115 + * the task was already cancelled. (Loosely speaking, this method
156.116 + * returns <tt>true</tt> if it prevents one or more scheduled
156.117 + * executions from taking place.)
156.118 + */
156.119 + public boolean cancel() {
156.120 + synchronized(lock) {
156.121 + boolean result = (state == SCHEDULED);
156.122 + state = CANCELLED;
156.123 + return result;
156.124 + }
156.125 + }
156.126 +
156.127 + /**
156.128 + * Returns the <i>scheduled</i> execution time of the most recent
156.129 + * <i>actual</i> execution of this task. (If this method is invoked
156.130 + * while task execution is in progress, the return value is the scheduled
156.131 + * execution time of the ongoing task execution.)
156.132 + *
156.133 + * <p>This method is typically invoked from within a task's run method, to
156.134 + * determine whether the current execution of the task is sufficiently
156.135 + * timely to warrant performing the scheduled activity:
156.136 + * <pre>
156.137 + * public void run() {
156.138 + * if (System.currentTimeMillis() - scheduledExecutionTime() >=
156.139 + * MAX_TARDINESS)
156.140 + * return; // Too late; skip this execution.
156.141 + * // Perform the task
156.142 + * }
156.143 + * </pre>
156.144 + * This method is typically <i>not</i> used in conjunction with
156.145 + * <i>fixed-delay execution</i> repeating tasks, as their scheduled
156.146 + * execution times are allowed to drift over time, and so are not terribly
156.147 + * significant.
156.148 + *
156.149 + * @return the time at which the most recent execution of this task was
156.150 + * scheduled to occur, in the format returned by Date.getTime().
156.151 + * The return value is undefined if the task has yet to commence
156.152 + * its first execution.
156.153 + * @see Date#getTime()
156.154 + */
156.155 + public long scheduledExecutionTime() {
156.156 + synchronized(lock) {
156.157 + return (period < 0 ? nextExecutionTime + period
156.158 + : nextExecutionTime - period);
156.159 + }
156.160 + }
156.161 +}
157.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
157.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/ConcurrentHashMap.java Tue Feb 11 13:31:42 2014 +0100
157.3 @@ -0,0 +1,327 @@
157.4 +/*
157.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
157.6 + *
157.7 + * This code is free software; you can redistribute it and/or modify it
157.8 + * under the terms of the GNU General Public License version 2 only, as
157.9 + * published by the Free Software Foundation. Oracle designates this
157.10 + * particular file as subject to the "Classpath" exception as provided
157.11 + * by Oracle in the LICENSE file that accompanied this code.
157.12 + *
157.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
157.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
157.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
157.16 + * version 2 for more details (a copy is included in the LICENSE file that
157.17 + * accompanied this code).
157.18 + *
157.19 + * You should have received a copy of the GNU General Public License version
157.20 + * 2 along with this work; if not, write to the Free Software Foundation,
157.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
157.22 + *
157.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
157.24 + * or visit www.oracle.com if you need additional information or have any
157.25 + * questions.
157.26 + */
157.27 +
157.28 +/*
157.29 + * This file is available under and governed by the GNU General Public
157.30 + * License version 2 only, as published by the Free Software Foundation.
157.31 + * However, the following notice accompanied the original version of this
157.32 + * file:
157.33 + *
157.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
157.35 + * Expert Group and released to the public domain, as explained at
157.36 + * http://creativecommons.org/publicdomain/zero/1.0/
157.37 + */
157.38 +package java.util.concurrent;
157.39 +
157.40 +import java.util.*;
157.41 +import java.io.Serializable;
157.42 +import java.io.IOException;
157.43 +import java.io.ObjectInputStream;
157.44 +import java.io.ObjectOutputStream;
157.45 +
157.46 +/**
157.47 + * A hash table supporting full concurrency of retrievals and adjustable
157.48 + * expected concurrency for updates. This class obeys the same functional
157.49 + * specification as {@link java.util.Hashtable}, and includes versions of
157.50 + * methods corresponding to each method of
157.51 + * <tt>Hashtable</tt>. However, even though all operations are thread-safe,
157.52 + * retrieval operations do <em>not</em> entail locking, and there is
157.53 + * <em>not</em> any support for locking the entire table in a way that prevents
157.54 + * all access. This class is fully interoperable with <tt>Hashtable</tt> in
157.55 + * programs that rely on its thread safety but not on its synchronization
157.56 + * details.
157.57 + *
157.58 + * <p>
157.59 + * Retrieval operations (including <tt>get</tt>) generally do not block, so may
157.60 + * overlap with update operations (including
157.61 + * <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results of the most
157.62 + * recently <em>completed</em> update operations holding upon their onset. For
157.63 + * aggregate operations such as <tt>putAll</tt>
157.64 + * and <tt>clear</tt>, concurrent retrievals may reflect insertion or removal of
157.65 + * only some entries. Similarly, Iterators and Enumerations return elements
157.66 + * reflecting the state of the hash table at some point at or since the creation
157.67 + * of the iterator/enumeration. They do <em>not</em> throw
157.68 + * {@link ConcurrentModificationException}. However, iterators are designed to
157.69 + * be used by only one thread at a time.
157.70 + *
157.71 + * <p>
157.72 + * The allowed concurrency among update operations is guided by the optional
157.73 + * <tt>concurrencyLevel</tt> constructor argument (default <tt>16</tt>), which
157.74 + * is used as a hint for internal sizing. The table is internally partitioned to
157.75 + * try to permit the indicated number of concurrent updates without contention.
157.76 + * Because placement in hash tables is essentially random, the actual
157.77 + * concurrency will vary. Ideally, you should choose a value to accommodate as
157.78 + * many threads as will ever concurrently modify the table. Using a
157.79 + * significantly higher value than you need can waste space and time, and a
157.80 + * significantly lower value can lead to thread contention. But overestimates
157.81 + * and underestimates within an order of magnitude do not usually have much
157.82 + * noticeable impact. A value of one is appropriate when it is known that only
157.83 + * one thread will modify and all others will only read. Also, resizing this or
157.84 + * any other kind of hash table is a relatively slow operation, so, when
157.85 + * possible, it is a good idea to provide estimates of expected table sizes in
157.86 + * constructors.
157.87 + *
157.88 + * <p>
157.89 + * This class and its views and iterators implement all of the
157.90 + * <em>optional</em> methods of the {@link Map} and {@link Iterator} interfaces.
157.91 + *
157.92 + * <p>
157.93 + * Like {@link Hashtable} but unlike {@link HashMap}, this class does
157.94 + * <em>not</em> allow <tt>null</tt> to be used as a key or value.
157.95 + *
157.96 + * <p>
157.97 + * This class is a member of the
157.98 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
157.99 + * Java Collections Framework</a>.
157.100 + *
157.101 + * @since 1.5
157.102 + * @author Doug Lea
157.103 + * @param <K> the type of keys maintained by this map
157.104 + * @param <V> the type of mapped values
157.105 + */
157.106 +public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
157.107 + implements ConcurrentMap<K, V>, Serializable {
157.108 +
157.109 + private static final long serialVersionUID = 7249069246763182397L;
157.110 + /**
157.111 + * The default initial capacity for this table,
157.112 + * used when not otherwise specified in a constructor.
157.113 + */
157.114 + static final int DEFAULT_INITIAL_CAPACITY = 16;
157.115 +
157.116 + /**
157.117 + * The default load factor for this table, used when not
157.118 + * otherwise specified in a constructor.
157.119 + */
157.120 + static final float DEFAULT_LOAD_FACTOR = 0.75f;
157.121 +
157.122 + /**
157.123 + * The default concurrency level for this table, used when not
157.124 + * otherwise specified in a constructor.
157.125 + */
157.126 + static final int DEFAULT_CONCURRENCY_LEVEL = 16;
157.127 +
157.128 + private final Map<K, V> delegate;
157.129 +
157.130 +
157.131 + /**
157.132 + * Creates a new, empty map with the specified initial
157.133 + * capacity, load factor and concurrency level.
157.134 + *
157.135 + * @param initialCapacity the initial capacity. The implementation
157.136 + * performs internal sizing to accommodate this many elements.
157.137 + * @param loadFactor the load factor threshold, used to control resizing.
157.138 + * Resizing may be performed when the average number of elements per
157.139 + * bin exceeds this threshold.
157.140 + * @param concurrencyLevel the estimated number of concurrently
157.141 + * updating threads. The implementation performs internal sizing
157.142 + * to try to accommodate this many threads.
157.143 + * @throws IllegalArgumentException if the initial capacity is
157.144 + * negative or the load factor or concurrencyLevel are
157.145 + * nonpositive.
157.146 + */
157.147 + @SuppressWarnings("unchecked")
157.148 + public ConcurrentHashMap(int initialCapacity,
157.149 + float loadFactor, int concurrencyLevel) {
157.150 + if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
157.151 + throw new IllegalArgumentException();
157.152 + delegate = new HashMap<>(initialCapacity, loadFactor);
157.153 + }
157.154 +
157.155 + /**
157.156 + * Creates a new, empty map with the specified initial capacity
157.157 + * and load factor and with the default concurrencyLevel (16).
157.158 + *
157.159 + * @param initialCapacity The implementation performs internal
157.160 + * sizing to accommodate this many elements.
157.161 + * @param loadFactor the load factor threshold, used to control resizing.
157.162 + * Resizing may be performed when the average number of elements per
157.163 + * bin exceeds this threshold.
157.164 + * @throws IllegalArgumentException if the initial capacity of
157.165 + * elements is negative or the load factor is nonpositive
157.166 + *
157.167 + * @since 1.6
157.168 + */
157.169 + public ConcurrentHashMap(int initialCapacity, float loadFactor) {
157.170 + this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
157.171 + }
157.172 +
157.173 + /**
157.174 + * Creates a new, empty map with the specified initial capacity,
157.175 + * and with default load factor (0.75) and concurrencyLevel (16).
157.176 + *
157.177 + * @param initialCapacity the initial capacity. The implementation
157.178 + * performs internal sizing to accommodate this many elements.
157.179 + * @throws IllegalArgumentException if the initial capacity of
157.180 + * elements is negative.
157.181 + */
157.182 + public ConcurrentHashMap(int initialCapacity) {
157.183 + this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
157.184 + }
157.185 +
157.186 + /**
157.187 + * Creates a new, empty map with a default initial capacity (16),
157.188 + * load factor (0.75) and concurrencyLevel (16).
157.189 + */
157.190 + public ConcurrentHashMap() {
157.191 + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
157.192 + }
157.193 +
157.194 + /**
157.195 + * Creates a new map with the same mappings as the given map.
157.196 + * The map is created with a capacity of 1.5 times the number
157.197 + * of mappings in the given map or 16 (whichever is greater),
157.198 + * and a default load factor (0.75) and concurrencyLevel (16).
157.199 + *
157.200 + * @param m the map
157.201 + */
157.202 + public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
157.203 + this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
157.204 + DEFAULT_INITIAL_CAPACITY),
157.205 + DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
157.206 + putAll(m);
157.207 + }
157.208 +
157.209 +
157.210 + @Override
157.211 + public int size() {
157.212 + return delegate.size();
157.213 + }
157.214 +
157.215 + @Override
157.216 + public boolean isEmpty() {
157.217 + return delegate.isEmpty();
157.218 + }
157.219 +
157.220 + @Override
157.221 + public boolean containsKey(Object key) {
157.222 + return delegate.containsKey(key);
157.223 + }
157.224 +
157.225 + @Override
157.226 + public boolean containsValue(Object value) {
157.227 + return delegate.containsValue(value);
157.228 + }
157.229 +
157.230 + @Override
157.231 + public V get(Object key) {
157.232 + return delegate.get(key);
157.233 + }
157.234 +
157.235 + @Override
157.236 + public V put(K key, V value) {
157.237 + return delegate.put(key, value);
157.238 + }
157.239 +
157.240 + @Override
157.241 + public V remove(Object key) {
157.242 + return delegate.remove(key);
157.243 + }
157.244 +
157.245 + @Override
157.246 + public void putAll(Map<? extends K, ? extends V> m) {
157.247 + delegate.putAll(m);
157.248 + }
157.249 +
157.250 + @Override
157.251 + public void clear() {
157.252 + delegate.clear();
157.253 + }
157.254 +
157.255 + @Override
157.256 + public Set<K> keySet() {
157.257 + return delegate.keySet();
157.258 + }
157.259 +
157.260 + @Override
157.261 + public Collection<V> values() {
157.262 + return delegate.values();
157.263 + }
157.264 +
157.265 + @Override
157.266 + public Set<Entry<K, V>> entrySet() {
157.267 + return delegate.entrySet();
157.268 + }
157.269 +
157.270 + @Override
157.271 + public boolean equals(Object o) {
157.272 + return delegate.equals(o);
157.273 + }
157.274 +
157.275 + @Override
157.276 + public int hashCode() {
157.277 + return delegate.hashCode();
157.278 + }
157.279 +
157.280 + @Override
157.281 + public String toString() {
157.282 + return delegate.toString();
157.283 + }
157.284 +
157.285 + @Override
157.286 + public V putIfAbsent(K key, V value) {
157.287 + V old = delegate.get(key);
157.288 + if (old == null) {
157.289 + return delegate.put(key, value);
157.290 + }
157.291 + return old;
157.292 + }
157.293 +
157.294 + @Override
157.295 + public boolean remove(Object key, Object value) {
157.296 + if (equals(value, delegate.get(key))) {
157.297 + delegate.remove(key);
157.298 + return true;
157.299 + } else {
157.300 + return false;
157.301 + }
157.302 + }
157.303 +
157.304 + @Override
157.305 + public boolean replace(K key, V oldValue, V newValue) {
157.306 + if (equals(oldValue, delegate.get(key))) {
157.307 + delegate.put(key, newValue);
157.308 + return true;
157.309 + } else {
157.310 + return false;
157.311 + }
157.312 + }
157.313 +
157.314 + @Override
157.315 + public V replace(K key, V value) {
157.316 + if (delegate.containsKey(key)) {
157.317 + return delegate.put(key, value);
157.318 + } else {
157.319 + return null;
157.320 + }
157.321 + }
157.322 +
157.323 + private static boolean equals(Object a, Object b) {
157.324 + if (a == null) {
157.325 + return b == null;
157.326 + } else {
157.327 + return a.equals(b);
157.328 + }
157.329 + }
157.330 +}
158.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
158.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/ConcurrentMap.java Tue Feb 11 13:31:42 2014 +0100
158.3 @@ -0,0 +1,165 @@
158.4 +/*
158.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
158.6 + *
158.7 + * This code is free software; you can redistribute it and/or modify it
158.8 + * under the terms of the GNU General Public License version 2 only, as
158.9 + * published by the Free Software Foundation. Oracle designates this
158.10 + * particular file as subject to the "Classpath" exception as provided
158.11 + * by Oracle in the LICENSE file that accompanied this code.
158.12 + *
158.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
158.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
158.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
158.16 + * version 2 for more details (a copy is included in the LICENSE file that
158.17 + * accompanied this code).
158.18 + *
158.19 + * You should have received a copy of the GNU General Public License version
158.20 + * 2 along with this work; if not, write to the Free Software Foundation,
158.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
158.22 + *
158.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
158.24 + * or visit www.oracle.com if you need additional information or have any
158.25 + * questions.
158.26 + */
158.27 +
158.28 +/*
158.29 + * This file is available under and governed by the GNU General Public
158.30 + * License version 2 only, as published by the Free Software Foundation.
158.31 + * However, the following notice accompanied the original version of this
158.32 + * file:
158.33 + *
158.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
158.35 + * Expert Group and released to the public domain, as explained at
158.36 + * http://creativecommons.org/publicdomain/zero/1.0/
158.37 + */
158.38 +
158.39 +package java.util.concurrent;
158.40 +import java.util.Map;
158.41 +
158.42 +/**
158.43 + * A {@link java.util.Map} providing additional atomic
158.44 + * <tt>putIfAbsent</tt>, <tt>remove</tt>, and <tt>replace</tt> methods.
158.45 + *
158.46 + * <p>Memory consistency effects: As with other concurrent
158.47 + * collections, actions in a thread prior to placing an object into a
158.48 + * {@code ConcurrentMap} as a key or value
158.49 + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
158.50 + * actions subsequent to the access or removal of that object from
158.51 + * the {@code ConcurrentMap} in another thread.
158.52 + *
158.53 + * <p>This interface is a member of the
158.54 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
158.55 + * Java Collections Framework</a>.
158.56 + *
158.57 + * @since 1.5
158.58 + * @author Doug Lea
158.59 + * @param <K> the type of keys maintained by this map
158.60 + * @param <V> the type of mapped values
158.61 + */
158.62 +public interface ConcurrentMap<K, V> extends Map<K, V> {
158.63 + /**
158.64 + * If the specified key is not already associated
158.65 + * with a value, associate it with the given value.
158.66 + * This is equivalent to
158.67 + * <pre>
158.68 + * if (!map.containsKey(key))
158.69 + * return map.put(key, value);
158.70 + * else
158.71 + * return map.get(key);</pre>
158.72 + * except that the action is performed atomically.
158.73 + *
158.74 + * @param key key with which the specified value is to be associated
158.75 + * @param value value to be associated with the specified key
158.76 + * @return the previous value associated with the specified key, or
158.77 + * <tt>null</tt> if there was no mapping for the key.
158.78 + * (A <tt>null</tt> return can also indicate that the map
158.79 + * previously associated <tt>null</tt> with the key,
158.80 + * if the implementation supports null values.)
158.81 + * @throws UnsupportedOperationException if the <tt>put</tt> operation
158.82 + * is not supported by this map
158.83 + * @throws ClassCastException if the class of the specified key or value
158.84 + * prevents it from being stored in this map
158.85 + * @throws NullPointerException if the specified key or value is null,
158.86 + * and this map does not permit null keys or values
158.87 + * @throws IllegalArgumentException if some property of the specified key
158.88 + * or value prevents it from being stored in this map
158.89 + *
158.90 + */
158.91 + V putIfAbsent(K key, V value);
158.92 +
158.93 + /**
158.94 + * Removes the entry for a key only if currently mapped to a given value.
158.95 + * This is equivalent to
158.96 + * <pre>
158.97 + * if (map.containsKey(key) && map.get(key).equals(value)) {
158.98 + * map.remove(key);
158.99 + * return true;
158.100 + * } else return false;</pre>
158.101 + * except that the action is performed atomically.
158.102 + *
158.103 + * @param key key with which the specified value is associated
158.104 + * @param value value expected to be associated with the specified key
158.105 + * @return <tt>true</tt> if the value was removed
158.106 + * @throws UnsupportedOperationException if the <tt>remove</tt> operation
158.107 + * is not supported by this map
158.108 + * @throws ClassCastException if the key or value is of an inappropriate
158.109 + * type for this map
158.110 + * (<a href="../Collection.html#optional-restrictions">optional</a>)
158.111 + * @throws NullPointerException if the specified key or value is null,
158.112 + * and this map does not permit null keys or values
158.113 + * (<a href="../Collection.html#optional-restrictions">optional</a>)
158.114 + */
158.115 + boolean remove(Object key, Object value);
158.116 +
158.117 + /**
158.118 + * Replaces the entry for a key only if currently mapped to a given value.
158.119 + * This is equivalent to
158.120 + * <pre>
158.121 + * if (map.containsKey(key) && map.get(key).equals(oldValue)) {
158.122 + * map.put(key, newValue);
158.123 + * return true;
158.124 + * } else return false;</pre>
158.125 + * except that the action is performed atomically.
158.126 + *
158.127 + * @param key key with which the specified value is associated
158.128 + * @param oldValue value expected to be associated with the specified key
158.129 + * @param newValue value to be associated with the specified key
158.130 + * @return <tt>true</tt> if the value was replaced
158.131 + * @throws UnsupportedOperationException if the <tt>put</tt> operation
158.132 + * is not supported by this map
158.133 + * @throws ClassCastException if the class of a specified key or value
158.134 + * prevents it from being stored in this map
158.135 + * @throws NullPointerException if a specified key or value is null,
158.136 + * and this map does not permit null keys or values
158.137 + * @throws IllegalArgumentException if some property of a specified key
158.138 + * or value prevents it from being stored in this map
158.139 + */
158.140 + boolean replace(K key, V oldValue, V newValue);
158.141 +
158.142 + /**
158.143 + * Replaces the entry for a key only if currently mapped to some value.
158.144 + * This is equivalent to
158.145 + * <pre>
158.146 + * if (map.containsKey(key)) {
158.147 + * return map.put(key, value);
158.148 + * } else return null;</pre>
158.149 + * except that the action is performed atomically.
158.150 + *
158.151 + * @param key key with which the specified value is associated
158.152 + * @param value value to be associated with the specified key
158.153 + * @return the previous value associated with the specified key, or
158.154 + * <tt>null</tt> if there was no mapping for the key.
158.155 + * (A <tt>null</tt> return can also indicate that the map
158.156 + * previously associated <tt>null</tt> with the key,
158.157 + * if the implementation supports null values.)
158.158 + * @throws UnsupportedOperationException if the <tt>put</tt> operation
158.159 + * is not supported by this map
158.160 + * @throws ClassCastException if the class of the specified key or value
158.161 + * prevents it from being stored in this map
158.162 + * @throws NullPointerException if the specified key or value is null,
158.163 + * and this map does not permit null keys or values
158.164 + * @throws IllegalArgumentException if some property of the specified key
158.165 + * or value prevents it from being stored in this map
158.166 + */
158.167 + V replace(K key, V value);
158.168 +}
159.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
159.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java Tue Feb 11 13:31:42 2014 +0100
159.3 @@ -0,0 +1,155 @@
159.4 +/*
159.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
159.6 + *
159.7 + * This code is free software; you can redistribute it and/or modify it
159.8 + * under the terms of the GNU General Public License version 2 only, as
159.9 + * published by the Free Software Foundation. Oracle designates this
159.10 + * particular file as subject to the "Classpath" exception as provided
159.11 + * by Oracle in the LICENSE file that accompanied this code.
159.12 + *
159.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
159.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
159.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
159.16 + * version 2 for more details (a copy is included in the LICENSE file that
159.17 + * accompanied this code).
159.18 + *
159.19 + * You should have received a copy of the GNU General Public License version
159.20 + * 2 along with this work; if not, write to the Free Software Foundation,
159.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
159.22 + *
159.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
159.24 + * or visit www.oracle.com if you need additional information or have any
159.25 + * questions.
159.26 + */
159.27 +
159.28 +/*
159.29 + * This file is available under and governed by the GNU General Public
159.30 + * License version 2 only, as published by the Free Software Foundation.
159.31 + * However, the following notice accompanied the original version of this
159.32 + * file:
159.33 + *
159.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
159.35 + * Expert Group and released to the public domain, as explained at
159.36 + * http://creativecommons.org/publicdomain/zero/1.0/
159.37 + */
159.38 +
159.39 +package java.util.concurrent.atomic;
159.40 +
159.41 +/**
159.42 + * A {@code boolean} value that may be updated atomically. See the
159.43 + * {@link java.util.concurrent.atomic} package specification for
159.44 + * description of the properties of atomic variables. An
159.45 + * {@code AtomicBoolean} is used in applications such as atomically
159.46 + * updated flags, and cannot be used as a replacement for a
159.47 + * {@link java.lang.Boolean}.
159.48 + *
159.49 + * @since 1.5
159.50 + * @author Doug Lea
159.51 + */
159.52 +public class AtomicBoolean implements java.io.Serializable {
159.53 + private static final long serialVersionUID = 4654671469794556979L;
159.54 +
159.55 + private volatile int value;
159.56 +
159.57 + /**
159.58 + * Creates a new {@code AtomicBoolean} with the given initial value.
159.59 + *
159.60 + * @param initialValue the initial value
159.61 + */
159.62 + public AtomicBoolean(boolean initialValue) {
159.63 + value = initialValue ? 1 : 0;
159.64 + }
159.65 +
159.66 + /**
159.67 + * Creates a new {@code AtomicBoolean} with initial value {@code false}.
159.68 + */
159.69 + public AtomicBoolean() {
159.70 + }
159.71 +
159.72 + /**
159.73 + * Returns the current value.
159.74 + *
159.75 + * @return the current value
159.76 + */
159.77 + public final boolean get() {
159.78 + return value != 0;
159.79 + }
159.80 +
159.81 + /**
159.82 + * Atomically sets the value to the given updated value
159.83 + * if the current value {@code ==} the expected value.
159.84 + *
159.85 + * @param expect the expected value
159.86 + * @param update the new value
159.87 + * @return true if successful. False return indicates that
159.88 + * the actual value was not equal to the expected value.
159.89 + */
159.90 + public final boolean compareAndSet(boolean expect, boolean update) {
159.91 + int e = expect ? 1 : 0;
159.92 + int u = update ? 1 : 0;
159.93 + if (this.value == e) {
159.94 + this.value = u;
159.95 + return true;
159.96 + } else {
159.97 + return false;
159.98 + }
159.99 + }
159.100 +
159.101 + /**
159.102 + * Atomically sets the value to the given updated value
159.103 + * if the current value {@code ==} the expected value.
159.104 + *
159.105 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
159.106 + * and does not provide ordering guarantees, so is only rarely an
159.107 + * appropriate alternative to {@code compareAndSet}.
159.108 + *
159.109 + * @param expect the expected value
159.110 + * @param update the new value
159.111 + * @return true if successful.
159.112 + */
159.113 + public boolean weakCompareAndSet(boolean expect, boolean update) {
159.114 + return compareAndSet(expect, update);
159.115 + }
159.116 +
159.117 + /**
159.118 + * Unconditionally sets to the given value.
159.119 + *
159.120 + * @param newValue the new value
159.121 + */
159.122 + public final void set(boolean newValue) {
159.123 + value = newValue ? 1 : 0;
159.124 + }
159.125 +
159.126 + /**
159.127 + * Eventually sets to the given value.
159.128 + *
159.129 + * @param newValue the new value
159.130 + * @since 1.6
159.131 + */
159.132 + public final void lazySet(boolean newValue) {
159.133 + set(newValue);
159.134 + }
159.135 +
159.136 + /**
159.137 + * Atomically sets to the given value and returns the previous value.
159.138 + *
159.139 + * @param newValue the new value
159.140 + * @return the previous value
159.141 + */
159.142 + public final boolean getAndSet(boolean newValue) {
159.143 + for (;;) {
159.144 + boolean current = get();
159.145 + if (compareAndSet(current, newValue))
159.146 + return current;
159.147 + }
159.148 + }
159.149 +
159.150 + /**
159.151 + * Returns the String representation of the current value.
159.152 + * @return the String representation of the current value.
159.153 + */
159.154 + public String toString() {
159.155 + return Boolean.toString(get());
159.156 + }
159.157 +
159.158 +}
160.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
160.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicInteger.java Tue Feb 11 13:31:42 2014 +0100
160.3 @@ -0,0 +1,258 @@
160.4 +/*
160.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
160.6 + *
160.7 + * This code is free software; you can redistribute it and/or modify it
160.8 + * under the terms of the GNU General Public License version 2 only, as
160.9 + * published by the Free Software Foundation. Oracle designates this
160.10 + * particular file as subject to the "Classpath" exception as provided
160.11 + * by Oracle in the LICENSE file that accompanied this code.
160.12 + *
160.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
160.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
160.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
160.16 + * version 2 for more details (a copy is included in the LICENSE file that
160.17 + * accompanied this code).
160.18 + *
160.19 + * You should have received a copy of the GNU General Public License version
160.20 + * 2 along with this work; if not, write to the Free Software Foundation,
160.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
160.22 + *
160.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
160.24 + * or visit www.oracle.com if you need additional information or have any
160.25 + * questions.
160.26 + */
160.27 +
160.28 +/*
160.29 + * This file is available under and governed by the GNU General Public
160.30 + * License version 2 only, as published by the Free Software Foundation.
160.31 + * However, the following notice accompanied the original version of this
160.32 + * file:
160.33 + *
160.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
160.35 + * Expert Group and released to the public domain, as explained at
160.36 + * http://creativecommons.org/publicdomain/zero/1.0/
160.37 + */
160.38 +
160.39 +package java.util.concurrent.atomic;
160.40 +
160.41 +/**
160.42 + * An {@code int} value that may be updated atomically. See the
160.43 + * {@link java.util.concurrent.atomic} package specification for
160.44 + * description of the properties of atomic variables. An
160.45 + * {@code AtomicInteger} is used in applications such as atomically
160.46 + * incremented counters, and cannot be used as a replacement for an
160.47 + * {@link java.lang.Integer}. However, this class does extend
160.48 + * {@code Number} to allow uniform access by tools and utilities that
160.49 + * deal with numerically-based classes.
160.50 + *
160.51 + * @since 1.5
160.52 + * @author Doug Lea
160.53 +*/
160.54 +public class AtomicInteger extends Number implements java.io.Serializable {
160.55 + private static final long serialVersionUID = 6214790243416807050L;
160.56 +
160.57 + private volatile int value;
160.58 +
160.59 + /**
160.60 + * Creates a new AtomicInteger with the given initial value.
160.61 + *
160.62 + * @param initialValue the initial value
160.63 + */
160.64 + public AtomicInteger(int initialValue) {
160.65 + value = initialValue;
160.66 + }
160.67 +
160.68 + /**
160.69 + * Creates a new AtomicInteger with initial value {@code 0}.
160.70 + */
160.71 + public AtomicInteger() {
160.72 + }
160.73 +
160.74 + /**
160.75 + * Gets the current value.
160.76 + *
160.77 + * @return the current value
160.78 + */
160.79 + public final int get() {
160.80 + return value;
160.81 + }
160.82 +
160.83 + /**
160.84 + * Sets to the given value.
160.85 + *
160.86 + * @param newValue the new value
160.87 + */
160.88 + public final void set(int newValue) {
160.89 + value = newValue;
160.90 + }
160.91 +
160.92 + /**
160.93 + * Eventually sets to the given value.
160.94 + *
160.95 + * @param newValue the new value
160.96 + * @since 1.6
160.97 + */
160.98 + public final void lazySet(int newValue) {
160.99 + value = newValue;
160.100 + }
160.101 +
160.102 + /**
160.103 + * Atomically sets to the given value and returns the old value.
160.104 + *
160.105 + * @param newValue the new value
160.106 + * @return the previous value
160.107 + */
160.108 + public final int getAndSet(int newValue) {
160.109 + for (;;) {
160.110 + int current = get();
160.111 + if (compareAndSet(current, newValue))
160.112 + return current;
160.113 + }
160.114 + }
160.115 +
160.116 + /**
160.117 + * Atomically sets the value to the given updated value
160.118 + * if the current value {@code ==} the expected value.
160.119 + *
160.120 + * @param expect the expected value
160.121 + * @param update the new value
160.122 + * @return true if successful. False return indicates that
160.123 + * the actual value was not equal to the expected value.
160.124 + */
160.125 + public final boolean compareAndSet(int expect, int update) {
160.126 + if (value == expect) {
160.127 + value = update;
160.128 + return true;
160.129 + } else {
160.130 + return false;
160.131 + }
160.132 + }
160.133 +
160.134 + /**
160.135 + * Atomically sets the value to the given updated value
160.136 + * if the current value {@code ==} the expected value.
160.137 + *
160.138 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
160.139 + * and does not provide ordering guarantees, so is only rarely an
160.140 + * appropriate alternative to {@code compareAndSet}.
160.141 + *
160.142 + * @param expect the expected value
160.143 + * @param update the new value
160.144 + * @return true if successful.
160.145 + */
160.146 + public final boolean weakCompareAndSet(int expect, int update) {
160.147 + return compareAndSet(expect, update);
160.148 + }
160.149 +
160.150 + /**
160.151 + * Atomically increments by one the current value.
160.152 + *
160.153 + * @return the previous value
160.154 + */
160.155 + public final int getAndIncrement() {
160.156 + for (;;) {
160.157 + int current = get();
160.158 + int next = current + 1;
160.159 + if (compareAndSet(current, next))
160.160 + return current;
160.161 + }
160.162 + }
160.163 +
160.164 + /**
160.165 + * Atomically decrements by one the current value.
160.166 + *
160.167 + * @return the previous value
160.168 + */
160.169 + public final int getAndDecrement() {
160.170 + for (;;) {
160.171 + int current = get();
160.172 + int next = current - 1;
160.173 + if (compareAndSet(current, next))
160.174 + return current;
160.175 + }
160.176 + }
160.177 +
160.178 + /**
160.179 + * Atomically adds the given value to the current value.
160.180 + *
160.181 + * @param delta the value to add
160.182 + * @return the previous value
160.183 + */
160.184 + public final int getAndAdd(int delta) {
160.185 + for (;;) {
160.186 + int current = get();
160.187 + int next = current + delta;
160.188 + if (compareAndSet(current, next))
160.189 + return current;
160.190 + }
160.191 + }
160.192 +
160.193 + /**
160.194 + * Atomically increments by one the current value.
160.195 + *
160.196 + * @return the updated value
160.197 + */
160.198 + public final int incrementAndGet() {
160.199 + for (;;) {
160.200 + int current = get();
160.201 + int next = current + 1;
160.202 + if (compareAndSet(current, next))
160.203 + return next;
160.204 + }
160.205 + }
160.206 +
160.207 + /**
160.208 + * Atomically decrements by one the current value.
160.209 + *
160.210 + * @return the updated value
160.211 + */
160.212 + public final int decrementAndGet() {
160.213 + for (;;) {
160.214 + int current = get();
160.215 + int next = current - 1;
160.216 + if (compareAndSet(current, next))
160.217 + return next;
160.218 + }
160.219 + }
160.220 +
160.221 + /**
160.222 + * Atomically adds the given value to the current value.
160.223 + *
160.224 + * @param delta the value to add
160.225 + * @return the updated value
160.226 + */
160.227 + public final int addAndGet(int delta) {
160.228 + for (;;) {
160.229 + int current = get();
160.230 + int next = current + delta;
160.231 + if (compareAndSet(current, next))
160.232 + return next;
160.233 + }
160.234 + }
160.235 +
160.236 + /**
160.237 + * Returns the String representation of the current value.
160.238 + * @return the String representation of the current value.
160.239 + */
160.240 + public String toString() {
160.241 + return Integer.toString(get());
160.242 + }
160.243 +
160.244 +
160.245 + public int intValue() {
160.246 + return get();
160.247 + }
160.248 +
160.249 + public long longValue() {
160.250 + return (long)get();
160.251 + }
160.252 +
160.253 + public float floatValue() {
160.254 + return (float)get();
160.255 + }
160.256 +
160.257 + public double doubleValue() {
160.258 + return (double)get();
160.259 + }
160.260 +
160.261 +}
161.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
161.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java Tue Feb 11 13:31:42 2014 +0100
161.3 @@ -0,0 +1,247 @@
161.4 +/*
161.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
161.6 + *
161.7 + * This code is free software; you can redistribute it and/or modify it
161.8 + * under the terms of the GNU General Public License version 2 only, as
161.9 + * published by the Free Software Foundation. Oracle designates this
161.10 + * particular file as subject to the "Classpath" exception as provided
161.11 + * by Oracle in the LICENSE file that accompanied this code.
161.12 + *
161.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
161.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
161.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
161.16 + * version 2 for more details (a copy is included in the LICENSE file that
161.17 + * accompanied this code).
161.18 + *
161.19 + * You should have received a copy of the GNU General Public License version
161.20 + * 2 along with this work; if not, write to the Free Software Foundation,
161.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
161.22 + *
161.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
161.24 + * or visit www.oracle.com if you need additional information or have any
161.25 + * questions.
161.26 + */
161.27 +
161.28 +/*
161.29 + * This file is available under and governed by the GNU General Public
161.30 + * License version 2 only, as published by the Free Software Foundation.
161.31 + * However, the following notice accompanied the original version of this
161.32 + * file:
161.33 + *
161.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
161.35 + * Expert Group and released to the public domain, as explained at
161.36 + * http://creativecommons.org/publicdomain/zero/1.0/
161.37 + */
161.38 +
161.39 +package java.util.concurrent.atomic;
161.40 +
161.41 +/**
161.42 + * An {@code int} array in which elements may be updated atomically.
161.43 + * See the {@link java.util.concurrent.atomic} package
161.44 + * specification for description of the properties of atomic
161.45 + * variables.
161.46 + * @since 1.5
161.47 + * @author Doug Lea
161.48 + */
161.49 +public class AtomicIntegerArray implements java.io.Serializable {
161.50 + private static final long serialVersionUID = 2862133569453604235L;
161.51 +
161.52 + private final int[] array;
161.53 +
161.54 + /**
161.55 + * Creates a new AtomicIntegerArray of the given length, with all
161.56 + * elements initially zero.
161.57 + *
161.58 + * @param length the length of the array
161.59 + */
161.60 + public AtomicIntegerArray(int length) {
161.61 + array = new int[length];
161.62 + }
161.63 +
161.64 + /**
161.65 + * Creates a new AtomicIntegerArray with the same length as, and
161.66 + * all elements copied from, the given array.
161.67 + *
161.68 + * @param array the array to copy elements from
161.69 + * @throws NullPointerException if array is null
161.70 + */
161.71 + public AtomicIntegerArray(int[] array) {
161.72 + // Visibility guaranteed by final field guarantees
161.73 + this.array = array.clone();
161.74 + }
161.75 +
161.76 + /**
161.77 + * Returns the length of the array.
161.78 + *
161.79 + * @return the length of the array
161.80 + */
161.81 + public final int length() {
161.82 + return array.length;
161.83 + }
161.84 +
161.85 + /**
161.86 + * Gets the current value at position {@code i}.
161.87 + *
161.88 + * @param i the index
161.89 + * @return the current value
161.90 + */
161.91 + public final int get(int i) {
161.92 + return array[i];
161.93 + }
161.94 +
161.95 + /**
161.96 + * Sets the element at position {@code i} to the given value.
161.97 + *
161.98 + * @param i the index
161.99 + * @param newValue the new value
161.100 + */
161.101 + public final void set(int i, int newValue) {
161.102 + array[i] = newValue;
161.103 + }
161.104 +
161.105 + /**
161.106 + * Eventually sets the element at position {@code i} to the given value.
161.107 + *
161.108 + * @param i the index
161.109 + * @param newValue the new value
161.110 + * @since 1.6
161.111 + */
161.112 + public final void lazySet(int i, int newValue) {
161.113 + array[i] = newValue;
161.114 + }
161.115 +
161.116 + /**
161.117 + * Atomically sets the element at position {@code i} to the given
161.118 + * value and returns the old value.
161.119 + *
161.120 + * @param i the index
161.121 + * @param newValue the new value
161.122 + * @return the previous value
161.123 + */
161.124 + public final int getAndSet(int i, int newValue) {
161.125 + int current = array[i];
161.126 + array[i] = newValue;
161.127 + return current;
161.128 + }
161.129 +
161.130 + /**
161.131 + * Atomically sets the element at position {@code i} to the given
161.132 + * updated value if the current value {@code ==} the expected value.
161.133 + *
161.134 + * @param i the index
161.135 + * @param expect the expected value
161.136 + * @param update the new value
161.137 + * @return true if successful. False return indicates that
161.138 + * the actual value was not equal to the expected value.
161.139 + */
161.140 + public final boolean compareAndSet(int i, int expect, int update) {
161.141 + if (array[i] == expect) {
161.142 + array[i] = update;
161.143 + return true;
161.144 + } else {
161.145 + return false;
161.146 + }
161.147 + }
161.148 +
161.149 + /**
161.150 + * Atomically sets the element at position {@code i} to the given
161.151 + * updated value if the current value {@code ==} the expected value.
161.152 + *
161.153 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
161.154 + * and does not provide ordering guarantees, so is only rarely an
161.155 + * appropriate alternative to {@code compareAndSet}.
161.156 + *
161.157 + * @param i the index
161.158 + * @param expect the expected value
161.159 + * @param update the new value
161.160 + * @return true if successful.
161.161 + */
161.162 + public final boolean weakCompareAndSet(int i, int expect, int update) {
161.163 + return compareAndSet(i, expect, update);
161.164 + }
161.165 +
161.166 + /**
161.167 + * Atomically increments by one the element at index {@code i}.
161.168 + *
161.169 + * @param i the index
161.170 + * @return the previous value
161.171 + */
161.172 + public final int getAndIncrement(int i) {
161.173 + return getAndAdd(i, 1);
161.174 + }
161.175 +
161.176 + /**
161.177 + * Atomically decrements by one the element at index {@code i}.
161.178 + *
161.179 + * @param i the index
161.180 + * @return the previous value
161.181 + */
161.182 + public final int getAndDecrement(int i) {
161.183 + return getAndAdd(i, -1);
161.184 + }
161.185 +
161.186 + /**
161.187 + * Atomically adds the given value to the element at index {@code i}.
161.188 + *
161.189 + * @param i the index
161.190 + * @param delta the value to add
161.191 + * @return the previous value
161.192 + */
161.193 + public final int getAndAdd(int i, int delta) {
161.194 + int v = array[i];
161.195 + array[i] += delta;
161.196 + return v;
161.197 + }
161.198 +
161.199 + /**
161.200 + * Atomically increments by one the element at index {@code i}.
161.201 + *
161.202 + * @param i the index
161.203 + * @return the updated value
161.204 + */
161.205 + public final int incrementAndGet(int i) {
161.206 + return addAndGet(i, 1);
161.207 + }
161.208 +
161.209 + /**
161.210 + * Atomically decrements by one the element at index {@code i}.
161.211 + *
161.212 + * @param i the index
161.213 + * @return the updated value
161.214 + */
161.215 + public final int decrementAndGet(int i) {
161.216 + return addAndGet(i, -1);
161.217 + }
161.218 +
161.219 + /**
161.220 + * Atomically adds the given value to the element at index {@code i}.
161.221 + *
161.222 + * @param i the index
161.223 + * @param delta the value to add
161.224 + * @return the updated value
161.225 + */
161.226 + public final int addAndGet(int i, int delta) {
161.227 + array[i] += delta;
161.228 + return array[i];
161.229 + }
161.230 +
161.231 + /**
161.232 + * Returns the String representation of the current values of array.
161.233 + * @return the String representation of the current values of array
161.234 + */
161.235 + public String toString() {
161.236 + int iMax = array.length - 1;
161.237 + if (iMax == -1)
161.238 + return "[]";
161.239 +
161.240 + StringBuilder b = new StringBuilder();
161.241 + b.append('[');
161.242 + for (int i = 0; ; i++) {
161.243 + b.append(get(i));
161.244 + if (i == iMax)
161.245 + return b.append(']').toString();
161.246 + b.append(',').append(' ');
161.247 + }
161.248 + }
161.249 +
161.250 +}
162.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
162.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicLong.java Tue Feb 11 13:31:42 2014 +0100
162.3 @@ -0,0 +1,265 @@
162.4 +/*
162.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
162.6 + *
162.7 + * This code is free software; you can redistribute it and/or modify it
162.8 + * under the terms of the GNU General Public License version 2 only, as
162.9 + * published by the Free Software Foundation. Oracle designates this
162.10 + * particular file as subject to the "Classpath" exception as provided
162.11 + * by Oracle in the LICENSE file that accompanied this code.
162.12 + *
162.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
162.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
162.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
162.16 + * version 2 for more details (a copy is included in the LICENSE file that
162.17 + * accompanied this code).
162.18 + *
162.19 + * You should have received a copy of the GNU General Public License version
162.20 + * 2 along with this work; if not, write to the Free Software Foundation,
162.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
162.22 + *
162.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
162.24 + * or visit www.oracle.com if you need additional information or have any
162.25 + * questions.
162.26 + */
162.27 +
162.28 +/*
162.29 + * This file is available under and governed by the GNU General Public
162.30 + * License version 2 only, as published by the Free Software Foundation.
162.31 + * However, the following notice accompanied the original version of this
162.32 + * file:
162.33 + *
162.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
162.35 + * Expert Group and released to the public domain, as explained at
162.36 + * http://creativecommons.org/publicdomain/zero/1.0/
162.37 + */
162.38 +
162.39 +package java.util.concurrent.atomic;
162.40 +
162.41 +/**
162.42 + * A {@code long} value that may be updated atomically. See the
162.43 + * {@link java.util.concurrent.atomic} package specification for
162.44 + * description of the properties of atomic variables. An
162.45 + * {@code AtomicLong} is used in applications such as atomically
162.46 + * incremented sequence numbers, and cannot be used as a replacement
162.47 + * for a {@link java.lang.Long}. However, this class does extend
162.48 + * {@code Number} to allow uniform access by tools and utilities that
162.49 + * deal with numerically-based classes.
162.50 + *
162.51 + * @since 1.5
162.52 + * @author Doug Lea
162.53 + */
162.54 +public class AtomicLong extends Number implements java.io.Serializable {
162.55 + private static final long serialVersionUID = 1927816293512124184L;
162.56 +
162.57 +
162.58 + /**
162.59 + * Returns whether underlying JVM supports lockless CompareAndSet
162.60 + * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
162.61 + */
162.62 + private static native boolean VMSupportsCS8();
162.63 +
162.64 + private volatile long value;
162.65 +
162.66 + /**
162.67 + * Creates a new AtomicLong with the given initial value.
162.68 + *
162.69 + * @param initialValue the initial value
162.70 + */
162.71 + public AtomicLong(long initialValue) {
162.72 + value = initialValue;
162.73 + }
162.74 +
162.75 + /**
162.76 + * Creates a new AtomicLong with initial value {@code 0}.
162.77 + */
162.78 + public AtomicLong() {
162.79 + }
162.80 +
162.81 + /**
162.82 + * Gets the current value.
162.83 + *
162.84 + * @return the current value
162.85 + */
162.86 + public final long get() {
162.87 + return value;
162.88 + }
162.89 +
162.90 + /**
162.91 + * Sets to the given value.
162.92 + *
162.93 + * @param newValue the new value
162.94 + */
162.95 + public final void set(long newValue) {
162.96 + value = newValue;
162.97 + }
162.98 +
162.99 + /**
162.100 + * Eventually sets to the given value.
162.101 + *
162.102 + * @param newValue the new value
162.103 + * @since 1.6
162.104 + */
162.105 + public final void lazySet(long newValue) {
162.106 + value = newValue;
162.107 + }
162.108 +
162.109 + /**
162.110 + * Atomically sets to the given value and returns the old value.
162.111 + *
162.112 + * @param newValue the new value
162.113 + * @return the previous value
162.114 + */
162.115 + public final long getAndSet(long newValue) {
162.116 + while (true) {
162.117 + long current = get();
162.118 + if (compareAndSet(current, newValue))
162.119 + return current;
162.120 + }
162.121 + }
162.122 +
162.123 + /**
162.124 + * Atomically sets the value to the given updated value
162.125 + * if the current value {@code ==} the expected value.
162.126 + *
162.127 + * @param expect the expected value
162.128 + * @param update the new value
162.129 + * @return true if successful. False return indicates that
162.130 + * the actual value was not equal to the expected value.
162.131 + */
162.132 + public final boolean compareAndSet(long expect, long update) {
162.133 + if (value == expect) {
162.134 + value = update;
162.135 + return true;
162.136 + } else {
162.137 + return false;
162.138 + }
162.139 + }
162.140 +
162.141 + /**
162.142 + * Atomically sets the value to the given updated value
162.143 + * if the current value {@code ==} the expected value.
162.144 + *
162.145 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
162.146 + * and does not provide ordering guarantees, so is only rarely an
162.147 + * appropriate alternative to {@code compareAndSet}.
162.148 + *
162.149 + * @param expect the expected value
162.150 + * @param update the new value
162.151 + * @return true if successful.
162.152 + */
162.153 + public final boolean weakCompareAndSet(long expect, long update) {
162.154 + return compareAndSet(expect, update);
162.155 + }
162.156 +
162.157 + /**
162.158 + * Atomically increments by one the current value.
162.159 + *
162.160 + * @return the previous value
162.161 + */
162.162 + public final long getAndIncrement() {
162.163 + while (true) {
162.164 + long current = get();
162.165 + long next = current + 1;
162.166 + if (compareAndSet(current, next))
162.167 + return current;
162.168 + }
162.169 + }
162.170 +
162.171 + /**
162.172 + * Atomically decrements by one the current value.
162.173 + *
162.174 + * @return the previous value
162.175 + */
162.176 + public final long getAndDecrement() {
162.177 + while (true) {
162.178 + long current = get();
162.179 + long next = current - 1;
162.180 + if (compareAndSet(current, next))
162.181 + return current;
162.182 + }
162.183 + }
162.184 +
162.185 + /**
162.186 + * Atomically adds the given value to the current value.
162.187 + *
162.188 + * @param delta the value to add
162.189 + * @return the previous value
162.190 + */
162.191 + public final long getAndAdd(long delta) {
162.192 + while (true) {
162.193 + long current = get();
162.194 + long next = current + delta;
162.195 + if (compareAndSet(current, next))
162.196 + return current;
162.197 + }
162.198 + }
162.199 +
162.200 + /**
162.201 + * Atomically increments by one the current value.
162.202 + *
162.203 + * @return the updated value
162.204 + */
162.205 + public final long incrementAndGet() {
162.206 + for (;;) {
162.207 + long current = get();
162.208 + long next = current + 1;
162.209 + if (compareAndSet(current, next))
162.210 + return next;
162.211 + }
162.212 + }
162.213 +
162.214 + /**
162.215 + * Atomically decrements by one the current value.
162.216 + *
162.217 + * @return the updated value
162.218 + */
162.219 + public final long decrementAndGet() {
162.220 + for (;;) {
162.221 + long current = get();
162.222 + long next = current - 1;
162.223 + if (compareAndSet(current, next))
162.224 + return next;
162.225 + }
162.226 + }
162.227 +
162.228 + /**
162.229 + * Atomically adds the given value to the current value.
162.230 + *
162.231 + * @param delta the value to add
162.232 + * @return the updated value
162.233 + */
162.234 + public final long addAndGet(long delta) {
162.235 + for (;;) {
162.236 + long current = get();
162.237 + long next = current + delta;
162.238 + if (compareAndSet(current, next))
162.239 + return next;
162.240 + }
162.241 + }
162.242 +
162.243 + /**
162.244 + * Returns the String representation of the current value.
162.245 + * @return the String representation of the current value.
162.246 + */
162.247 + public String toString() {
162.248 + return Long.toString(get());
162.249 + }
162.250 +
162.251 +
162.252 + public int intValue() {
162.253 + return (int)get();
162.254 + }
162.255 +
162.256 + public long longValue() {
162.257 + return get();
162.258 + }
162.259 +
162.260 + public float floatValue() {
162.261 + return (float)get();
162.262 + }
162.263 +
162.264 + public double doubleValue() {
162.265 + return (double)get();
162.266 + }
162.267 +
162.268 +}
163.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
163.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java Tue Feb 11 13:31:42 2014 +0100
163.3 @@ -0,0 +1,247 @@
163.4 +/*
163.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
163.6 + *
163.7 + * This code is free software; you can redistribute it and/or modify it
163.8 + * under the terms of the GNU General Public License version 2 only, as
163.9 + * published by the Free Software Foundation. Oracle designates this
163.10 + * particular file as subject to the "Classpath" exception as provided
163.11 + * by Oracle in the LICENSE file that accompanied this code.
163.12 + *
163.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
163.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
163.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
163.16 + * version 2 for more details (a copy is included in the LICENSE file that
163.17 + * accompanied this code).
163.18 + *
163.19 + * You should have received a copy of the GNU General Public License version
163.20 + * 2 along with this work; if not, write to the Free Software Foundation,
163.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
163.22 + *
163.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
163.24 + * or visit www.oracle.com if you need additional information or have any
163.25 + * questions.
163.26 + */
163.27 +
163.28 +/*
163.29 + * This file is available under and governed by the GNU General Public
163.30 + * License version 2 only, as published by the Free Software Foundation.
163.31 + * However, the following notice accompanied the original version of this
163.32 + * file:
163.33 + *
163.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
163.35 + * Expert Group and released to the public domain, as explained at
163.36 + * http://creativecommons.org/publicdomain/zero/1.0/
163.37 + */
163.38 +
163.39 +package java.util.concurrent.atomic;
163.40 +
163.41 +/**
163.42 + * A {@code long} array in which elements may be updated atomically.
163.43 + * See the {@link java.util.concurrent.atomic} package specification
163.44 + * for description of the properties of atomic variables.
163.45 + * @since 1.5
163.46 + * @author Doug Lea
163.47 + */
163.48 +public class AtomicLongArray implements java.io.Serializable {
163.49 + private static final long serialVersionUID = -2308431214976778248L;
163.50 +
163.51 + private final long[] array;
163.52 +
163.53 + /**
163.54 + * Creates a new AtomicLongArray of the given length, with all
163.55 + * elements initially zero.
163.56 + *
163.57 + * @param length the length of the array
163.58 + */
163.59 + public AtomicLongArray(int length) {
163.60 + array = new long[length];
163.61 + }
163.62 +
163.63 + /**
163.64 + * Creates a new AtomicLongArray with the same length as, and
163.65 + * all elements copied from, the given array.
163.66 + *
163.67 + * @param array the array to copy elements from
163.68 + * @throws NullPointerException if array is null
163.69 + */
163.70 + public AtomicLongArray(long[] array) {
163.71 + // Visibility guaranteed by final field guarantees
163.72 + this.array = array.clone();
163.73 + }
163.74 +
163.75 + /**
163.76 + * Returns the length of the array.
163.77 + *
163.78 + * @return the length of the array
163.79 + */
163.80 + public final int length() {
163.81 + return array.length;
163.82 + }
163.83 +
163.84 + /**
163.85 + * Gets the current value at position {@code i}.
163.86 + *
163.87 + * @param i the index
163.88 + * @return the current value
163.89 + */
163.90 + public final long get(int i) {
163.91 + return array[i];
163.92 + }
163.93 +
163.94 + /**
163.95 + * Sets the element at position {@code i} to the given value.
163.96 + *
163.97 + * @param i the index
163.98 + * @param newValue the new value
163.99 + */
163.100 + public final void set(int i, long newValue) {
163.101 + array[i] = newValue;
163.102 + }
163.103 +
163.104 + /**
163.105 + * Eventually sets the element at position {@code i} to the given value.
163.106 + *
163.107 + * @param i the index
163.108 + * @param newValue the new value
163.109 + * @since 1.6
163.110 + */
163.111 + public final void lazySet(int i, long newValue) {
163.112 + array[i] = newValue;
163.113 + }
163.114 +
163.115 +
163.116 + /**
163.117 + * Atomically sets the element at position {@code i} to the given value
163.118 + * and returns the old value.
163.119 + *
163.120 + * @param i the index
163.121 + * @param newValue the new value
163.122 + * @return the previous value
163.123 + */
163.124 + public final long getAndSet(int i, long newValue) {
163.125 + long v = array[i];
163.126 + array[i] = newValue;
163.127 + return v;
163.128 + }
163.129 +
163.130 + /**
163.131 + * Atomically sets the element at position {@code i} to the given
163.132 + * updated value if the current value {@code ==} the expected value.
163.133 + *
163.134 + * @param i the index
163.135 + * @param expect the expected value
163.136 + * @param update the new value
163.137 + * @return true if successful. False return indicates that
163.138 + * the actual value was not equal to the expected value.
163.139 + */
163.140 + public final boolean compareAndSet(int i, long expect, long update) {
163.141 + if (array[i] == expect) {
163.142 + array[i] = update;
163.143 + return true;
163.144 + } else {
163.145 + return false;
163.146 + }
163.147 + }
163.148 +
163.149 + /**
163.150 + * Atomically sets the element at position {@code i} to the given
163.151 + * updated value if the current value {@code ==} the expected value.
163.152 + *
163.153 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
163.154 + * and does not provide ordering guarantees, so is only rarely an
163.155 + * appropriate alternative to {@code compareAndSet}.
163.156 + *
163.157 + * @param i the index
163.158 + * @param expect the expected value
163.159 + * @param update the new value
163.160 + * @return true if successful.
163.161 + */
163.162 + public final boolean weakCompareAndSet(int i, long expect, long update) {
163.163 + return compareAndSet(i, expect, update);
163.164 + }
163.165 +
163.166 + /**
163.167 + * Atomically increments by one the element at index {@code i}.
163.168 + *
163.169 + * @param i the index
163.170 + * @return the previous value
163.171 + */
163.172 + public final long getAndIncrement(int i) {
163.173 + return getAndAdd(i, 1);
163.174 + }
163.175 +
163.176 + /**
163.177 + * Atomically decrements by one the element at index {@code i}.
163.178 + *
163.179 + * @param i the index
163.180 + * @return the previous value
163.181 + */
163.182 + public final long getAndDecrement(int i) {
163.183 + return getAndAdd(i, -1);
163.184 + }
163.185 +
163.186 + /**
163.187 + * Atomically adds the given value to the element at index {@code i}.
163.188 + *
163.189 + * @param i the index
163.190 + * @param delta the value to add
163.191 + * @return the previous value
163.192 + */
163.193 + public final long getAndAdd(int i, long delta) {
163.194 + long v = array[i];
163.195 + array[i] += delta;
163.196 + return v;
163.197 + }
163.198 +
163.199 + /**
163.200 + * Atomically increments by one the element at index {@code i}.
163.201 + *
163.202 + * @param i the index
163.203 + * @return the updated value
163.204 + */
163.205 + public final long incrementAndGet(int i) {
163.206 + return addAndGet(i, 1);
163.207 + }
163.208 +
163.209 + /**
163.210 + * Atomically decrements by one the element at index {@code i}.
163.211 + *
163.212 + * @param i the index
163.213 + * @return the updated value
163.214 + */
163.215 + public final long decrementAndGet(int i) {
163.216 + return addAndGet(i, -1);
163.217 + }
163.218 +
163.219 + /**
163.220 + * Atomically adds the given value to the element at index {@code i}.
163.221 + *
163.222 + * @param i the index
163.223 + * @param delta the value to add
163.224 + * @return the updated value
163.225 + */
163.226 + public long addAndGet(int i, long delta) {
163.227 + array[i] += delta;
163.228 + return array[i];
163.229 + }
163.230 +
163.231 + /**
163.232 + * Returns the String representation of the current values of array.
163.233 + * @return the String representation of the current values of array
163.234 + */
163.235 + public String toString() {
163.236 + int iMax = array.length - 1;
163.237 + if (iMax == -1)
163.238 + return "[]";
163.239 +
163.240 + StringBuilder b = new StringBuilder();
163.241 + b.append('[');
163.242 + for (int i = 0; ; i++) {
163.243 + b.append(get(i));
163.244 + if (i == iMax)
163.245 + return b.append(']').toString();
163.246 + b.append(',').append(' ');
163.247 + }
163.248 + }
163.249 +
163.250 +}
164.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
164.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicReference.java Tue Feb 11 13:31:42 2014 +0100
164.3 @@ -0,0 +1,149 @@
164.4 +/*
164.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
164.6 + *
164.7 + * This code is free software; you can redistribute it and/or modify it
164.8 + * under the terms of the GNU General Public License version 2 only, as
164.9 + * published by the Free Software Foundation. Oracle designates this
164.10 + * particular file as subject to the "Classpath" exception as provided
164.11 + * by Oracle in the LICENSE file that accompanied this code.
164.12 + *
164.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
164.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
164.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
164.16 + * version 2 for more details (a copy is included in the LICENSE file that
164.17 + * accompanied this code).
164.18 + *
164.19 + * You should have received a copy of the GNU General Public License version
164.20 + * 2 along with this work; if not, write to the Free Software Foundation,
164.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
164.22 + *
164.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
164.24 + * or visit www.oracle.com if you need additional information or have any
164.25 + * questions.
164.26 + */
164.27 +
164.28 +/*
164.29 + * This file is available under and governed by the GNU General Public
164.30 + * License version 2 only, as published by the Free Software Foundation.
164.31 + * However, the following notice accompanied the original version of this
164.32 + * file:
164.33 + *
164.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
164.35 + * Expert Group and released to the public domain, as explained at
164.36 + * http://creativecommons.org/publicdomain/zero/1.0/
164.37 + */
164.38 +
164.39 +package java.util.concurrent.atomic;
164.40 +
164.41 +/**
164.42 + * An object reference that may be updated atomically. See the {@link
164.43 + * java.util.concurrent.atomic} package specification for description
164.44 + * of the properties of atomic variables.
164.45 + * @since 1.5
164.46 + * @author Doug Lea
164.47 + * @param <V> The type of object referred to by this reference
164.48 + */
164.49 +public class AtomicReference<V> implements java.io.Serializable {
164.50 + private static final long serialVersionUID = -1848883965231344442L;
164.51 +
164.52 + private volatile V value;
164.53 +
164.54 + /**
164.55 + * Creates a new AtomicReference with the given initial value.
164.56 + *
164.57 + * @param initialValue the initial value
164.58 + */
164.59 + public AtomicReference(V initialValue) {
164.60 + value = initialValue;
164.61 + }
164.62 +
164.63 + /**
164.64 + * Creates a new AtomicReference with null initial value.
164.65 + */
164.66 + public AtomicReference() {
164.67 + }
164.68 +
164.69 + /**
164.70 + * Gets the current value.
164.71 + *
164.72 + * @return the current value
164.73 + */
164.74 + public final V get() {
164.75 + return value;
164.76 + }
164.77 +
164.78 + /**
164.79 + * Sets to the given value.
164.80 + *
164.81 + * @param newValue the new value
164.82 + */
164.83 + public final void set(V newValue) {
164.84 + value = newValue;
164.85 + }
164.86 +
164.87 + /**
164.88 + * Eventually sets to the given value.
164.89 + *
164.90 + * @param newValue the new value
164.91 + * @since 1.6
164.92 + */
164.93 + public final void lazySet(V newValue) {
164.94 + value = newValue;
164.95 + }
164.96 +
164.97 + /**
164.98 + * Atomically sets the value to the given updated value
164.99 + * if the current value {@code ==} the expected value.
164.100 + * @param expect the expected value
164.101 + * @param update the new value
164.102 + * @return true if successful. False return indicates that
164.103 + * the actual value was not equal to the expected value.
164.104 + */
164.105 + public final boolean compareAndSet(V expect, V update) {
164.106 + if (value == expect) {
164.107 + value = update;
164.108 + return true;
164.109 + } else {
164.110 + return false;
164.111 + }
164.112 + }
164.113 +
164.114 + /**
164.115 + * Atomically sets the value to the given updated value
164.116 + * if the current value {@code ==} the expected value.
164.117 + *
164.118 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
164.119 + * and does not provide ordering guarantees, so is only rarely an
164.120 + * appropriate alternative to {@code compareAndSet}.
164.121 + *
164.122 + * @param expect the expected value
164.123 + * @param update the new value
164.124 + * @return true if successful.
164.125 + */
164.126 + public final boolean weakCompareAndSet(V expect, V update) {
164.127 + return compareAndSet(expect, update);
164.128 + }
164.129 +
164.130 + /**
164.131 + * Atomically sets to the given value and returns the old value.
164.132 + *
164.133 + * @param newValue the new value
164.134 + * @return the previous value
164.135 + */
164.136 + public final V getAndSet(V newValue) {
164.137 + while (true) {
164.138 + V x = get();
164.139 + if (compareAndSet(x, newValue))
164.140 + return x;
164.141 + }
164.142 + }
164.143 +
164.144 + /**
164.145 + * Returns the String representation of the current value.
164.146 + * @return the String representation of the current value.
164.147 + */
164.148 + public String toString() {
164.149 + return String.valueOf(get());
164.150 + }
164.151 +
164.152 +}
165.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
165.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java Tue Feb 11 13:31:42 2014 +0100
165.3 @@ -0,0 +1,184 @@
165.4 +/*
165.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
165.6 + *
165.7 + * This code is free software; you can redistribute it and/or modify it
165.8 + * under the terms of the GNU General Public License version 2 only, as
165.9 + * published by the Free Software Foundation. Oracle designates this
165.10 + * particular file as subject to the "Classpath" exception as provided
165.11 + * by Oracle in the LICENSE file that accompanied this code.
165.12 + *
165.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
165.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
165.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
165.16 + * version 2 for more details (a copy is included in the LICENSE file that
165.17 + * accompanied this code).
165.18 + *
165.19 + * You should have received a copy of the GNU General Public License version
165.20 + * 2 along with this work; if not, write to the Free Software Foundation,
165.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
165.22 + *
165.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
165.24 + * or visit www.oracle.com if you need additional information or have any
165.25 + * questions.
165.26 + */
165.27 +
165.28 +/*
165.29 + * This file is available under and governed by the GNU General Public
165.30 + * License version 2 only, as published by the Free Software Foundation.
165.31 + * However, the following notice accompanied the original version of this
165.32 + * file:
165.33 + *
165.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
165.35 + * Expert Group and released to the public domain, as explained at
165.36 + * http://creativecommons.org/publicdomain/zero/1.0/
165.37 + */
165.38 +
165.39 +package java.util.concurrent.atomic;
165.40 +
165.41 +/**
165.42 + * An array of object references in which elements may be updated
165.43 + * atomically. See the {@link java.util.concurrent.atomic} package
165.44 + * specification for description of the properties of atomic
165.45 + * variables.
165.46 + * @since 1.5
165.47 + * @author Doug Lea
165.48 + * @param <E> The base class of elements held in this array
165.49 + */
165.50 +public class AtomicReferenceArray<E> implements java.io.Serializable {
165.51 + private static final long serialVersionUID = -6209656149925076980L;
165.52 +
165.53 + private final Object[] array;
165.54 +
165.55 + /**
165.56 + * Creates a new AtomicReferenceArray of the given length, with all
165.57 + * elements initially null.
165.58 + *
165.59 + * @param length the length of the array
165.60 + */
165.61 + public AtomicReferenceArray(int length) {
165.62 + array = new Object[length];
165.63 + }
165.64 +
165.65 + /**
165.66 + * Creates a new AtomicReferenceArray with the same length as, and
165.67 + * all elements copied from, the given array.
165.68 + *
165.69 + * @param array the array to copy elements from
165.70 + * @throws NullPointerException if array is null
165.71 + */
165.72 + public AtomicReferenceArray(E[] array) {
165.73 + // Visibility guaranteed by final field guarantees
165.74 + this.array = array.clone();
165.75 + }
165.76 +
165.77 + /**
165.78 + * Returns the length of the array.
165.79 + *
165.80 + * @return the length of the array
165.81 + */
165.82 + public final int length() {
165.83 + return array.length;
165.84 + }
165.85 +
165.86 + /**
165.87 + * Gets the current value at position {@code i}.
165.88 + *
165.89 + * @param i the index
165.90 + * @return the current value
165.91 + */
165.92 + public final E get(int i) {
165.93 + return (E)array[i];
165.94 + }
165.95 +
165.96 + /**
165.97 + * Sets the element at position {@code i} to the given value.
165.98 + *
165.99 + * @param i the index
165.100 + * @param newValue the new value
165.101 + */
165.102 + public final void set(int i, E newValue) {
165.103 + array[i] = newValue;
165.104 + }
165.105 +
165.106 + /**
165.107 + * Eventually sets the element at position {@code i} to the given value.
165.108 + *
165.109 + * @param i the index
165.110 + * @param newValue the new value
165.111 + * @since 1.6
165.112 + */
165.113 + public final void lazySet(int i, E newValue) {
165.114 + array[i] = newValue;
165.115 + }
165.116 +
165.117 +
165.118 + /**
165.119 + * Atomically sets the element at position {@code i} to the given
165.120 + * value and returns the old value.
165.121 + *
165.122 + * @param i the index
165.123 + * @param newValue the new value
165.124 + * @return the previous value
165.125 + */
165.126 + public final E getAndSet(int i, E newValue) {
165.127 + E v = (E)array[i];
165.128 + array[i] = newValue;
165.129 + return v;
165.130 + }
165.131 +
165.132 + /**
165.133 + * Atomically sets the element at position {@code i} to the given
165.134 + * updated value if the current value {@code ==} the expected value.
165.135 + *
165.136 + * @param i the index
165.137 + * @param expect the expected value
165.138 + * @param update the new value
165.139 + * @return true if successful. False return indicates that
165.140 + * the actual value was not equal to the expected value.
165.141 + */
165.142 + public final boolean compareAndSet(int i, E expect, E update) {
165.143 + if (array[i] == expect) {
165.144 + array[i] = update;
165.145 + return true;
165.146 + } else {
165.147 + return false;
165.148 + }
165.149 + }
165.150 +
165.151 + /**
165.152 + * Atomically sets the element at position {@code i} to the given
165.153 + * updated value if the current value {@code ==} the expected value.
165.154 + *
165.155 + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
165.156 + * and does not provide ordering guarantees, so is only rarely an
165.157 + * appropriate alternative to {@code compareAndSet}.
165.158 + *
165.159 + * @param i the index
165.160 + * @param expect the expected value
165.161 + * @param update the new value
165.162 + * @return true if successful.
165.163 + */
165.164 + public final boolean weakCompareAndSet(int i, E expect, E update) {
165.165 + return compareAndSet(i, expect, update);
165.166 + }
165.167 +
165.168 + /**
165.169 + * Returns the String representation of the current values of array.
165.170 + * @return the String representation of the current values of array
165.171 + */
165.172 + public String toString() {
165.173 + int iMax = array.length - 1;
165.174 + if (iMax == -1)
165.175 + return "[]";
165.176 +
165.177 + StringBuilder b = new StringBuilder();
165.178 + b.append('[');
165.179 + for (int i = 0; ; i++) {
165.180 + b.append(get(i));
165.181 + if (i == iMax)
165.182 + return b.append(']').toString();
165.183 + b.append(',').append(' ');
165.184 + }
165.185 + }
165.186 +
165.187 +}
166.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
166.2 +++ b/rt/emul/compact/src/main/java/java/util/concurrent/atomic/package-info.java Tue Feb 11 13:31:42 2014 +0100
166.3 @@ -0,0 +1,199 @@
166.4 +/*
166.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
166.6 + *
166.7 + * This code is free software; you can redistribute it and/or modify it
166.8 + * under the terms of the GNU General Public License version 2 only, as
166.9 + * published by the Free Software Foundation. Oracle designates this
166.10 + * particular file as subject to the "Classpath" exception as provided
166.11 + * by Oracle in the LICENSE file that accompanied this code.
166.12 + *
166.13 + * This code is distributed in the hope that it will be useful, but WITHOUT
166.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
166.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
166.16 + * version 2 for more details (a copy is included in the LICENSE file that
166.17 + * accompanied this code).
166.18 + *
166.19 + * You should have received a copy of the GNU General Public License version
166.20 + * 2 along with this work; if not, write to the Free Software Foundation,
166.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
166.22 + *
166.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
166.24 + * or visit www.oracle.com if you need additional information or have any
166.25 + * questions.
166.26 + */
166.27 +
166.28 +/*
166.29 + * This file is available under and governed by the GNU General Public
166.30 + * License version 2 only, as published by the Free Software Foundation.
166.31 + * However, the following notice accompanied the original version of this
166.32 + * file:
166.33 + *
166.34 + * Written by Doug Lea with assistance from members of JCP JSR-166
166.35 + * Expert Group and released to the public domain, as explained at
166.36 + * http://creativecommons.org/publicdomain/zero/1.0/
166.37 + */
166.38 +
166.39 +/**
166.40 + * A small toolkit of classes that support lock-free thread-safe
166.41 + * programming on single variables. In essence, the classes in this
166.42 + * package extend the notion of {@code volatile} values, fields, and
166.43 + * array elements to those that also provide an atomic conditional update
166.44 + * operation of the form:
166.45 + *
166.46 + * <pre>
166.47 + * boolean compareAndSet(expectedValue, updateValue);
166.48 + * </pre>
166.49 + *
166.50 + * <p>This method (which varies in argument types across different
166.51 + * classes) atomically sets a variable to the {@code updateValue} if it
166.52 + * currently holds the {@code expectedValue}, reporting {@code true} on
166.53 + * success. The classes in this package also contain methods to get and
166.54 + * unconditionally set values, as well as a weaker conditional atomic
166.55 + * update operation {@code weakCompareAndSet} described below.
166.56 + *
166.57 + * <p>The specifications of these methods enable implementations to
166.58 + * employ efficient machine-level atomic instructions that are available
166.59 + * on contemporary processors. However on some platforms, support may
166.60 + * entail some form of internal locking. Thus the methods are not
166.61 + * strictly guaranteed to be non-blocking --
166.62 + * a thread may block transiently before performing the operation.
166.63 + *
166.64 + * <p>Instances of classes
166.65 + * {@link java.util.concurrent.atomic.AtomicBoolean},
166.66 + * {@link java.util.concurrent.atomic.AtomicInteger},
166.67 + * {@link java.util.concurrent.atomic.AtomicLong}, and
166.68 + * {@link java.util.concurrent.atomic.AtomicReference}
166.69 + * each provide access and updates to a single variable of the
166.70 + * corresponding type. Each class also provides appropriate utility
166.71 + * methods for that type. For example, classes {@code AtomicLong} and
166.72 + * {@code AtomicInteger} provide atomic increment methods. One
166.73 + * application is to generate sequence numbers, as in:
166.74 + *
166.75 + * <pre>
166.76 + * class Sequencer {
166.77 + * private final AtomicLong sequenceNumber
166.78 + * = new AtomicLong(0);
166.79 + * public long next() {
166.80 + * return sequenceNumber.getAndIncrement();
166.81 + * }
166.82 + * }
166.83 + * </pre>
166.84 + *
166.85 + * <p>The memory effects for accesses and updates of atomics generally
166.86 + * follow the rules for volatiles, as stated in section 17.4 of
166.87 + * <cite>The Java™ Language Specification</cite>.
166.88 + *
166.89 + * <ul>
166.90 + *
166.91 + * <li> {@code get} has the memory effects of reading a
166.92 + * {@code volatile} variable.
166.93 + *
166.94 + * <li> {@code set} has the memory effects of writing (assigning) a
166.95 + * {@code volatile} variable.
166.96 + *
166.97 + * <li> {@code lazySet} has the memory effects of writing (assigning)
166.98 + * a {@code volatile} variable except that it permits reorderings with
166.99 + * subsequent (but not previous) memory actions that do not themselves
166.100 + * impose reordering constraints with ordinary non-{@code volatile}
166.101 + * writes. Among other usage contexts, {@code lazySet} may apply when
166.102 + * nulling out, for the sake of garbage collection, a reference that is
166.103 + * never accessed again.
166.104 + *
166.105 + * <li>{@code weakCompareAndSet} atomically reads and conditionally
166.106 + * writes a variable but does <em>not</em>
166.107 + * create any happens-before orderings, so provides no guarantees
166.108 + * with respect to previous or subsequent reads and writes of any
166.109 + * variables other than the target of the {@code weakCompareAndSet}.
166.110 + *
166.111 + * <li> {@code compareAndSet}
166.112 + * and all other read-and-update operations such as {@code getAndIncrement}
166.113 + * have the memory effects of both reading and
166.114 + * writing {@code volatile} variables.
166.115 + * </ul>
166.116 + *
166.117 + * <p>In addition to classes representing single values, this package
166.118 + * contains <em>Updater</em> classes that can be used to obtain
166.119 + * {@code compareAndSet} operations on any selected {@code volatile}
166.120 + * field of any selected class.
166.121 + *
166.122 + * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater},
166.123 + * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and
166.124 + * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are
166.125 + * reflection-based utilities that provide access to the associated
166.126 + * field types. These are mainly of use in atomic data structures in
166.127 + * which several {@code volatile} fields of the same node (for
166.128 + * example, the links of a tree node) are independently subject to
166.129 + * atomic updates. These classes enable greater flexibility in how
166.130 + * and when to use atomic updates, at the expense of more awkward
166.131 + * reflection-based setup, less convenient usage, and weaker
166.132 + * guarantees.
166.133 + *
166.134 + * <p>The
166.135 + * {@link java.util.concurrent.atomic.AtomicIntegerArray},
166.136 + * {@link java.util.concurrent.atomic.AtomicLongArray}, and
166.137 + * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes
166.138 + * further extend atomic operation support to arrays of these types.
166.139 + * These classes are also notable in providing {@code volatile} access
166.140 + * semantics for their array elements, which is not supported for
166.141 + * ordinary arrays.
166.142 + *
166.143 + * <a name="Spurious">
166.144 + * <p>The atomic classes also support method {@code weakCompareAndSet},
166.145 + * which has limited applicability. On some platforms, the weak version
166.146 + * may be more efficient than {@code compareAndSet} in the normal case,
166.147 + * but differs in that any given invocation of the
166.148 + * {@code weakCompareAndSet} method may return {@code false}
166.149 + * <em>spuriously</em> (that is, for no apparent reason)</a>. A
166.150 + * {@code false} return means only that the operation may be retried if
166.151 + * desired, relying on the guarantee that repeated invocation when the
166.152 + * variable holds {@code expectedValue} and no other thread is also
166.153 + * attempting to set the variable will eventually succeed. (Such
166.154 + * spurious failures may for example be due to memory contention effects
166.155 + * that are unrelated to whether the expected and current values are
166.156 + * equal.) Additionally {@code weakCompareAndSet} does not provide
166.157 + * ordering guarantees that are usually needed for synchronization
166.158 + * control. However, the method may be useful for updating counters and
166.159 + * statistics when such updates are unrelated to the other
166.160 + * happens-before orderings of a program. When a thread sees an update
166.161 + * to an atomic variable caused by a {@code weakCompareAndSet}, it does
166.162 + * not necessarily see updates to any <em>other</em> variables that
166.163 + * occurred before the {@code weakCompareAndSet}. This may be
166.164 + * acceptable when, for example, updating performance statistics, but
166.165 + * rarely otherwise.
166.166 + *
166.167 + * <p>The {@link java.util.concurrent.atomic.AtomicMarkableReference}
166.168 + * class associates a single boolean with a reference. For example, this
166.169 + * bit might be used inside a data structure to mean that the object
166.170 + * being referenced has logically been deleted.
166.171 + *
166.172 + * The {@link java.util.concurrent.atomic.AtomicStampedReference}
166.173 + * class associates an integer value with a reference. This may be
166.174 + * used for example, to represent version numbers corresponding to
166.175 + * series of updates.
166.176 + *
166.177 + * <p>Atomic classes are designed primarily as building blocks for
166.178 + * implementing non-blocking data structures and related infrastructure
166.179 + * classes. The {@code compareAndSet} method is not a general
166.180 + * replacement for locking. It applies only when critical updates for an
166.181 + * object are confined to a <em>single</em> variable.
166.182 + *
166.183 + * <p>Atomic classes are not general purpose replacements for
166.184 + * {@code java.lang.Integer} and related classes. They do <em>not</em>
166.185 + * define methods such as {@code hashCode} and
166.186 + * {@code compareTo}. (Because atomic variables are expected to be
166.187 + * mutated, they are poor choices for hash table keys.) Additionally,
166.188 + * classes are provided only for those types that are commonly useful in
166.189 + * intended applications. For example, there is no atomic class for
166.190 + * representing {@code byte}. In those infrequent cases where you would
166.191 + * like to do so, you can use an {@code AtomicInteger} to hold
166.192 + * {@code byte} values, and cast appropriately.
166.193 + *
166.194 + * You can also hold floats using
166.195 + * {@link java.lang.Float#floatToIntBits} and
166.196 + * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using
166.197 + * {@link java.lang.Double#doubleToLongBits} and
166.198 + * {@link java.lang.Double#longBitsToDouble} conversions.
166.199 + *
166.200 + * @since 1.5
166.201 + */
166.202 +package java.util.concurrent.atomic;
167.1 --- a/rt/emul/compact/src/main/java/java/util/logging/Logger.java Tue Feb 11 10:48:24 2014 +0100
167.2 +++ b/rt/emul/compact/src/main/java/java/util/logging/Logger.java Tue Feb 11 13:31:42 2014 +0100
167.3 @@ -457,16 +457,22 @@
167.4 case "WARNING": method = "warn"; break;
167.5 default: method = "log"; break;
167.6 }
167.7 +
167.8 + String msg = record.getMessage();
167.9 + final Object[] params = record.getParameters();
167.10 + if (params != null && params.length != 0) {
167.11 + for (int i = 0; i < params.length; i++) {
167.12 + msg = msg.replace("{" + i + "}", params[i] == null ? "null" : params[i].toString());
167.13 + }
167.14 + }
167.15
167.16 consoleLog(
167.17 method,
167.18 - record.getLoggerName(),
167.19 - record.getMessage()
167.20 - );
167.21 + record.getLoggerName(), msg);
167.22 }
167.23
167.24 @JavaScriptBody(args = { "method", "logger", "msg" }, body =
167.25 - "window.console[method]('[' + logger + ']: ' + msg);"
167.26 + "if (typeof console !== 'undefined') console[method]('[' + logger + ']: ' + msg);"
167.27 )
167.28 private static native void consoleLog(
167.29 String method, String logger, String msg
168.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
168.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/ASCII.java Tue Feb 11 13:31:42 2014 +0100
168.3 @@ -0,0 +1,274 @@
168.4 +/*
168.5 + * Copyright (c) 1999, 2000, Oracle and/or its affiliates. All rights reserved.
168.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
168.7 + *
168.8 + * This code is free software; you can redistribute it and/or modify it
168.9 + * under the terms of the GNU General Public License version 2 only, as
168.10 + * published by the Free Software Foundation. Oracle designates this
168.11 + * particular file as subject to the "Classpath" exception as provided
168.12 + * by Oracle in the LICENSE file that accompanied this code.
168.13 + *
168.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
168.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
168.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
168.17 + * version 2 for more details (a copy is included in the LICENSE file that
168.18 + * accompanied this code).
168.19 + *
168.20 + * You should have received a copy of the GNU General Public License version
168.21 + * 2 along with this work; if not, write to the Free Software Foundation,
168.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
168.23 + *
168.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
168.25 + * or visit www.oracle.com if you need additional information or have any
168.26 + * questions.
168.27 + */
168.28 +
168.29 +package java.util.regex;
168.30 +
168.31 +
168.32 +/**
168.33 + * Utility class that implements the standard C ctype functionality.
168.34 + *
168.35 + * @author Hong Zhang
168.36 + */
168.37 +
168.38 +final class ASCII {
168.39 +
168.40 + static final int UPPER = 0x00000100;
168.41 +
168.42 + static final int LOWER = 0x00000200;
168.43 +
168.44 + static final int DIGIT = 0x00000400;
168.45 +
168.46 + static final int SPACE = 0x00000800;
168.47 +
168.48 + static final int PUNCT = 0x00001000;
168.49 +
168.50 + static final int CNTRL = 0x00002000;
168.51 +
168.52 + static final int BLANK = 0x00004000;
168.53 +
168.54 + static final int HEX = 0x00008000;
168.55 +
168.56 + static final int UNDER = 0x00010000;
168.57 +
168.58 + static final int ASCII = 0x0000FF00;
168.59 +
168.60 + static final int ALPHA = (UPPER|LOWER);
168.61 +
168.62 + static final int ALNUM = (UPPER|LOWER|DIGIT);
168.63 +
168.64 + static final int GRAPH = (PUNCT|UPPER|LOWER|DIGIT);
168.65 +
168.66 + static final int WORD = (UPPER|LOWER|UNDER|DIGIT);
168.67 +
168.68 + static final int XDIGIT = (HEX);
168.69 +
168.70 + private static final int[] ctype = new int[] {
168.71 + CNTRL, /* 00 (NUL) */
168.72 + CNTRL, /* 01 (SOH) */
168.73 + CNTRL, /* 02 (STX) */
168.74 + CNTRL, /* 03 (ETX) */
168.75 + CNTRL, /* 04 (EOT) */
168.76 + CNTRL, /* 05 (ENQ) */
168.77 + CNTRL, /* 06 (ACK) */
168.78 + CNTRL, /* 07 (BEL) */
168.79 + CNTRL, /* 08 (BS) */
168.80 + SPACE+CNTRL+BLANK, /* 09 (HT) */
168.81 + SPACE+CNTRL, /* 0A (LF) */
168.82 + SPACE+CNTRL, /* 0B (VT) */
168.83 + SPACE+CNTRL, /* 0C (FF) */
168.84 + SPACE+CNTRL, /* 0D (CR) */
168.85 + CNTRL, /* 0E (SI) */
168.86 + CNTRL, /* 0F (SO) */
168.87 + CNTRL, /* 10 (DLE) */
168.88 + CNTRL, /* 11 (DC1) */
168.89 + CNTRL, /* 12 (DC2) */
168.90 + CNTRL, /* 13 (DC3) */
168.91 + CNTRL, /* 14 (DC4) */
168.92 + CNTRL, /* 15 (NAK) */
168.93 + CNTRL, /* 16 (SYN) */
168.94 + CNTRL, /* 17 (ETB) */
168.95 + CNTRL, /* 18 (CAN) */
168.96 + CNTRL, /* 19 (EM) */
168.97 + CNTRL, /* 1A (SUB) */
168.98 + CNTRL, /* 1B (ESC) */
168.99 + CNTRL, /* 1C (FS) */
168.100 + CNTRL, /* 1D (GS) */
168.101 + CNTRL, /* 1E (RS) */
168.102 + CNTRL, /* 1F (US) */
168.103 + SPACE+BLANK, /* 20 SPACE */
168.104 + PUNCT, /* 21 ! */
168.105 + PUNCT, /* 22 " */
168.106 + PUNCT, /* 23 # */
168.107 + PUNCT, /* 24 $ */
168.108 + PUNCT, /* 25 % */
168.109 + PUNCT, /* 26 & */
168.110 + PUNCT, /* 27 ' */
168.111 + PUNCT, /* 28 ( */
168.112 + PUNCT, /* 29 ) */
168.113 + PUNCT, /* 2A * */
168.114 + PUNCT, /* 2B + */
168.115 + PUNCT, /* 2C , */
168.116 + PUNCT, /* 2D - */
168.117 + PUNCT, /* 2E . */
168.118 + PUNCT, /* 2F / */
168.119 + DIGIT+HEX+0, /* 30 0 */
168.120 + DIGIT+HEX+1, /* 31 1 */
168.121 + DIGIT+HEX+2, /* 32 2 */
168.122 + DIGIT+HEX+3, /* 33 3 */
168.123 + DIGIT+HEX+4, /* 34 4 */
168.124 + DIGIT+HEX+5, /* 35 5 */
168.125 + DIGIT+HEX+6, /* 36 6 */
168.126 + DIGIT+HEX+7, /* 37 7 */
168.127 + DIGIT+HEX+8, /* 38 8 */
168.128 + DIGIT+HEX+9, /* 39 9 */
168.129 + PUNCT, /* 3A : */
168.130 + PUNCT, /* 3B ; */
168.131 + PUNCT, /* 3C < */
168.132 + PUNCT, /* 3D = */
168.133 + PUNCT, /* 3E > */
168.134 + PUNCT, /* 3F ? */
168.135 + PUNCT, /* 40 @ */
168.136 + UPPER+HEX+10, /* 41 A */
168.137 + UPPER+HEX+11, /* 42 B */
168.138 + UPPER+HEX+12, /* 43 C */
168.139 + UPPER+HEX+13, /* 44 D */
168.140 + UPPER+HEX+14, /* 45 E */
168.141 + UPPER+HEX+15, /* 46 F */
168.142 + UPPER+16, /* 47 G */
168.143 + UPPER+17, /* 48 H */
168.144 + UPPER+18, /* 49 I */
168.145 + UPPER+19, /* 4A J */
168.146 + UPPER+20, /* 4B K */
168.147 + UPPER+21, /* 4C L */
168.148 + UPPER+22, /* 4D M */
168.149 + UPPER+23, /* 4E N */
168.150 + UPPER+24, /* 4F O */
168.151 + UPPER+25, /* 50 P */
168.152 + UPPER+26, /* 51 Q */
168.153 + UPPER+27, /* 52 R */
168.154 + UPPER+28, /* 53 S */
168.155 + UPPER+29, /* 54 T */
168.156 + UPPER+30, /* 55 U */
168.157 + UPPER+31, /* 56 V */
168.158 + UPPER+32, /* 57 W */
168.159 + UPPER+33, /* 58 X */
168.160 + UPPER+34, /* 59 Y */
168.161 + UPPER+35, /* 5A Z */
168.162 + PUNCT, /* 5B [ */
168.163 + PUNCT, /* 5C \ */
168.164 + PUNCT, /* 5D ] */
168.165 + PUNCT, /* 5E ^ */
168.166 + PUNCT|UNDER, /* 5F _ */
168.167 + PUNCT, /* 60 ` */
168.168 + LOWER+HEX+10, /* 61 a */
168.169 + LOWER+HEX+11, /* 62 b */
168.170 + LOWER+HEX+12, /* 63 c */
168.171 + LOWER+HEX+13, /* 64 d */
168.172 + LOWER+HEX+14, /* 65 e */
168.173 + LOWER+HEX+15, /* 66 f */
168.174 + LOWER+16, /* 67 g */
168.175 + LOWER+17, /* 68 h */
168.176 + LOWER+18, /* 69 i */
168.177 + LOWER+19, /* 6A j */
168.178 + LOWER+20, /* 6B k */
168.179 + LOWER+21, /* 6C l */
168.180 + LOWER+22, /* 6D m */
168.181 + LOWER+23, /* 6E n */
168.182 + LOWER+24, /* 6F o */
168.183 + LOWER+25, /* 70 p */
168.184 + LOWER+26, /* 71 q */
168.185 + LOWER+27, /* 72 r */
168.186 + LOWER+28, /* 73 s */
168.187 + LOWER+29, /* 74 t */
168.188 + LOWER+30, /* 75 u */
168.189 + LOWER+31, /* 76 v */
168.190 + LOWER+32, /* 77 w */
168.191 + LOWER+33, /* 78 x */
168.192 + LOWER+34, /* 79 y */
168.193 + LOWER+35, /* 7A z */
168.194 + PUNCT, /* 7B { */
168.195 + PUNCT, /* 7C | */
168.196 + PUNCT, /* 7D } */
168.197 + PUNCT, /* 7E ~ */
168.198 + CNTRL, /* 7F (DEL) */
168.199 + };
168.200 +
168.201 + static int getType(int ch) {
168.202 + return ((ch & 0xFFFFFF80) == 0 ? ctype[ch] : 0);
168.203 + }
168.204 +
168.205 + static boolean isType(int ch, int type) {
168.206 + return (getType(ch) & type) != 0;
168.207 + }
168.208 +
168.209 + static boolean isAscii(int ch) {
168.210 + return ((ch & 0xFFFFFF80) == 0);
168.211 + }
168.212 +
168.213 + static boolean isAlpha(int ch) {
168.214 + return isType(ch, ALPHA);
168.215 + }
168.216 +
168.217 + static boolean isDigit(int ch) {
168.218 + return ((ch-'0')|('9'-ch)) >= 0;
168.219 + }
168.220 +
168.221 + static boolean isAlnum(int ch) {
168.222 + return isType(ch, ALNUM);
168.223 + }
168.224 +
168.225 + static boolean isGraph(int ch) {
168.226 + return isType(ch, GRAPH);
168.227 + }
168.228 +
168.229 + static boolean isPrint(int ch) {
168.230 + return ((ch-0x20)|(0x7E-ch)) >= 0;
168.231 + }
168.232 +
168.233 + static boolean isPunct(int ch) {
168.234 + return isType(ch, PUNCT);
168.235 + }
168.236 +
168.237 + static boolean isSpace(int ch) {
168.238 + return isType(ch, SPACE);
168.239 + }
168.240 +
168.241 + static boolean isHexDigit(int ch) {
168.242 + return isType(ch, HEX);
168.243 + }
168.244 +
168.245 + static boolean isOctDigit(int ch) {
168.246 + return ((ch-'0')|('7'-ch)) >= 0;
168.247 + }
168.248 +
168.249 + static boolean isCntrl(int ch) {
168.250 + return isType(ch, CNTRL);
168.251 + }
168.252 +
168.253 + static boolean isLower(int ch) {
168.254 + return ((ch-'a')|('z'-ch)) >= 0;
168.255 + }
168.256 +
168.257 + static boolean isUpper(int ch) {
168.258 + return ((ch-'A')|('Z'-ch)) >= 0;
168.259 + }
168.260 +
168.261 + static boolean isWord(int ch) {
168.262 + return isType(ch, WORD);
168.263 + }
168.264 +
168.265 + static int toDigit(int ch) {
168.266 + return (ctype[ch & 0x7F] & 0x3F);
168.267 + }
168.268 +
168.269 + static int toLower(int ch) {
168.270 + return isUpper(ch) ? (ch + 0x20) : ch;
168.271 + }
168.272 +
168.273 + static int toUpper(int ch) {
168.274 + return isLower(ch) ? (ch - 0x20) : ch;
168.275 + }
168.276 +
168.277 +}
169.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
169.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/MatchResult.java Tue Feb 11 13:31:42 2014 +0100
169.3 @@ -0,0 +1,188 @@
169.4 +/*
169.5 + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
169.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
169.7 + *
169.8 + * This code is free software; you can redistribute it and/or modify it
169.9 + * under the terms of the GNU General Public License version 2 only, as
169.10 + * published by the Free Software Foundation. Oracle designates this
169.11 + * particular file as subject to the "Classpath" exception as provided
169.12 + * by Oracle in the LICENSE file that accompanied this code.
169.13 + *
169.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
169.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
169.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
169.17 + * version 2 for more details (a copy is included in the LICENSE file that
169.18 + * accompanied this code).
169.19 + *
169.20 + * You should have received a copy of the GNU General Public License version
169.21 + * 2 along with this work; if not, write to the Free Software Foundation,
169.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
169.23 + *
169.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
169.25 + * or visit www.oracle.com if you need additional information or have any
169.26 + * questions.
169.27 + */
169.28 +
169.29 +package java.util.regex;
169.30 +
169.31 +/**
169.32 + * The result of a match operation.
169.33 + *
169.34 + * <p>This interface contains query methods used to determine the
169.35 + * results of a match against a regular expression. The match boundaries,
169.36 + * groups and group boundaries can be seen but not modified through
169.37 + * a <code>MatchResult</code>.
169.38 + *
169.39 + * @author Michael McCloskey
169.40 + * @see Matcher
169.41 + * @since 1.5
169.42 + */
169.43 +public interface MatchResult {
169.44 +
169.45 + /**
169.46 + * Returns the start index of the match.
169.47 + *
169.48 + * @return The index of the first character matched
169.49 + *
169.50 + * @throws IllegalStateException
169.51 + * If no match has yet been attempted,
169.52 + * or if the previous match operation failed
169.53 + */
169.54 + public int start();
169.55 +
169.56 + /**
169.57 + * Returns the start index of the subsequence captured by the given group
169.58 + * during this match.
169.59 + *
169.60 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
169.61 + * to right, starting at one. Group zero denotes the entire pattern, so
169.62 + * the expression <i>m.</i><tt>start(0)</tt> is equivalent to
169.63 + * <i>m.</i><tt>start()</tt>. </p>
169.64 + *
169.65 + * @param group
169.66 + * The index of a capturing group in this matcher's pattern
169.67 + *
169.68 + * @return The index of the first character captured by the group,
169.69 + * or <tt>-1</tt> if the match was successful but the group
169.70 + * itself did not match anything
169.71 + *
169.72 + * @throws IllegalStateException
169.73 + * If no match has yet been attempted,
169.74 + * or if the previous match operation failed
169.75 + *
169.76 + * @throws IndexOutOfBoundsException
169.77 + * If there is no capturing group in the pattern
169.78 + * with the given index
169.79 + */
169.80 + public int start(int group);
169.81 +
169.82 + /**
169.83 + * Returns the offset after the last character matched. </p>
169.84 + *
169.85 + * @return @return The offset after the last character matched
169.86 + *
169.87 + * @throws IllegalStateException
169.88 + * If no match has yet been attempted,
169.89 + * or if the previous match operation failed
169.90 + */
169.91 + public int end();
169.92 +
169.93 + /**
169.94 + * Returns the offset after the last character of the subsequence
169.95 + * captured by the given group during this match.
169.96 + *
169.97 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
169.98 + * to right, starting at one. Group zero denotes the entire pattern, so
169.99 + * the expression <i>m.</i><tt>end(0)</tt> is equivalent to
169.100 + * <i>m.</i><tt>end()</tt>. </p>
169.101 + *
169.102 + * @param group
169.103 + * The index of a capturing group in this matcher's pattern
169.104 + *
169.105 + * @return The offset after the last character captured by the group,
169.106 + * or <tt>-1</tt> if the match was successful
169.107 + * but the group itself did not match anything
169.108 + *
169.109 + * @throws IllegalStateException
169.110 + * If no match has yet been attempted,
169.111 + * or if the previous match operation failed
169.112 + *
169.113 + * @throws IndexOutOfBoundsException
169.114 + * If there is no capturing group in the pattern
169.115 + * with the given index
169.116 + */
169.117 + public int end(int group);
169.118 +
169.119 + /**
169.120 + * Returns the input subsequence matched by the previous match.
169.121 + *
169.122 + * <p> For a matcher <i>m</i> with input sequence <i>s</i>,
169.123 + * the expressions <i>m.</i><tt>group()</tt> and
169.124 + * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(),</tt> <i>m.</i><tt>end())</tt>
169.125 + * are equivalent. </p>
169.126 + *
169.127 + * <p> Note that some patterns, for example <tt>a*</tt>, match the empty
169.128 + * string. This method will return the empty string when the pattern
169.129 + * successfully matches the empty string in the input. </p>
169.130 + *
169.131 + * @return The (possibly empty) subsequence matched by the previous match,
169.132 + * in string form
169.133 + *
169.134 + * @throws IllegalStateException
169.135 + * If no match has yet been attempted,
169.136 + * or if the previous match operation failed
169.137 + */
169.138 + public String group();
169.139 +
169.140 + /**
169.141 + * Returns the input subsequence captured by the given group during the
169.142 + * previous match operation.
169.143 + *
169.144 + * <p> For a matcher <i>m</i>, input sequence <i>s</i>, and group index
169.145 + * <i>g</i>, the expressions <i>m.</i><tt>group(</tt><i>g</i><tt>)</tt> and
169.146 + * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(</tt><i>g</i><tt>),</tt> <i>m.</i><tt>end(</tt><i>g</i><tt>))</tt>
169.147 + * are equivalent. </p>
169.148 + *
169.149 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
169.150 + * to right, starting at one. Group zero denotes the entire pattern, so
169.151 + * the expression <tt>m.group(0)</tt> is equivalent to <tt>m.group()</tt>.
169.152 + * </p>
169.153 + *
169.154 + * <p> If the match was successful but the group specified failed to match
169.155 + * any part of the input sequence, then <tt>null</tt> is returned. Note
169.156 + * that some groups, for example <tt>(a*)</tt>, match the empty string.
169.157 + * This method will return the empty string when such a group successfully
169.158 + * matches the empty string in the input. </p>
169.159 + *
169.160 + * @param group
169.161 + * The index of a capturing group in this matcher's pattern
169.162 + *
169.163 + * @return The (possibly empty) subsequence captured by the group
169.164 + * during the previous match, or <tt>null</tt> if the group
169.165 + * failed to match part of the input
169.166 + *
169.167 + * @throws IllegalStateException
169.168 + * If no match has yet been attempted,
169.169 + * or if the previous match operation failed
169.170 + *
169.171 + * @throws IndexOutOfBoundsException
169.172 + * If there is no capturing group in the pattern
169.173 + * with the given index
169.174 + */
169.175 + public String group(int group);
169.176 +
169.177 + /**
169.178 + * Returns the number of capturing groups in this match result's pattern.
169.179 + *
169.180 + * <p> Group zero denotes the entire pattern by convention. It is not
169.181 + * included in this count.
169.182 + *
169.183 + * <p> Any non-negative integer smaller than or equal to the value
169.184 + * returned by this method is guaranteed to be a valid group index for
169.185 + * this matcher. </p>
169.186 + *
169.187 + * @return The number of capturing groups in this matcher's pattern
169.188 + */
169.189 + public int groupCount();
169.190 +
169.191 +}
170.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
170.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/Matcher.java Tue Feb 11 13:31:42 2014 +0100
170.3 @@ -0,0 +1,1256 @@
170.4 +/*
170.5 + * Copyright (c) 1999, 2009, Oracle and/or its affiliates. All rights reserved.
170.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
170.7 + *
170.8 + * This code is free software; you can redistribute it and/or modify it
170.9 + * under the terms of the GNU General Public License version 2 only, as
170.10 + * published by the Free Software Foundation. Oracle designates this
170.11 + * particular file as subject to the "Classpath" exception as provided
170.12 + * by Oracle in the LICENSE file that accompanied this code.
170.13 + *
170.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
170.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
170.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
170.17 + * version 2 for more details (a copy is included in the LICENSE file that
170.18 + * accompanied this code).
170.19 + *
170.20 + * You should have received a copy of the GNU General Public License version
170.21 + * 2 along with this work; if not, write to the Free Software Foundation,
170.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
170.23 + *
170.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
170.25 + * or visit www.oracle.com if you need additional information or have any
170.26 + * questions.
170.27 + */
170.28 +
170.29 +package java.util.regex;
170.30 +
170.31 +
170.32 +/**
170.33 + * An engine that performs match operations on a {@link java.lang.CharSequence
170.34 + * </code>character sequence<code>} by interpreting a {@link Pattern}.
170.35 + *
170.36 + * <p> A matcher is created from a pattern by invoking the pattern's {@link
170.37 + * Pattern#matcher matcher} method. Once created, a matcher can be used to
170.38 + * perform three different kinds of match operations:
170.39 + *
170.40 + * <ul>
170.41 + *
170.42 + * <li><p> The {@link #matches matches} method attempts to match the entire
170.43 + * input sequence against the pattern. </p></li>
170.44 + *
170.45 + * <li><p> The {@link #lookingAt lookingAt} method attempts to match the
170.46 + * input sequence, starting at the beginning, against the pattern. </p></li>
170.47 + *
170.48 + * <li><p> The {@link #find find} method scans the input sequence looking for
170.49 + * the next subsequence that matches the pattern. </p></li>
170.50 + *
170.51 + * </ul>
170.52 + *
170.53 + * <p> Each of these methods returns a boolean indicating success or failure.
170.54 + * More information about a successful match can be obtained by querying the
170.55 + * state of the matcher.
170.56 + *
170.57 + * <p> A matcher finds matches in a subset of its input called the
170.58 + * <i>region</i>. By default, the region contains all of the matcher's input.
170.59 + * The region can be modified via the{@link #region region} method and queried
170.60 + * via the {@link #regionStart regionStart} and {@link #regionEnd regionEnd}
170.61 + * methods. The way that the region boundaries interact with some pattern
170.62 + * constructs can be changed. See {@link #useAnchoringBounds
170.63 + * useAnchoringBounds} and {@link #useTransparentBounds useTransparentBounds}
170.64 + * for more details.
170.65 + *
170.66 + * <p> This class also defines methods for replacing matched subsequences with
170.67 + * new strings whose contents can, if desired, be computed from the match
170.68 + * result. The {@link #appendReplacement appendReplacement} and {@link
170.69 + * #appendTail appendTail} methods can be used in tandem in order to collect
170.70 + * the result into an existing string buffer, or the more convenient {@link
170.71 + * #replaceAll replaceAll} method can be used to create a string in which every
170.72 + * matching subsequence in the input sequence is replaced.
170.73 + *
170.74 + * <p> The explicit state of a matcher includes the start and end indices of
170.75 + * the most recent successful match. It also includes the start and end
170.76 + * indices of the input subsequence captured by each <a
170.77 + * href="Pattern.html#cg">capturing group</a> in the pattern as well as a total
170.78 + * count of such subsequences. As a convenience, methods are also provided for
170.79 + * returning these captured subsequences in string form.
170.80 + *
170.81 + * <p> The explicit state of a matcher is initially undefined; attempting to
170.82 + * query any part of it before a successful match will cause an {@link
170.83 + * IllegalStateException} to be thrown. The explicit state of a matcher is
170.84 + * recomputed by every match operation.
170.85 + *
170.86 + * <p> The implicit state of a matcher includes the input character sequence as
170.87 + * well as the <i>append position</i>, which is initially zero and is updated
170.88 + * by the {@link #appendReplacement appendReplacement} method.
170.89 + *
170.90 + * <p> A matcher may be reset explicitly by invoking its {@link #reset()}
170.91 + * method or, if a new input sequence is desired, its {@link
170.92 + * #reset(java.lang.CharSequence) reset(CharSequence)} method. Resetting a
170.93 + * matcher discards its explicit state information and sets the append position
170.94 + * to zero.
170.95 + *
170.96 + * <p> Instances of this class are not safe for use by multiple concurrent
170.97 + * threads. </p>
170.98 + *
170.99 + *
170.100 + * @author Mike McCloskey
170.101 + * @author Mark Reinhold
170.102 + * @author JSR-51 Expert Group
170.103 + * @since 1.4
170.104 + * @spec JSR-51
170.105 + */
170.106 +
170.107 +public final class Matcher implements MatchResult {
170.108 +
170.109 + /**
170.110 + * The Pattern object that created this Matcher.
170.111 + */
170.112 + Pattern parentPattern;
170.113 +
170.114 + /**
170.115 + * The storage used by groups. They may contain invalid values if
170.116 + * a group was skipped during the matching.
170.117 + */
170.118 + int[] groups;
170.119 +
170.120 + /**
170.121 + * The range within the sequence that is to be matched. Anchors
170.122 + * will match at these "hard" boundaries. Changing the region
170.123 + * changes these values.
170.124 + */
170.125 + int from, to;
170.126 +
170.127 + /**
170.128 + * Lookbehind uses this value to ensure that the subexpression
170.129 + * match ends at the point where the lookbehind was encountered.
170.130 + */
170.131 + int lookbehindTo;
170.132 +
170.133 + /**
170.134 + * The original string being matched.
170.135 + */
170.136 + CharSequence text;
170.137 +
170.138 + /**
170.139 + * Matcher state used by the last node. NOANCHOR is used when a
170.140 + * match does not have to consume all of the input. ENDANCHOR is
170.141 + * the mode used for matching all the input.
170.142 + */
170.143 + static final int ENDANCHOR = 1;
170.144 + static final int NOANCHOR = 0;
170.145 + int acceptMode = NOANCHOR;
170.146 +
170.147 + /**
170.148 + * The range of string that last matched the pattern. If the last
170.149 + * match failed then first is -1; last initially holds 0 then it
170.150 + * holds the index of the end of the last match (which is where the
170.151 + * next search starts).
170.152 + */
170.153 + int first = -1, last = 0;
170.154 +
170.155 + /**
170.156 + * The end index of what matched in the last match operation.
170.157 + */
170.158 + int oldLast = -1;
170.159 +
170.160 + /**
170.161 + * The index of the last position appended in a substitution.
170.162 + */
170.163 + int lastAppendPosition = 0;
170.164 +
170.165 + /**
170.166 + * Storage used by nodes to tell what repetition they are on in
170.167 + * a pattern, and where groups begin. The nodes themselves are stateless,
170.168 + * so they rely on this field to hold state during a match.
170.169 + */
170.170 + int[] locals;
170.171 +
170.172 + /**
170.173 + * Boolean indicating whether or not more input could change
170.174 + * the results of the last match.
170.175 + *
170.176 + * If hitEnd is true, and a match was found, then more input
170.177 + * might cause a different match to be found.
170.178 + * If hitEnd is true and a match was not found, then more
170.179 + * input could cause a match to be found.
170.180 + * If hitEnd is false and a match was found, then more input
170.181 + * will not change the match.
170.182 + * If hitEnd is false and a match was not found, then more
170.183 + * input will not cause a match to be found.
170.184 + */
170.185 + boolean hitEnd;
170.186 +
170.187 + /**
170.188 + * Boolean indicating whether or not more input could change
170.189 + * a positive match into a negative one.
170.190 + *
170.191 + * If requireEnd is true, and a match was found, then more
170.192 + * input could cause the match to be lost.
170.193 + * If requireEnd is false and a match was found, then more
170.194 + * input might change the match but the match won't be lost.
170.195 + * If a match was not found, then requireEnd has no meaning.
170.196 + */
170.197 + boolean requireEnd;
170.198 +
170.199 + /**
170.200 + * If transparentBounds is true then the boundaries of this
170.201 + * matcher's region are transparent to lookahead, lookbehind,
170.202 + * and boundary matching constructs that try to see beyond them.
170.203 + */
170.204 + boolean transparentBounds = false;
170.205 +
170.206 + /**
170.207 + * If anchoringBounds is true then the boundaries of this
170.208 + * matcher's region match anchors such as ^ and $.
170.209 + */
170.210 + boolean anchoringBounds = true;
170.211 +
170.212 + /**
170.213 + * No default constructor.
170.214 + */
170.215 + Matcher() {
170.216 + }
170.217 +
170.218 + /**
170.219 + * All matchers have the state used by Pattern during a match.
170.220 + */
170.221 + Matcher(Pattern parent, CharSequence text) {
170.222 + this.parentPattern = parent;
170.223 + this.text = text;
170.224 +
170.225 + // Allocate state storage
170.226 + int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
170.227 + groups = new int[parentGroupCount * 2];
170.228 + locals = new int[parent.localCount];
170.229 +
170.230 + // Put fields into initial states
170.231 + reset();
170.232 + }
170.233 +
170.234 + /**
170.235 + * Returns the pattern that is interpreted by this matcher.
170.236 + *
170.237 + * @return The pattern for which this matcher was created
170.238 + */
170.239 + public Pattern pattern() {
170.240 + return parentPattern;
170.241 + }
170.242 +
170.243 + /**
170.244 + * Returns the match state of this matcher as a {@link MatchResult}.
170.245 + * The result is unaffected by subsequent operations performed upon this
170.246 + * matcher.
170.247 + *
170.248 + * @return a <code>MatchResult</code> with the state of this matcher
170.249 + * @since 1.5
170.250 + */
170.251 + public MatchResult toMatchResult() {
170.252 + Matcher result = new Matcher(this.parentPattern, text.toString());
170.253 + result.first = this.first;
170.254 + result.last = this.last;
170.255 + result.groups = this.groups.clone();
170.256 + return result;
170.257 + }
170.258 +
170.259 + /**
170.260 + * Changes the <tt>Pattern</tt> that this <tt>Matcher</tt> uses to
170.261 + * find matches with.
170.262 + *
170.263 + * <p> This method causes this matcher to lose information
170.264 + * about the groups of the last match that occurred. The
170.265 + * matcher's position in the input is maintained and its
170.266 + * last append position is unaffected.</p>
170.267 + *
170.268 + * @param newPattern
170.269 + * The new pattern used by this matcher
170.270 + * @return This matcher
170.271 + * @throws IllegalArgumentException
170.272 + * If newPattern is <tt>null</tt>
170.273 + * @since 1.5
170.274 + */
170.275 + public Matcher usePattern(Pattern newPattern) {
170.276 + if (newPattern == null)
170.277 + throw new IllegalArgumentException("Pattern cannot be null");
170.278 + parentPattern = newPattern;
170.279 +
170.280 + // Reallocate state storage
170.281 + int parentGroupCount = Math.max(newPattern.capturingGroupCount, 10);
170.282 + groups = new int[parentGroupCount * 2];
170.283 + locals = new int[newPattern.localCount];
170.284 + for (int i = 0; i < groups.length; i++)
170.285 + groups[i] = -1;
170.286 + for (int i = 0; i < locals.length; i++)
170.287 + locals[i] = -1;
170.288 + return this;
170.289 + }
170.290 +
170.291 + /**
170.292 + * Resets this matcher.
170.293 + *
170.294 + * <p> Resetting a matcher discards all of its explicit state information
170.295 + * and sets its append position to zero. The matcher's region is set to the
170.296 + * default region, which is its entire character sequence. The anchoring
170.297 + * and transparency of this matcher's region boundaries are unaffected.
170.298 + *
170.299 + * @return This matcher
170.300 + */
170.301 + public Matcher reset() {
170.302 + first = -1;
170.303 + last = 0;
170.304 + oldLast = -1;
170.305 + for(int i=0; i<groups.length; i++)
170.306 + groups[i] = -1;
170.307 + for(int i=0; i<locals.length; i++)
170.308 + locals[i] = -1;
170.309 + lastAppendPosition = 0;
170.310 + from = 0;
170.311 + to = getTextLength();
170.312 + return this;
170.313 + }
170.314 +
170.315 + /**
170.316 + * Resets this matcher with a new input sequence.
170.317 + *
170.318 + * <p> Resetting a matcher discards all of its explicit state information
170.319 + * and sets its append position to zero. The matcher's region is set to
170.320 + * the default region, which is its entire character sequence. The
170.321 + * anchoring and transparency of this matcher's region boundaries are
170.322 + * unaffected.
170.323 + *
170.324 + * @param input
170.325 + * The new input character sequence
170.326 + *
170.327 + * @return This matcher
170.328 + */
170.329 + public Matcher reset(CharSequence input) {
170.330 + text = input;
170.331 + return reset();
170.332 + }
170.333 +
170.334 + /**
170.335 + * Returns the start index of the previous match. </p>
170.336 + *
170.337 + * @return The index of the first character matched
170.338 + *
170.339 + * @throws IllegalStateException
170.340 + * If no match has yet been attempted,
170.341 + * or if the previous match operation failed
170.342 + */
170.343 + public int start() {
170.344 + if (first < 0)
170.345 + throw new IllegalStateException("No match available");
170.346 + return first;
170.347 + }
170.348 +
170.349 + /**
170.350 + * Returns the start index of the subsequence captured by the given group
170.351 + * during the previous match operation.
170.352 + *
170.353 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
170.354 + * to right, starting at one. Group zero denotes the entire pattern, so
170.355 + * the expression <i>m.</i><tt>start(0)</tt> is equivalent to
170.356 + * <i>m.</i><tt>start()</tt>. </p>
170.357 + *
170.358 + * @param group
170.359 + * The index of a capturing group in this matcher's pattern
170.360 + *
170.361 + * @return The index of the first character captured by the group,
170.362 + * or <tt>-1</tt> if the match was successful but the group
170.363 + * itself did not match anything
170.364 + *
170.365 + * @throws IllegalStateException
170.366 + * If no match has yet been attempted,
170.367 + * or if the previous match operation failed
170.368 + *
170.369 + * @throws IndexOutOfBoundsException
170.370 + * If there is no capturing group in the pattern
170.371 + * with the given index
170.372 + */
170.373 + public int start(int group) {
170.374 + if (first < 0)
170.375 + throw new IllegalStateException("No match available");
170.376 + if (group > groupCount())
170.377 + throw new IndexOutOfBoundsException("No group " + group);
170.378 + return groups[group * 2];
170.379 + }
170.380 +
170.381 + /**
170.382 + * Returns the offset after the last character matched. </p>
170.383 + *
170.384 + * @return The offset after the last character matched
170.385 + *
170.386 + * @throws IllegalStateException
170.387 + * If no match has yet been attempted,
170.388 + * or if the previous match operation failed
170.389 + */
170.390 + public int end() {
170.391 + if (first < 0)
170.392 + throw new IllegalStateException("No match available");
170.393 + return last;
170.394 + }
170.395 +
170.396 + /**
170.397 + * Returns the offset after the last character of the subsequence
170.398 + * captured by the given group during the previous match operation.
170.399 + *
170.400 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
170.401 + * to right, starting at one. Group zero denotes the entire pattern, so
170.402 + * the expression <i>m.</i><tt>end(0)</tt> is equivalent to
170.403 + * <i>m.</i><tt>end()</tt>. </p>
170.404 + *
170.405 + * @param group
170.406 + * The index of a capturing group in this matcher's pattern
170.407 + *
170.408 + * @return The offset after the last character captured by the group,
170.409 + * or <tt>-1</tt> if the match was successful
170.410 + * but the group itself did not match anything
170.411 + *
170.412 + * @throws IllegalStateException
170.413 + * If no match has yet been attempted,
170.414 + * or if the previous match operation failed
170.415 + *
170.416 + * @throws IndexOutOfBoundsException
170.417 + * If there is no capturing group in the pattern
170.418 + * with the given index
170.419 + */
170.420 + public int end(int group) {
170.421 + if (first < 0)
170.422 + throw new IllegalStateException("No match available");
170.423 + if (group > groupCount())
170.424 + throw new IndexOutOfBoundsException("No group " + group);
170.425 + return groups[group * 2 + 1];
170.426 + }
170.427 +
170.428 + /**
170.429 + * Returns the input subsequence matched by the previous match.
170.430 + *
170.431 + * <p> For a matcher <i>m</i> with input sequence <i>s</i>,
170.432 + * the expressions <i>m.</i><tt>group()</tt> and
170.433 + * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(),</tt> <i>m.</i><tt>end())</tt>
170.434 + * are equivalent. </p>
170.435 + *
170.436 + * <p> Note that some patterns, for example <tt>a*</tt>, match the empty
170.437 + * string. This method will return the empty string when the pattern
170.438 + * successfully matches the empty string in the input. </p>
170.439 + *
170.440 + * @return The (possibly empty) subsequence matched by the previous match,
170.441 + * in string form
170.442 + *
170.443 + * @throws IllegalStateException
170.444 + * If no match has yet been attempted,
170.445 + * or if the previous match operation failed
170.446 + */
170.447 + public String group() {
170.448 + return group(0);
170.449 + }
170.450 +
170.451 + /**
170.452 + * Returns the input subsequence captured by the given group during the
170.453 + * previous match operation.
170.454 + *
170.455 + * <p> For a matcher <i>m</i>, input sequence <i>s</i>, and group index
170.456 + * <i>g</i>, the expressions <i>m.</i><tt>group(</tt><i>g</i><tt>)</tt> and
170.457 + * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(</tt><i>g</i><tt>),</tt> <i>m.</i><tt>end(</tt><i>g</i><tt>))</tt>
170.458 + * are equivalent. </p>
170.459 + *
170.460 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
170.461 + * to right, starting at one. Group zero denotes the entire pattern, so
170.462 + * the expression <tt>m.group(0)</tt> is equivalent to <tt>m.group()</tt>.
170.463 + * </p>
170.464 + *
170.465 + * <p> If the match was successful but the group specified failed to match
170.466 + * any part of the input sequence, then <tt>null</tt> is returned. Note
170.467 + * that some groups, for example <tt>(a*)</tt>, match the empty string.
170.468 + * This method will return the empty string when such a group successfully
170.469 + * matches the empty string in the input. </p>
170.470 + *
170.471 + * @param group
170.472 + * The index of a capturing group in this matcher's pattern
170.473 + *
170.474 + * @return The (possibly empty) subsequence captured by the group
170.475 + * during the previous match, or <tt>null</tt> if the group
170.476 + * failed to match part of the input
170.477 + *
170.478 + * @throws IllegalStateException
170.479 + * If no match has yet been attempted,
170.480 + * or if the previous match operation failed
170.481 + *
170.482 + * @throws IndexOutOfBoundsException
170.483 + * If there is no capturing group in the pattern
170.484 + * with the given index
170.485 + */
170.486 + public String group(int group) {
170.487 + if (first < 0)
170.488 + throw new IllegalStateException("No match found");
170.489 + if (group < 0 || group > groupCount())
170.490 + throw new IndexOutOfBoundsException("No group " + group);
170.491 + if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
170.492 + return null;
170.493 + return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
170.494 + }
170.495 +
170.496 + /**
170.497 + * Returns the input subsequence captured by the given
170.498 + * <a href="Pattern.html#groupname">named-capturing group</a> during the previous
170.499 + * match operation.
170.500 + *
170.501 + * <p> If the match was successful but the group specified failed to match
170.502 + * any part of the input sequence, then <tt>null</tt> is returned. Note
170.503 + * that some groups, for example <tt>(a*)</tt>, match the empty string.
170.504 + * This method will return the empty string when such a group successfully
170.505 + * matches the empty string in the input. </p>
170.506 + *
170.507 + * @param name
170.508 + * The name of a named-capturing group in this matcher's pattern
170.509 + *
170.510 + * @return The (possibly empty) subsequence captured by the named group
170.511 + * during the previous match, or <tt>null</tt> if the group
170.512 + * failed to match part of the input
170.513 + *
170.514 + * @throws IllegalStateException
170.515 + * If no match has yet been attempted,
170.516 + * or if the previous match operation failed
170.517 + *
170.518 + * @throws IllegalArgumentException
170.519 + * If there is no capturing group in the pattern
170.520 + * with the given name
170.521 + */
170.522 + public String group(String name) {
170.523 + if (name == null)
170.524 + throw new NullPointerException("Null group name");
170.525 + if (first < 0)
170.526 + throw new IllegalStateException("No match found");
170.527 + if (!parentPattern.namedGroups().containsKey(name))
170.528 + throw new IllegalArgumentException("No group with name <" + name + ">");
170.529 + int group = parentPattern.namedGroups().get(name);
170.530 + if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
170.531 + return null;
170.532 + return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
170.533 + }
170.534 +
170.535 + /**
170.536 + * Returns the number of capturing groups in this matcher's pattern.
170.537 + *
170.538 + * <p> Group zero denotes the entire pattern by convention. It is not
170.539 + * included in this count.
170.540 + *
170.541 + * <p> Any non-negative integer smaller than or equal to the value
170.542 + * returned by this method is guaranteed to be a valid group index for
170.543 + * this matcher. </p>
170.544 + *
170.545 + * @return The number of capturing groups in this matcher's pattern
170.546 + */
170.547 + public int groupCount() {
170.548 + return parentPattern.capturingGroupCount - 1;
170.549 + }
170.550 +
170.551 + /**
170.552 + * Attempts to match the entire region against the pattern.
170.553 + *
170.554 + * <p> If the match succeeds then more information can be obtained via the
170.555 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
170.556 + *
170.557 + * @return <tt>true</tt> if, and only if, the entire region sequence
170.558 + * matches this matcher's pattern
170.559 + */
170.560 + public boolean matches() {
170.561 + return match(from, ENDANCHOR);
170.562 + }
170.563 +
170.564 + /**
170.565 + * Attempts to find the next subsequence of the input sequence that matches
170.566 + * the pattern.
170.567 + *
170.568 + * <p> This method starts at the beginning of this matcher's region, or, if
170.569 + * a previous invocation of the method was successful and the matcher has
170.570 + * not since been reset, at the first character not matched by the previous
170.571 + * match.
170.572 + *
170.573 + * <p> If the match succeeds then more information can be obtained via the
170.574 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
170.575 + *
170.576 + * @return <tt>true</tt> if, and only if, a subsequence of the input
170.577 + * sequence matches this matcher's pattern
170.578 + */
170.579 + public boolean find() {
170.580 + int nextSearchIndex = last;
170.581 + if (nextSearchIndex == first)
170.582 + nextSearchIndex++;
170.583 +
170.584 + // If next search starts before region, start it at region
170.585 + if (nextSearchIndex < from)
170.586 + nextSearchIndex = from;
170.587 +
170.588 + // If next search starts beyond region then it fails
170.589 + if (nextSearchIndex > to) {
170.590 + for (int i = 0; i < groups.length; i++)
170.591 + groups[i] = -1;
170.592 + return false;
170.593 + }
170.594 + return search(nextSearchIndex);
170.595 + }
170.596 +
170.597 + /**
170.598 + * Resets this matcher and then attempts to find the next subsequence of
170.599 + * the input sequence that matches the pattern, starting at the specified
170.600 + * index.
170.601 + *
170.602 + * <p> If the match succeeds then more information can be obtained via the
170.603 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods, and subsequent
170.604 + * invocations of the {@link #find()} method will start at the first
170.605 + * character not matched by this match. </p>
170.606 + *
170.607 + * @throws IndexOutOfBoundsException
170.608 + * If start is less than zero or if start is greater than the
170.609 + * length of the input sequence.
170.610 + *
170.611 + * @return <tt>true</tt> if, and only if, a subsequence of the input
170.612 + * sequence starting at the given index matches this matcher's
170.613 + * pattern
170.614 + */
170.615 + public boolean find(int start) {
170.616 + int limit = getTextLength();
170.617 + if ((start < 0) || (start > limit))
170.618 + throw new IndexOutOfBoundsException("Illegal start index");
170.619 + reset();
170.620 + return search(start);
170.621 + }
170.622 +
170.623 + /**
170.624 + * Attempts to match the input sequence, starting at the beginning of the
170.625 + * region, against the pattern.
170.626 + *
170.627 + * <p> Like the {@link #matches matches} method, this method always starts
170.628 + * at the beginning of the region; unlike that method, it does not
170.629 + * require that the entire region be matched.
170.630 + *
170.631 + * <p> If the match succeeds then more information can be obtained via the
170.632 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
170.633 + *
170.634 + * @return <tt>true</tt> if, and only if, a prefix of the input
170.635 + * sequence matches this matcher's pattern
170.636 + */
170.637 + public boolean lookingAt() {
170.638 + return match(from, NOANCHOR);
170.639 + }
170.640 +
170.641 + /**
170.642 + * Returns a literal replacement <code>String</code> for the specified
170.643 + * <code>String</code>.
170.644 + *
170.645 + * This method produces a <code>String</code> that will work
170.646 + * as a literal replacement <code>s</code> in the
170.647 + * <code>appendReplacement</code> method of the {@link Matcher} class.
170.648 + * The <code>String</code> produced will match the sequence of characters
170.649 + * in <code>s</code> treated as a literal sequence. Slashes ('\') and
170.650 + * dollar signs ('$') will be given no special meaning.
170.651 + *
170.652 + * @param s The string to be literalized
170.653 + * @return A literal string replacement
170.654 + * @since 1.5
170.655 + */
170.656 + public static String quoteReplacement(String s) {
170.657 + if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1))
170.658 + return s;
170.659 + StringBuilder sb = new StringBuilder();
170.660 + for (int i=0; i<s.length(); i++) {
170.661 + char c = s.charAt(i);
170.662 + if (c == '\\' || c == '$') {
170.663 + sb.append('\\');
170.664 + }
170.665 + sb.append(c);
170.666 + }
170.667 + return sb.toString();
170.668 + }
170.669 +
170.670 + /**
170.671 + * Implements a non-terminal append-and-replace step.
170.672 + *
170.673 + * <p> This method performs the following actions: </p>
170.674 + *
170.675 + * <ol>
170.676 + *
170.677 + * <li><p> It reads characters from the input sequence, starting at the
170.678 + * append position, and appends them to the given string buffer. It
170.679 + * stops after reading the last character preceding the previous match,
170.680 + * that is, the character at index {@link
170.681 + * #start()} <tt>-</tt> <tt>1</tt>. </p></li>
170.682 + *
170.683 + * <li><p> It appends the given replacement string to the string buffer.
170.684 + * </p></li>
170.685 + *
170.686 + * <li><p> It sets the append position of this matcher to the index of
170.687 + * the last character matched, plus one, that is, to {@link #end()}.
170.688 + * </p></li>
170.689 + *
170.690 + * </ol>
170.691 + *
170.692 + * <p> The replacement string may contain references to subsequences
170.693 + * captured during the previous match: Each occurrence of
170.694 + * <tt>${</tt><i>name</i><tt>}</tt> or <tt>$</tt><i>g</i>
170.695 + * will be replaced by the result of evaluating the corresponding
170.696 + * {@link #group(String) group(name)} or {@link #group(int) group(g)</tt>}
170.697 + * respectively. For <tt>$</tt><i>g</i><tt></tt>,
170.698 + * the first number after the <tt>$</tt> is always treated as part of
170.699 + * the group reference. Subsequent numbers are incorporated into g if
170.700 + * they would form a legal group reference. Only the numerals '0'
170.701 + * through '9' are considered as potential components of the group
170.702 + * reference. If the second group matched the string <tt>"foo"</tt>, for
170.703 + * example, then passing the replacement string <tt>"$2bar"</tt> would
170.704 + * cause <tt>"foobar"</tt> to be appended to the string buffer. A dollar
170.705 + * sign (<tt>$</tt>) may be included as a literal in the replacement
170.706 + * string by preceding it with a backslash (<tt>\$</tt>).
170.707 + *
170.708 + * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
170.709 + * the replacement string may cause the results to be different than if it
170.710 + * were being treated as a literal replacement string. Dollar signs may be
170.711 + * treated as references to captured subsequences as described above, and
170.712 + * backslashes are used to escape literal characters in the replacement
170.713 + * string.
170.714 + *
170.715 + * <p> This method is intended to be used in a loop together with the
170.716 + * {@link #appendTail appendTail} and {@link #find find} methods. The
170.717 + * following code, for example, writes <tt>one dog two dogs in the
170.718 + * yard</tt> to the standard-output stream: </p>
170.719 + *
170.720 + * <blockquote><pre>
170.721 + * Pattern p = Pattern.compile("cat");
170.722 + * Matcher m = p.matcher("one cat two cats in the yard");
170.723 + * StringBuffer sb = new StringBuffer();
170.724 + * while (m.find()) {
170.725 + * m.appendReplacement(sb, "dog");
170.726 + * }
170.727 + * m.appendTail(sb);
170.728 + * System.out.println(sb.toString());</pre></blockquote>
170.729 + *
170.730 + * @param sb
170.731 + * The target string buffer
170.732 + *
170.733 + * @param replacement
170.734 + * The replacement string
170.735 + *
170.736 + * @return This matcher
170.737 + *
170.738 + * @throws IllegalStateException
170.739 + * If no match has yet been attempted,
170.740 + * or if the previous match operation failed
170.741 + *
170.742 + * @throws IllegalArgumentException
170.743 + * If the replacement string refers to a named-capturing
170.744 + * group that does not exist in the pattern
170.745 + *
170.746 + * @throws IndexOutOfBoundsException
170.747 + * If the replacement string refers to a capturing group
170.748 + * that does not exist in the pattern
170.749 + */
170.750 + public Matcher appendReplacement(StringBuffer sb, String replacement) {
170.751 +
170.752 + // If no match, return error
170.753 + if (first < 0)
170.754 + throw new IllegalStateException("No match available");
170.755 +
170.756 + // Process substitution string to replace group references with groups
170.757 + int cursor = 0;
170.758 + StringBuilder result = new StringBuilder();
170.759 +
170.760 + while (cursor < replacement.length()) {
170.761 + char nextChar = replacement.charAt(cursor);
170.762 + if (nextChar == '\\') {
170.763 + cursor++;
170.764 + nextChar = replacement.charAt(cursor);
170.765 + result.append(nextChar);
170.766 + cursor++;
170.767 + } else if (nextChar == '$') {
170.768 + // Skip past $
170.769 + cursor++;
170.770 + // A StringIndexOutOfBoundsException is thrown if
170.771 + // this "$" is the last character in replacement
170.772 + // string in current implementation, a IAE might be
170.773 + // more appropriate.
170.774 + nextChar = replacement.charAt(cursor);
170.775 + int refNum = -1;
170.776 + if (nextChar == '{') {
170.777 + cursor++;
170.778 + StringBuilder gsb = new StringBuilder();
170.779 + while (cursor < replacement.length()) {
170.780 + nextChar = replacement.charAt(cursor);
170.781 + if (ASCII.isLower(nextChar) ||
170.782 + ASCII.isUpper(nextChar) ||
170.783 + ASCII.isDigit(nextChar)) {
170.784 + gsb.append(nextChar);
170.785 + cursor++;
170.786 + } else {
170.787 + break;
170.788 + }
170.789 + }
170.790 + if (gsb.length() == 0)
170.791 + throw new IllegalArgumentException(
170.792 + "named capturing group has 0 length name");
170.793 + if (nextChar != '}')
170.794 + throw new IllegalArgumentException(
170.795 + "named capturing group is missing trailing '}'");
170.796 + String gname = gsb.toString();
170.797 + if (ASCII.isDigit(gname.charAt(0)))
170.798 + throw new IllegalArgumentException(
170.799 + "capturing group name {" + gname +
170.800 + "} starts with digit character");
170.801 + if (!parentPattern.namedGroups().containsKey(gname))
170.802 + throw new IllegalArgumentException(
170.803 + "No group with name {" + gname + "}");
170.804 + refNum = parentPattern.namedGroups().get(gname);
170.805 + cursor++;
170.806 + } else {
170.807 + // The first number is always a group
170.808 + refNum = (int)nextChar - '0';
170.809 + if ((refNum < 0)||(refNum > 9))
170.810 + throw new IllegalArgumentException(
170.811 + "Illegal group reference");
170.812 + cursor++;
170.813 + // Capture the largest legal group string
170.814 + boolean done = false;
170.815 + while (!done) {
170.816 + if (cursor >= replacement.length()) {
170.817 + break;
170.818 + }
170.819 + int nextDigit = replacement.charAt(cursor) - '0';
170.820 + if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
170.821 + break;
170.822 + }
170.823 + int newRefNum = (refNum * 10) + nextDigit;
170.824 + if (groupCount() < newRefNum) {
170.825 + done = true;
170.826 + } else {
170.827 + refNum = newRefNum;
170.828 + cursor++;
170.829 + }
170.830 + }
170.831 + }
170.832 + // Append group
170.833 + if (start(refNum) != -1 && end(refNum) != -1)
170.834 + result.append(text, start(refNum), end(refNum));
170.835 + } else {
170.836 + result.append(nextChar);
170.837 + cursor++;
170.838 + }
170.839 + }
170.840 + // Append the intervening text
170.841 + sb.append(text, lastAppendPosition, first);
170.842 + // Append the match substitution
170.843 + sb.append(result);
170.844 +
170.845 + lastAppendPosition = last;
170.846 + return this;
170.847 + }
170.848 +
170.849 + /**
170.850 + * Implements a terminal append-and-replace step.
170.851 + *
170.852 + * <p> This method reads characters from the input sequence, starting at
170.853 + * the append position, and appends them to the given string buffer. It is
170.854 + * intended to be invoked after one or more invocations of the {@link
170.855 + * #appendReplacement appendReplacement} method in order to copy the
170.856 + * remainder of the input sequence. </p>
170.857 + *
170.858 + * @param sb
170.859 + * The target string buffer
170.860 + *
170.861 + * @return The target string buffer
170.862 + */
170.863 + public StringBuffer appendTail(StringBuffer sb) {
170.864 + sb.append(text, lastAppendPosition, getTextLength());
170.865 + return sb;
170.866 + }
170.867 +
170.868 + /**
170.869 + * Replaces every subsequence of the input sequence that matches the
170.870 + * pattern with the given replacement string.
170.871 + *
170.872 + * <p> This method first resets this matcher. It then scans the input
170.873 + * sequence looking for matches of the pattern. Characters that are not
170.874 + * part of any match are appended directly to the result string; each match
170.875 + * is replaced in the result by the replacement string. The replacement
170.876 + * string may contain references to captured subsequences as in the {@link
170.877 + * #appendReplacement appendReplacement} method.
170.878 + *
170.879 + * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
170.880 + * the replacement string may cause the results to be different than if it
170.881 + * were being treated as a literal replacement string. Dollar signs may be
170.882 + * treated as references to captured subsequences as described above, and
170.883 + * backslashes are used to escape literal characters in the replacement
170.884 + * string.
170.885 + *
170.886 + * <p> Given the regular expression <tt>a*b</tt>, the input
170.887 + * <tt>"aabfooaabfooabfoob"</tt>, and the replacement string
170.888 + * <tt>"-"</tt>, an invocation of this method on a matcher for that
170.889 + * expression would yield the string <tt>"-foo-foo-foo-"</tt>.
170.890 + *
170.891 + * <p> Invoking this method changes this matcher's state. If the matcher
170.892 + * is to be used in further matching operations then it should first be
170.893 + * reset. </p>
170.894 + *
170.895 + * @param replacement
170.896 + * The replacement string
170.897 + *
170.898 + * @return The string constructed by replacing each matching subsequence
170.899 + * by the replacement string, substituting captured subsequences
170.900 + * as needed
170.901 + */
170.902 + public String replaceAll(String replacement) {
170.903 + reset();
170.904 + boolean result = find();
170.905 + if (result) {
170.906 + StringBuffer sb = new StringBuffer();
170.907 + do {
170.908 + appendReplacement(sb, replacement);
170.909 + result = find();
170.910 + } while (result);
170.911 + appendTail(sb);
170.912 + return sb.toString();
170.913 + }
170.914 + return text.toString();
170.915 + }
170.916 +
170.917 + /**
170.918 + * Replaces the first subsequence of the input sequence that matches the
170.919 + * pattern with the given replacement string.
170.920 + *
170.921 + * <p> This method first resets this matcher. It then scans the input
170.922 + * sequence looking for a match of the pattern. Characters that are not
170.923 + * part of the match are appended directly to the result string; the match
170.924 + * is replaced in the result by the replacement string. The replacement
170.925 + * string may contain references to captured subsequences as in the {@link
170.926 + * #appendReplacement appendReplacement} method.
170.927 + *
170.928 + * <p>Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
170.929 + * the replacement string may cause the results to be different than if it
170.930 + * were being treated as a literal replacement string. Dollar signs may be
170.931 + * treated as references to captured subsequences as described above, and
170.932 + * backslashes are used to escape literal characters in the replacement
170.933 + * string.
170.934 + *
170.935 + * <p> Given the regular expression <tt>dog</tt>, the input
170.936 + * <tt>"zzzdogzzzdogzzz"</tt>, and the replacement string
170.937 + * <tt>"cat"</tt>, an invocation of this method on a matcher for that
170.938 + * expression would yield the string <tt>"zzzcatzzzdogzzz"</tt>. </p>
170.939 + *
170.940 + * <p> Invoking this method changes this matcher's state. If the matcher
170.941 + * is to be used in further matching operations then it should first be
170.942 + * reset. </p>
170.943 + *
170.944 + * @param replacement
170.945 + * The replacement string
170.946 + * @return The string constructed by replacing the first matching
170.947 + * subsequence by the replacement string, substituting captured
170.948 + * subsequences as needed
170.949 + */
170.950 + public String replaceFirst(String replacement) {
170.951 + if (replacement == null)
170.952 + throw new NullPointerException("replacement");
170.953 + reset();
170.954 + if (!find())
170.955 + return text.toString();
170.956 + StringBuffer sb = new StringBuffer();
170.957 + appendReplacement(sb, replacement);
170.958 + appendTail(sb);
170.959 + return sb.toString();
170.960 + }
170.961 +
170.962 + /**
170.963 + * Sets the limits of this matcher's region. The region is the part of the
170.964 + * input sequence that will be searched to find a match. Invoking this
170.965 + * method resets the matcher, and then sets the region to start at the
170.966 + * index specified by the <code>start</code> parameter and end at the
170.967 + * index specified by the <code>end</code> parameter.
170.968 + *
170.969 + * <p>Depending on the transparency and anchoring being used (see
170.970 + * {@link #useTransparentBounds useTransparentBounds} and
170.971 + * {@link #useAnchoringBounds useAnchoringBounds}), certain constructs such
170.972 + * as anchors may behave differently at or around the boundaries of the
170.973 + * region.
170.974 + *
170.975 + * @param start
170.976 + * The index to start searching at (inclusive)
170.977 + * @param end
170.978 + * The index to end searching at (exclusive)
170.979 + * @throws IndexOutOfBoundsException
170.980 + * If start or end is less than zero, if
170.981 + * start is greater than the length of the input sequence, if
170.982 + * end is greater than the length of the input sequence, or if
170.983 + * start is greater than end.
170.984 + * @return this matcher
170.985 + * @since 1.5
170.986 + */
170.987 + public Matcher region(int start, int end) {
170.988 + if ((start < 0) || (start > getTextLength()))
170.989 + throw new IndexOutOfBoundsException("start");
170.990 + if ((end < 0) || (end > getTextLength()))
170.991 + throw new IndexOutOfBoundsException("end");
170.992 + if (start > end)
170.993 + throw new IndexOutOfBoundsException("start > end");
170.994 + reset();
170.995 + from = start;
170.996 + to = end;
170.997 + return this;
170.998 + }
170.999 +
170.1000 + /**
170.1001 + * Reports the start index of this matcher's region. The
170.1002 + * searches this matcher conducts are limited to finding matches
170.1003 + * within {@link #regionStart regionStart} (inclusive) and
170.1004 + * {@link #regionEnd regionEnd} (exclusive).
170.1005 + *
170.1006 + * @return The starting point of this matcher's region
170.1007 + * @since 1.5
170.1008 + */
170.1009 + public int regionStart() {
170.1010 + return from;
170.1011 + }
170.1012 +
170.1013 + /**
170.1014 + * Reports the end index (exclusive) of this matcher's region.
170.1015 + * The searches this matcher conducts are limited to finding matches
170.1016 + * within {@link #regionStart regionStart} (inclusive) and
170.1017 + * {@link #regionEnd regionEnd} (exclusive).
170.1018 + *
170.1019 + * @return the ending point of this matcher's region
170.1020 + * @since 1.5
170.1021 + */
170.1022 + public int regionEnd() {
170.1023 + return to;
170.1024 + }
170.1025 +
170.1026 + /**
170.1027 + * Queries the transparency of region bounds for this matcher.
170.1028 + *
170.1029 + * <p> This method returns <tt>true</tt> if this matcher uses
170.1030 + * <i>transparent</i> bounds, <tt>false</tt> if it uses <i>opaque</i>
170.1031 + * bounds.
170.1032 + *
170.1033 + * <p> See {@link #useTransparentBounds useTransparentBounds} for a
170.1034 + * description of transparent and opaque bounds.
170.1035 + *
170.1036 + * <p> By default, a matcher uses opaque region boundaries.
170.1037 + *
170.1038 + * @return <tt>true</tt> iff this matcher is using transparent bounds,
170.1039 + * <tt>false</tt> otherwise.
170.1040 + * @see java.util.regex.Matcher#useTransparentBounds(boolean)
170.1041 + * @since 1.5
170.1042 + */
170.1043 + public boolean hasTransparentBounds() {
170.1044 + return transparentBounds;
170.1045 + }
170.1046 +
170.1047 + /**
170.1048 + * Sets the transparency of region bounds for this matcher.
170.1049 + *
170.1050 + * <p> Invoking this method with an argument of <tt>true</tt> will set this
170.1051 + * matcher to use <i>transparent</i> bounds. If the boolean
170.1052 + * argument is <tt>false</tt>, then <i>opaque</i> bounds will be used.
170.1053 + *
170.1054 + * <p> Using transparent bounds, the boundaries of this
170.1055 + * matcher's region are transparent to lookahead, lookbehind,
170.1056 + * and boundary matching constructs. Those constructs can see beyond the
170.1057 + * boundaries of the region to see if a match is appropriate.
170.1058 + *
170.1059 + * <p> Using opaque bounds, the boundaries of this matcher's
170.1060 + * region are opaque to lookahead, lookbehind, and boundary matching
170.1061 + * constructs that may try to see beyond them. Those constructs cannot
170.1062 + * look past the boundaries so they will fail to match anything outside
170.1063 + * of the region.
170.1064 + *
170.1065 + * <p> By default, a matcher uses opaque bounds.
170.1066 + *
170.1067 + * @param b a boolean indicating whether to use opaque or transparent
170.1068 + * regions
170.1069 + * @return this matcher
170.1070 + * @see java.util.regex.Matcher#hasTransparentBounds
170.1071 + * @since 1.5
170.1072 + */
170.1073 + public Matcher useTransparentBounds(boolean b) {
170.1074 + transparentBounds = b;
170.1075 + return this;
170.1076 + }
170.1077 +
170.1078 + /**
170.1079 + * Queries the anchoring of region bounds for this matcher.
170.1080 + *
170.1081 + * <p> This method returns <tt>true</tt> if this matcher uses
170.1082 + * <i>anchoring</i> bounds, <tt>false</tt> otherwise.
170.1083 + *
170.1084 + * <p> See {@link #useAnchoringBounds useAnchoringBounds} for a
170.1085 + * description of anchoring bounds.
170.1086 + *
170.1087 + * <p> By default, a matcher uses anchoring region boundaries.
170.1088 + *
170.1089 + * @return <tt>true</tt> iff this matcher is using anchoring bounds,
170.1090 + * <tt>false</tt> otherwise.
170.1091 + * @see java.util.regex.Matcher#useAnchoringBounds(boolean)
170.1092 + * @since 1.5
170.1093 + */
170.1094 + public boolean hasAnchoringBounds() {
170.1095 + return anchoringBounds;
170.1096 + }
170.1097 +
170.1098 + /**
170.1099 + * Sets the anchoring of region bounds for this matcher.
170.1100 + *
170.1101 + * <p> Invoking this method with an argument of <tt>true</tt> will set this
170.1102 + * matcher to use <i>anchoring</i> bounds. If the boolean
170.1103 + * argument is <tt>false</tt>, then <i>non-anchoring</i> bounds will be
170.1104 + * used.
170.1105 + *
170.1106 + * <p> Using anchoring bounds, the boundaries of this
170.1107 + * matcher's region match anchors such as ^ and $.
170.1108 + *
170.1109 + * <p> Without anchoring bounds, the boundaries of this
170.1110 + * matcher's region will not match anchors such as ^ and $.
170.1111 + *
170.1112 + * <p> By default, a matcher uses anchoring region boundaries.
170.1113 + *
170.1114 + * @param b a boolean indicating whether or not to use anchoring bounds.
170.1115 + * @return this matcher
170.1116 + * @see java.util.regex.Matcher#hasAnchoringBounds
170.1117 + * @since 1.5
170.1118 + */
170.1119 + public Matcher useAnchoringBounds(boolean b) {
170.1120 + anchoringBounds = b;
170.1121 + return this;
170.1122 + }
170.1123 +
170.1124 + /**
170.1125 + * <p>Returns the string representation of this matcher. The
170.1126 + * string representation of a <code>Matcher</code> contains information
170.1127 + * that may be useful for debugging. The exact format is unspecified.
170.1128 + *
170.1129 + * @return The string representation of this matcher
170.1130 + * @since 1.5
170.1131 + */
170.1132 + public String toString() {
170.1133 + StringBuilder sb = new StringBuilder();
170.1134 + sb.append("java.util.regex.Matcher");
170.1135 + sb.append("[pattern=" + pattern());
170.1136 + sb.append(" region=");
170.1137 + sb.append(regionStart() + "," + regionEnd());
170.1138 + sb.append(" lastmatch=");
170.1139 + if ((first >= 0) && (group() != null)) {
170.1140 + sb.append(group());
170.1141 + }
170.1142 + sb.append("]");
170.1143 + return sb.toString();
170.1144 + }
170.1145 +
170.1146 + /**
170.1147 + * <p>Returns true if the end of input was hit by the search engine in
170.1148 + * the last match operation performed by this matcher.
170.1149 + *
170.1150 + * <p>When this method returns true, then it is possible that more input
170.1151 + * would have changed the result of the last search.
170.1152 + *
170.1153 + * @return true iff the end of input was hit in the last match; false
170.1154 + * otherwise
170.1155 + * @since 1.5
170.1156 + */
170.1157 + public boolean hitEnd() {
170.1158 + return hitEnd;
170.1159 + }
170.1160 +
170.1161 + /**
170.1162 + * <p>Returns true if more input could change a positive match into a
170.1163 + * negative one.
170.1164 + *
170.1165 + * <p>If this method returns true, and a match was found, then more
170.1166 + * input could cause the match to be lost. If this method returns false
170.1167 + * and a match was found, then more input might change the match but the
170.1168 + * match won't be lost. If a match was not found, then requireEnd has no
170.1169 + * meaning.
170.1170 + *
170.1171 + * @return true iff more input could change a positive match into a
170.1172 + * negative one.
170.1173 + * @since 1.5
170.1174 + */
170.1175 + public boolean requireEnd() {
170.1176 + return requireEnd;
170.1177 + }
170.1178 +
170.1179 + /**
170.1180 + * Initiates a search to find a Pattern within the given bounds.
170.1181 + * The groups are filled with default values and the match of the root
170.1182 + * of the state machine is called. The state machine will hold the state
170.1183 + * of the match as it proceeds in this matcher.
170.1184 + *
170.1185 + * Matcher.from is not set here, because it is the "hard" boundary
170.1186 + * of the start of the search which anchors will set to. The from param
170.1187 + * is the "soft" boundary of the start of the search, meaning that the
170.1188 + * regex tries to match at that index but ^ won't match there. Subsequent
170.1189 + * calls to the search methods start at a new "soft" boundary which is
170.1190 + * the end of the previous match.
170.1191 + */
170.1192 + boolean search(int from) {
170.1193 + this.hitEnd = false;
170.1194 + this.requireEnd = false;
170.1195 + from = from < 0 ? 0 : from;
170.1196 + this.first = from;
170.1197 + this.oldLast = oldLast < 0 ? from : oldLast;
170.1198 + for (int i = 0; i < groups.length; i++)
170.1199 + groups[i] = -1;
170.1200 + acceptMode = NOANCHOR;
170.1201 + boolean result = parentPattern.root.match(this, from, text);
170.1202 + if (!result)
170.1203 + this.first = -1;
170.1204 + this.oldLast = this.last;
170.1205 + return result;
170.1206 + }
170.1207 +
170.1208 + /**
170.1209 + * Initiates a search for an anchored match to a Pattern within the given
170.1210 + * bounds. The groups are filled with default values and the match of the
170.1211 + * root of the state machine is called. The state machine will hold the
170.1212 + * state of the match as it proceeds in this matcher.
170.1213 + */
170.1214 + boolean match(int from, int anchor) {
170.1215 + this.hitEnd = false;
170.1216 + this.requireEnd = false;
170.1217 + from = from < 0 ? 0 : from;
170.1218 + this.first = from;
170.1219 + this.oldLast = oldLast < 0 ? from : oldLast;
170.1220 + for (int i = 0; i < groups.length; i++)
170.1221 + groups[i] = -1;
170.1222 + acceptMode = anchor;
170.1223 + boolean result = parentPattern.matchRoot.match(this, from, text);
170.1224 + if (!result)
170.1225 + this.first = -1;
170.1226 + this.oldLast = this.last;
170.1227 + return result;
170.1228 + }
170.1229 +
170.1230 + /**
170.1231 + * Returns the end index of the text.
170.1232 + *
170.1233 + * @return the index after the last character in the text
170.1234 + */
170.1235 + int getTextLength() {
170.1236 + return text.length();
170.1237 + }
170.1238 +
170.1239 + /**
170.1240 + * Generates a String from this Matcher's input in the specified range.
170.1241 + *
170.1242 + * @param beginIndex the beginning index, inclusive
170.1243 + * @param endIndex the ending index, exclusive
170.1244 + * @return A String generated from this Matcher's input
170.1245 + */
170.1246 + CharSequence getSubSequence(int beginIndex, int endIndex) {
170.1247 + return text.subSequence(beginIndex, endIndex);
170.1248 + }
170.1249 +
170.1250 + /**
170.1251 + * Returns this Matcher's input character at index i.
170.1252 + *
170.1253 + * @return A char from the specified index
170.1254 + */
170.1255 + char charAt(int i) {
170.1256 + return text.charAt(i);
170.1257 + }
170.1258 +
170.1259 +}
171.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
171.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/Pattern.java Tue Feb 11 13:31:42 2014 +0100
171.3 @@ -0,0 +1,5657 @@
171.4 +/*
171.5 + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
171.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
171.7 + *
171.8 + * This code is free software; you can redistribute it and/or modify it
171.9 + * under the terms of the GNU General Public License version 2 only, as
171.10 + * published by the Free Software Foundation. Oracle designates this
171.11 + * particular file as subject to the "Classpath" exception as provided
171.12 + * by Oracle in the LICENSE file that accompanied this code.
171.13 + *
171.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
171.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
171.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
171.17 + * version 2 for more details (a copy is included in the LICENSE file that
171.18 + * accompanied this code).
171.19 + *
171.20 + * You should have received a copy of the GNU General Public License version
171.21 + * 2 along with this work; if not, write to the Free Software Foundation,
171.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
171.23 + *
171.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
171.25 + * or visit www.oracle.com if you need additional information or have any
171.26 + * questions.
171.27 + */
171.28 +
171.29 +package java.util.regex;
171.30 +
171.31 +import java.util.Locale;
171.32 +import java.util.Map;
171.33 +import java.util.ArrayList;
171.34 +import java.util.HashMap;
171.35 +import java.util.Arrays;
171.36 +
171.37 +
171.38 +/**
171.39 + * A compiled representation of a regular expression.
171.40 + *
171.41 + * <p> A regular expression, specified as a string, must first be compiled into
171.42 + * an instance of this class. The resulting pattern can then be used to create
171.43 + * a {@link Matcher} object that can match arbitrary {@link
171.44 + * java.lang.CharSequence </code>character sequences<code>} against the regular
171.45 + * expression. All of the state involved in performing a match resides in the
171.46 + * matcher, so many matchers can share the same pattern.
171.47 + *
171.48 + * <p> A typical invocation sequence is thus
171.49 + *
171.50 + * <blockquote><pre>
171.51 + * Pattern p = Pattern.{@link #compile compile}("a*b");
171.52 + * Matcher m = p.{@link #matcher matcher}("aaaaab");
171.53 + * boolean b = m.{@link Matcher#matches matches}();</pre></blockquote>
171.54 + *
171.55 + * <p> A {@link #matches matches} method is defined by this class as a
171.56 + * convenience for when a regular expression is used just once. This method
171.57 + * compiles an expression and matches an input sequence against it in a single
171.58 + * invocation. The statement
171.59 + *
171.60 + * <blockquote><pre>
171.61 + * boolean b = Pattern.matches("a*b", "aaaaab");</pre></blockquote>
171.62 + *
171.63 + * is equivalent to the three statements above, though for repeated matches it
171.64 + * is less efficient since it does not allow the compiled pattern to be reused.
171.65 + *
171.66 + * <p> Instances of this class are immutable and are safe for use by multiple
171.67 + * concurrent threads. Instances of the {@link Matcher} class are not safe for
171.68 + * such use.
171.69 + *
171.70 + *
171.71 + * <a name="sum">
171.72 + * <h4> Summary of regular-expression constructs </h4>
171.73 + *
171.74 + * <table border="0" cellpadding="1" cellspacing="0"
171.75 + * summary="Regular expression constructs, and what they match">
171.76 + *
171.77 + * <tr align="left">
171.78 + * <th bgcolor="#CCCCFF" align="left" id="construct">Construct</th>
171.79 + * <th bgcolor="#CCCCFF" align="left" id="matches">Matches</th>
171.80 + * </tr>
171.81 + *
171.82 + * <tr><th> </th></tr>
171.83 + * <tr align="left"><th colspan="2" id="characters">Characters</th></tr>
171.84 + *
171.85 + * <tr><td valign="top" headers="construct characters"><i>x</i></td>
171.86 + * <td headers="matches">The character <i>x</i></td></tr>
171.87 + * <tr><td valign="top" headers="construct characters"><tt>\\</tt></td>
171.88 + * <td headers="matches">The backslash character</td></tr>
171.89 + * <tr><td valign="top" headers="construct characters"><tt>\0</tt><i>n</i></td>
171.90 + * <td headers="matches">The character with octal value <tt>0</tt><i>n</i>
171.91 + * (0 <tt><=</tt> <i>n</i> <tt><=</tt> 7)</td></tr>
171.92 + * <tr><td valign="top" headers="construct characters"><tt>\0</tt><i>nn</i></td>
171.93 + * <td headers="matches">The character with octal value <tt>0</tt><i>nn</i>
171.94 + * (0 <tt><=</tt> <i>n</i> <tt><=</tt> 7)</td></tr>
171.95 + * <tr><td valign="top" headers="construct characters"><tt>\0</tt><i>mnn</i></td>
171.96 + * <td headers="matches">The character with octal value <tt>0</tt><i>mnn</i>
171.97 + * (0 <tt><=</tt> <i>m</i> <tt><=</tt> 3,
171.98 + * 0 <tt><=</tt> <i>n</i> <tt><=</tt> 7)</td></tr>
171.99 + * <tr><td valign="top" headers="construct characters"><tt>\x</tt><i>hh</i></td>
171.100 + * <td headers="matches">The character with hexadecimal value <tt>0x</tt><i>hh</i></td></tr>
171.101 + * <tr><td valign="top" headers="construct characters"><tt>\u</tt><i>hhhh</i></td>
171.102 + * <td headers="matches">The character with hexadecimal value <tt>0x</tt><i>hhhh</i></td></tr>
171.103 + * <tr><td valign="top" headers="construct characters"><tt>\x</tt><i>{h...h}</i></td>
171.104 + * <td headers="matches">The character with hexadecimal value <tt>0x</tt><i>h...h</i>
171.105 + * ({@link java.lang.Character#MIN_CODE_POINT Character.MIN_CODE_POINT}
171.106 + * <= <tt>0x</tt><i>h...h</i> <= 
171.107 + * {@link java.lang.Character#MAX_CODE_POINT Character.MAX_CODE_POINT})</td></tr>
171.108 + * <tr><td valign="top" headers="matches"><tt>\t</tt></td>
171.109 + * <td headers="matches">The tab character (<tt>'\u0009'</tt>)</td></tr>
171.110 + * <tr><td valign="top" headers="construct characters"><tt>\n</tt></td>
171.111 + * <td headers="matches">The newline (line feed) character (<tt>'\u000A'</tt>)</td></tr>
171.112 + * <tr><td valign="top" headers="construct characters"><tt>\r</tt></td>
171.113 + * <td headers="matches">The carriage-return character (<tt>'\u000D'</tt>)</td></tr>
171.114 + * <tr><td valign="top" headers="construct characters"><tt>\f</tt></td>
171.115 + * <td headers="matches">The form-feed character (<tt>'\u000C'</tt>)</td></tr>
171.116 + * <tr><td valign="top" headers="construct characters"><tt>\a</tt></td>
171.117 + * <td headers="matches">The alert (bell) character (<tt>'\u0007'</tt>)</td></tr>
171.118 + * <tr><td valign="top" headers="construct characters"><tt>\e</tt></td>
171.119 + * <td headers="matches">The escape character (<tt>'\u001B'</tt>)</td></tr>
171.120 + * <tr><td valign="top" headers="construct characters"><tt>\c</tt><i>x</i></td>
171.121 + * <td headers="matches">The control character corresponding to <i>x</i></td></tr>
171.122 + *
171.123 + * <tr><th> </th></tr>
171.124 + * <tr align="left"><th colspan="2" id="classes">Character classes</th></tr>
171.125 + *
171.126 + * <tr><td valign="top" headers="construct classes"><tt>[abc]</tt></td>
171.127 + * <td headers="matches"><tt>a</tt>, <tt>b</tt>, or <tt>c</tt> (simple class)</td></tr>
171.128 + * <tr><td valign="top" headers="construct classes"><tt>[^abc]</tt></td>
171.129 + * <td headers="matches">Any character except <tt>a</tt>, <tt>b</tt>, or <tt>c</tt> (negation)</td></tr>
171.130 + * <tr><td valign="top" headers="construct classes"><tt>[a-zA-Z]</tt></td>
171.131 + * <td headers="matches"><tt>a</tt> through <tt>z</tt>
171.132 + * or <tt>A</tt> through <tt>Z</tt>, inclusive (range)</td></tr>
171.133 + * <tr><td valign="top" headers="construct classes"><tt>[a-d[m-p]]</tt></td>
171.134 + * <td headers="matches"><tt>a</tt> through <tt>d</tt>,
171.135 + * or <tt>m</tt> through <tt>p</tt>: <tt>[a-dm-p]</tt> (union)</td></tr>
171.136 + * <tr><td valign="top" headers="construct classes"><tt>[a-z&&[def]]</tt></td>
171.137 + * <td headers="matches"><tt>d</tt>, <tt>e</tt>, or <tt>f</tt> (intersection)</tr>
171.138 + * <tr><td valign="top" headers="construct classes"><tt>[a-z&&[^bc]]</tt></td>
171.139 + * <td headers="matches"><tt>a</tt> through <tt>z</tt>,
171.140 + * except for <tt>b</tt> and <tt>c</tt>: <tt>[ad-z]</tt> (subtraction)</td></tr>
171.141 + * <tr><td valign="top" headers="construct classes"><tt>[a-z&&[^m-p]]</tt></td>
171.142 + * <td headers="matches"><tt>a</tt> through <tt>z</tt>,
171.143 + * and not <tt>m</tt> through <tt>p</tt>: <tt>[a-lq-z]</tt>(subtraction)</td></tr>
171.144 + * <tr><th> </th></tr>
171.145 + *
171.146 + * <tr align="left"><th colspan="2" id="predef">Predefined character classes</th></tr>
171.147 + *
171.148 + * <tr><td valign="top" headers="construct predef"><tt>.</tt></td>
171.149 + * <td headers="matches">Any character (may or may not match <a href="#lt">line terminators</a>)</td></tr>
171.150 + * <tr><td valign="top" headers="construct predef"><tt>\d</tt></td>
171.151 + * <td headers="matches">A digit: <tt>[0-9]</tt></td></tr>
171.152 + * <tr><td valign="top" headers="construct predef"><tt>\D</tt></td>
171.153 + * <td headers="matches">A non-digit: <tt>[^0-9]</tt></td></tr>
171.154 + * <tr><td valign="top" headers="construct predef"><tt>\s</tt></td>
171.155 + * <td headers="matches">A whitespace character: <tt>[ \t\n\x0B\f\r]</tt></td></tr>
171.156 + * <tr><td valign="top" headers="construct predef"><tt>\S</tt></td>
171.157 + * <td headers="matches">A non-whitespace character: <tt>[^\s]</tt></td></tr>
171.158 + * <tr><td valign="top" headers="construct predef"><tt>\w</tt></td>
171.159 + * <td headers="matches">A word character: <tt>[a-zA-Z_0-9]</tt></td></tr>
171.160 + * <tr><td valign="top" headers="construct predef"><tt>\W</tt></td>
171.161 + * <td headers="matches">A non-word character: <tt>[^\w]</tt></td></tr>
171.162 + *
171.163 + * <tr><th> </th></tr>
171.164 + * <tr align="left"><th colspan="2" id="posix">POSIX character classes</b> (US-ASCII only)<b></th></tr>
171.165 + *
171.166 + * <tr><td valign="top" headers="construct posix"><tt>\p{Lower}</tt></td>
171.167 + * <td headers="matches">A lower-case alphabetic character: <tt>[a-z]</tt></td></tr>
171.168 + * <tr><td valign="top" headers="construct posix"><tt>\p{Upper}</tt></td>
171.169 + * <td headers="matches">An upper-case alphabetic character:<tt>[A-Z]</tt></td></tr>
171.170 + * <tr><td valign="top" headers="construct posix"><tt>\p{ASCII}</tt></td>
171.171 + * <td headers="matches">All ASCII:<tt>[\x00-\x7F]</tt></td></tr>
171.172 + * <tr><td valign="top" headers="construct posix"><tt>\p{Alpha}</tt></td>
171.173 + * <td headers="matches">An alphabetic character:<tt>[\p{Lower}\p{Upper}]</tt></td></tr>
171.174 + * <tr><td valign="top" headers="construct posix"><tt>\p{Digit}</tt></td>
171.175 + * <td headers="matches">A decimal digit: <tt>[0-9]</tt></td></tr>
171.176 + * <tr><td valign="top" headers="construct posix"><tt>\p{Alnum}</tt></td>
171.177 + * <td headers="matches">An alphanumeric character:<tt>[\p{Alpha}\p{Digit}]</tt></td></tr>
171.178 + * <tr><td valign="top" headers="construct posix"><tt>\p{Punct}</tt></td>
171.179 + * <td headers="matches">Punctuation: One of <tt>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</tt></td></tr>
171.180 + * <!-- <tt>[\!"#\$%&'\(\)\*\+,\-\./:;\<=\>\?@\[\\\]\^_`\{\|\}~]</tt>
171.181 + * <tt>[\X21-\X2F\X31-\X40\X5B-\X60\X7B-\X7E]</tt> -->
171.182 + * <tr><td valign="top" headers="construct posix"><tt>\p{Graph}</tt></td>
171.183 + * <td headers="matches">A visible character: <tt>[\p{Alnum}\p{Punct}]</tt></td></tr>
171.184 + * <tr><td valign="top" headers="construct posix"><tt>\p{Print}</tt></td>
171.185 + * <td headers="matches">A printable character: <tt>[\p{Graph}\x20]</tt></td></tr>
171.186 + * <tr><td valign="top" headers="construct posix"><tt>\p{Blank}</tt></td>
171.187 + * <td headers="matches">A space or a tab: <tt>[ \t]</tt></td></tr>
171.188 + * <tr><td valign="top" headers="construct posix"><tt>\p{Cntrl}</tt></td>
171.189 + * <td headers="matches">A control character: <tt>[\x00-\x1F\x7F]</tt></td></tr>
171.190 + * <tr><td valign="top" headers="construct posix"><tt>\p{XDigit}</tt></td>
171.191 + * <td headers="matches">A hexadecimal digit: <tt>[0-9a-fA-F]</tt></td></tr>
171.192 + * <tr><td valign="top" headers="construct posix"><tt>\p{Space}</tt></td>
171.193 + * <td headers="matches">A whitespace character: <tt>[ \t\n\x0B\f\r]</tt></td></tr>
171.194 + *
171.195 + * <tr><th> </th></tr>
171.196 + * <tr align="left"><th colspan="2">java.lang.Character classes (simple <a href="#jcc">java character type</a>)</th></tr>
171.197 + *
171.198 + * <tr><td valign="top"><tt>\p{javaLowerCase}</tt></td>
171.199 + * <td>Equivalent to java.lang.Character.isLowerCase()</td></tr>
171.200 + * <tr><td valign="top"><tt>\p{javaUpperCase}</tt></td>
171.201 + * <td>Equivalent to java.lang.Character.isUpperCase()</td></tr>
171.202 + * <tr><td valign="top"><tt>\p{javaWhitespace}</tt></td>
171.203 + * <td>Equivalent to java.lang.Character.isWhitespace()</td></tr>
171.204 + * <tr><td valign="top"><tt>\p{javaMirrored}</tt></td>
171.205 + * <td>Equivalent to java.lang.Character.isMirrored()</td></tr>
171.206 + *
171.207 + * <tr><th> </th></tr>
171.208 + * <tr align="left"><th colspan="2" id="unicode">Classes for Unicode scripts, blocks, categories and binary properties</th></tr>
171.209 + * * <tr><td valign="top" headers="construct unicode"><tt>\p{IsLatin}</tt></td>
171.210 + * <td headers="matches">A Latin script character (<a href="#usc">script</a>)</td></tr>
171.211 + * <tr><td valign="top" headers="construct unicode"><tt>\p{InGreek}</tt></td>
171.212 + * <td headers="matches">A character in the Greek block (<a href="#ubc">block</a>)</td></tr>
171.213 + * <tr><td valign="top" headers="construct unicode"><tt>\p{Lu}</tt></td>
171.214 + * <td headers="matches">An uppercase letter (<a href="#ucc">category</a>)</td></tr>
171.215 + * <tr><td valign="top" headers="construct unicode"><tt>\p{IsAlphabetic}</tt></td>
171.216 + * <td headers="matches">An alphabetic character (<a href="#ubpc">binary property</a>)</td></tr>
171.217 + * <tr><td valign="top" headers="construct unicode"><tt>\p{Sc}</tt></td>
171.218 + * <td headers="matches">A currency symbol</td></tr>
171.219 + * <tr><td valign="top" headers="construct unicode"><tt>\P{InGreek}</tt></td>
171.220 + * <td headers="matches">Any character except one in the Greek block (negation)</td></tr>
171.221 + * <tr><td valign="top" headers="construct unicode"><tt>[\p{L}&&[^\p{Lu}]] </tt></td>
171.222 + * <td headers="matches">Any letter except an uppercase letter (subtraction)</td></tr>
171.223 + *
171.224 + * <tr><th> </th></tr>
171.225 + * <tr align="left"><th colspan="2" id="bounds">Boundary matchers</th></tr>
171.226 + *
171.227 + * <tr><td valign="top" headers="construct bounds"><tt>^</tt></td>
171.228 + * <td headers="matches">The beginning of a line</td></tr>
171.229 + * <tr><td valign="top" headers="construct bounds"><tt>$</tt></td>
171.230 + * <td headers="matches">The end of a line</td></tr>
171.231 + * <tr><td valign="top" headers="construct bounds"><tt>\b</tt></td>
171.232 + * <td headers="matches">A word boundary</td></tr>
171.233 + * <tr><td valign="top" headers="construct bounds"><tt>\B</tt></td>
171.234 + * <td headers="matches">A non-word boundary</td></tr>
171.235 + * <tr><td valign="top" headers="construct bounds"><tt>\A</tt></td>
171.236 + * <td headers="matches">The beginning of the input</td></tr>
171.237 + * <tr><td valign="top" headers="construct bounds"><tt>\G</tt></td>
171.238 + * <td headers="matches">The end of the previous match</td></tr>
171.239 + * <tr><td valign="top" headers="construct bounds"><tt>\Z</tt></td>
171.240 + * <td headers="matches">The end of the input but for the final
171.241 + * <a href="#lt">terminator</a>, if any</td></tr>
171.242 + * <tr><td valign="top" headers="construct bounds"><tt>\z</tt></td>
171.243 + * <td headers="matches">The end of the input</td></tr>
171.244 + *
171.245 + * <tr><th> </th></tr>
171.246 + * <tr align="left"><th colspan="2" id="greedy">Greedy quantifiers</th></tr>
171.247 + *
171.248 + * <tr><td valign="top" headers="construct greedy"><i>X</i><tt>?</tt></td>
171.249 + * <td headers="matches"><i>X</i>, once or not at all</td></tr>
171.250 + * <tr><td valign="top" headers="construct greedy"><i>X</i><tt>*</tt></td>
171.251 + * <td headers="matches"><i>X</i>, zero or more times</td></tr>
171.252 + * <tr><td valign="top" headers="construct greedy"><i>X</i><tt>+</tt></td>
171.253 + * <td headers="matches"><i>X</i>, one or more times</td></tr>
171.254 + * <tr><td valign="top" headers="construct greedy"><i>X</i><tt>{</tt><i>n</i><tt>}</tt></td>
171.255 + * <td headers="matches"><i>X</i>, exactly <i>n</i> times</td></tr>
171.256 + * <tr><td valign="top" headers="construct greedy"><i>X</i><tt>{</tt><i>n</i><tt>,}</tt></td>
171.257 + * <td headers="matches"><i>X</i>, at least <i>n</i> times</td></tr>
171.258 + * <tr><td valign="top" headers="construct greedy"><i>X</i><tt>{</tt><i>n</i><tt>,</tt><i>m</i><tt>}</tt></td>
171.259 + * <td headers="matches"><i>X</i>, at least <i>n</i> but not more than <i>m</i> times</td></tr>
171.260 + *
171.261 + * <tr><th> </th></tr>
171.262 + * <tr align="left"><th colspan="2" id="reluc">Reluctant quantifiers</th></tr>
171.263 + *
171.264 + * <tr><td valign="top" headers="construct reluc"><i>X</i><tt>??</tt></td>
171.265 + * <td headers="matches"><i>X</i>, once or not at all</td></tr>
171.266 + * <tr><td valign="top" headers="construct reluc"><i>X</i><tt>*?</tt></td>
171.267 + * <td headers="matches"><i>X</i>, zero or more times</td></tr>
171.268 + * <tr><td valign="top" headers="construct reluc"><i>X</i><tt>+?</tt></td>
171.269 + * <td headers="matches"><i>X</i>, one or more times</td></tr>
171.270 + * <tr><td valign="top" headers="construct reluc"><i>X</i><tt>{</tt><i>n</i><tt>}?</tt></td>
171.271 + * <td headers="matches"><i>X</i>, exactly <i>n</i> times</td></tr>
171.272 + * <tr><td valign="top" headers="construct reluc"><i>X</i><tt>{</tt><i>n</i><tt>,}?</tt></td>
171.273 + * <td headers="matches"><i>X</i>, at least <i>n</i> times</td></tr>
171.274 + * <tr><td valign="top" headers="construct reluc"><i>X</i><tt>{</tt><i>n</i><tt>,</tt><i>m</i><tt>}?</tt></td>
171.275 + * <td headers="matches"><i>X</i>, at least <i>n</i> but not more than <i>m</i> times</td></tr>
171.276 + *
171.277 + * <tr><th> </th></tr>
171.278 + * <tr align="left"><th colspan="2" id="poss">Possessive quantifiers</th></tr>
171.279 + *
171.280 + * <tr><td valign="top" headers="construct poss"><i>X</i><tt>?+</tt></td>
171.281 + * <td headers="matches"><i>X</i>, once or not at all</td></tr>
171.282 + * <tr><td valign="top" headers="construct poss"><i>X</i><tt>*+</tt></td>
171.283 + * <td headers="matches"><i>X</i>, zero or more times</td></tr>
171.284 + * <tr><td valign="top" headers="construct poss"><i>X</i><tt>++</tt></td>
171.285 + * <td headers="matches"><i>X</i>, one or more times</td></tr>
171.286 + * <tr><td valign="top" headers="construct poss"><i>X</i><tt>{</tt><i>n</i><tt>}+</tt></td>
171.287 + * <td headers="matches"><i>X</i>, exactly <i>n</i> times</td></tr>
171.288 + * <tr><td valign="top" headers="construct poss"><i>X</i><tt>{</tt><i>n</i><tt>,}+</tt></td>
171.289 + * <td headers="matches"><i>X</i>, at least <i>n</i> times</td></tr>
171.290 + * <tr><td valign="top" headers="construct poss"><i>X</i><tt>{</tt><i>n</i><tt>,</tt><i>m</i><tt>}+</tt></td>
171.291 + * <td headers="matches"><i>X</i>, at least <i>n</i> but not more than <i>m</i> times</td></tr>
171.292 + *
171.293 + * <tr><th> </th></tr>
171.294 + * <tr align="left"><th colspan="2" id="logical">Logical operators</th></tr>
171.295 + *
171.296 + * <tr><td valign="top" headers="construct logical"><i>XY</i></td>
171.297 + * <td headers="matches"><i>X</i> followed by <i>Y</i></td></tr>
171.298 + * <tr><td valign="top" headers="construct logical"><i>X</i><tt>|</tt><i>Y</i></td>
171.299 + * <td headers="matches">Either <i>X</i> or <i>Y</i></td></tr>
171.300 + * <tr><td valign="top" headers="construct logical"><tt>(</tt><i>X</i><tt>)</tt></td>
171.301 + * <td headers="matches">X, as a <a href="#cg">capturing group</a></td></tr>
171.302 + *
171.303 + * <tr><th> </th></tr>
171.304 + * <tr align="left"><th colspan="2" id="backref">Back references</th></tr>
171.305 + *
171.306 + * <tr><td valign="bottom" headers="construct backref"><tt>\</tt><i>n</i></td>
171.307 + * <td valign="bottom" headers="matches">Whatever the <i>n</i><sup>th</sup>
171.308 + * <a href="#cg">capturing group</a> matched</td></tr>
171.309 + *
171.310 + * <tr><td valign="bottom" headers="construct backref"><tt>\</tt><i>k</i><<i>name</i>></td>
171.311 + * <td valign="bottom" headers="matches">Whatever the
171.312 + * <a href="#groupname">named-capturing group</a> "name" matched</td></tr>
171.313 + *
171.314 + * <tr><th> </th></tr>
171.315 + * <tr align="left"><th colspan="2" id="quot">Quotation</th></tr>
171.316 + *
171.317 + * <tr><td valign="top" headers="construct quot"><tt>\</tt></td>
171.318 + * <td headers="matches">Nothing, but quotes the following character</td></tr>
171.319 + * <tr><td valign="top" headers="construct quot"><tt>\Q</tt></td>
171.320 + * <td headers="matches">Nothing, but quotes all characters until <tt>\E</tt></td></tr>
171.321 + * <tr><td valign="top" headers="construct quot"><tt>\E</tt></td>
171.322 + * <td headers="matches">Nothing, but ends quoting started by <tt>\Q</tt></td></tr>
171.323 + * <!-- Metachars: !$()*+.<>?[\]^{|} -->
171.324 + *
171.325 + * <tr><th> </th></tr>
171.326 + * <tr align="left"><th colspan="2" id="special">Special constructs (named-capturing and non-capturing)</th></tr>
171.327 + *
171.328 + * <tr><td valign="top" headers="construct special"><tt>(?<<a href="#groupname">name</a>></tt><i>X</i><tt>)</tt></td>
171.329 + * <td headers="matches"><i>X</i>, as a named-capturing group</td></tr>
171.330 + * <tr><td valign="top" headers="construct special"><tt>(?:</tt><i>X</i><tt>)</tt></td>
171.331 + * <td headers="matches"><i>X</i>, as a non-capturing group</td></tr>
171.332 + * <tr><td valign="top" headers="construct special"><tt>(?idmsuxU-idmsuxU) </tt></td>
171.333 + * <td headers="matches">Nothing, but turns match flags <a href="#CASE_INSENSITIVE">i</a>
171.334 + * <a href="#UNIX_LINES">d</a> <a href="#MULTILINE">m</a> <a href="#DOTALL">s</a>
171.335 + * <a href="#UNICODE_CASE">u</a> <a href="#COMMENTS">x</a> <a href="#UNICODE_CHARACTER_CLASS">U</a>
171.336 + * on - off</td></tr>
171.337 + * <tr><td valign="top" headers="construct special"><tt>(?idmsux-idmsux:</tt><i>X</i><tt>)</tt> </td>
171.338 + * <td headers="matches"><i>X</i>, as a <a href="#cg">non-capturing group</a> with the
171.339 + * given flags <a href="#CASE_INSENSITIVE">i</a> <a href="#UNIX_LINES">d</a>
171.340 + * <a href="#MULTILINE">m</a> <a href="#DOTALL">s</a> <a href="#UNICODE_CASE">u</a >
171.341 + * <a href="#COMMENTS">x</a> on - off</td></tr>
171.342 + * <tr><td valign="top" headers="construct special"><tt>(?=</tt><i>X</i><tt>)</tt></td>
171.343 + * <td headers="matches"><i>X</i>, via zero-width positive lookahead</td></tr>
171.344 + * <tr><td valign="top" headers="construct special"><tt>(?!</tt><i>X</i><tt>)</tt></td>
171.345 + * <td headers="matches"><i>X</i>, via zero-width negative lookahead</td></tr>
171.346 + * <tr><td valign="top" headers="construct special"><tt>(?<=</tt><i>X</i><tt>)</tt></td>
171.347 + * <td headers="matches"><i>X</i>, via zero-width positive lookbehind</td></tr>
171.348 + * <tr><td valign="top" headers="construct special"><tt>(?<!</tt><i>X</i><tt>)</tt></td>
171.349 + * <td headers="matches"><i>X</i>, via zero-width negative lookbehind</td></tr>
171.350 + * <tr><td valign="top" headers="construct special"><tt>(?></tt><i>X</i><tt>)</tt></td>
171.351 + * <td headers="matches"><i>X</i>, as an independent, non-capturing group</td></tr>
171.352 + *
171.353 + * </table>
171.354 + *
171.355 + * <hr>
171.356 + *
171.357 + *
171.358 + * <a name="bs">
171.359 + * <h4> Backslashes, escapes, and quoting </h4>
171.360 + *
171.361 + * <p> The backslash character (<tt>'\'</tt>) serves to introduce escaped
171.362 + * constructs, as defined in the table above, as well as to quote characters
171.363 + * that otherwise would be interpreted as unescaped constructs. Thus the
171.364 + * expression <tt>\\</tt> matches a single backslash and <tt>\{</tt> matches a
171.365 + * left brace.
171.366 + *
171.367 + * <p> It is an error to use a backslash prior to any alphabetic character that
171.368 + * does not denote an escaped construct; these are reserved for future
171.369 + * extensions to the regular-expression language. A backslash may be used
171.370 + * prior to a non-alphabetic character regardless of whether that character is
171.371 + * part of an unescaped construct.
171.372 + *
171.373 + * <p> Backslashes within string literals in Java source code are interpreted
171.374 + * as required by
171.375 + * <cite>The Java™ Language Specification</cite>
171.376 + * as either Unicode escapes (section 3.3) or other character escapes (section 3.10.6)
171.377 + * It is therefore necessary to double backslashes in string
171.378 + * literals that represent regular expressions to protect them from
171.379 + * interpretation by the Java bytecode compiler. The string literal
171.380 + * <tt>"\b"</tt>, for example, matches a single backspace character when
171.381 + * interpreted as a regular expression, while <tt>"\\b"</tt> matches a
171.382 + * word boundary. The string literal <tt>"\(hello\)"</tt> is illegal
171.383 + * and leads to a compile-time error; in order to match the string
171.384 + * <tt>(hello)</tt> the string literal <tt>"\\(hello\\)"</tt>
171.385 + * must be used.
171.386 + *
171.387 + * <a name="cc">
171.388 + * <h4> Character Classes </h4>
171.389 + *
171.390 + * <p> Character classes may appear within other character classes, and
171.391 + * may be composed by the union operator (implicit) and the intersection
171.392 + * operator (<tt>&&</tt>).
171.393 + * The union operator denotes a class that contains every character that is
171.394 + * in at least one of its operand classes. The intersection operator
171.395 + * denotes a class that contains every character that is in both of its
171.396 + * operand classes.
171.397 + *
171.398 + * <p> The precedence of character-class operators is as follows, from
171.399 + * highest to lowest:
171.400 + *
171.401 + * <blockquote><table border="0" cellpadding="1" cellspacing="0"
171.402 + * summary="Precedence of character class operators.">
171.403 + * <tr><th>1 </th>
171.404 + * <td>Literal escape </td>
171.405 + * <td><tt>\x</tt></td></tr>
171.406 + * <tr><th>2 </th>
171.407 + * <td>Grouping</td>
171.408 + * <td><tt>[...]</tt></td></tr>
171.409 + * <tr><th>3 </th>
171.410 + * <td>Range</td>
171.411 + * <td><tt>a-z</tt></td></tr>
171.412 + * <tr><th>4 </th>
171.413 + * <td>Union</td>
171.414 + * <td><tt>[a-e][i-u]</tt></td></tr>
171.415 + * <tr><th>5 </th>
171.416 + * <td>Intersection</td>
171.417 + * <td><tt>[a-z&&[aeiou]]</tt></td></tr>
171.418 + * </table></blockquote>
171.419 + *
171.420 + * <p> Note that a different set of metacharacters are in effect inside
171.421 + * a character class than outside a character class. For instance, the
171.422 + * regular expression <tt>.</tt> loses its special meaning inside a
171.423 + * character class, while the expression <tt>-</tt> becomes a range
171.424 + * forming metacharacter.
171.425 + *
171.426 + * <a name="lt">
171.427 + * <h4> Line terminators </h4>
171.428 + *
171.429 + * <p> A <i>line terminator</i> is a one- or two-character sequence that marks
171.430 + * the end of a line of the input character sequence. The following are
171.431 + * recognized as line terminators:
171.432 + *
171.433 + * <ul>
171.434 + *
171.435 + * <li> A newline (line feed) character (<tt>'\n'</tt>),
171.436 + *
171.437 + * <li> A carriage-return character followed immediately by a newline
171.438 + * character (<tt>"\r\n"</tt>),
171.439 + *
171.440 + * <li> A standalone carriage-return character (<tt>'\r'</tt>),
171.441 + *
171.442 + * <li> A next-line character (<tt>'\u0085'</tt>),
171.443 + *
171.444 + * <li> A line-separator character (<tt>'\u2028'</tt>), or
171.445 + *
171.446 + * <li> A paragraph-separator character (<tt>'\u2029</tt>).
171.447 + *
171.448 + * </ul>
171.449 + * <p>If {@link #UNIX_LINES} mode is activated, then the only line terminators
171.450 + * recognized are newline characters.
171.451 + *
171.452 + * <p> The regular expression <tt>.</tt> matches any character except a line
171.453 + * terminator unless the {@link #DOTALL} flag is specified.
171.454 + *
171.455 + * <p> By default, the regular expressions <tt>^</tt> and <tt>$</tt> ignore
171.456 + * line terminators and only match at the beginning and the end, respectively,
171.457 + * of the entire input sequence. If {@link #MULTILINE} mode is activated then
171.458 + * <tt>^</tt> matches at the beginning of input and after any line terminator
171.459 + * except at the end of input. When in {@link #MULTILINE} mode <tt>$</tt>
171.460 + * matches just before a line terminator or the end of the input sequence.
171.461 + *
171.462 + * <a name="cg">
171.463 + * <h4> Groups and capturing </h4>
171.464 + *
171.465 + * <a name="gnumber">
171.466 + * <h5> Group number </h5>
171.467 + * <p> Capturing groups are numbered by counting their opening parentheses from
171.468 + * left to right. In the expression <tt>((A)(B(C)))</tt>, for example, there
171.469 + * are four such groups: </p>
171.470 + *
171.471 + * <blockquote><table cellpadding=1 cellspacing=0 summary="Capturing group numberings">
171.472 + * <tr><th>1 </th>
171.473 + * <td><tt>((A)(B(C)))</tt></td></tr>
171.474 + * <tr><th>2 </th>
171.475 + * <td><tt>(A)</tt></td></tr>
171.476 + * <tr><th>3 </th>
171.477 + * <td><tt>(B(C))</tt></td></tr>
171.478 + * <tr><th>4 </th>
171.479 + * <td><tt>(C)</tt></td></tr>
171.480 + * </table></blockquote>
171.481 + *
171.482 + * <p> Group zero always stands for the entire expression.
171.483 + *
171.484 + * <p> Capturing groups are so named because, during a match, each subsequence
171.485 + * of the input sequence that matches such a group is saved. The captured
171.486 + * subsequence may be used later in the expression, via a back reference, and
171.487 + * may also be retrieved from the matcher once the match operation is complete.
171.488 + *
171.489 + * <a name="groupname">
171.490 + * <h5> Group name </h5>
171.491 + * <p>A capturing group can also be assigned a "name", a <tt>named-capturing group</tt>,
171.492 + * and then be back-referenced later by the "name". Group names are composed of
171.493 + * the following characters. The first character must be a <tt>letter</tt>.
171.494 + *
171.495 + * <ul>
171.496 + * <li> The uppercase letters <tt>'A'</tt> through <tt>'Z'</tt>
171.497 + * (<tt>'\u0041'</tt> through <tt>'\u005a'</tt>),
171.498 + * <li> The lowercase letters <tt>'a'</tt> through <tt>'z'</tt>
171.499 + * (<tt>'\u0061'</tt> through <tt>'\u007a'</tt>),
171.500 + * <li> The digits <tt>'0'</tt> through <tt>'9'</tt>
171.501 + * (<tt>'\u0030'</tt> through <tt>'\u0039'</tt>),
171.502 + * </ul>
171.503 + *
171.504 + * <p> A <tt>named-capturing group</tt> is still numbered as described in
171.505 + * <a href="#gnumber">Group number</a>.
171.506 + *
171.507 + * <p> The captured input associated with a group is always the subsequence
171.508 + * that the group most recently matched. If a group is evaluated a second time
171.509 + * because of quantification then its previously-captured value, if any, will
171.510 + * be retained if the second evaluation fails. Matching the string
171.511 + * <tt>"aba"</tt> against the expression <tt>(a(b)?)+</tt>, for example, leaves
171.512 + * group two set to <tt>"b"</tt>. All captured input is discarded at the
171.513 + * beginning of each match.
171.514 + *
171.515 + * <p> Groups beginning with <tt>(?</tt> are either pure, <i>non-capturing</i> groups
171.516 + * that do not capture text and do not count towards the group total, or
171.517 + * <i>named-capturing</i> group.
171.518 + *
171.519 + * <h4> Unicode support </h4>
171.520 + *
171.521 + * <p> This class is in conformance with Level 1 of <a
171.522 + * href="http://www.unicode.org/reports/tr18/"><i>Unicode Technical
171.523 + * Standard #18: Unicode Regular Expression</i></a>, plus RL2.1
171.524 + * Canonical Equivalents.
171.525 + * <p>
171.526 + * <b>Unicode escape sequences</b> such as <tt>\u2014</tt> in Java source code
171.527 + * are processed as described in section 3.3 of
171.528 + * <cite>The Java™ Language Specification</cite>.
171.529 + * Such escape sequences are also implemented directly by the regular-expression
171.530 + * parser so that Unicode escapes can be used in expressions that are read from
171.531 + * files or from the keyboard. Thus the strings <tt>"\u2014"</tt> and
171.532 + * <tt>"\\u2014"</tt>, while not equal, compile into the same pattern, which
171.533 + * matches the character with hexadecimal value <tt>0x2014</tt>.
171.534 + * <p>
171.535 + * A Unicode character can also be represented in a regular-expression by
171.536 + * using its <b>Hex notation</b>(hexadecimal code point value) directly as described in construct
171.537 + * <tt>\x{...}</tt>, for example a supplementary character U+2011F
171.538 + * can be specified as <tt>\x{2011F}</tt>, instead of two consecutive
171.539 + * Unicode escape sequences of the surrogate pair
171.540 + * <tt>\uD840</tt><tt>\uDD1F</tt>.
171.541 + * <p>
171.542 + * Unicode scripts, blocks, categories and binary properties are written with
171.543 + * the <tt>\p</tt> and <tt>\P</tt> constructs as in Perl.
171.544 + * <tt>\p{</tt><i>prop</i><tt>}</tt> matches if
171.545 + * the input has the property <i>prop</i>, while <tt>\P{</tt><i>prop</i><tt>}</tt>
171.546 + * does not match if the input has that property.
171.547 + * <p>
171.548 + * Scripts, blocks, categories and binary properties can be used both inside
171.549 + * and outside of a character class.
171.550 + * <a name="usc">
171.551 + * <p>
171.552 + * <b>Scripts</b> are specified either with the prefix {@code Is}, as in
171.553 + * {@code IsHiragana}, or by using the {@code script} keyword (or its short
171.554 + * form {@code sc})as in {@code script=Hiragana} or {@code sc=Hiragana}.
171.555 + * <p>
171.556 + * The script names supported by <code>Pattern</code> are the valid script names
171.557 + * accepted and defined by
171.558 + * {@link java.lang.Character.UnicodeScript#forName(String) UnicodeScript.forName}.
171.559 + * <a name="ubc">
171.560 + * <p>
171.561 + * <b>Blocks</b> are specified with the prefix {@code In}, as in
171.562 + * {@code InMongolian}, or by using the keyword {@code block} (or its short
171.563 + * form {@code blk}) as in {@code block=Mongolian} or {@code blk=Mongolian}.
171.564 + * <p>
171.565 + * The block names supported by <code>Pattern</code> are the valid block names
171.566 + * accepted and defined by
171.567 + * {@link java.lang.Character.UnicodeBlock#forName(String) UnicodeBlock.forName}.
171.568 + * <p>
171.569 + * <a name="ucc">
171.570 + * <b>Categories</b> may be specified with the optional prefix {@code Is}:
171.571 + * Both {@code \p{L}} and {@code \p{IsL}} denote the category of Unicode
171.572 + * letters. Same as scripts and blocks, categories can also be specified
171.573 + * by using the keyword {@code general_category} (or its short form
171.574 + * {@code gc}) as in {@code general_category=Lu} or {@code gc=Lu}.
171.575 + * <p>
171.576 + * The supported categories are those of
171.577 + * <a href="http://www.unicode.org/unicode/standard/standard.html">
171.578 + * <i>The Unicode Standard</i></a> in the version specified by the
171.579 + * {@link java.lang.Character Character} class. The category names are those
171.580 + * defined in the Standard, both normative and informative.
171.581 + * <p>
171.582 + * <a name="ubpc">
171.583 + * <b>Binary properties</b> are specified with the prefix {@code Is}, as in
171.584 + * {@code IsAlphabetic}. The supported binary properties by <code>Pattern</code>
171.585 + * are
171.586 + * <ul>
171.587 + * <li> Alphabetic
171.588 + * <li> Ideographic
171.589 + * <li> Letter
171.590 + * <li> Lowercase
171.591 + * <li> Uppercase
171.592 + * <li> Titlecase
171.593 + * <li> Punctuation
171.594 + * <Li> Control
171.595 + * <li> White_Space
171.596 + * <li> Digit
171.597 + * <li> Hex_Digit
171.598 + * <li> Noncharacter_Code_Point
171.599 + * <li> Assigned
171.600 + * </ul>
171.601 +
171.602 +
171.603 + * <p>
171.604 + * <b>Predefined Character classes</b> and <b>POSIX character classes</b> are in
171.605 + * conformance with the recommendation of <i>Annex C: Compatibility Properties</i>
171.606 + * of <a href="http://www.unicode.org/reports/tr18/"><i>Unicode Regular Expression
171.607 + * </i></a>, when {@link #UNICODE_CHARACTER_CLASS} flag is specified.
171.608 + * <p>
171.609 + * <table border="0" cellpadding="1" cellspacing="0"
171.610 + * summary="predefined and posix character classes in Unicode mode">
171.611 + * <tr align="left">
171.612 + * <th bgcolor="#CCCCFF" align="left" id="classes">Classes</th>
171.613 + * <th bgcolor="#CCCCFF" align="left" id="matches">Matches</th>
171.614 + *</tr>
171.615 + * <tr><td><tt>\p{Lower}</tt></td>
171.616 + * <td>A lowercase character:<tt>\p{IsLowercase}</tt></td></tr>
171.617 + * <tr><td><tt>\p{Upper}</tt></td>
171.618 + * <td>An uppercase character:<tt>\p{IsUppercase}</tt></td></tr>
171.619 + * <tr><td><tt>\p{ASCII}</tt></td>
171.620 + * <td>All ASCII:<tt>[\x00-\x7F]</tt></td></tr>
171.621 + * <tr><td><tt>\p{Alpha}</tt></td>
171.622 + * <td>An alphabetic character:<tt>\p{IsAlphabetic}</tt></td></tr>
171.623 + * <tr><td><tt>\p{Digit}</tt></td>
171.624 + * <td>A decimal digit character:<tt>p{IsDigit}</tt></td></tr>
171.625 + * <tr><td><tt>\p{Alnum}</tt></td>
171.626 + * <td>An alphanumeric character:<tt>[\p{IsAlphabetic}\p{IsDigit}]</tt></td></tr>
171.627 + * <tr><td><tt>\p{Punct}</tt></td>
171.628 + * <td>A punctuation character:<tt>p{IsPunctuation}</tt></td></tr>
171.629 + * <tr><td><tt>\p{Graph}</tt></td>
171.630 + * <td>A visible character: <tt>[^\p{IsWhite_Space}\p{gc=Cc}\p{gc=Cs}\p{gc=Cn}]</tt></td></tr>
171.631 + * <tr><td><tt>\p{Print}</tt></td>
171.632 + * <td>A printable character: <tt>[\p{Graph}\p{Blank}&&[^\p{Cntrl}]]</tt></td></tr>
171.633 + * <tr><td><tt>\p{Blank}</tt></td>
171.634 + * <td>A space or a tab: <tt>[\p{IsWhite_Space}&&[^\p{gc=Zl}\p{gc=Zp}\x0a\x0b\x0c\x0d\x85]]</tt></td></tr>
171.635 + * <tr><td><tt>\p{Cntrl}</tt></td>
171.636 + * <td>A control character: <tt>\p{gc=Cc}</tt></td></tr>
171.637 + * <tr><td><tt>\p{XDigit}</tt></td>
171.638 + * <td>A hexadecimal digit: <tt>[\p{gc=Nd}\p{IsHex_Digit}]</tt></td></tr>
171.639 + * <tr><td><tt>\p{Space}</tt></td>
171.640 + * <td>A whitespace character:<tt>\p{IsWhite_Space}</tt></td></tr>
171.641 + * <tr><td><tt>\d</tt></td>
171.642 + * <td>A digit: <tt>\p{IsDigit}</tt></td></tr>
171.643 + * <tr><td><tt>\D</tt></td>
171.644 + * <td>A non-digit: <tt>[^\d]</tt></td></tr>
171.645 + * <tr><td><tt>\s</tt></td>
171.646 + * <td>A whitespace character: <tt>\p{IsWhite_Space}</tt></td></tr>
171.647 + * <tr><td><tt>\S</tt></td>
171.648 + * <td>A non-whitespace character: <tt>[^\s]</tt></td></tr>
171.649 + * <tr><td><tt>\w</tt></td>
171.650 + * <td>A word character: <tt>[\p{Alpha}\p{gc=Mn}\p{gc=Me}\p{gc=Mc}\p{Digit}\p{gc=Pc}]</tt></td></tr>
171.651 + * <tr><td><tt>\W</tt></td>
171.652 + * <td>A non-word character: <tt>[^\w]</tt></td></tr>
171.653 + * </table>
171.654 + * <p>
171.655 + * <a name="jcc">
171.656 + * Categories that behave like the java.lang.Character
171.657 + * boolean is<i>methodname</i> methods (except for the deprecated ones) are
171.658 + * available through the same <tt>\p{</tt><i>prop</i><tt>}</tt> syntax where
171.659 + * the specified property has the name <tt>java<i>methodname</i></tt>.
171.660 + *
171.661 + * <h4> Comparison to Perl 5 </h4>
171.662 + *
171.663 + * <p>The <code>Pattern</code> engine performs traditional NFA-based matching
171.664 + * with ordered alternation as occurs in Perl 5.
171.665 + *
171.666 + * <p> Perl constructs not supported by this class: </p>
171.667 + *
171.668 + * <ul>
171.669 + * <li><p> Predefined character classes (Unicode character)
171.670 + * <p><tt>\h </tt>A horizontal whitespace
171.671 + * <p><tt>\H </tt>A non horizontal whitespace
171.672 + * <p><tt>\v </tt>A vertical whitespace
171.673 + * <p><tt>\V </tt>A non vertical whitespace
171.674 + * <p><tt>\R </tt>Any Unicode linebreak sequence
171.675 + * <tt>\u005cu000D\u005cu000A|[\u005cu000A\u005cu000B\u005cu000C\u005cu000D\u005cu0085\u005cu2028\u005cu2029]</tt>
171.676 + * <p><tt>\X </tt>Match Unicode
171.677 + * <a href="http://www.unicode.org/reports/tr18/#Default_Grapheme_Clusters">
171.678 + * <i>extended grapheme cluster</i></a>
171.679 + * </p></li>
171.680 + *
171.681 + * <li><p> The backreference constructs, <tt>\g{</tt><i>n</i><tt>}</tt> for
171.682 + * the <i>n</i><sup>th</sup><a href="#cg">capturing group</a> and
171.683 + * <tt>\g{</tt><i>name</i><tt>}</tt> for
171.684 + * <a href="#groupname">named-capturing group</a>.
171.685 + * </p></li>
171.686 + *
171.687 + * <li><p> The named character construct, <tt>\N{</tt><i>name</i><tt>}</tt>
171.688 + * for a Unicode character by its name.
171.689 + * </p></li>
171.690 + *
171.691 + * <li><p> The conditional constructs
171.692 + * <tt>(?(</tt><i>condition</i><tt>)</tt><i>X</i><tt>)</tt> and
171.693 + * <tt>(?(</tt><i>condition</i><tt>)</tt><i>X</i><tt>|</tt><i>Y</i><tt>)</tt>,
171.694 + * </p></li>
171.695 + *
171.696 + * <li><p> The embedded code constructs <tt>(?{</tt><i>code</i><tt>})</tt>
171.697 + * and <tt>(??{</tt><i>code</i><tt>})</tt>,</p></li>
171.698 + *
171.699 + * <li><p> The embedded comment syntax <tt>(?#comment)</tt>, and </p></li>
171.700 + *
171.701 + * <li><p> The preprocessing operations <tt>\l</tt> <tt>\u</tt>,
171.702 + * <tt>\L</tt>, and <tt>\U</tt>. </p></li>
171.703 + *
171.704 + * </ul>
171.705 + *
171.706 + * <p> Constructs supported by this class but not by Perl: </p>
171.707 + *
171.708 + * <ul>
171.709 + *
171.710 + * <li><p> Character-class union and intersection as described
171.711 + * <a href="#cc">above</a>.</p></li>
171.712 + *
171.713 + * </ul>
171.714 + *
171.715 + * <p> Notable differences from Perl: </p>
171.716 + *
171.717 + * <ul>
171.718 + *
171.719 + * <li><p> In Perl, <tt>\1</tt> through <tt>\9</tt> are always interpreted
171.720 + * as back references; a backslash-escaped number greater than <tt>9</tt> is
171.721 + * treated as a back reference if at least that many subexpressions exist,
171.722 + * otherwise it is interpreted, if possible, as an octal escape. In this
171.723 + * class octal escapes must always begin with a zero. In this class,
171.724 + * <tt>\1</tt> through <tt>\9</tt> are always interpreted as back
171.725 + * references, and a larger number is accepted as a back reference if at
171.726 + * least that many subexpressions exist at that point in the regular
171.727 + * expression, otherwise the parser will drop digits until the number is
171.728 + * smaller or equal to the existing number of groups or it is one digit.
171.729 + * </p></li>
171.730 + *
171.731 + * <li><p> Perl uses the <tt>g</tt> flag to request a match that resumes
171.732 + * where the last match left off. This functionality is provided implicitly
171.733 + * by the {@link Matcher} class: Repeated invocations of the {@link
171.734 + * Matcher#find find} method will resume where the last match left off,
171.735 + * unless the matcher is reset. </p></li>
171.736 + *
171.737 + * <li><p> In Perl, embedded flags at the top level of an expression affect
171.738 + * the whole expression. In this class, embedded flags always take effect
171.739 + * at the point at which they appear, whether they are at the top level or
171.740 + * within a group; in the latter case, flags are restored at the end of the
171.741 + * group just as in Perl. </p></li>
171.742 + *
171.743 + * </ul>
171.744 + *
171.745 + *
171.746 + * <p> For a more precise description of the behavior of regular expression
171.747 + * constructs, please see <a href="http://www.oreilly.com/catalog/regex3/">
171.748 + * <i>Mastering Regular Expressions, 3nd Edition</i>, Jeffrey E. F. Friedl,
171.749 + * O'Reilly and Associates, 2006.</a>
171.750 + * </p>
171.751 + *
171.752 + * @see java.lang.String#split(String, int)
171.753 + * @see java.lang.String#split(String)
171.754 + *
171.755 + * @author Mike McCloskey
171.756 + * @author Mark Reinhold
171.757 + * @author JSR-51 Expert Group
171.758 + * @since 1.4
171.759 + * @spec JSR-51
171.760 + */
171.761 +
171.762 +public final class Pattern
171.763 + implements java.io.Serializable
171.764 +{
171.765 +
171.766 + /**
171.767 + * Regular expression modifier values. Instead of being passed as
171.768 + * arguments, they can also be passed as inline modifiers.
171.769 + * For example, the following statements have the same effect.
171.770 + * <pre>
171.771 + * RegExp r1 = RegExp.compile("abc", Pattern.I|Pattern.M);
171.772 + * RegExp r2 = RegExp.compile("(?im)abc", 0);
171.773 + * </pre>
171.774 + *
171.775 + * The flags are duplicated so that the familiar Perl match flag
171.776 + * names are available.
171.777 + */
171.778 +
171.779 + /**
171.780 + * Enables Unix lines mode.
171.781 + *
171.782 + * <p> In this mode, only the <tt>'\n'</tt> line terminator is recognized
171.783 + * in the behavior of <tt>.</tt>, <tt>^</tt>, and <tt>$</tt>.
171.784 + *
171.785 + * <p> Unix lines mode can also be enabled via the embedded flag
171.786 + * expression <tt>(?d)</tt>.
171.787 + */
171.788 + public static final int UNIX_LINES = 0x01;
171.789 +
171.790 + /**
171.791 + * Enables case-insensitive matching.
171.792 + *
171.793 + * <p> By default, case-insensitive matching assumes that only characters
171.794 + * in the US-ASCII charset are being matched. Unicode-aware
171.795 + * case-insensitive matching can be enabled by specifying the {@link
171.796 + * #UNICODE_CASE} flag in conjunction with this flag.
171.797 + *
171.798 + * <p> Case-insensitive matching can also be enabled via the embedded flag
171.799 + * expression <tt>(?i)</tt>.
171.800 + *
171.801 + * <p> Specifying this flag may impose a slight performance penalty. </p>
171.802 + */
171.803 + public static final int CASE_INSENSITIVE = 0x02;
171.804 +
171.805 + /**
171.806 + * Permits whitespace and comments in pattern.
171.807 + *
171.808 + * <p> In this mode, whitespace is ignored, and embedded comments starting
171.809 + * with <tt>#</tt> are ignored until the end of a line.
171.810 + *
171.811 + * <p> Comments mode can also be enabled via the embedded flag
171.812 + * expression <tt>(?x)</tt>.
171.813 + */
171.814 + public static final int COMMENTS = 0x04;
171.815 +
171.816 + /**
171.817 + * Enables multiline mode.
171.818 + *
171.819 + * <p> In multiline mode the expressions <tt>^</tt> and <tt>$</tt> match
171.820 + * just after or just before, respectively, a line terminator or the end of
171.821 + * the input sequence. By default these expressions only match at the
171.822 + * beginning and the end of the entire input sequence.
171.823 + *
171.824 + * <p> Multiline mode can also be enabled via the embedded flag
171.825 + * expression <tt>(?m)</tt>. </p>
171.826 + */
171.827 + public static final int MULTILINE = 0x08;
171.828 +
171.829 + /**
171.830 + * Enables literal parsing of the pattern.
171.831 + *
171.832 + * <p> When this flag is specified then the input string that specifies
171.833 + * the pattern is treated as a sequence of literal characters.
171.834 + * Metacharacters or escape sequences in the input sequence will be
171.835 + * given no special meaning.
171.836 + *
171.837 + * <p>The flags CASE_INSENSITIVE and UNICODE_CASE retain their impact on
171.838 + * matching when used in conjunction with this flag. The other flags
171.839 + * become superfluous.
171.840 + *
171.841 + * <p> There is no embedded flag character for enabling literal parsing.
171.842 + * @since 1.5
171.843 + */
171.844 + public static final int LITERAL = 0x10;
171.845 +
171.846 + /**
171.847 + * Enables dotall mode.
171.848 + *
171.849 + * <p> In dotall mode, the expression <tt>.</tt> matches any character,
171.850 + * including a line terminator. By default this expression does not match
171.851 + * line terminators.
171.852 + *
171.853 + * <p> Dotall mode can also be enabled via the embedded flag
171.854 + * expression <tt>(?s)</tt>. (The <tt>s</tt> is a mnemonic for
171.855 + * "single-line" mode, which is what this is called in Perl.) </p>
171.856 + */
171.857 + public static final int DOTALL = 0x20;
171.858 +
171.859 + /**
171.860 + * Enables Unicode-aware case folding.
171.861 + *
171.862 + * <p> When this flag is specified then case-insensitive matching, when
171.863 + * enabled by the {@link #CASE_INSENSITIVE} flag, is done in a manner
171.864 + * consistent with the Unicode Standard. By default, case-insensitive
171.865 + * matching assumes that only characters in the US-ASCII charset are being
171.866 + * matched.
171.867 + *
171.868 + * <p> Unicode-aware case folding can also be enabled via the embedded flag
171.869 + * expression <tt>(?u)</tt>.
171.870 + *
171.871 + * <p> Specifying this flag may impose a performance penalty. </p>
171.872 + */
171.873 + public static final int UNICODE_CASE = 0x40;
171.874 +
171.875 + /**
171.876 + * Enables canonical equivalence.
171.877 + *
171.878 + * <p> When this flag is specified then two characters will be considered
171.879 + * to match if, and only if, their full canonical decompositions match.
171.880 + * The expression <tt>"a\u030A"</tt>, for example, will match the
171.881 + * string <tt>"\u00E5"</tt> when this flag is specified. By default,
171.882 + * matching does not take canonical equivalence into account.
171.883 + *
171.884 + * <p> There is no embedded flag character for enabling canonical
171.885 + * equivalence.
171.886 + *
171.887 + * <p> Specifying this flag may impose a performance penalty. </p>
171.888 + */
171.889 + public static final int CANON_EQ = 0x80;
171.890 +
171.891 + /**
171.892 + * Enables the Unicode version of <i>Predefined character classes</i> and
171.893 + * <i>POSIX character classes</i>.
171.894 + *
171.895 + * <p> When this flag is specified then the (US-ASCII only)
171.896 + * <i>Predefined character classes</i> and <i>POSIX character classes</i>
171.897 + * are in conformance with
171.898 + * <a href="http://www.unicode.org/reports/tr18/"><i>Unicode Technical
171.899 + * Standard #18: Unicode Regular Expression</i></a>
171.900 + * <i>Annex C: Compatibility Properties</i>.
171.901 + * <p>
171.902 + * The UNICODE_CHARACTER_CLASS mode can also be enabled via the embedded
171.903 + * flag expression <tt>(?U)</tt>.
171.904 + * <p>
171.905 + * The flag implies UNICODE_CASE, that is, it enables Unicode-aware case
171.906 + * folding.
171.907 + * <p>
171.908 + * Specifying this flag may impose a performance penalty. </p>
171.909 + * @since 1.7
171.910 + */
171.911 + public static final int UNICODE_CHARACTER_CLASS = 0x100;
171.912 +
171.913 + /* Pattern has only two serialized components: The pattern string
171.914 + * and the flags, which are all that is needed to recompile the pattern
171.915 + * when it is deserialized.
171.916 + */
171.917 +
171.918 + /** use serialVersionUID from Merlin b59 for interoperability */
171.919 + private static final long serialVersionUID = 5073258162644648461L;
171.920 +
171.921 + /**
171.922 + * The original regular-expression pattern string.
171.923 + *
171.924 + * @serial
171.925 + */
171.926 + private String pattern;
171.927 +
171.928 + /**
171.929 + * The original pattern flags.
171.930 + *
171.931 + * @serial
171.932 + */
171.933 + private int flags;
171.934 +
171.935 + /**
171.936 + * Boolean indicating this Pattern is compiled; this is necessary in order
171.937 + * to lazily compile deserialized Patterns.
171.938 + */
171.939 + private transient volatile boolean compiled = false;
171.940 +
171.941 + /**
171.942 + * The normalized pattern string.
171.943 + */
171.944 + private transient String normalizedPattern;
171.945 +
171.946 + /**
171.947 + * The starting point of state machine for the find operation. This allows
171.948 + * a match to start anywhere in the input.
171.949 + */
171.950 + transient Node root;
171.951 +
171.952 + /**
171.953 + * The root of object tree for a match operation. The pattern is matched
171.954 + * at the beginning. This may include a find that uses BnM or a First
171.955 + * node.
171.956 + */
171.957 + transient Node matchRoot;
171.958 +
171.959 + /**
171.960 + * Temporary storage used by parsing pattern slice.
171.961 + */
171.962 + transient int[] buffer;
171.963 +
171.964 + /**
171.965 + * Map the "name" of the "named capturing group" to its group id
171.966 + * node.
171.967 + */
171.968 + transient volatile Map<String, Integer> namedGroups;
171.969 +
171.970 + /**
171.971 + * Temporary storage used while parsing group references.
171.972 + */
171.973 + transient GroupHead[] groupNodes;
171.974 +
171.975 + /**
171.976 + * Temporary null terminated code point array used by pattern compiling.
171.977 + */
171.978 + private transient int[] temp;
171.979 +
171.980 + /**
171.981 + * The number of capturing groups in this Pattern. Used by matchers to
171.982 + * allocate storage needed to perform a match.
171.983 + */
171.984 + transient int capturingGroupCount;
171.985 +
171.986 + /**
171.987 + * The local variable count used by parsing tree. Used by matchers to
171.988 + * allocate storage needed to perform a match.
171.989 + */
171.990 + transient int localCount;
171.991 +
171.992 + /**
171.993 + * Index into the pattern string that keeps track of how much has been
171.994 + * parsed.
171.995 + */
171.996 + private transient int cursor;
171.997 +
171.998 + /**
171.999 + * Holds the length of the pattern string.
171.1000 + */
171.1001 + private transient int patternLength;
171.1002 +
171.1003 + /**
171.1004 + * If the Start node might possibly match supplementary characters.
171.1005 + * It is set to true during compiling if
171.1006 + * (1) There is supplementary char in pattern, or
171.1007 + * (2) There is complement node of Category or Block
171.1008 + */
171.1009 + private transient boolean hasSupplementary;
171.1010 +
171.1011 + /**
171.1012 + * Compiles the given regular expression into a pattern. </p>
171.1013 + *
171.1014 + * @param regex
171.1015 + * The expression to be compiled
171.1016 + *
171.1017 + * @throws PatternSyntaxException
171.1018 + * If the expression's syntax is invalid
171.1019 + */
171.1020 + public static Pattern compile(String regex) {
171.1021 + return new Pattern(regex, 0);
171.1022 + }
171.1023 +
171.1024 + /**
171.1025 + * Compiles the given regular expression into a pattern with the given
171.1026 + * flags. </p>
171.1027 + *
171.1028 + * @param regex
171.1029 + * The expression to be compiled
171.1030 + *
171.1031 + * @param flags
171.1032 + * Match flags, a bit mask that may include
171.1033 + * {@link #CASE_INSENSITIVE}, {@link #MULTILINE}, {@link #DOTALL},
171.1034 + * {@link #UNICODE_CASE}, {@link #CANON_EQ}, {@link #UNIX_LINES},
171.1035 + * {@link #LITERAL}, {@link #UNICODE_CHARACTER_CLASS}
171.1036 + * and {@link #COMMENTS}
171.1037 + *
171.1038 + * @throws IllegalArgumentException
171.1039 + * If bit values other than those corresponding to the defined
171.1040 + * match flags are set in <tt>flags</tt>
171.1041 + *
171.1042 + * @throws PatternSyntaxException
171.1043 + * If the expression's syntax is invalid
171.1044 + */
171.1045 + public static Pattern compile(String regex, int flags) {
171.1046 + return new Pattern(regex, flags);
171.1047 + }
171.1048 +
171.1049 + /**
171.1050 + * Returns the regular expression from which this pattern was compiled.
171.1051 + * </p>
171.1052 + *
171.1053 + * @return The source of this pattern
171.1054 + */
171.1055 + public String pattern() {
171.1056 + return pattern;
171.1057 + }
171.1058 +
171.1059 + /**
171.1060 + * <p>Returns the string representation of this pattern. This
171.1061 + * is the regular expression from which this pattern was
171.1062 + * compiled.</p>
171.1063 + *
171.1064 + * @return The string representation of this pattern
171.1065 + * @since 1.5
171.1066 + */
171.1067 + public String toString() {
171.1068 + return pattern;
171.1069 + }
171.1070 +
171.1071 + /**
171.1072 + * Creates a matcher that will match the given input against this pattern.
171.1073 + * </p>
171.1074 + *
171.1075 + * @param input
171.1076 + * The character sequence to be matched
171.1077 + *
171.1078 + * @return A new matcher for this pattern
171.1079 + */
171.1080 + public Matcher matcher(CharSequence input) {
171.1081 + if (!compiled) {
171.1082 + synchronized(this) {
171.1083 + if (!compiled)
171.1084 + compile();
171.1085 + }
171.1086 + }
171.1087 + Matcher m = new Matcher(this, input);
171.1088 + return m;
171.1089 + }
171.1090 +
171.1091 + /**
171.1092 + * Returns this pattern's match flags. </p>
171.1093 + *
171.1094 + * @return The match flags specified when this pattern was compiled
171.1095 + */
171.1096 + public int flags() {
171.1097 + return flags;
171.1098 + }
171.1099 +
171.1100 + /**
171.1101 + * Compiles the given regular expression and attempts to match the given
171.1102 + * input against it.
171.1103 + *
171.1104 + * <p> An invocation of this convenience method of the form
171.1105 + *
171.1106 + * <blockquote><pre>
171.1107 + * Pattern.matches(regex, input);</pre></blockquote>
171.1108 + *
171.1109 + * behaves in exactly the same way as the expression
171.1110 + *
171.1111 + * <blockquote><pre>
171.1112 + * Pattern.compile(regex).matcher(input).matches()</pre></blockquote>
171.1113 + *
171.1114 + * <p> If a pattern is to be used multiple times, compiling it once and reusing
171.1115 + * it will be more efficient than invoking this method each time. </p>
171.1116 + *
171.1117 + * @param regex
171.1118 + * The expression to be compiled
171.1119 + *
171.1120 + * @param input
171.1121 + * The character sequence to be matched
171.1122 + *
171.1123 + * @throws PatternSyntaxException
171.1124 + * If the expression's syntax is invalid
171.1125 + */
171.1126 + public static boolean matches(String regex, CharSequence input) {
171.1127 + Pattern p = Pattern.compile(regex);
171.1128 + Matcher m = p.matcher(input);
171.1129 + return m.matches();
171.1130 + }
171.1131 +
171.1132 + /**
171.1133 + * Splits the given input sequence around matches of this pattern.
171.1134 + *
171.1135 + * <p> The array returned by this method contains each substring of the
171.1136 + * input sequence that is terminated by another subsequence that matches
171.1137 + * this pattern or is terminated by the end of the input sequence. The
171.1138 + * substrings in the array are in the order in which they occur in the
171.1139 + * input. If this pattern does not match any subsequence of the input then
171.1140 + * the resulting array has just one element, namely the input sequence in
171.1141 + * string form.
171.1142 + *
171.1143 + * <p> The <tt>limit</tt> parameter controls the number of times the
171.1144 + * pattern is applied and therefore affects the length of the resulting
171.1145 + * array. If the limit <i>n</i> is greater than zero then the pattern
171.1146 + * will be applied at most <i>n</i> - 1 times, the array's
171.1147 + * length will be no greater than <i>n</i>, and the array's last entry
171.1148 + * will contain all input beyond the last matched delimiter. If <i>n</i>
171.1149 + * is non-positive then the pattern will be applied as many times as
171.1150 + * possible and the array can have any length. If <i>n</i> is zero then
171.1151 + * the pattern will be applied as many times as possible, the array can
171.1152 + * have any length, and trailing empty strings will be discarded.
171.1153 + *
171.1154 + * <p> The input <tt>"boo:and:foo"</tt>, for example, yields the following
171.1155 + * results with these parameters:
171.1156 + *
171.1157 + * <blockquote><table cellpadding=1 cellspacing=0
171.1158 + * summary="Split examples showing regex, limit, and result">
171.1159 + * <tr><th><P align="left"><i>Regex </i></th>
171.1160 + * <th><P align="left"><i>Limit </i></th>
171.1161 + * <th><P align="left"><i>Result </i></th></tr>
171.1162 + * <tr><td align=center>:</td>
171.1163 + * <td align=center>2</td>
171.1164 + * <td><tt>{ "boo", "and:foo" }</tt></td></tr>
171.1165 + * <tr><td align=center>:</td>
171.1166 + * <td align=center>5</td>
171.1167 + * <td><tt>{ "boo", "and", "foo" }</tt></td></tr>
171.1168 + * <tr><td align=center>:</td>
171.1169 + * <td align=center>-2</td>
171.1170 + * <td><tt>{ "boo", "and", "foo" }</tt></td></tr>
171.1171 + * <tr><td align=center>o</td>
171.1172 + * <td align=center>5</td>
171.1173 + * <td><tt>{ "b", "", ":and:f", "", "" }</tt></td></tr>
171.1174 + * <tr><td align=center>o</td>
171.1175 + * <td align=center>-2</td>
171.1176 + * <td><tt>{ "b", "", ":and:f", "", "" }</tt></td></tr>
171.1177 + * <tr><td align=center>o</td>
171.1178 + * <td align=center>0</td>
171.1179 + * <td><tt>{ "b", "", ":and:f" }</tt></td></tr>
171.1180 + * </table></blockquote>
171.1181 + *
171.1182 + *
171.1183 + * @param input
171.1184 + * The character sequence to be split
171.1185 + *
171.1186 + * @param limit
171.1187 + * The result threshold, as described above
171.1188 + *
171.1189 + * @return The array of strings computed by splitting the input
171.1190 + * around matches of this pattern
171.1191 + */
171.1192 + public String[] split(CharSequence input, int limit) {
171.1193 + int index = 0;
171.1194 + boolean matchLimited = limit > 0;
171.1195 + ArrayList<String> matchList = new ArrayList<>();
171.1196 + Matcher m = matcher(input);
171.1197 +
171.1198 + // Add segments before each match found
171.1199 + while(m.find()) {
171.1200 + if (!matchLimited || matchList.size() < limit - 1) {
171.1201 + String match = input.subSequence(index, m.start()).toString();
171.1202 + matchList.add(match);
171.1203 + index = m.end();
171.1204 + } else if (matchList.size() == limit - 1) { // last one
171.1205 + String match = input.subSequence(index,
171.1206 + input.length()).toString();
171.1207 + matchList.add(match);
171.1208 + index = m.end();
171.1209 + }
171.1210 + }
171.1211 +
171.1212 + // If no match was found, return this
171.1213 + if (index == 0)
171.1214 + return new String[] {input.toString()};
171.1215 +
171.1216 + // Add remaining segment
171.1217 + if (!matchLimited || matchList.size() < limit)
171.1218 + matchList.add(input.subSequence(index, input.length()).toString());
171.1219 +
171.1220 + // Construct result
171.1221 + int resultSize = matchList.size();
171.1222 + if (limit == 0)
171.1223 + while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
171.1224 + resultSize--;
171.1225 + String[] result = new String[resultSize];
171.1226 + return matchList.subList(0, resultSize).toArray(result);
171.1227 + }
171.1228 +
171.1229 + /**
171.1230 + * Splits the given input sequence around matches of this pattern.
171.1231 + *
171.1232 + * <p> This method works as if by invoking the two-argument {@link
171.1233 + * #split(java.lang.CharSequence, int) split} method with the given input
171.1234 + * sequence and a limit argument of zero. Trailing empty strings are
171.1235 + * therefore not included in the resulting array. </p>
171.1236 + *
171.1237 + * <p> The input <tt>"boo:and:foo"</tt>, for example, yields the following
171.1238 + * results with these expressions:
171.1239 + *
171.1240 + * <blockquote><table cellpadding=1 cellspacing=0
171.1241 + * summary="Split examples showing regex and result">
171.1242 + * <tr><th><P align="left"><i>Regex </i></th>
171.1243 + * <th><P align="left"><i>Result</i></th></tr>
171.1244 + * <tr><td align=center>:</td>
171.1245 + * <td><tt>{ "boo", "and", "foo" }</tt></td></tr>
171.1246 + * <tr><td align=center>o</td>
171.1247 + * <td><tt>{ "b", "", ":and:f" }</tt></td></tr>
171.1248 + * </table></blockquote>
171.1249 + *
171.1250 + *
171.1251 + * @param input
171.1252 + * The character sequence to be split
171.1253 + *
171.1254 + * @return The array of strings computed by splitting the input
171.1255 + * around matches of this pattern
171.1256 + */
171.1257 + public String[] split(CharSequence input) {
171.1258 + return split(input, 0);
171.1259 + }
171.1260 +
171.1261 + /**
171.1262 + * Returns a literal pattern <code>String</code> for the specified
171.1263 + * <code>String</code>.
171.1264 + *
171.1265 + * <p>This method produces a <code>String</code> that can be used to
171.1266 + * create a <code>Pattern</code> that would match the string
171.1267 + * <code>s</code> as if it were a literal pattern.</p> Metacharacters
171.1268 + * or escape sequences in the input sequence will be given no special
171.1269 + * meaning.
171.1270 + *
171.1271 + * @param s The string to be literalized
171.1272 + * @return A literal string replacement
171.1273 + * @since 1.5
171.1274 + */
171.1275 + public static String quote(String s) {
171.1276 + int slashEIndex = s.indexOf("\\E");
171.1277 + if (slashEIndex == -1)
171.1278 + return "\\Q" + s + "\\E";
171.1279 +
171.1280 + StringBuilder sb = new StringBuilder(s.length() * 2);
171.1281 + sb.append("\\Q");
171.1282 + slashEIndex = 0;
171.1283 + int current = 0;
171.1284 + while ((slashEIndex = s.indexOf("\\E", current)) != -1) {
171.1285 + sb.append(s.substring(current, slashEIndex));
171.1286 + current = slashEIndex + 2;
171.1287 + sb.append("\\E\\\\E\\Q");
171.1288 + }
171.1289 + sb.append(s.substring(current, s.length()));
171.1290 + sb.append("\\E");
171.1291 + return sb.toString();
171.1292 + }
171.1293 +
171.1294 + /**
171.1295 + * Recompile the Pattern instance from a stream. The original pattern
171.1296 + * string is read in and the object tree is recompiled from it.
171.1297 + */
171.1298 + private void readObject(java.io.ObjectInputStream s)
171.1299 + throws java.io.IOException, ClassNotFoundException {
171.1300 +
171.1301 + // Read in all fields
171.1302 + s.defaultReadObject();
171.1303 +
171.1304 + // Initialize counts
171.1305 + capturingGroupCount = 1;
171.1306 + localCount = 0;
171.1307 +
171.1308 + // if length > 0, the Pattern is lazily compiled
171.1309 + compiled = false;
171.1310 + if (pattern.length() == 0) {
171.1311 + root = new Start(lastAccept);
171.1312 + matchRoot = lastAccept;
171.1313 + compiled = true;
171.1314 + }
171.1315 + }
171.1316 +
171.1317 + /**
171.1318 + * This private constructor is used to create all Patterns. The pattern
171.1319 + * string and match flags are all that is needed to completely describe
171.1320 + * a Pattern. An empty pattern string results in an object tree with
171.1321 + * only a Start node and a LastNode node.
171.1322 + */
171.1323 + private Pattern(String p, int f) {
171.1324 + pattern = p;
171.1325 + flags = f;
171.1326 +
171.1327 + // to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
171.1328 + if ((flags & UNICODE_CHARACTER_CLASS) != 0)
171.1329 + flags |= UNICODE_CASE;
171.1330 +
171.1331 + // Reset group index count
171.1332 + capturingGroupCount = 1;
171.1333 + localCount = 0;
171.1334 +
171.1335 + if (pattern.length() > 0) {
171.1336 + compile();
171.1337 + } else {
171.1338 + root = new Start(lastAccept);
171.1339 + matchRoot = lastAccept;
171.1340 + }
171.1341 + }
171.1342 +
171.1343 + /**
171.1344 + * The pattern is converted to normalizedD form and then a pure group
171.1345 + * is constructed to match canonical equivalences of the characters.
171.1346 + */
171.1347 + private void normalize() {
171.1348 + boolean inCharClass = false;
171.1349 + int lastCodePoint = -1;
171.1350 +
171.1351 + // Convert pattern into normalizedD form
171.1352 + normalizedPattern = Normalizer.normalize(pattern, Normalizer.NFD);
171.1353 + patternLength = normalizedPattern.length();
171.1354 +
171.1355 + // Modify pattern to match canonical equivalences
171.1356 + StringBuilder newPattern = new StringBuilder(patternLength);
171.1357 + for(int i=0; i<patternLength; ) {
171.1358 + int c = normalizedPattern.codePointAt(i);
171.1359 + StringBuilder sequenceBuffer;
171.1360 + if ((Character.getType(c) == Character.NON_SPACING_MARK)
171.1361 + && (lastCodePoint != -1)) {
171.1362 + sequenceBuffer = new StringBuilder();
171.1363 + sequenceBuffer.appendCodePoint(lastCodePoint);
171.1364 + sequenceBuffer.appendCodePoint(c);
171.1365 + while(Character.getType(c) == Character.NON_SPACING_MARK) {
171.1366 + i += Character.charCount(c);
171.1367 + if (i >= patternLength)
171.1368 + break;
171.1369 + c = normalizedPattern.codePointAt(i);
171.1370 + sequenceBuffer.appendCodePoint(c);
171.1371 + }
171.1372 + String ea = produceEquivalentAlternation(
171.1373 + sequenceBuffer.toString());
171.1374 + newPattern.setLength(newPattern.length()-Character.charCount(lastCodePoint));
171.1375 + newPattern.append("(?:").append(ea).append(")");
171.1376 + } else if (c == '[' && lastCodePoint != '\\') {
171.1377 + i = normalizeCharClass(newPattern, i);
171.1378 + } else {
171.1379 + newPattern.appendCodePoint(c);
171.1380 + }
171.1381 + lastCodePoint = c;
171.1382 + i += Character.charCount(c);
171.1383 + }
171.1384 + normalizedPattern = newPattern.toString();
171.1385 + }
171.1386 +
171.1387 + /**
171.1388 + * Complete the character class being parsed and add a set
171.1389 + * of alternations to it that will match the canonical equivalences
171.1390 + * of the characters within the class.
171.1391 + */
171.1392 + private int normalizeCharClass(StringBuilder newPattern, int i) {
171.1393 + StringBuilder charClass = new StringBuilder();
171.1394 + StringBuilder eq = null;
171.1395 + int lastCodePoint = -1;
171.1396 + String result;
171.1397 +
171.1398 + i++;
171.1399 + charClass.append("[");
171.1400 + while(true) {
171.1401 + int c = normalizedPattern.codePointAt(i);
171.1402 + StringBuilder sequenceBuffer;
171.1403 +
171.1404 + if (c == ']' && lastCodePoint != '\\') {
171.1405 + charClass.append((char)c);
171.1406 + break;
171.1407 + } else if (Character.getType(c) == Character.NON_SPACING_MARK) {
171.1408 + sequenceBuffer = new StringBuilder();
171.1409 + sequenceBuffer.appendCodePoint(lastCodePoint);
171.1410 + while(Character.getType(c) == Character.NON_SPACING_MARK) {
171.1411 + sequenceBuffer.appendCodePoint(c);
171.1412 + i += Character.charCount(c);
171.1413 + if (i >= normalizedPattern.length())
171.1414 + break;
171.1415 + c = normalizedPattern.codePointAt(i);
171.1416 + }
171.1417 + String ea = produceEquivalentAlternation(
171.1418 + sequenceBuffer.toString());
171.1419 +
171.1420 + charClass.setLength(charClass.length()-Character.charCount(lastCodePoint));
171.1421 + if (eq == null)
171.1422 + eq = new StringBuilder();
171.1423 + eq.append('|');
171.1424 + eq.append(ea);
171.1425 + } else {
171.1426 + charClass.appendCodePoint(c);
171.1427 + i++;
171.1428 + }
171.1429 + if (i == normalizedPattern.length())
171.1430 + throw error("Unclosed character class");
171.1431 + lastCodePoint = c;
171.1432 + }
171.1433 +
171.1434 + if (eq != null) {
171.1435 + result = "(?:"+charClass.toString()+eq.toString()+")";
171.1436 + } else {
171.1437 + result = charClass.toString();
171.1438 + }
171.1439 +
171.1440 + newPattern.append(result);
171.1441 + return i;
171.1442 + }
171.1443 +
171.1444 + /**
171.1445 + * Given a specific sequence composed of a regular character and
171.1446 + * combining marks that follow it, produce the alternation that will
171.1447 + * match all canonical equivalences of that sequence.
171.1448 + */
171.1449 + private String produceEquivalentAlternation(String source) {
171.1450 + int len = countChars(source, 0, 1);
171.1451 + if (source.length() == len)
171.1452 + // source has one character.
171.1453 + return source;
171.1454 +
171.1455 + String base = source.substring(0,len);
171.1456 + String combiningMarks = source.substring(len);
171.1457 +
171.1458 + String[] perms = producePermutations(combiningMarks);
171.1459 + StringBuilder result = new StringBuilder(source);
171.1460 +
171.1461 + // Add combined permutations
171.1462 + for(int x=0; x<perms.length; x++) {
171.1463 + String next = base + perms[x];
171.1464 + if (x>0)
171.1465 + result.append("|"+next);
171.1466 + next = composeOneStep(next);
171.1467 + if (next != null)
171.1468 + result.append("|"+produceEquivalentAlternation(next));
171.1469 + }
171.1470 + return result.toString();
171.1471 + }
171.1472 +
171.1473 + /**
171.1474 + * Returns an array of strings that have all the possible
171.1475 + * permutations of the characters in the input string.
171.1476 + * This is used to get a list of all possible orderings
171.1477 + * of a set of combining marks. Note that some of the permutations
171.1478 + * are invalid because of combining class collisions, and these
171.1479 + * possibilities must be removed because they are not canonically
171.1480 + * equivalent.
171.1481 + */
171.1482 + private String[] producePermutations(String input) {
171.1483 + if (input.length() == countChars(input, 0, 1))
171.1484 + return new String[] {input};
171.1485 +
171.1486 + if (input.length() == countChars(input, 0, 2)) {
171.1487 + int c0 = Character.codePointAt(input, 0);
171.1488 + int c1 = Character.codePointAt(input, Character.charCount(c0));
171.1489 + if (getClass(c1) == getClass(c0)) {
171.1490 + return new String[] {input};
171.1491 + }
171.1492 + String[] result = new String[2];
171.1493 + result[0] = input;
171.1494 + StringBuilder sb = new StringBuilder(2);
171.1495 + sb.appendCodePoint(c1);
171.1496 + sb.appendCodePoint(c0);
171.1497 + result[1] = sb.toString();
171.1498 + return result;
171.1499 + }
171.1500 +
171.1501 + int length = 1;
171.1502 + int nCodePoints = countCodePoints(input);
171.1503 + for(int x=1; x<nCodePoints; x++)
171.1504 + length = length * (x+1);
171.1505 +
171.1506 + String[] temp = new String[length];
171.1507 +
171.1508 + int combClass[] = new int[nCodePoints];
171.1509 + for(int x=0, i=0; x<nCodePoints; x++) {
171.1510 + int c = Character.codePointAt(input, i);
171.1511 + combClass[x] = getClass(c);
171.1512 + i += Character.charCount(c);
171.1513 + }
171.1514 +
171.1515 + // For each char, take it out and add the permutations
171.1516 + // of the remaining chars
171.1517 + int index = 0;
171.1518 + int len;
171.1519 + // offset maintains the index in code units.
171.1520 +loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
171.1521 + len = countChars(input, offset, 1);
171.1522 + boolean skip = false;
171.1523 + for(int y=x-1; y>=0; y--) {
171.1524 + if (combClass[y] == combClass[x]) {
171.1525 + continue loop;
171.1526 + }
171.1527 + }
171.1528 + StringBuilder sb = new StringBuilder(input);
171.1529 + String otherChars = sb.delete(offset, offset+len).toString();
171.1530 + String[] subResult = producePermutations(otherChars);
171.1531 +
171.1532 + String prefix = input.substring(offset, offset+len);
171.1533 + for(int y=0; y<subResult.length; y++)
171.1534 + temp[index++] = prefix + subResult[y];
171.1535 + }
171.1536 + String[] result = new String[index];
171.1537 + for (int x=0; x<index; x++)
171.1538 + result[x] = temp[x];
171.1539 + return result;
171.1540 + }
171.1541 +
171.1542 + private int getClass(int c) {
171.1543 + return Normalizer.getCombiningClass(c);
171.1544 + }
171.1545 +
171.1546 + /**
171.1547 + * Attempts to compose input by combining the first character
171.1548 + * with the first combining mark following it. Returns a String
171.1549 + * that is the composition of the leading character with its first
171.1550 + * combining mark followed by the remaining combining marks. Returns
171.1551 + * null if the first two characters cannot be further composed.
171.1552 + */
171.1553 + private String composeOneStep(String input) {
171.1554 + int len = countChars(input, 0, 2);
171.1555 + String firstTwoCharacters = input.substring(0, len);
171.1556 + String result = Normalizer.normalize(firstTwoCharacters, Normalizer.NFC);
171.1557 +
171.1558 + if (result.equals(firstTwoCharacters))
171.1559 + return null;
171.1560 + else {
171.1561 + String remainder = input.substring(len);
171.1562 + return result + remainder;
171.1563 + }
171.1564 + }
171.1565 +
171.1566 + /**
171.1567 + * Preprocess any \Q...\E sequences in `temp', meta-quoting them.
171.1568 + * See the description of `quotemeta' in perlfunc(1).
171.1569 + */
171.1570 + private void RemoveQEQuoting() {
171.1571 + final int pLen = patternLength;
171.1572 + int i = 0;
171.1573 + while (i < pLen-1) {
171.1574 + if (temp[i] != '\\')
171.1575 + i += 1;
171.1576 + else if (temp[i + 1] != 'Q')
171.1577 + i += 2;
171.1578 + else
171.1579 + break;
171.1580 + }
171.1581 + if (i >= pLen - 1) // No \Q sequence found
171.1582 + return;
171.1583 + int j = i;
171.1584 + i += 2;
171.1585 + int[] newtemp = new int[j + 2*(pLen-i) + 2];
171.1586 + System.arraycopy(temp, 0, newtemp, 0, j);
171.1587 +
171.1588 + boolean inQuote = true;
171.1589 + while (i < pLen) {
171.1590 + int c = temp[i++];
171.1591 + if (! ASCII.isAscii(c) || ASCII.isAlnum(c)) {
171.1592 + newtemp[j++] = c;
171.1593 + } else if (c != '\\') {
171.1594 + if (inQuote) newtemp[j++] = '\\';
171.1595 + newtemp[j++] = c;
171.1596 + } else if (inQuote) {
171.1597 + if (temp[i] == 'E') {
171.1598 + i++;
171.1599 + inQuote = false;
171.1600 + } else {
171.1601 + newtemp[j++] = '\\';
171.1602 + newtemp[j++] = '\\';
171.1603 + }
171.1604 + } else {
171.1605 + if (temp[i] == 'Q') {
171.1606 + i++;
171.1607 + inQuote = true;
171.1608 + } else {
171.1609 + newtemp[j++] = c;
171.1610 + if (i != pLen)
171.1611 + newtemp[j++] = temp[i++];
171.1612 + }
171.1613 + }
171.1614 + }
171.1615 +
171.1616 + patternLength = j;
171.1617 + temp = Arrays.copyOf(newtemp, j + 2); // double zero termination
171.1618 + }
171.1619 +
171.1620 + /**
171.1621 + * Copies regular expression to an int array and invokes the parsing
171.1622 + * of the expression which will create the object tree.
171.1623 + */
171.1624 + private void compile() {
171.1625 + // Handle canonical equivalences
171.1626 + if (has(CANON_EQ) && !has(LITERAL)) {
171.1627 + normalize();
171.1628 + } else {
171.1629 + normalizedPattern = pattern;
171.1630 + }
171.1631 + patternLength = normalizedPattern.length();
171.1632 +
171.1633 + // Copy pattern to int array for convenience
171.1634 + // Use double zero to terminate pattern
171.1635 + temp = new int[patternLength + 2];
171.1636 +
171.1637 + hasSupplementary = false;
171.1638 + int c, count = 0;
171.1639 + // Convert all chars into code points
171.1640 + for (int x = 0; x < patternLength; x += Character.charCount(c)) {
171.1641 + c = normalizedPattern.codePointAt(x);
171.1642 + if (isSupplementary(c)) {
171.1643 + hasSupplementary = true;
171.1644 + }
171.1645 + temp[count++] = c;
171.1646 + }
171.1647 +
171.1648 + patternLength = count; // patternLength now in code points
171.1649 +
171.1650 + if (! has(LITERAL))
171.1651 + RemoveQEQuoting();
171.1652 +
171.1653 + // Allocate all temporary objects here.
171.1654 + buffer = new int[32];
171.1655 + groupNodes = new GroupHead[10];
171.1656 + namedGroups = null;
171.1657 +
171.1658 + if (has(LITERAL)) {
171.1659 + // Literal pattern handling
171.1660 + matchRoot = newSlice(temp, patternLength, hasSupplementary);
171.1661 + matchRoot.next = lastAccept;
171.1662 + } else {
171.1663 + // Start recursive descent parsing
171.1664 + matchRoot = expr(lastAccept);
171.1665 + // Check extra pattern characters
171.1666 + if (patternLength != cursor) {
171.1667 + if (peek() == ')') {
171.1668 + throw error("Unmatched closing ')'");
171.1669 + } else {
171.1670 + throw error("Unexpected internal error");
171.1671 + }
171.1672 + }
171.1673 + }
171.1674 +
171.1675 + // Peephole optimization
171.1676 + if (matchRoot instanceof Slice) {
171.1677 + root = BnM.optimize(matchRoot);
171.1678 + if (root == matchRoot) {
171.1679 + root = hasSupplementary ? new StartS(matchRoot) : new Start(matchRoot);
171.1680 + }
171.1681 + } else if (matchRoot instanceof Begin || matchRoot instanceof First) {
171.1682 + root = matchRoot;
171.1683 + } else {
171.1684 + root = hasSupplementary ? new StartS(matchRoot) : new Start(matchRoot);
171.1685 + }
171.1686 +
171.1687 + // Release temporary storage
171.1688 + temp = null;
171.1689 + buffer = null;
171.1690 + groupNodes = null;
171.1691 + patternLength = 0;
171.1692 + compiled = true;
171.1693 + }
171.1694 +
171.1695 + Map<String, Integer> namedGroups() {
171.1696 + if (namedGroups == null)
171.1697 + namedGroups = new HashMap<>(2);
171.1698 + return namedGroups;
171.1699 + }
171.1700 +
171.1701 + /**
171.1702 + * Used to print out a subtree of the Pattern to help with debugging.
171.1703 + */
171.1704 + private static void printObjectTree(Node node) {
171.1705 + while(node != null) {
171.1706 + if (node instanceof Prolog) {
171.1707 + System.out.println(node);
171.1708 + printObjectTree(((Prolog)node).loop);
171.1709 + System.out.println("**** end contents prolog loop");
171.1710 + } else if (node instanceof Loop) {
171.1711 + System.out.println(node);
171.1712 + printObjectTree(((Loop)node).body);
171.1713 + System.out.println("**** end contents Loop body");
171.1714 + } else if (node instanceof Curly) {
171.1715 + System.out.println(node);
171.1716 + printObjectTree(((Curly)node).atom);
171.1717 + System.out.println("**** end contents Curly body");
171.1718 + } else if (node instanceof GroupCurly) {
171.1719 + System.out.println(node);
171.1720 + printObjectTree(((GroupCurly)node).atom);
171.1721 + System.out.println("**** end contents GroupCurly body");
171.1722 + } else if (node instanceof GroupTail) {
171.1723 + System.out.println(node);
171.1724 + System.out.println("Tail next is "+node.next);
171.1725 + return;
171.1726 + } else {
171.1727 + System.out.println(node);
171.1728 + }
171.1729 + node = node.next;
171.1730 + if (node != null)
171.1731 + System.out.println("->next:");
171.1732 + if (node == Pattern.accept) {
171.1733 + System.out.println("Accept Node");
171.1734 + node = null;
171.1735 + }
171.1736 + }
171.1737 + }
171.1738 +
171.1739 + /**
171.1740 + * Used to accumulate information about a subtree of the object graph
171.1741 + * so that optimizations can be applied to the subtree.
171.1742 + */
171.1743 + static final class TreeInfo {
171.1744 + int minLength;
171.1745 + int maxLength;
171.1746 + boolean maxValid;
171.1747 + boolean deterministic;
171.1748 +
171.1749 + TreeInfo() {
171.1750 + reset();
171.1751 + }
171.1752 + void reset() {
171.1753 + minLength = 0;
171.1754 + maxLength = 0;
171.1755 + maxValid = true;
171.1756 + deterministic = true;
171.1757 + }
171.1758 + }
171.1759 +
171.1760 + /*
171.1761 + * The following private methods are mainly used to improve the
171.1762 + * readability of the code. In order to let the Java compiler easily
171.1763 + * inline them, we should not put many assertions or error checks in them.
171.1764 + */
171.1765 +
171.1766 + /**
171.1767 + * Indicates whether a particular flag is set or not.
171.1768 + */
171.1769 + private boolean has(int f) {
171.1770 + return (flags & f) != 0;
171.1771 + }
171.1772 +
171.1773 + /**
171.1774 + * Match next character, signal error if failed.
171.1775 + */
171.1776 + private void accept(int ch, String s) {
171.1777 + int testChar = temp[cursor++];
171.1778 + if (has(COMMENTS))
171.1779 + testChar = parsePastWhitespace(testChar);
171.1780 + if (ch != testChar) {
171.1781 + throw error(s);
171.1782 + }
171.1783 + }
171.1784 +
171.1785 + /**
171.1786 + * Mark the end of pattern with a specific character.
171.1787 + */
171.1788 + private void mark(int c) {
171.1789 + temp[patternLength] = c;
171.1790 + }
171.1791 +
171.1792 + /**
171.1793 + * Peek the next character, and do not advance the cursor.
171.1794 + */
171.1795 + private int peek() {
171.1796 + int ch = temp[cursor];
171.1797 + if (has(COMMENTS))
171.1798 + ch = peekPastWhitespace(ch);
171.1799 + return ch;
171.1800 + }
171.1801 +
171.1802 + /**
171.1803 + * Read the next character, and advance the cursor by one.
171.1804 + */
171.1805 + private int read() {
171.1806 + int ch = temp[cursor++];
171.1807 + if (has(COMMENTS))
171.1808 + ch = parsePastWhitespace(ch);
171.1809 + return ch;
171.1810 + }
171.1811 +
171.1812 + /**
171.1813 + * Read the next character, and advance the cursor by one,
171.1814 + * ignoring the COMMENTS setting
171.1815 + */
171.1816 + private int readEscaped() {
171.1817 + int ch = temp[cursor++];
171.1818 + return ch;
171.1819 + }
171.1820 +
171.1821 + /**
171.1822 + * Advance the cursor by one, and peek the next character.
171.1823 + */
171.1824 + private int next() {
171.1825 + int ch = temp[++cursor];
171.1826 + if (has(COMMENTS))
171.1827 + ch = peekPastWhitespace(ch);
171.1828 + return ch;
171.1829 + }
171.1830 +
171.1831 + /**
171.1832 + * Advance the cursor by one, and peek the next character,
171.1833 + * ignoring the COMMENTS setting
171.1834 + */
171.1835 + private int nextEscaped() {
171.1836 + int ch = temp[++cursor];
171.1837 + return ch;
171.1838 + }
171.1839 +
171.1840 + /**
171.1841 + * If in xmode peek past whitespace and comments.
171.1842 + */
171.1843 + private int peekPastWhitespace(int ch) {
171.1844 + while (ASCII.isSpace(ch) || ch == '#') {
171.1845 + while (ASCII.isSpace(ch))
171.1846 + ch = temp[++cursor];
171.1847 + if (ch == '#') {
171.1848 + ch = peekPastLine();
171.1849 + }
171.1850 + }
171.1851 + return ch;
171.1852 + }
171.1853 +
171.1854 + /**
171.1855 + * If in xmode parse past whitespace and comments.
171.1856 + */
171.1857 + private int parsePastWhitespace(int ch) {
171.1858 + while (ASCII.isSpace(ch) || ch == '#') {
171.1859 + while (ASCII.isSpace(ch))
171.1860 + ch = temp[cursor++];
171.1861 + if (ch == '#')
171.1862 + ch = parsePastLine();
171.1863 + }
171.1864 + return ch;
171.1865 + }
171.1866 +
171.1867 + /**
171.1868 + * xmode parse past comment to end of line.
171.1869 + */
171.1870 + private int parsePastLine() {
171.1871 + int ch = temp[cursor++];
171.1872 + while (ch != 0 && !isLineSeparator(ch))
171.1873 + ch = temp[cursor++];
171.1874 + return ch;
171.1875 + }
171.1876 +
171.1877 + /**
171.1878 + * xmode peek past comment to end of line.
171.1879 + */
171.1880 + private int peekPastLine() {
171.1881 + int ch = temp[++cursor];
171.1882 + while (ch != 0 && !isLineSeparator(ch))
171.1883 + ch = temp[++cursor];
171.1884 + return ch;
171.1885 + }
171.1886 +
171.1887 + /**
171.1888 + * Determines if character is a line separator in the current mode
171.1889 + */
171.1890 + private boolean isLineSeparator(int ch) {
171.1891 + if (has(UNIX_LINES)) {
171.1892 + return ch == '\n';
171.1893 + } else {
171.1894 + return (ch == '\n' ||
171.1895 + ch == '\r' ||
171.1896 + (ch|1) == '\u2029' ||
171.1897 + ch == '\u0085');
171.1898 + }
171.1899 + }
171.1900 +
171.1901 + /**
171.1902 + * Read the character after the next one, and advance the cursor by two.
171.1903 + */
171.1904 + private int skip() {
171.1905 + int i = cursor;
171.1906 + int ch = temp[i+1];
171.1907 + cursor = i + 2;
171.1908 + return ch;
171.1909 + }
171.1910 +
171.1911 + /**
171.1912 + * Unread one next character, and retreat cursor by one.
171.1913 + */
171.1914 + private void unread() {
171.1915 + cursor--;
171.1916 + }
171.1917 +
171.1918 + /**
171.1919 + * Internal method used for handling all syntax errors. The pattern is
171.1920 + * displayed with a pointer to aid in locating the syntax error.
171.1921 + */
171.1922 + private PatternSyntaxException error(String s) {
171.1923 + return new PatternSyntaxException(s, normalizedPattern, cursor - 1);
171.1924 + }
171.1925 +
171.1926 + /**
171.1927 + * Determines if there is any supplementary character or unpaired
171.1928 + * surrogate in the specified range.
171.1929 + */
171.1930 + private boolean findSupplementary(int start, int end) {
171.1931 + for (int i = start; i < end; i++) {
171.1932 + if (isSupplementary(temp[i]))
171.1933 + return true;
171.1934 + }
171.1935 + return false;
171.1936 + }
171.1937 +
171.1938 + /**
171.1939 + * Determines if the specified code point is a supplementary
171.1940 + * character or unpaired surrogate.
171.1941 + */
171.1942 + private static final boolean isSupplementary(int ch) {
171.1943 + return ch >= Character.MIN_SUPPLEMENTARY_CODE_POINT ||
171.1944 + Character.isSurrogate((char)ch);
171.1945 + }
171.1946 +
171.1947 + /**
171.1948 + * The following methods handle the main parsing. They are sorted
171.1949 + * according to their precedence order, the lowest one first.
171.1950 + */
171.1951 +
171.1952 + /**
171.1953 + * The expression is parsed with branch nodes added for alternations.
171.1954 + * This may be called recursively to parse sub expressions that may
171.1955 + * contain alternations.
171.1956 + */
171.1957 + private Node expr(Node end) {
171.1958 + Node prev = null;
171.1959 + Node firstTail = null;
171.1960 + Node branchConn = null;
171.1961 +
171.1962 + for (;;) {
171.1963 + Node node = sequence(end);
171.1964 + Node nodeTail = root; //double return
171.1965 + if (prev == null) {
171.1966 + prev = node;
171.1967 + firstTail = nodeTail;
171.1968 + } else {
171.1969 + // Branch
171.1970 + if (branchConn == null) {
171.1971 + branchConn = new BranchConn();
171.1972 + branchConn.next = end;
171.1973 + }
171.1974 + if (node == end) {
171.1975 + // if the node returned from sequence() is "end"
171.1976 + // we have an empty expr, set a null atom into
171.1977 + // the branch to indicate to go "next" directly.
171.1978 + node = null;
171.1979 + } else {
171.1980 + // the "tail.next" of each atom goes to branchConn
171.1981 + nodeTail.next = branchConn;
171.1982 + }
171.1983 + if (prev instanceof Branch) {
171.1984 + ((Branch)prev).add(node);
171.1985 + } else {
171.1986 + if (prev == end) {
171.1987 + prev = null;
171.1988 + } else {
171.1989 + // replace the "end" with "branchConn" at its tail.next
171.1990 + // when put the "prev" into the branch as the first atom.
171.1991 + firstTail.next = branchConn;
171.1992 + }
171.1993 + prev = new Branch(prev, node, branchConn);
171.1994 + }
171.1995 + }
171.1996 + if (peek() != '|') {
171.1997 + return prev;
171.1998 + }
171.1999 + next();
171.2000 + }
171.2001 + }
171.2002 +
171.2003 + /**
171.2004 + * Parsing of sequences between alternations.
171.2005 + */
171.2006 + private Node sequence(Node end) {
171.2007 + Node head = null;
171.2008 + Node tail = null;
171.2009 + Node node = null;
171.2010 + LOOP:
171.2011 + for (;;) {
171.2012 + int ch = peek();
171.2013 + switch (ch) {
171.2014 + case '(':
171.2015 + // Because group handles its own closure,
171.2016 + // we need to treat it differently
171.2017 + node = group0();
171.2018 + // Check for comment or flag group
171.2019 + if (node == null)
171.2020 + continue;
171.2021 + if (head == null)
171.2022 + head = node;
171.2023 + else
171.2024 + tail.next = node;
171.2025 + // Double return: Tail was returned in root
171.2026 + tail = root;
171.2027 + continue;
171.2028 + case '[':
171.2029 + node = clazz(true);
171.2030 + break;
171.2031 + case '\\':
171.2032 + ch = nextEscaped();
171.2033 + if (ch == 'p' || ch == 'P') {
171.2034 + boolean oneLetter = true;
171.2035 + boolean comp = (ch == 'P');
171.2036 + ch = next(); // Consume { if present
171.2037 + if (ch != '{') {
171.2038 + unread();
171.2039 + } else {
171.2040 + oneLetter = false;
171.2041 + }
171.2042 + node = family(oneLetter, comp);
171.2043 + } else {
171.2044 + unread();
171.2045 + node = atom();
171.2046 + }
171.2047 + break;
171.2048 + case '^':
171.2049 + next();
171.2050 + if (has(MULTILINE)) {
171.2051 + if (has(UNIX_LINES))
171.2052 + node = new UnixCaret();
171.2053 + else
171.2054 + node = new Caret();
171.2055 + } else {
171.2056 + node = new Begin();
171.2057 + }
171.2058 + break;
171.2059 + case '$':
171.2060 + next();
171.2061 + if (has(UNIX_LINES))
171.2062 + node = new UnixDollar(has(MULTILINE));
171.2063 + else
171.2064 + node = new Dollar(has(MULTILINE));
171.2065 + break;
171.2066 + case '.':
171.2067 + next();
171.2068 + if (has(DOTALL)) {
171.2069 + node = new All();
171.2070 + } else {
171.2071 + if (has(UNIX_LINES))
171.2072 + node = new UnixDot();
171.2073 + else {
171.2074 + node = new Dot();
171.2075 + }
171.2076 + }
171.2077 + break;
171.2078 + case '|':
171.2079 + case ')':
171.2080 + break LOOP;
171.2081 + case ']': // Now interpreting dangling ] and } as literals
171.2082 + case '}':
171.2083 + node = atom();
171.2084 + break;
171.2085 + case '?':
171.2086 + case '*':
171.2087 + case '+':
171.2088 + next();
171.2089 + throw error("Dangling meta character '" + ((char)ch) + "'");
171.2090 + case 0:
171.2091 + if (cursor >= patternLength) {
171.2092 + break LOOP;
171.2093 + }
171.2094 + // Fall through
171.2095 + default:
171.2096 + node = atom();
171.2097 + break;
171.2098 + }
171.2099 +
171.2100 + node = closure(node);
171.2101 +
171.2102 + if (head == null) {
171.2103 + head = tail = node;
171.2104 + } else {
171.2105 + tail.next = node;
171.2106 + tail = node;
171.2107 + }
171.2108 + }
171.2109 + if (head == null) {
171.2110 + return end;
171.2111 + }
171.2112 + tail.next = end;
171.2113 + root = tail; //double return
171.2114 + return head;
171.2115 + }
171.2116 +
171.2117 + /**
171.2118 + * Parse and add a new Single or Slice.
171.2119 + */
171.2120 + private Node atom() {
171.2121 + int first = 0;
171.2122 + int prev = -1;
171.2123 + boolean hasSupplementary = false;
171.2124 + int ch = peek();
171.2125 + for (;;) {
171.2126 + switch (ch) {
171.2127 + case '*':
171.2128 + case '+':
171.2129 + case '?':
171.2130 + case '{':
171.2131 + if (first > 1) {
171.2132 + cursor = prev; // Unwind one character
171.2133 + first--;
171.2134 + }
171.2135 + break;
171.2136 + case '$':
171.2137 + case '.':
171.2138 + case '^':
171.2139 + case '(':
171.2140 + case '[':
171.2141 + case '|':
171.2142 + case ')':
171.2143 + break;
171.2144 + case '\\':
171.2145 + ch = nextEscaped();
171.2146 + if (ch == 'p' || ch == 'P') { // Property
171.2147 + if (first > 0) { // Slice is waiting; handle it first
171.2148 + unread();
171.2149 + break;
171.2150 + } else { // No slice; just return the family node
171.2151 + boolean comp = (ch == 'P');
171.2152 + boolean oneLetter = true;
171.2153 + ch = next(); // Consume { if present
171.2154 + if (ch != '{')
171.2155 + unread();
171.2156 + else
171.2157 + oneLetter = false;
171.2158 + return family(oneLetter, comp);
171.2159 + }
171.2160 + }
171.2161 + unread();
171.2162 + prev = cursor;
171.2163 + ch = escape(false, first == 0);
171.2164 + if (ch >= 0) {
171.2165 + append(ch, first);
171.2166 + first++;
171.2167 + if (isSupplementary(ch)) {
171.2168 + hasSupplementary = true;
171.2169 + }
171.2170 + ch = peek();
171.2171 + continue;
171.2172 + } else if (first == 0) {
171.2173 + return root;
171.2174 + }
171.2175 + // Unwind meta escape sequence
171.2176 + cursor = prev;
171.2177 + break;
171.2178 + case 0:
171.2179 + if (cursor >= patternLength) {
171.2180 + break;
171.2181 + }
171.2182 + // Fall through
171.2183 + default:
171.2184 + prev = cursor;
171.2185 + append(ch, first);
171.2186 + first++;
171.2187 + if (isSupplementary(ch)) {
171.2188 + hasSupplementary = true;
171.2189 + }
171.2190 + ch = next();
171.2191 + continue;
171.2192 + }
171.2193 + break;
171.2194 + }
171.2195 + if (first == 1) {
171.2196 + return newSingle(buffer[0]);
171.2197 + } else {
171.2198 + return newSlice(buffer, first, hasSupplementary);
171.2199 + }
171.2200 + }
171.2201 +
171.2202 + private void append(int ch, int len) {
171.2203 + if (len >= buffer.length) {
171.2204 + int[] tmp = new int[len+len];
171.2205 + System.arraycopy(buffer, 0, tmp, 0, len);
171.2206 + buffer = tmp;
171.2207 + }
171.2208 + buffer[len] = ch;
171.2209 + }
171.2210 +
171.2211 + /**
171.2212 + * Parses a backref greedily, taking as many numbers as it
171.2213 + * can. The first digit is always treated as a backref, but
171.2214 + * multi digit numbers are only treated as a backref if at
171.2215 + * least that many backrefs exist at this point in the regex.
171.2216 + */
171.2217 + private Node ref(int refNum) {
171.2218 + boolean done = false;
171.2219 + while(!done) {
171.2220 + int ch = peek();
171.2221 + switch(ch) {
171.2222 + case '0':
171.2223 + case '1':
171.2224 + case '2':
171.2225 + case '3':
171.2226 + case '4':
171.2227 + case '5':
171.2228 + case '6':
171.2229 + case '7':
171.2230 + case '8':
171.2231 + case '9':
171.2232 + int newRefNum = (refNum * 10) + (ch - '0');
171.2233 + // Add another number if it doesn't make a group
171.2234 + // that doesn't exist
171.2235 + if (capturingGroupCount - 1 < newRefNum) {
171.2236 + done = true;
171.2237 + break;
171.2238 + }
171.2239 + refNum = newRefNum;
171.2240 + read();
171.2241 + break;
171.2242 + default:
171.2243 + done = true;
171.2244 + break;
171.2245 + }
171.2246 + }
171.2247 + if (has(CASE_INSENSITIVE))
171.2248 + return new CIBackRef(refNum, has(UNICODE_CASE));
171.2249 + else
171.2250 + return new BackRef(refNum);
171.2251 + }
171.2252 +
171.2253 + /**
171.2254 + * Parses an escape sequence to determine the actual value that needs
171.2255 + * to be matched.
171.2256 + * If -1 is returned and create was true a new object was added to the tree
171.2257 + * to handle the escape sequence.
171.2258 + * If the returned value is greater than zero, it is the value that
171.2259 + * matches the escape sequence.
171.2260 + */
171.2261 + private int escape(boolean inclass, boolean create) {
171.2262 + int ch = skip();
171.2263 + switch (ch) {
171.2264 + case '0':
171.2265 + return o();
171.2266 + case '1':
171.2267 + case '2':
171.2268 + case '3':
171.2269 + case '4':
171.2270 + case '5':
171.2271 + case '6':
171.2272 + case '7':
171.2273 + case '8':
171.2274 + case '9':
171.2275 + if (inclass) break;
171.2276 + if (create) {
171.2277 + root = ref((ch - '0'));
171.2278 + }
171.2279 + return -1;
171.2280 + case 'A':
171.2281 + if (inclass) break;
171.2282 + if (create) root = new Begin();
171.2283 + return -1;
171.2284 + case 'B':
171.2285 + if (inclass) break;
171.2286 + if (create) root = new Bound(Bound.NONE, has(UNICODE_CHARACTER_CLASS));
171.2287 + return -1;
171.2288 + case 'C':
171.2289 + break;
171.2290 + case 'D':
171.2291 + if (create) root = has(UNICODE_CHARACTER_CLASS)
171.2292 + ? new Utype(UnicodeProp.DIGIT).complement()
171.2293 + : new Ctype(ASCII.DIGIT).complement();
171.2294 + return -1;
171.2295 + case 'E':
171.2296 + case 'F':
171.2297 + break;
171.2298 + case 'G':
171.2299 + if (inclass) break;
171.2300 + if (create) root = new LastMatch();
171.2301 + return -1;
171.2302 + case 'H':
171.2303 + case 'I':
171.2304 + case 'J':
171.2305 + case 'K':
171.2306 + case 'L':
171.2307 + case 'M':
171.2308 + case 'N':
171.2309 + case 'O':
171.2310 + case 'P':
171.2311 + case 'Q':
171.2312 + case 'R':
171.2313 + break;
171.2314 + case 'S':
171.2315 + if (create) root = has(UNICODE_CHARACTER_CLASS)
171.2316 + ? new Utype(UnicodeProp.WHITE_SPACE).complement()
171.2317 + : new Ctype(ASCII.SPACE).complement();
171.2318 + return -1;
171.2319 + case 'T':
171.2320 + case 'U':
171.2321 + case 'V':
171.2322 + break;
171.2323 + case 'W':
171.2324 + if (create) root = has(UNICODE_CHARACTER_CLASS)
171.2325 + ? new Utype(UnicodeProp.WORD).complement()
171.2326 + : new Ctype(ASCII.WORD).complement();
171.2327 + return -1;
171.2328 + case 'X':
171.2329 + case 'Y':
171.2330 + break;
171.2331 + case 'Z':
171.2332 + if (inclass) break;
171.2333 + if (create) {
171.2334 + if (has(UNIX_LINES))
171.2335 + root = new UnixDollar(false);
171.2336 + else
171.2337 + root = new Dollar(false);
171.2338 + }
171.2339 + return -1;
171.2340 + case 'a':
171.2341 + return '\007';
171.2342 + case 'b':
171.2343 + if (inclass) break;
171.2344 + if (create) root = new Bound(Bound.BOTH, has(UNICODE_CHARACTER_CLASS));
171.2345 + return -1;
171.2346 + case 'c':
171.2347 + return c();
171.2348 + case 'd':
171.2349 + if (create) root = has(UNICODE_CHARACTER_CLASS)
171.2350 + ? new Utype(UnicodeProp.DIGIT)
171.2351 + : new Ctype(ASCII.DIGIT);
171.2352 + return -1;
171.2353 + case 'e':
171.2354 + return '\033';
171.2355 + case 'f':
171.2356 + return '\f';
171.2357 + case 'g':
171.2358 + case 'h':
171.2359 + case 'i':
171.2360 + case 'j':
171.2361 + break;
171.2362 + case 'k':
171.2363 + if (inclass)
171.2364 + break;
171.2365 + if (read() != '<')
171.2366 + throw error("\\k is not followed by '<' for named capturing group");
171.2367 + String name = groupname(read());
171.2368 + if (!namedGroups().containsKey(name))
171.2369 + throw error("(named capturing group <"+ name+"> does not exit");
171.2370 + if (create) {
171.2371 + if (has(CASE_INSENSITIVE))
171.2372 + root = new CIBackRef(namedGroups().get(name), has(UNICODE_CASE));
171.2373 + else
171.2374 + root = new BackRef(namedGroups().get(name));
171.2375 + }
171.2376 + return -1;
171.2377 + case 'l':
171.2378 + case 'm':
171.2379 + break;
171.2380 + case 'n':
171.2381 + return '\n';
171.2382 + case 'o':
171.2383 + case 'p':
171.2384 + case 'q':
171.2385 + break;
171.2386 + case 'r':
171.2387 + return '\r';
171.2388 + case 's':
171.2389 + if (create) root = has(UNICODE_CHARACTER_CLASS)
171.2390 + ? new Utype(UnicodeProp.WHITE_SPACE)
171.2391 + : new Ctype(ASCII.SPACE);
171.2392 + return -1;
171.2393 + case 't':
171.2394 + return '\t';
171.2395 + case 'u':
171.2396 + return u();
171.2397 + case 'v':
171.2398 + return '\013';
171.2399 + case 'w':
171.2400 + if (create) root = has(UNICODE_CHARACTER_CLASS)
171.2401 + ? new Utype(UnicodeProp.WORD)
171.2402 + : new Ctype(ASCII.WORD);
171.2403 + return -1;
171.2404 + case 'x':
171.2405 + return x();
171.2406 + case 'y':
171.2407 + break;
171.2408 + case 'z':
171.2409 + if (inclass) break;
171.2410 + if (create) root = new End();
171.2411 + return -1;
171.2412 + default:
171.2413 + return ch;
171.2414 + }
171.2415 + throw error("Illegal/unsupported escape sequence");
171.2416 + }
171.2417 +
171.2418 + /**
171.2419 + * Parse a character class, and return the node that matches it.
171.2420 + *
171.2421 + * Consumes a ] on the way out if consume is true. Usually consume
171.2422 + * is true except for the case of [abc&&def] where def is a separate
171.2423 + * right hand node with "understood" brackets.
171.2424 + */
171.2425 + private CharProperty clazz(boolean consume) {
171.2426 + CharProperty prev = null;
171.2427 + CharProperty node = null;
171.2428 + BitClass bits = new BitClass();
171.2429 + boolean include = true;
171.2430 + boolean firstInClass = true;
171.2431 + int ch = next();
171.2432 + for (;;) {
171.2433 + switch (ch) {
171.2434 + case '^':
171.2435 + // Negates if first char in a class, otherwise literal
171.2436 + if (firstInClass) {
171.2437 + if (temp[cursor-1] != '[')
171.2438 + break;
171.2439 + ch = next();
171.2440 + include = !include;
171.2441 + continue;
171.2442 + } else {
171.2443 + // ^ not first in class, treat as literal
171.2444 + break;
171.2445 + }
171.2446 + case '[':
171.2447 + firstInClass = false;
171.2448 + node = clazz(true);
171.2449 + if (prev == null)
171.2450 + prev = node;
171.2451 + else
171.2452 + prev = union(prev, node);
171.2453 + ch = peek();
171.2454 + continue;
171.2455 + case '&':
171.2456 + firstInClass = false;
171.2457 + ch = next();
171.2458 + if (ch == '&') {
171.2459 + ch = next();
171.2460 + CharProperty rightNode = null;
171.2461 + while (ch != ']' && ch != '&') {
171.2462 + if (ch == '[') {
171.2463 + if (rightNode == null)
171.2464 + rightNode = clazz(true);
171.2465 + else
171.2466 + rightNode = union(rightNode, clazz(true));
171.2467 + } else { // abc&&def
171.2468 + unread();
171.2469 + rightNode = clazz(false);
171.2470 + }
171.2471 + ch = peek();
171.2472 + }
171.2473 + if (rightNode != null)
171.2474 + node = rightNode;
171.2475 + if (prev == null) {
171.2476 + if (rightNode == null)
171.2477 + throw error("Bad class syntax");
171.2478 + else
171.2479 + prev = rightNode;
171.2480 + } else {
171.2481 + prev = intersection(prev, node);
171.2482 + }
171.2483 + } else {
171.2484 + // treat as a literal &
171.2485 + unread();
171.2486 + break;
171.2487 + }
171.2488 + continue;
171.2489 + case 0:
171.2490 + firstInClass = false;
171.2491 + if (cursor >= patternLength)
171.2492 + throw error("Unclosed character class");
171.2493 + break;
171.2494 + case ']':
171.2495 + firstInClass = false;
171.2496 + if (prev != null) {
171.2497 + if (consume)
171.2498 + next();
171.2499 + return prev;
171.2500 + }
171.2501 + break;
171.2502 + default:
171.2503 + firstInClass = false;
171.2504 + break;
171.2505 + }
171.2506 + node = range(bits);
171.2507 + if (include) {
171.2508 + if (prev == null) {
171.2509 + prev = node;
171.2510 + } else {
171.2511 + if (prev != node)
171.2512 + prev = union(prev, node);
171.2513 + }
171.2514 + } else {
171.2515 + if (prev == null) {
171.2516 + prev = node.complement();
171.2517 + } else {
171.2518 + if (prev != node)
171.2519 + prev = setDifference(prev, node);
171.2520 + }
171.2521 + }
171.2522 + ch = peek();
171.2523 + }
171.2524 + }
171.2525 +
171.2526 + private CharProperty bitsOrSingle(BitClass bits, int ch) {
171.2527 + /* Bits can only handle codepoints in [u+0000-u+00ff] range.
171.2528 + Use "single" node instead of bits when dealing with unicode
171.2529 + case folding for codepoints listed below.
171.2530 + (1)Uppercase out of range: u+00ff, u+00b5
171.2531 + toUpperCase(u+00ff) -> u+0178
171.2532 + toUpperCase(u+00b5) -> u+039c
171.2533 + (2)LatinSmallLetterLongS u+17f
171.2534 + toUpperCase(u+017f) -> u+0053
171.2535 + (3)LatinSmallLetterDotlessI u+131
171.2536 + toUpperCase(u+0131) -> u+0049
171.2537 + (4)LatinCapitalLetterIWithDotAbove u+0130
171.2538 + toLowerCase(u+0130) -> u+0069
171.2539 + (5)KelvinSign u+212a
171.2540 + toLowerCase(u+212a) ==> u+006B
171.2541 + (6)AngstromSign u+212b
171.2542 + toLowerCase(u+212b) ==> u+00e5
171.2543 + */
171.2544 + int d;
171.2545 + if (ch < 256 &&
171.2546 + !(has(CASE_INSENSITIVE) && has(UNICODE_CASE) &&
171.2547 + (ch == 0xff || ch == 0xb5 ||
171.2548 + ch == 0x49 || ch == 0x69 || //I and i
171.2549 + ch == 0x53 || ch == 0x73 || //S and s
171.2550 + ch == 0x4b || ch == 0x6b || //K and k
171.2551 + ch == 0xc5 || ch == 0xe5))) //A+ring
171.2552 + return bits.add(ch, flags());
171.2553 + return newSingle(ch);
171.2554 + }
171.2555 +
171.2556 + /**
171.2557 + * Parse a single character or a character range in a character class
171.2558 + * and return its representative node.
171.2559 + */
171.2560 + private CharProperty range(BitClass bits) {
171.2561 + int ch = peek();
171.2562 + if (ch == '\\') {
171.2563 + ch = nextEscaped();
171.2564 + if (ch == 'p' || ch == 'P') { // A property
171.2565 + boolean comp = (ch == 'P');
171.2566 + boolean oneLetter = true;
171.2567 + // Consume { if present
171.2568 + ch = next();
171.2569 + if (ch != '{')
171.2570 + unread();
171.2571 + else
171.2572 + oneLetter = false;
171.2573 + return family(oneLetter, comp);
171.2574 + } else { // ordinary escape
171.2575 + unread();
171.2576 + ch = escape(true, true);
171.2577 + if (ch == -1)
171.2578 + return (CharProperty) root;
171.2579 + }
171.2580 + } else {
171.2581 + ch = single();
171.2582 + }
171.2583 + if (ch >= 0) {
171.2584 + if (peek() == '-') {
171.2585 + int endRange = temp[cursor+1];
171.2586 + if (endRange == '[') {
171.2587 + return bitsOrSingle(bits, ch);
171.2588 + }
171.2589 + if (endRange != ']') {
171.2590 + next();
171.2591 + int m = single();
171.2592 + if (m < ch)
171.2593 + throw error("Illegal character range");
171.2594 + if (has(CASE_INSENSITIVE))
171.2595 + return caseInsensitiveRangeFor(ch, m);
171.2596 + else
171.2597 + return rangeFor(ch, m);
171.2598 + }
171.2599 + }
171.2600 + return bitsOrSingle(bits, ch);
171.2601 + }
171.2602 + throw error("Unexpected character '"+((char)ch)+"'");
171.2603 + }
171.2604 +
171.2605 + private int single() {
171.2606 + int ch = peek();
171.2607 + switch (ch) {
171.2608 + case '\\':
171.2609 + return escape(true, false);
171.2610 + default:
171.2611 + next();
171.2612 + return ch;
171.2613 + }
171.2614 + }
171.2615 +
171.2616 + /**
171.2617 + * Parses a Unicode character family and returns its representative node.
171.2618 + */
171.2619 + private CharProperty family(boolean singleLetter,
171.2620 + boolean maybeComplement)
171.2621 + {
171.2622 + next();
171.2623 + String name;
171.2624 + CharProperty node = null;
171.2625 +
171.2626 + if (singleLetter) {
171.2627 + int c = temp[cursor];
171.2628 + if (!Character.isSupplementaryCodePoint(c)) {
171.2629 + name = String.valueOf((char)c);
171.2630 + } else {
171.2631 + name = new String(temp, cursor, 1);
171.2632 + }
171.2633 + read();
171.2634 + } else {
171.2635 + int i = cursor;
171.2636 + mark('}');
171.2637 + while(read() != '}') {
171.2638 + }
171.2639 + mark('\000');
171.2640 + int j = cursor;
171.2641 + if (j > patternLength)
171.2642 + throw error("Unclosed character family");
171.2643 + if (i + 1 >= j)
171.2644 + throw error("Empty character family");
171.2645 + name = new String(temp, i, j-i-1);
171.2646 + }
171.2647 +
171.2648 + int i = name.indexOf('=');
171.2649 + if (i != -1) {
171.2650 + // property construct \p{name=value}
171.2651 + String value = name.substring(i + 1);
171.2652 + name = name.substring(0, i).toLowerCase(Locale.ENGLISH);
171.2653 + /* if ("sc".equals(name) || "script".equals(name)) {
171.2654 + node = unicodeScriptPropertyFor(value);
171.2655 + } else if ("blk".equals(name) || "block".equals(name)) {
171.2656 + node = unicodeBlockPropertyFor(value);
171.2657 + } else*/ if ("gc".equals(name) || "general_category".equals(name)) {
171.2658 + node = charPropertyNodeFor(value);
171.2659 + } else {
171.2660 + throw error("Unknown Unicode property {name=<" + name + ">, "
171.2661 + + "value=<" + value + ">}");
171.2662 + }
171.2663 + } else {
171.2664 + /*if (name.startsWith("In")) {
171.2665 + // \p{inBlockName}
171.2666 + node = unicodeBlockPropertyFor(name.substring(2));
171.2667 + } else if (name.startsWith("Is")) {
171.2668 + // \p{isGeneralCategory} and \p{isScriptName}
171.2669 + name = name.substring(2);
171.2670 + UnicodeProp uprop = UnicodeProp.forName(name);
171.2671 + if (uprop != null)
171.2672 + node = new Utype(uprop);
171.2673 + if (node == null)
171.2674 + node = CharPropertyNames.charPropertyFor(name);
171.2675 + if (node == null)
171.2676 + node = unicodeScriptPropertyFor(name);
171.2677 + } else*/ {
171.2678 + if (has(UNICODE_CHARACTER_CLASS)) {
171.2679 + UnicodeProp uprop = UnicodeProp.forPOSIXName(name);
171.2680 + if (uprop != null)
171.2681 + node = new Utype(uprop);
171.2682 + }
171.2683 + if (node == null)
171.2684 + node = charPropertyNodeFor(name);
171.2685 + }
171.2686 + }
171.2687 + if (maybeComplement) {
171.2688 + if (node instanceof Category /*|| node instanceof Block*/)
171.2689 + hasSupplementary = true;
171.2690 + node = node.complement();
171.2691 + }
171.2692 + return node;
171.2693 + }
171.2694 +
171.2695 +
171.2696 + /**
171.2697 + * Returns a CharProperty matching all characters belong to
171.2698 + * a UnicodeScript.
171.2699 + *
171.2700 + private CharProperty unicodeScriptPropertyFor(String name) {
171.2701 + final Character.UnicodeScript script;
171.2702 + try {
171.2703 + script = Character.UnicodeScript.forName(name);
171.2704 + } catch (IllegalArgumentException iae) {
171.2705 + throw error("Unknown character script name {" + name + "}");
171.2706 + }
171.2707 + return new Script(script);
171.2708 + }
171.2709 +
171.2710 + /**
171.2711 + * Returns a CharProperty matching all characters in a UnicodeBlock.
171.2712 + *
171.2713 + private CharProperty unicodeBlockPropertyFor(String name) {
171.2714 + final Character.UnicodeBlock block;
171.2715 + try {
171.2716 + block = Character.UnicodeBlock.forName(name);
171.2717 + } catch (IllegalArgumentException iae) {
171.2718 + throw error("Unknown character block name {" + name + "}");
171.2719 + }
171.2720 + return new Block(block);
171.2721 + }
171.2722 +
171.2723 + /**
171.2724 + * Returns a CharProperty matching all characters in a named property.
171.2725 + */
171.2726 + private CharProperty charPropertyNodeFor(String name) {
171.2727 + CharProperty p = CharPropertyNames.charPropertyFor(name);
171.2728 + if (p == null)
171.2729 + throw error("Unknown character property name {" + name + "}");
171.2730 + return p;
171.2731 + }
171.2732 +
171.2733 + /**
171.2734 + * Parses and returns the name of a "named capturing group", the trailing
171.2735 + * ">" is consumed after parsing.
171.2736 + */
171.2737 + private String groupname(int ch) {
171.2738 + StringBuilder sb = new StringBuilder();
171.2739 + sb.append(Character.toChars(ch));
171.2740 + while (ASCII.isLower(ch=read()) || ASCII.isUpper(ch) ||
171.2741 + ASCII.isDigit(ch)) {
171.2742 + sb.append(Character.toChars(ch));
171.2743 + }
171.2744 + if (sb.length() == 0)
171.2745 + throw error("named capturing group has 0 length name");
171.2746 + if (ch != '>')
171.2747 + throw error("named capturing group is missing trailing '>'");
171.2748 + return sb.toString();
171.2749 + }
171.2750 +
171.2751 + /**
171.2752 + * Parses a group and returns the head node of a set of nodes that process
171.2753 + * the group. Sometimes a double return system is used where the tail is
171.2754 + * returned in root.
171.2755 + */
171.2756 + private Node group0() {
171.2757 + boolean capturingGroup = false;
171.2758 + Node head = null;
171.2759 + Node tail = null;
171.2760 + int save = flags;
171.2761 + root = null;
171.2762 + int ch = next();
171.2763 + if (ch == '?') {
171.2764 + ch = skip();
171.2765 + switch (ch) {
171.2766 + case ':': // (?:xxx) pure group
171.2767 + head = createGroup(true);
171.2768 + tail = root;
171.2769 + head.next = expr(tail);
171.2770 + break;
171.2771 + case '=': // (?=xxx) and (?!xxx) lookahead
171.2772 + case '!':
171.2773 + head = createGroup(true);
171.2774 + tail = root;
171.2775 + head.next = expr(tail);
171.2776 + if (ch == '=') {
171.2777 + head = tail = new Pos(head);
171.2778 + } else {
171.2779 + head = tail = new Neg(head);
171.2780 + }
171.2781 + break;
171.2782 + case '>': // (?>xxx) independent group
171.2783 + head = createGroup(true);
171.2784 + tail = root;
171.2785 + head.next = expr(tail);
171.2786 + head = tail = new Ques(head, INDEPENDENT);
171.2787 + break;
171.2788 + case '<': // (?<xxx) look behind
171.2789 + ch = read();
171.2790 + if (ASCII.isLower(ch) || ASCII.isUpper(ch)) {
171.2791 + // named captured group
171.2792 + String name = groupname(ch);
171.2793 + if (namedGroups().containsKey(name))
171.2794 + throw error("Named capturing group <" + name
171.2795 + + "> is already defined");
171.2796 + capturingGroup = true;
171.2797 + head = createGroup(false);
171.2798 + tail = root;
171.2799 + namedGroups().put(name, capturingGroupCount-1);
171.2800 + head.next = expr(tail);
171.2801 + break;
171.2802 + }
171.2803 + int start = cursor;
171.2804 + head = createGroup(true);
171.2805 + tail = root;
171.2806 + head.next = expr(tail);
171.2807 + tail.next = lookbehindEnd;
171.2808 + TreeInfo info = new TreeInfo();
171.2809 + head.study(info);
171.2810 + if (info.maxValid == false) {
171.2811 + throw error("Look-behind group does not have "
171.2812 + + "an obvious maximum length");
171.2813 + }
171.2814 + boolean hasSupplementary = findSupplementary(start, patternLength);
171.2815 + if (ch == '=') {
171.2816 + head = tail = (hasSupplementary ?
171.2817 + new BehindS(head, info.maxLength,
171.2818 + info.minLength) :
171.2819 + new Behind(head, info.maxLength,
171.2820 + info.minLength));
171.2821 + } else if (ch == '!') {
171.2822 + head = tail = (hasSupplementary ?
171.2823 + new NotBehindS(head, info.maxLength,
171.2824 + info.minLength) :
171.2825 + new NotBehind(head, info.maxLength,
171.2826 + info.minLength));
171.2827 + } else {
171.2828 + throw error("Unknown look-behind group");
171.2829 + }
171.2830 + break;
171.2831 + case '$':
171.2832 + case '@':
171.2833 + throw error("Unknown group type");
171.2834 + default: // (?xxx:) inlined match flags
171.2835 + unread();
171.2836 + addFlag();
171.2837 + ch = read();
171.2838 + if (ch == ')') {
171.2839 + return null; // Inline modifier only
171.2840 + }
171.2841 + if (ch != ':') {
171.2842 + throw error("Unknown inline modifier");
171.2843 + }
171.2844 + head = createGroup(true);
171.2845 + tail = root;
171.2846 + head.next = expr(tail);
171.2847 + break;
171.2848 + }
171.2849 + } else { // (xxx) a regular group
171.2850 + capturingGroup = true;
171.2851 + head = createGroup(false);
171.2852 + tail = root;
171.2853 + head.next = expr(tail);
171.2854 + }
171.2855 +
171.2856 + accept(')', "Unclosed group");
171.2857 + flags = save;
171.2858 +
171.2859 + // Check for quantifiers
171.2860 + Node node = closure(head);
171.2861 + if (node == head) { // No closure
171.2862 + root = tail;
171.2863 + return node; // Dual return
171.2864 + }
171.2865 + if (head == tail) { // Zero length assertion
171.2866 + root = node;
171.2867 + return node; // Dual return
171.2868 + }
171.2869 +
171.2870 + if (node instanceof Ques) {
171.2871 + Ques ques = (Ques) node;
171.2872 + if (ques.type == POSSESSIVE) {
171.2873 + root = node;
171.2874 + return node;
171.2875 + }
171.2876 + tail.next = new BranchConn();
171.2877 + tail = tail.next;
171.2878 + if (ques.type == GREEDY) {
171.2879 + head = new Branch(head, null, tail);
171.2880 + } else { // Reluctant quantifier
171.2881 + head = new Branch(null, head, tail);
171.2882 + }
171.2883 + root = tail;
171.2884 + return head;
171.2885 + } else if (node instanceof Curly) {
171.2886 + Curly curly = (Curly) node;
171.2887 + if (curly.type == POSSESSIVE) {
171.2888 + root = node;
171.2889 + return node;
171.2890 + }
171.2891 + // Discover if the group is deterministic
171.2892 + TreeInfo info = new TreeInfo();
171.2893 + if (head.study(info)) { // Deterministic
171.2894 + GroupTail temp = (GroupTail) tail;
171.2895 + head = root = new GroupCurly(head.next, curly.cmin,
171.2896 + curly.cmax, curly.type,
171.2897 + ((GroupTail)tail).localIndex,
171.2898 + ((GroupTail)tail).groupIndex,
171.2899 + capturingGroup);
171.2900 + return head;
171.2901 + } else { // Non-deterministic
171.2902 + int temp = ((GroupHead) head).localIndex;
171.2903 + Loop loop;
171.2904 + if (curly.type == GREEDY)
171.2905 + loop = new Loop(this.localCount, temp);
171.2906 + else // Reluctant Curly
171.2907 + loop = new LazyLoop(this.localCount, temp);
171.2908 + Prolog prolog = new Prolog(loop);
171.2909 + this.localCount += 1;
171.2910 + loop.cmin = curly.cmin;
171.2911 + loop.cmax = curly.cmax;
171.2912 + loop.body = head;
171.2913 + tail.next = loop;
171.2914 + root = loop;
171.2915 + return prolog; // Dual return
171.2916 + }
171.2917 + }
171.2918 + throw error("Internal logic error");
171.2919 + }
171.2920 +
171.2921 + /**
171.2922 + * Create group head and tail nodes using double return. If the group is
171.2923 + * created with anonymous true then it is a pure group and should not
171.2924 + * affect group counting.
171.2925 + */
171.2926 + private Node createGroup(boolean anonymous) {
171.2927 + int localIndex = localCount++;
171.2928 + int groupIndex = 0;
171.2929 + if (!anonymous)
171.2930 + groupIndex = capturingGroupCount++;
171.2931 + GroupHead head = new GroupHead(localIndex);
171.2932 + root = new GroupTail(localIndex, groupIndex);
171.2933 + if (!anonymous && groupIndex < 10)
171.2934 + groupNodes[groupIndex] = head;
171.2935 + return head;
171.2936 + }
171.2937 +
171.2938 + /**
171.2939 + * Parses inlined match flags and set them appropriately.
171.2940 + */
171.2941 + private void addFlag() {
171.2942 + int ch = peek();
171.2943 + for (;;) {
171.2944 + switch (ch) {
171.2945 + case 'i':
171.2946 + flags |= CASE_INSENSITIVE;
171.2947 + break;
171.2948 + case 'm':
171.2949 + flags |= MULTILINE;
171.2950 + break;
171.2951 + case 's':
171.2952 + flags |= DOTALL;
171.2953 + break;
171.2954 + case 'd':
171.2955 + flags |= UNIX_LINES;
171.2956 + break;
171.2957 + case 'u':
171.2958 + flags |= UNICODE_CASE;
171.2959 + break;
171.2960 + case 'c':
171.2961 + flags |= CANON_EQ;
171.2962 + break;
171.2963 + case 'x':
171.2964 + flags |= COMMENTS;
171.2965 + break;
171.2966 + case 'U':
171.2967 + flags |= (UNICODE_CHARACTER_CLASS | UNICODE_CASE);
171.2968 + break;
171.2969 + case '-': // subFlag then fall through
171.2970 + ch = next();
171.2971 + subFlag();
171.2972 + default:
171.2973 + return;
171.2974 + }
171.2975 + ch = next();
171.2976 + }
171.2977 + }
171.2978 +
171.2979 + /**
171.2980 + * Parses the second part of inlined match flags and turns off
171.2981 + * flags appropriately.
171.2982 + */
171.2983 + private void subFlag() {
171.2984 + int ch = peek();
171.2985 + for (;;) {
171.2986 + switch (ch) {
171.2987 + case 'i':
171.2988 + flags &= ~CASE_INSENSITIVE;
171.2989 + break;
171.2990 + case 'm':
171.2991 + flags &= ~MULTILINE;
171.2992 + break;
171.2993 + case 's':
171.2994 + flags &= ~DOTALL;
171.2995 + break;
171.2996 + case 'd':
171.2997 + flags &= ~UNIX_LINES;
171.2998 + break;
171.2999 + case 'u':
171.3000 + flags &= ~UNICODE_CASE;
171.3001 + break;
171.3002 + case 'c':
171.3003 + flags &= ~CANON_EQ;
171.3004 + break;
171.3005 + case 'x':
171.3006 + flags &= ~COMMENTS;
171.3007 + break;
171.3008 + case 'U':
171.3009 + flags &= ~(UNICODE_CHARACTER_CLASS | UNICODE_CASE);
171.3010 + default:
171.3011 + return;
171.3012 + }
171.3013 + ch = next();
171.3014 + }
171.3015 + }
171.3016 +
171.3017 + static final int MAX_REPS = 0x7FFFFFFF;
171.3018 +
171.3019 + static final int GREEDY = 0;
171.3020 +
171.3021 + static final int LAZY = 1;
171.3022 +
171.3023 + static final int POSSESSIVE = 2;
171.3024 +
171.3025 + static final int INDEPENDENT = 3;
171.3026 +
171.3027 + /**
171.3028 + * Processes repetition. If the next character peeked is a quantifier
171.3029 + * then new nodes must be appended to handle the repetition.
171.3030 + * Prev could be a single or a group, so it could be a chain of nodes.
171.3031 + */
171.3032 + private Node closure(Node prev) {
171.3033 + Node atom;
171.3034 + int ch = peek();
171.3035 + switch (ch) {
171.3036 + case '?':
171.3037 + ch = next();
171.3038 + if (ch == '?') {
171.3039 + next();
171.3040 + return new Ques(prev, LAZY);
171.3041 + } else if (ch == '+') {
171.3042 + next();
171.3043 + return new Ques(prev, POSSESSIVE);
171.3044 + }
171.3045 + return new Ques(prev, GREEDY);
171.3046 + case '*':
171.3047 + ch = next();
171.3048 + if (ch == '?') {
171.3049 + next();
171.3050 + return new Curly(prev, 0, MAX_REPS, LAZY);
171.3051 + } else if (ch == '+') {
171.3052 + next();
171.3053 + return new Curly(prev, 0, MAX_REPS, POSSESSIVE);
171.3054 + }
171.3055 + return new Curly(prev, 0, MAX_REPS, GREEDY);
171.3056 + case '+':
171.3057 + ch = next();
171.3058 + if (ch == '?') {
171.3059 + next();
171.3060 + return new Curly(prev, 1, MAX_REPS, LAZY);
171.3061 + } else if (ch == '+') {
171.3062 + next();
171.3063 + return new Curly(prev, 1, MAX_REPS, POSSESSIVE);
171.3064 + }
171.3065 + return new Curly(prev, 1, MAX_REPS, GREEDY);
171.3066 + case '{':
171.3067 + ch = temp[cursor+1];
171.3068 + if (ASCII.isDigit(ch)) {
171.3069 + skip();
171.3070 + int cmin = 0;
171.3071 + do {
171.3072 + cmin = cmin * 10 + (ch - '0');
171.3073 + } while (ASCII.isDigit(ch = read()));
171.3074 + int cmax = cmin;
171.3075 + if (ch == ',') {
171.3076 + ch = read();
171.3077 + cmax = MAX_REPS;
171.3078 + if (ch != '}') {
171.3079 + cmax = 0;
171.3080 + while (ASCII.isDigit(ch)) {
171.3081 + cmax = cmax * 10 + (ch - '0');
171.3082 + ch = read();
171.3083 + }
171.3084 + }
171.3085 + }
171.3086 + if (ch != '}')
171.3087 + throw error("Unclosed counted closure");
171.3088 + if (((cmin) | (cmax) | (cmax - cmin)) < 0)
171.3089 + throw error("Illegal repetition range");
171.3090 + Curly curly;
171.3091 + ch = peek();
171.3092 + if (ch == '?') {
171.3093 + next();
171.3094 + curly = new Curly(prev, cmin, cmax, LAZY);
171.3095 + } else if (ch == '+') {
171.3096 + next();
171.3097 + curly = new Curly(prev, cmin, cmax, POSSESSIVE);
171.3098 + } else {
171.3099 + curly = new Curly(prev, cmin, cmax, GREEDY);
171.3100 + }
171.3101 + return curly;
171.3102 + } else {
171.3103 + throw error("Illegal repetition");
171.3104 + }
171.3105 + default:
171.3106 + return prev;
171.3107 + }
171.3108 + }
171.3109 +
171.3110 + /**
171.3111 + * Utility method for parsing control escape sequences.
171.3112 + */
171.3113 + private int c() {
171.3114 + if (cursor < patternLength) {
171.3115 + return read() ^ 64;
171.3116 + }
171.3117 + throw error("Illegal control escape sequence");
171.3118 + }
171.3119 +
171.3120 + /**
171.3121 + * Utility method for parsing octal escape sequences.
171.3122 + */
171.3123 + private int o() {
171.3124 + int n = read();
171.3125 + if (((n-'0')|('7'-n)) >= 0) {
171.3126 + int m = read();
171.3127 + if (((m-'0')|('7'-m)) >= 0) {
171.3128 + int o = read();
171.3129 + if ((((o-'0')|('7'-o)) >= 0) && (((n-'0')|('3'-n)) >= 0)) {
171.3130 + return (n - '0') * 64 + (m - '0') * 8 + (o - '0');
171.3131 + }
171.3132 + unread();
171.3133 + return (n - '0') * 8 + (m - '0');
171.3134 + }
171.3135 + unread();
171.3136 + return (n - '0');
171.3137 + }
171.3138 + throw error("Illegal octal escape sequence");
171.3139 + }
171.3140 +
171.3141 + /**
171.3142 + * Utility method for parsing hexadecimal escape sequences.
171.3143 + */
171.3144 + private int x() {
171.3145 + int n = read();
171.3146 + if (ASCII.isHexDigit(n)) {
171.3147 + int m = read();
171.3148 + if (ASCII.isHexDigit(m)) {
171.3149 + return ASCII.toDigit(n) * 16 + ASCII.toDigit(m);
171.3150 + }
171.3151 + } else if (n == '{' && ASCII.isHexDigit(peek())) {
171.3152 + int ch = 0;
171.3153 + while (ASCII.isHexDigit(n = read())) {
171.3154 + ch = (ch << 4) + ASCII.toDigit(n);
171.3155 + if (ch > Character.MAX_CODE_POINT)
171.3156 + throw error("Hexadecimal codepoint is too big");
171.3157 + }
171.3158 + if (n != '}')
171.3159 + throw error("Unclosed hexadecimal escape sequence");
171.3160 + return ch;
171.3161 + }
171.3162 + throw error("Illegal hexadecimal escape sequence");
171.3163 + }
171.3164 +
171.3165 + /**
171.3166 + * Utility method for parsing unicode escape sequences.
171.3167 + */
171.3168 + private int cursor() {
171.3169 + return cursor;
171.3170 + }
171.3171 +
171.3172 + private void setcursor(int pos) {
171.3173 + cursor = pos;
171.3174 + }
171.3175 +
171.3176 + private int uxxxx() {
171.3177 + int n = 0;
171.3178 + for (int i = 0; i < 4; i++) {
171.3179 + int ch = read();
171.3180 + if (!ASCII.isHexDigit(ch)) {
171.3181 + throw error("Illegal Unicode escape sequence");
171.3182 + }
171.3183 + n = n * 16 + ASCII.toDigit(ch);
171.3184 + }
171.3185 + return n;
171.3186 + }
171.3187 +
171.3188 + private int u() {
171.3189 + int n = uxxxx();
171.3190 + if (Character.isHighSurrogate((char)n)) {
171.3191 + int cur = cursor();
171.3192 + if (read() == '\\' && read() == 'u') {
171.3193 + int n2 = uxxxx();
171.3194 + if (Character.isLowSurrogate((char)n2))
171.3195 + return Character.toCodePoint((char)n, (char)n2);
171.3196 + }
171.3197 + setcursor(cur);
171.3198 + }
171.3199 + return n;
171.3200 + }
171.3201 +
171.3202 + //
171.3203 + // Utility methods for code point support
171.3204 + //
171.3205 +
171.3206 + private static final int countChars(CharSequence seq, int index,
171.3207 + int lengthInCodePoints) {
171.3208 + // optimization
171.3209 + if (lengthInCodePoints == 1 && !Character.isHighSurrogate(seq.charAt(index))) {
171.3210 + assert (index >= 0 && index < seq.length());
171.3211 + return 1;
171.3212 + }
171.3213 + int length = seq.length();
171.3214 + int x = index;
171.3215 + if (lengthInCodePoints >= 0) {
171.3216 + assert (index >= 0 && index < length);
171.3217 + for (int i = 0; x < length && i < lengthInCodePoints; i++) {
171.3218 + if (Character.isHighSurrogate(seq.charAt(x++))) {
171.3219 + if (x < length && Character.isLowSurrogate(seq.charAt(x))) {
171.3220 + x++;
171.3221 + }
171.3222 + }
171.3223 + }
171.3224 + return x - index;
171.3225 + }
171.3226 +
171.3227 + assert (index >= 0 && index <= length);
171.3228 + if (index == 0) {
171.3229 + return 0;
171.3230 + }
171.3231 + int len = -lengthInCodePoints;
171.3232 + for (int i = 0; x > 0 && i < len; i++) {
171.3233 + if (Character.isLowSurrogate(seq.charAt(--x))) {
171.3234 + if (x > 0 && Character.isHighSurrogate(seq.charAt(x-1))) {
171.3235 + x--;
171.3236 + }
171.3237 + }
171.3238 + }
171.3239 + return index - x;
171.3240 + }
171.3241 +
171.3242 + private static final int countCodePoints(CharSequence seq) {
171.3243 + int length = seq.length();
171.3244 + int n = 0;
171.3245 + for (int i = 0; i < length; ) {
171.3246 + n++;
171.3247 + if (Character.isHighSurrogate(seq.charAt(i++))) {
171.3248 + if (i < length && Character.isLowSurrogate(seq.charAt(i))) {
171.3249 + i++;
171.3250 + }
171.3251 + }
171.3252 + }
171.3253 + return n;
171.3254 + }
171.3255 +
171.3256 + /**
171.3257 + * Creates a bit vector for matching Latin-1 values. A normal BitClass
171.3258 + * never matches values above Latin-1, and a complemented BitClass always
171.3259 + * matches values above Latin-1.
171.3260 + */
171.3261 + private static final class BitClass extends BmpCharProperty {
171.3262 + final boolean[] bits;
171.3263 + BitClass() { bits = new boolean[256]; }
171.3264 + private BitClass(boolean[] bits) { this.bits = bits; }
171.3265 + BitClass add(int c, int flags) {
171.3266 + assert c >= 0 && c <= 255;
171.3267 + if ((flags & CASE_INSENSITIVE) != 0) {
171.3268 + if (ASCII.isAscii(c)) {
171.3269 + bits[ASCII.toUpper(c)] = true;
171.3270 + bits[ASCII.toLower(c)] = true;
171.3271 + } else if ((flags & UNICODE_CASE) != 0) {
171.3272 + bits[Character.toLowerCase(c)] = true;
171.3273 + bits[Character.toUpperCase(c)] = true;
171.3274 + }
171.3275 + }
171.3276 + bits[c] = true;
171.3277 + return this;
171.3278 + }
171.3279 + boolean isSatisfiedBy(int ch) {
171.3280 + return ch < 256 && bits[ch];
171.3281 + }
171.3282 + }
171.3283 +
171.3284 + /**
171.3285 + * Returns a suitably optimized, single character matcher.
171.3286 + */
171.3287 + private CharProperty newSingle(final int ch) {
171.3288 + if (has(CASE_INSENSITIVE)) {
171.3289 + int lower, upper;
171.3290 + if (has(UNICODE_CASE)) {
171.3291 + upper = Character.toUpperCase(ch);
171.3292 + lower = Character.toLowerCase(upper);
171.3293 + if (upper != lower)
171.3294 + return new SingleU(lower);
171.3295 + } else if (ASCII.isAscii(ch)) {
171.3296 + lower = ASCII.toLower(ch);
171.3297 + upper = ASCII.toUpper(ch);
171.3298 + if (lower != upper)
171.3299 + return new SingleI(lower, upper);
171.3300 + }
171.3301 + }
171.3302 + if (isSupplementary(ch))
171.3303 + return new SingleS(ch); // Match a given Unicode character
171.3304 + return new Single(ch); // Match a given BMP character
171.3305 + }
171.3306 +
171.3307 + /**
171.3308 + * Utility method for creating a string slice matcher.
171.3309 + */
171.3310 + private Node newSlice(int[] buf, int count, boolean hasSupplementary) {
171.3311 + int[] tmp = new int[count];
171.3312 + if (has(CASE_INSENSITIVE)) {
171.3313 + if (has(UNICODE_CASE)) {
171.3314 + for (int i = 0; i < count; i++) {
171.3315 + tmp[i] = Character.toLowerCase(
171.3316 + Character.toUpperCase(buf[i]));
171.3317 + }
171.3318 + return hasSupplementary? new SliceUS(tmp) : new SliceU(tmp);
171.3319 + }
171.3320 + for (int i = 0; i < count; i++) {
171.3321 + tmp[i] = ASCII.toLower(buf[i]);
171.3322 + }
171.3323 + return hasSupplementary? new SliceIS(tmp) : new SliceI(tmp);
171.3324 + }
171.3325 + for (int i = 0; i < count; i++) {
171.3326 + tmp[i] = buf[i];
171.3327 + }
171.3328 + return hasSupplementary ? new SliceS(tmp) : new Slice(tmp);
171.3329 + }
171.3330 +
171.3331 + /**
171.3332 + * The following classes are the building components of the object
171.3333 + * tree that represents a compiled regular expression. The object tree
171.3334 + * is made of individual elements that handle constructs in the Pattern.
171.3335 + * Each type of object knows how to match its equivalent construct with
171.3336 + * the match() method.
171.3337 + */
171.3338 +
171.3339 + /**
171.3340 + * Base class for all node classes. Subclasses should override the match()
171.3341 + * method as appropriate. This class is an accepting node, so its match()
171.3342 + * always returns true.
171.3343 + */
171.3344 + static class Node extends Object {
171.3345 + Node next;
171.3346 + Node() {
171.3347 + next = Pattern.accept;
171.3348 + }
171.3349 + /**
171.3350 + * This method implements the classic accept node.
171.3351 + */
171.3352 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3353 + matcher.last = i;
171.3354 + matcher.groups[0] = matcher.first;
171.3355 + matcher.groups[1] = matcher.last;
171.3356 + return true;
171.3357 + }
171.3358 + /**
171.3359 + * This method is good for all zero length assertions.
171.3360 + */
171.3361 + boolean study(TreeInfo info) {
171.3362 + if (next != null) {
171.3363 + return next.study(info);
171.3364 + } else {
171.3365 + return info.deterministic;
171.3366 + }
171.3367 + }
171.3368 + }
171.3369 +
171.3370 + static class LastNode extends Node {
171.3371 + /**
171.3372 + * This method implements the classic accept node with
171.3373 + * the addition of a check to see if the match occurred
171.3374 + * using all of the input.
171.3375 + */
171.3376 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3377 + if (matcher.acceptMode == Matcher.ENDANCHOR && i != matcher.to)
171.3378 + return false;
171.3379 + matcher.last = i;
171.3380 + matcher.groups[0] = matcher.first;
171.3381 + matcher.groups[1] = matcher.last;
171.3382 + return true;
171.3383 + }
171.3384 + }
171.3385 +
171.3386 + /**
171.3387 + * Used for REs that can start anywhere within the input string.
171.3388 + * This basically tries to match repeatedly at each spot in the
171.3389 + * input string, moving forward after each try. An anchored search
171.3390 + * or a BnM will bypass this node completely.
171.3391 + */
171.3392 + static class Start extends Node {
171.3393 + int minLength;
171.3394 + Start(Node node) {
171.3395 + this.next = node;
171.3396 + TreeInfo info = new TreeInfo();
171.3397 + next.study(info);
171.3398 + minLength = info.minLength;
171.3399 + }
171.3400 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3401 + if (i > matcher.to - minLength) {
171.3402 + matcher.hitEnd = true;
171.3403 + return false;
171.3404 + }
171.3405 + int guard = matcher.to - minLength;
171.3406 + for (; i <= guard; i++) {
171.3407 + if (next.match(matcher, i, seq)) {
171.3408 + matcher.first = i;
171.3409 + matcher.groups[0] = matcher.first;
171.3410 + matcher.groups[1] = matcher.last;
171.3411 + return true;
171.3412 + }
171.3413 + }
171.3414 + matcher.hitEnd = true;
171.3415 + return false;
171.3416 + }
171.3417 + boolean study(TreeInfo info) {
171.3418 + next.study(info);
171.3419 + info.maxValid = false;
171.3420 + info.deterministic = false;
171.3421 + return false;
171.3422 + }
171.3423 + }
171.3424 +
171.3425 + /*
171.3426 + * StartS supports supplementary characters, including unpaired surrogates.
171.3427 + */
171.3428 + static final class StartS extends Start {
171.3429 + StartS(Node node) {
171.3430 + super(node);
171.3431 + }
171.3432 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3433 + if (i > matcher.to - minLength) {
171.3434 + matcher.hitEnd = true;
171.3435 + return false;
171.3436 + }
171.3437 + int guard = matcher.to - minLength;
171.3438 + while (i <= guard) {
171.3439 + //if ((ret = next.match(matcher, i, seq)) || i == guard)
171.3440 + if (next.match(matcher, i, seq)) {
171.3441 + matcher.first = i;
171.3442 + matcher.groups[0] = matcher.first;
171.3443 + matcher.groups[1] = matcher.last;
171.3444 + return true;
171.3445 + }
171.3446 + if (i == guard)
171.3447 + break;
171.3448 + // Optimization to move to the next character. This is
171.3449 + // faster than countChars(seq, i, 1).
171.3450 + if (Character.isHighSurrogate(seq.charAt(i++))) {
171.3451 + if (i < seq.length() &&
171.3452 + Character.isLowSurrogate(seq.charAt(i))) {
171.3453 + i++;
171.3454 + }
171.3455 + }
171.3456 + }
171.3457 + matcher.hitEnd = true;
171.3458 + return false;
171.3459 + }
171.3460 + }
171.3461 +
171.3462 + /**
171.3463 + * Node to anchor at the beginning of input. This object implements the
171.3464 + * match for a \A sequence, and the caret anchor will use this if not in
171.3465 + * multiline mode.
171.3466 + */
171.3467 + static final class Begin extends Node {
171.3468 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3469 + int fromIndex = (matcher.anchoringBounds) ?
171.3470 + matcher.from : 0;
171.3471 + if (i == fromIndex && next.match(matcher, i, seq)) {
171.3472 + matcher.first = i;
171.3473 + matcher.groups[0] = i;
171.3474 + matcher.groups[1] = matcher.last;
171.3475 + return true;
171.3476 + } else {
171.3477 + return false;
171.3478 + }
171.3479 + }
171.3480 + }
171.3481 +
171.3482 + /**
171.3483 + * Node to anchor at the end of input. This is the absolute end, so this
171.3484 + * should not match at the last newline before the end as $ will.
171.3485 + */
171.3486 + static final class End extends Node {
171.3487 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3488 + int endIndex = (matcher.anchoringBounds) ?
171.3489 + matcher.to : matcher.getTextLength();
171.3490 + if (i == endIndex) {
171.3491 + matcher.hitEnd = true;
171.3492 + return next.match(matcher, i, seq);
171.3493 + }
171.3494 + return false;
171.3495 + }
171.3496 + }
171.3497 +
171.3498 + /**
171.3499 + * Node to anchor at the beginning of a line. This is essentially the
171.3500 + * object to match for the multiline ^.
171.3501 + */
171.3502 + static final class Caret extends Node {
171.3503 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3504 + int startIndex = matcher.from;
171.3505 + int endIndex = matcher.to;
171.3506 + if (!matcher.anchoringBounds) {
171.3507 + startIndex = 0;
171.3508 + endIndex = matcher.getTextLength();
171.3509 + }
171.3510 + // Perl does not match ^ at end of input even after newline
171.3511 + if (i == endIndex) {
171.3512 + matcher.hitEnd = true;
171.3513 + return false;
171.3514 + }
171.3515 + if (i > startIndex) {
171.3516 + char ch = seq.charAt(i-1);
171.3517 + if (ch != '\n' && ch != '\r'
171.3518 + && (ch|1) != '\u2029'
171.3519 + && ch != '\u0085' ) {
171.3520 + return false;
171.3521 + }
171.3522 + // Should treat /r/n as one newline
171.3523 + if (ch == '\r' && seq.charAt(i) == '\n')
171.3524 + return false;
171.3525 + }
171.3526 + return next.match(matcher, i, seq);
171.3527 + }
171.3528 + }
171.3529 +
171.3530 + /**
171.3531 + * Node to anchor at the beginning of a line when in unixdot mode.
171.3532 + */
171.3533 + static final class UnixCaret extends Node {
171.3534 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3535 + int startIndex = matcher.from;
171.3536 + int endIndex = matcher.to;
171.3537 + if (!matcher.anchoringBounds) {
171.3538 + startIndex = 0;
171.3539 + endIndex = matcher.getTextLength();
171.3540 + }
171.3541 + // Perl does not match ^ at end of input even after newline
171.3542 + if (i == endIndex) {
171.3543 + matcher.hitEnd = true;
171.3544 + return false;
171.3545 + }
171.3546 + if (i > startIndex) {
171.3547 + char ch = seq.charAt(i-1);
171.3548 + if (ch != '\n') {
171.3549 + return false;
171.3550 + }
171.3551 + }
171.3552 + return next.match(matcher, i, seq);
171.3553 + }
171.3554 + }
171.3555 +
171.3556 + /**
171.3557 + * Node to match the location where the last match ended.
171.3558 + * This is used for the \G construct.
171.3559 + */
171.3560 + static final class LastMatch extends Node {
171.3561 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3562 + if (i != matcher.oldLast)
171.3563 + return false;
171.3564 + return next.match(matcher, i, seq);
171.3565 + }
171.3566 + }
171.3567 +
171.3568 + /**
171.3569 + * Node to anchor at the end of a line or the end of input based on the
171.3570 + * multiline mode.
171.3571 + *
171.3572 + * When not in multiline mode, the $ can only match at the very end
171.3573 + * of the input, unless the input ends in a line terminator in which
171.3574 + * it matches right before the last line terminator.
171.3575 + *
171.3576 + * Note that \r\n is considered an atomic line terminator.
171.3577 + *
171.3578 + * Like ^ the $ operator matches at a position, it does not match the
171.3579 + * line terminators themselves.
171.3580 + */
171.3581 + static final class Dollar extends Node {
171.3582 + boolean multiline;
171.3583 + Dollar(boolean mul) {
171.3584 + multiline = mul;
171.3585 + }
171.3586 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3587 + int endIndex = (matcher.anchoringBounds) ?
171.3588 + matcher.to : matcher.getTextLength();
171.3589 + if (!multiline) {
171.3590 + if (i < endIndex - 2)
171.3591 + return false;
171.3592 + if (i == endIndex - 2) {
171.3593 + char ch = seq.charAt(i);
171.3594 + if (ch != '\r')
171.3595 + return false;
171.3596 + ch = seq.charAt(i + 1);
171.3597 + if (ch != '\n')
171.3598 + return false;
171.3599 + }
171.3600 + }
171.3601 + // Matches before any line terminator; also matches at the
171.3602 + // end of input
171.3603 + // Before line terminator:
171.3604 + // If multiline, we match here no matter what
171.3605 + // If not multiline, fall through so that the end
171.3606 + // is marked as hit; this must be a /r/n or a /n
171.3607 + // at the very end so the end was hit; more input
171.3608 + // could make this not match here
171.3609 + if (i < endIndex) {
171.3610 + char ch = seq.charAt(i);
171.3611 + if (ch == '\n') {
171.3612 + // No match between \r\n
171.3613 + if (i > 0 && seq.charAt(i-1) == '\r')
171.3614 + return false;
171.3615 + if (multiline)
171.3616 + return next.match(matcher, i, seq);
171.3617 + } else if (ch == '\r' || ch == '\u0085' ||
171.3618 + (ch|1) == '\u2029') {
171.3619 + if (multiline)
171.3620 + return next.match(matcher, i, seq);
171.3621 + } else { // No line terminator, no match
171.3622 + return false;
171.3623 + }
171.3624 + }
171.3625 + // Matched at current end so hit end
171.3626 + matcher.hitEnd = true;
171.3627 + // If a $ matches because of end of input, then more input
171.3628 + // could cause it to fail!
171.3629 + matcher.requireEnd = true;
171.3630 + return next.match(matcher, i, seq);
171.3631 + }
171.3632 + boolean study(TreeInfo info) {
171.3633 + next.study(info);
171.3634 + return info.deterministic;
171.3635 + }
171.3636 + }
171.3637 +
171.3638 + /**
171.3639 + * Node to anchor at the end of a line or the end of input based on the
171.3640 + * multiline mode when in unix lines mode.
171.3641 + */
171.3642 + static final class UnixDollar extends Node {
171.3643 + boolean multiline;
171.3644 + UnixDollar(boolean mul) {
171.3645 + multiline = mul;
171.3646 + }
171.3647 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3648 + int endIndex = (matcher.anchoringBounds) ?
171.3649 + matcher.to : matcher.getTextLength();
171.3650 + if (i < endIndex) {
171.3651 + char ch = seq.charAt(i);
171.3652 + if (ch == '\n') {
171.3653 + // If not multiline, then only possible to
171.3654 + // match at very end or one before end
171.3655 + if (multiline == false && i != endIndex - 1)
171.3656 + return false;
171.3657 + // If multiline return next.match without setting
171.3658 + // matcher.hitEnd
171.3659 + if (multiline)
171.3660 + return next.match(matcher, i, seq);
171.3661 + } else {
171.3662 + return false;
171.3663 + }
171.3664 + }
171.3665 + // Matching because at the end or 1 before the end;
171.3666 + // more input could change this so set hitEnd
171.3667 + matcher.hitEnd = true;
171.3668 + // If a $ matches because of end of input, then more input
171.3669 + // could cause it to fail!
171.3670 + matcher.requireEnd = true;
171.3671 + return next.match(matcher, i, seq);
171.3672 + }
171.3673 + boolean study(TreeInfo info) {
171.3674 + next.study(info);
171.3675 + return info.deterministic;
171.3676 + }
171.3677 + }
171.3678 +
171.3679 + /**
171.3680 + * Abstract node class to match one character satisfying some
171.3681 + * boolean property.
171.3682 + */
171.3683 + private static abstract class CharProperty extends Node {
171.3684 + abstract boolean isSatisfiedBy(int ch);
171.3685 + CharProperty complement() {
171.3686 + return new CharProperty() {
171.3687 + boolean isSatisfiedBy(int ch) {
171.3688 + return ! CharProperty.this.isSatisfiedBy(ch);}};
171.3689 + }
171.3690 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3691 + if (i < matcher.to) {
171.3692 + int ch = Character.codePointAt(seq, i);
171.3693 + return isSatisfiedBy(ch)
171.3694 + && next.match(matcher, i+Character.charCount(ch), seq);
171.3695 + } else {
171.3696 + matcher.hitEnd = true;
171.3697 + return false;
171.3698 + }
171.3699 + }
171.3700 + boolean study(TreeInfo info) {
171.3701 + info.minLength++;
171.3702 + info.maxLength++;
171.3703 + return next.study(info);
171.3704 + }
171.3705 + }
171.3706 +
171.3707 + /**
171.3708 + * Optimized version of CharProperty that works only for
171.3709 + * properties never satisfied by Supplementary characters.
171.3710 + */
171.3711 + private static abstract class BmpCharProperty extends CharProperty {
171.3712 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3713 + if (i < matcher.to) {
171.3714 + return isSatisfiedBy(seq.charAt(i))
171.3715 + && next.match(matcher, i+1, seq);
171.3716 + } else {
171.3717 + matcher.hitEnd = true;
171.3718 + return false;
171.3719 + }
171.3720 + }
171.3721 + }
171.3722 +
171.3723 + /**
171.3724 + * Node class that matches a Supplementary Unicode character
171.3725 + */
171.3726 + static final class SingleS extends CharProperty {
171.3727 + final int c;
171.3728 + SingleS(int c) { this.c = c; }
171.3729 + boolean isSatisfiedBy(int ch) {
171.3730 + return ch == c;
171.3731 + }
171.3732 + }
171.3733 +
171.3734 + /**
171.3735 + * Optimization -- matches a given BMP character
171.3736 + */
171.3737 + static final class Single extends BmpCharProperty {
171.3738 + final int c;
171.3739 + Single(int c) { this.c = c; }
171.3740 + boolean isSatisfiedBy(int ch) {
171.3741 + return ch == c;
171.3742 + }
171.3743 + }
171.3744 +
171.3745 + /**
171.3746 + * Case insensitive matches a given BMP character
171.3747 + */
171.3748 + static final class SingleI extends BmpCharProperty {
171.3749 + final int lower;
171.3750 + final int upper;
171.3751 + SingleI(int lower, int upper) {
171.3752 + this.lower = lower;
171.3753 + this.upper = upper;
171.3754 + }
171.3755 + boolean isSatisfiedBy(int ch) {
171.3756 + return ch == lower || ch == upper;
171.3757 + }
171.3758 + }
171.3759 +
171.3760 + /**
171.3761 + * Unicode case insensitive matches a given Unicode character
171.3762 + */
171.3763 + static final class SingleU extends CharProperty {
171.3764 + final int lower;
171.3765 + SingleU(int lower) {
171.3766 + this.lower = lower;
171.3767 + }
171.3768 + boolean isSatisfiedBy(int ch) {
171.3769 + return lower == ch ||
171.3770 + lower == Character.toLowerCase(Character.toUpperCase(ch));
171.3771 + }
171.3772 + }
171.3773 +
171.3774 +
171.3775 + /**
171.3776 + * Node class that matches a Unicode block.
171.3777 + *
171.3778 + static final class Block extends CharProperty {
171.3779 + final Character.UnicodeBlock block;
171.3780 + Block(Character.UnicodeBlock block) {
171.3781 + this.block = block;
171.3782 + }
171.3783 + boolean isSatisfiedBy(int ch) {
171.3784 + return block == Character.UnicodeBlock.of(ch);
171.3785 + }
171.3786 + }
171.3787 +
171.3788 + /**
171.3789 + * Node class that matches a Unicode script
171.3790 + *
171.3791 + static final class Script extends CharProperty {
171.3792 + final Character.UnicodeScript script;
171.3793 + Script(Character.UnicodeScript script) {
171.3794 + this.script = script;
171.3795 + }
171.3796 + boolean isSatisfiedBy(int ch) {
171.3797 + return script == Character.UnicodeScript.of(ch);
171.3798 + }
171.3799 + }
171.3800 +
171.3801 + /**
171.3802 + * Node class that matches a Unicode category.
171.3803 + */
171.3804 + static final class Category extends CharProperty {
171.3805 + final int typeMask;
171.3806 + Category(int typeMask) { this.typeMask = typeMask; }
171.3807 + boolean isSatisfiedBy(int ch) {
171.3808 + return (typeMask & (1 << Character.getType(ch))) != 0;
171.3809 + }
171.3810 + }
171.3811 +
171.3812 + /**
171.3813 + * Node class that matches a Unicode "type"
171.3814 + */
171.3815 + static final class Utype extends CharProperty {
171.3816 + final UnicodeProp uprop;
171.3817 + Utype(UnicodeProp uprop) { this.uprop = uprop; }
171.3818 + boolean isSatisfiedBy(int ch) {
171.3819 + return uprop.is(ch);
171.3820 + }
171.3821 + }
171.3822 +
171.3823 +
171.3824 + /**
171.3825 + * Node class that matches a POSIX type.
171.3826 + */
171.3827 + static final class Ctype extends BmpCharProperty {
171.3828 + final int ctype;
171.3829 + Ctype(int ctype) { this.ctype = ctype; }
171.3830 + boolean isSatisfiedBy(int ch) {
171.3831 + return ch < 128 && ASCII.isType(ch, ctype);
171.3832 + }
171.3833 + }
171.3834 +
171.3835 + /**
171.3836 + * Base class for all Slice nodes
171.3837 + */
171.3838 + static class SliceNode extends Node {
171.3839 + int[] buffer;
171.3840 + SliceNode(int[] buf) {
171.3841 + buffer = buf;
171.3842 + }
171.3843 + boolean study(TreeInfo info) {
171.3844 + info.minLength += buffer.length;
171.3845 + info.maxLength += buffer.length;
171.3846 + return next.study(info);
171.3847 + }
171.3848 + }
171.3849 +
171.3850 + /**
171.3851 + * Node class for a case sensitive/BMP-only sequence of literal
171.3852 + * characters.
171.3853 + */
171.3854 + static final class Slice extends SliceNode {
171.3855 + Slice(int[] buf) {
171.3856 + super(buf);
171.3857 + }
171.3858 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3859 + int[] buf = buffer;
171.3860 + int len = buf.length;
171.3861 + for (int j=0; j<len; j++) {
171.3862 + if ((i+j) >= matcher.to) {
171.3863 + matcher.hitEnd = true;
171.3864 + return false;
171.3865 + }
171.3866 + if (buf[j] != seq.charAt(i+j))
171.3867 + return false;
171.3868 + }
171.3869 + return next.match(matcher, i+len, seq);
171.3870 + }
171.3871 + }
171.3872 +
171.3873 + /**
171.3874 + * Node class for a case_insensitive/BMP-only sequence of literal
171.3875 + * characters.
171.3876 + */
171.3877 + static class SliceI extends SliceNode {
171.3878 + SliceI(int[] buf) {
171.3879 + super(buf);
171.3880 + }
171.3881 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3882 + int[] buf = buffer;
171.3883 + int len = buf.length;
171.3884 + for (int j=0; j<len; j++) {
171.3885 + if ((i+j) >= matcher.to) {
171.3886 + matcher.hitEnd = true;
171.3887 + return false;
171.3888 + }
171.3889 + int c = seq.charAt(i+j);
171.3890 + if (buf[j] != c &&
171.3891 + buf[j] != ASCII.toLower(c))
171.3892 + return false;
171.3893 + }
171.3894 + return next.match(matcher, i+len, seq);
171.3895 + }
171.3896 + }
171.3897 +
171.3898 + /**
171.3899 + * Node class for a unicode_case_insensitive/BMP-only sequence of
171.3900 + * literal characters. Uses unicode case folding.
171.3901 + */
171.3902 + static final class SliceU extends SliceNode {
171.3903 + SliceU(int[] buf) {
171.3904 + super(buf);
171.3905 + }
171.3906 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3907 + int[] buf = buffer;
171.3908 + int len = buf.length;
171.3909 + for (int j=0; j<len; j++) {
171.3910 + if ((i+j) >= matcher.to) {
171.3911 + matcher.hitEnd = true;
171.3912 + return false;
171.3913 + }
171.3914 + int c = seq.charAt(i+j);
171.3915 + if (buf[j] != c &&
171.3916 + buf[j] != Character.toLowerCase(Character.toUpperCase(c)))
171.3917 + return false;
171.3918 + }
171.3919 + return next.match(matcher, i+len, seq);
171.3920 + }
171.3921 + }
171.3922 +
171.3923 + /**
171.3924 + * Node class for a case sensitive sequence of literal characters
171.3925 + * including supplementary characters.
171.3926 + */
171.3927 + static final class SliceS extends SliceNode {
171.3928 + SliceS(int[] buf) {
171.3929 + super(buf);
171.3930 + }
171.3931 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3932 + int[] buf = buffer;
171.3933 + int x = i;
171.3934 + for (int j = 0; j < buf.length; j++) {
171.3935 + if (x >= matcher.to) {
171.3936 + matcher.hitEnd = true;
171.3937 + return false;
171.3938 + }
171.3939 + int c = Character.codePointAt(seq, x);
171.3940 + if (buf[j] != c)
171.3941 + return false;
171.3942 + x += Character.charCount(c);
171.3943 + if (x > matcher.to) {
171.3944 + matcher.hitEnd = true;
171.3945 + return false;
171.3946 + }
171.3947 + }
171.3948 + return next.match(matcher, x, seq);
171.3949 + }
171.3950 + }
171.3951 +
171.3952 + /**
171.3953 + * Node class for a case insensitive sequence of literal characters
171.3954 + * including supplementary characters.
171.3955 + */
171.3956 + static class SliceIS extends SliceNode {
171.3957 + SliceIS(int[] buf) {
171.3958 + super(buf);
171.3959 + }
171.3960 + int toLower(int c) {
171.3961 + return ASCII.toLower(c);
171.3962 + }
171.3963 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.3964 + int[] buf = buffer;
171.3965 + int x = i;
171.3966 + for (int j = 0; j < buf.length; j++) {
171.3967 + if (x >= matcher.to) {
171.3968 + matcher.hitEnd = true;
171.3969 + return false;
171.3970 + }
171.3971 + int c = Character.codePointAt(seq, x);
171.3972 + if (buf[j] != c && buf[j] != toLower(c))
171.3973 + return false;
171.3974 + x += Character.charCount(c);
171.3975 + if (x > matcher.to) {
171.3976 + matcher.hitEnd = true;
171.3977 + return false;
171.3978 + }
171.3979 + }
171.3980 + return next.match(matcher, x, seq);
171.3981 + }
171.3982 + }
171.3983 +
171.3984 + /**
171.3985 + * Node class for a case insensitive sequence of literal characters.
171.3986 + * Uses unicode case folding.
171.3987 + */
171.3988 + static final class SliceUS extends SliceIS {
171.3989 + SliceUS(int[] buf) {
171.3990 + super(buf);
171.3991 + }
171.3992 + int toLower(int c) {
171.3993 + return Character.toLowerCase(Character.toUpperCase(c));
171.3994 + }
171.3995 + }
171.3996 +
171.3997 + private static boolean inRange(int lower, int ch, int upper) {
171.3998 + return lower <= ch && ch <= upper;
171.3999 + }
171.4000 +
171.4001 + /**
171.4002 + * Returns node for matching characters within an explicit value range.
171.4003 + */
171.4004 + private static CharProperty rangeFor(final int lower,
171.4005 + final int upper) {
171.4006 + return new CharProperty() {
171.4007 + boolean isSatisfiedBy(int ch) {
171.4008 + return inRange(lower, ch, upper);}};
171.4009 + }
171.4010 +
171.4011 + /**
171.4012 + * Returns node for matching characters within an explicit value
171.4013 + * range in a case insensitive manner.
171.4014 + */
171.4015 + private CharProperty caseInsensitiveRangeFor(final int lower,
171.4016 + final int upper) {
171.4017 + if (has(UNICODE_CASE))
171.4018 + return new CharProperty() {
171.4019 + boolean isSatisfiedBy(int ch) {
171.4020 + if (inRange(lower, ch, upper))
171.4021 + return true;
171.4022 + int up = Character.toUpperCase(ch);
171.4023 + return inRange(lower, up, upper) ||
171.4024 + inRange(lower, Character.toLowerCase(up), upper);}};
171.4025 + return new CharProperty() {
171.4026 + boolean isSatisfiedBy(int ch) {
171.4027 + return inRange(lower, ch, upper) ||
171.4028 + ASCII.isAscii(ch) &&
171.4029 + (inRange(lower, ASCII.toUpper(ch), upper) ||
171.4030 + inRange(lower, ASCII.toLower(ch), upper));
171.4031 + }};
171.4032 + }
171.4033 +
171.4034 + /**
171.4035 + * Implements the Unicode category ALL and the dot metacharacter when
171.4036 + * in dotall mode.
171.4037 + */
171.4038 + static final class All extends CharProperty {
171.4039 + boolean isSatisfiedBy(int ch) {
171.4040 + return true;
171.4041 + }
171.4042 + }
171.4043 +
171.4044 + /**
171.4045 + * Node class for the dot metacharacter when dotall is not enabled.
171.4046 + */
171.4047 + static final class Dot extends CharProperty {
171.4048 + boolean isSatisfiedBy(int ch) {
171.4049 + return (ch != '\n' && ch != '\r'
171.4050 + && (ch|1) != '\u2029'
171.4051 + && ch != '\u0085');
171.4052 + }
171.4053 + }
171.4054 +
171.4055 + /**
171.4056 + * Node class for the dot metacharacter when dotall is not enabled
171.4057 + * but UNIX_LINES is enabled.
171.4058 + */
171.4059 + static final class UnixDot extends CharProperty {
171.4060 + boolean isSatisfiedBy(int ch) {
171.4061 + return ch != '\n';
171.4062 + }
171.4063 + }
171.4064 +
171.4065 + /**
171.4066 + * The 0 or 1 quantifier. This one class implements all three types.
171.4067 + */
171.4068 + static final class Ques extends Node {
171.4069 + Node atom;
171.4070 + int type;
171.4071 + Ques(Node node, int type) {
171.4072 + this.atom = node;
171.4073 + this.type = type;
171.4074 + }
171.4075 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4076 + switch (type) {
171.4077 + case GREEDY:
171.4078 + return (atom.match(matcher, i, seq) && next.match(matcher, matcher.last, seq))
171.4079 + || next.match(matcher, i, seq);
171.4080 + case LAZY:
171.4081 + return next.match(matcher, i, seq)
171.4082 + || (atom.match(matcher, i, seq) && next.match(matcher, matcher.last, seq));
171.4083 + case POSSESSIVE:
171.4084 + if (atom.match(matcher, i, seq)) i = matcher.last;
171.4085 + return next.match(matcher, i, seq);
171.4086 + default:
171.4087 + return atom.match(matcher, i, seq) && next.match(matcher, matcher.last, seq);
171.4088 + }
171.4089 + }
171.4090 + boolean study(TreeInfo info) {
171.4091 + if (type != INDEPENDENT) {
171.4092 + int minL = info.minLength;
171.4093 + atom.study(info);
171.4094 + info.minLength = minL;
171.4095 + info.deterministic = false;
171.4096 + return next.study(info);
171.4097 + } else {
171.4098 + atom.study(info);
171.4099 + return next.study(info);
171.4100 + }
171.4101 + }
171.4102 + }
171.4103 +
171.4104 + /**
171.4105 + * Handles the curly-brace style repetition with a specified minimum and
171.4106 + * maximum occurrences. The * quantifier is handled as a special case.
171.4107 + * This class handles the three types.
171.4108 + */
171.4109 + static final class Curly extends Node {
171.4110 + Node atom;
171.4111 + int type;
171.4112 + int cmin;
171.4113 + int cmax;
171.4114 +
171.4115 + Curly(Node node, int cmin, int cmax, int type) {
171.4116 + this.atom = node;
171.4117 + this.type = type;
171.4118 + this.cmin = cmin;
171.4119 + this.cmax = cmax;
171.4120 + }
171.4121 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4122 + int j;
171.4123 + for (j = 0; j < cmin; j++) {
171.4124 + if (atom.match(matcher, i, seq)) {
171.4125 + i = matcher.last;
171.4126 + continue;
171.4127 + }
171.4128 + return false;
171.4129 + }
171.4130 + if (type == GREEDY)
171.4131 + return match0(matcher, i, j, seq);
171.4132 + else if (type == LAZY)
171.4133 + return match1(matcher, i, j, seq);
171.4134 + else
171.4135 + return match2(matcher, i, j, seq);
171.4136 + }
171.4137 + // Greedy match.
171.4138 + // i is the index to start matching at
171.4139 + // j is the number of atoms that have matched
171.4140 + boolean match0(Matcher matcher, int i, int j, CharSequence seq) {
171.4141 + if (j >= cmax) {
171.4142 + // We have matched the maximum... continue with the rest of
171.4143 + // the regular expression
171.4144 + return next.match(matcher, i, seq);
171.4145 + }
171.4146 + int backLimit = j;
171.4147 + while (atom.match(matcher, i, seq)) {
171.4148 + // k is the length of this match
171.4149 + int k = matcher.last - i;
171.4150 + if (k == 0) // Zero length match
171.4151 + break;
171.4152 + // Move up index and number matched
171.4153 + i = matcher.last;
171.4154 + j++;
171.4155 + // We are greedy so match as many as we can
171.4156 + while (j < cmax) {
171.4157 + if (!atom.match(matcher, i, seq))
171.4158 + break;
171.4159 + if (i + k != matcher.last) {
171.4160 + if (match0(matcher, matcher.last, j+1, seq))
171.4161 + return true;
171.4162 + break;
171.4163 + }
171.4164 + i += k;
171.4165 + j++;
171.4166 + }
171.4167 + // Handle backing off if match fails
171.4168 + while (j >= backLimit) {
171.4169 + if (next.match(matcher, i, seq))
171.4170 + return true;
171.4171 + i -= k;
171.4172 + j--;
171.4173 + }
171.4174 + return false;
171.4175 + }
171.4176 + return next.match(matcher, i, seq);
171.4177 + }
171.4178 + // Reluctant match. At this point, the minimum has been satisfied.
171.4179 + // i is the index to start matching at
171.4180 + // j is the number of atoms that have matched
171.4181 + boolean match1(Matcher matcher, int i, int j, CharSequence seq) {
171.4182 + for (;;) {
171.4183 + // Try finishing match without consuming any more
171.4184 + if (next.match(matcher, i, seq))
171.4185 + return true;
171.4186 + // At the maximum, no match found
171.4187 + if (j >= cmax)
171.4188 + return false;
171.4189 + // Okay, must try one more atom
171.4190 + if (!atom.match(matcher, i, seq))
171.4191 + return false;
171.4192 + // If we haven't moved forward then must break out
171.4193 + if (i == matcher.last)
171.4194 + return false;
171.4195 + // Move up index and number matched
171.4196 + i = matcher.last;
171.4197 + j++;
171.4198 + }
171.4199 + }
171.4200 + boolean match2(Matcher matcher, int i, int j, CharSequence seq) {
171.4201 + for (; j < cmax; j++) {
171.4202 + if (!atom.match(matcher, i, seq))
171.4203 + break;
171.4204 + if (i == matcher.last)
171.4205 + break;
171.4206 + i = matcher.last;
171.4207 + }
171.4208 + return next.match(matcher, i, seq);
171.4209 + }
171.4210 + boolean study(TreeInfo info) {
171.4211 + // Save original info
171.4212 + int minL = info.minLength;
171.4213 + int maxL = info.maxLength;
171.4214 + boolean maxV = info.maxValid;
171.4215 + boolean detm = info.deterministic;
171.4216 + info.reset();
171.4217 +
171.4218 + atom.study(info);
171.4219 +
171.4220 + int temp = info.minLength * cmin + minL;
171.4221 + if (temp < minL) {
171.4222 + temp = 0xFFFFFFF; // arbitrary large number
171.4223 + }
171.4224 + info.minLength = temp;
171.4225 +
171.4226 + if (maxV & info.maxValid) {
171.4227 + temp = info.maxLength * cmax + maxL;
171.4228 + info.maxLength = temp;
171.4229 + if (temp < maxL) {
171.4230 + info.maxValid = false;
171.4231 + }
171.4232 + } else {
171.4233 + info.maxValid = false;
171.4234 + }
171.4235 +
171.4236 + if (info.deterministic && cmin == cmax)
171.4237 + info.deterministic = detm;
171.4238 + else
171.4239 + info.deterministic = false;
171.4240 +
171.4241 + return next.study(info);
171.4242 + }
171.4243 + }
171.4244 +
171.4245 + /**
171.4246 + * Handles the curly-brace style repetition with a specified minimum and
171.4247 + * maximum occurrences in deterministic cases. This is an iterative
171.4248 + * optimization over the Prolog and Loop system which would handle this
171.4249 + * in a recursive way. The * quantifier is handled as a special case.
171.4250 + * If capture is true then this class saves group settings and ensures
171.4251 + * that groups are unset when backing off of a group match.
171.4252 + */
171.4253 + static final class GroupCurly extends Node {
171.4254 + Node atom;
171.4255 + int type;
171.4256 + int cmin;
171.4257 + int cmax;
171.4258 + int localIndex;
171.4259 + int groupIndex;
171.4260 + boolean capture;
171.4261 +
171.4262 + GroupCurly(Node node, int cmin, int cmax, int type, int local,
171.4263 + int group, boolean capture) {
171.4264 + this.atom = node;
171.4265 + this.type = type;
171.4266 + this.cmin = cmin;
171.4267 + this.cmax = cmax;
171.4268 + this.localIndex = local;
171.4269 + this.groupIndex = group;
171.4270 + this.capture = capture;
171.4271 + }
171.4272 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4273 + int[] groups = matcher.groups;
171.4274 + int[] locals = matcher.locals;
171.4275 + int save0 = locals[localIndex];
171.4276 + int save1 = 0;
171.4277 + int save2 = 0;
171.4278 +
171.4279 + if (capture) {
171.4280 + save1 = groups[groupIndex];
171.4281 + save2 = groups[groupIndex+1];
171.4282 + }
171.4283 +
171.4284 + // Notify GroupTail there is no need to setup group info
171.4285 + // because it will be set here
171.4286 + locals[localIndex] = -1;
171.4287 +
171.4288 + boolean ret = true;
171.4289 + for (int j = 0; j < cmin; j++) {
171.4290 + if (atom.match(matcher, i, seq)) {
171.4291 + if (capture) {
171.4292 + groups[groupIndex] = i;
171.4293 + groups[groupIndex+1] = matcher.last;
171.4294 + }
171.4295 + i = matcher.last;
171.4296 + } else {
171.4297 + ret = false;
171.4298 + break;
171.4299 + }
171.4300 + }
171.4301 + if (ret) {
171.4302 + if (type == GREEDY) {
171.4303 + ret = match0(matcher, i, cmin, seq);
171.4304 + } else if (type == LAZY) {
171.4305 + ret = match1(matcher, i, cmin, seq);
171.4306 + } else {
171.4307 + ret = match2(matcher, i, cmin, seq);
171.4308 + }
171.4309 + }
171.4310 + if (!ret) {
171.4311 + locals[localIndex] = save0;
171.4312 + if (capture) {
171.4313 + groups[groupIndex] = save1;
171.4314 + groups[groupIndex+1] = save2;
171.4315 + }
171.4316 + }
171.4317 + return ret;
171.4318 + }
171.4319 + // Aggressive group match
171.4320 + boolean match0(Matcher matcher, int i, int j, CharSequence seq) {
171.4321 + int[] groups = matcher.groups;
171.4322 + int save0 = 0;
171.4323 + int save1 = 0;
171.4324 + if (capture) {
171.4325 + save0 = groups[groupIndex];
171.4326 + save1 = groups[groupIndex+1];
171.4327 + }
171.4328 + for (;;) {
171.4329 + if (j >= cmax)
171.4330 + break;
171.4331 + if (!atom.match(matcher, i, seq))
171.4332 + break;
171.4333 + int k = matcher.last - i;
171.4334 + if (k <= 0) {
171.4335 + if (capture) {
171.4336 + groups[groupIndex] = i;
171.4337 + groups[groupIndex+1] = i + k;
171.4338 + }
171.4339 + i = i + k;
171.4340 + break;
171.4341 + }
171.4342 + for (;;) {
171.4343 + if (capture) {
171.4344 + groups[groupIndex] = i;
171.4345 + groups[groupIndex+1] = i + k;
171.4346 + }
171.4347 + i = i + k;
171.4348 + if (++j >= cmax)
171.4349 + break;
171.4350 + if (!atom.match(matcher, i, seq))
171.4351 + break;
171.4352 + if (i + k != matcher.last) {
171.4353 + if (match0(matcher, i, j, seq))
171.4354 + return true;
171.4355 + break;
171.4356 + }
171.4357 + }
171.4358 + while (j > cmin) {
171.4359 + if (next.match(matcher, i, seq)) {
171.4360 + if (capture) {
171.4361 + groups[groupIndex+1] = i;
171.4362 + groups[groupIndex] = i - k;
171.4363 + }
171.4364 + i = i - k;
171.4365 + return true;
171.4366 + }
171.4367 + // backing off
171.4368 + if (capture) {
171.4369 + groups[groupIndex+1] = i;
171.4370 + groups[groupIndex] = i - k;
171.4371 + }
171.4372 + i = i - k;
171.4373 + j--;
171.4374 + }
171.4375 + break;
171.4376 + }
171.4377 + if (capture) {
171.4378 + groups[groupIndex] = save0;
171.4379 + groups[groupIndex+1] = save1;
171.4380 + }
171.4381 + return next.match(matcher, i, seq);
171.4382 + }
171.4383 + // Reluctant matching
171.4384 + boolean match1(Matcher matcher, int i, int j, CharSequence seq) {
171.4385 + for (;;) {
171.4386 + if (next.match(matcher, i, seq))
171.4387 + return true;
171.4388 + if (j >= cmax)
171.4389 + return false;
171.4390 + if (!atom.match(matcher, i, seq))
171.4391 + return false;
171.4392 + if (i == matcher.last)
171.4393 + return false;
171.4394 + if (capture) {
171.4395 + matcher.groups[groupIndex] = i;
171.4396 + matcher.groups[groupIndex+1] = matcher.last;
171.4397 + }
171.4398 + i = matcher.last;
171.4399 + j++;
171.4400 + }
171.4401 + }
171.4402 + // Possessive matching
171.4403 + boolean match2(Matcher matcher, int i, int j, CharSequence seq) {
171.4404 + for (; j < cmax; j++) {
171.4405 + if (!atom.match(matcher, i, seq)) {
171.4406 + break;
171.4407 + }
171.4408 + if (capture) {
171.4409 + matcher.groups[groupIndex] = i;
171.4410 + matcher.groups[groupIndex+1] = matcher.last;
171.4411 + }
171.4412 + if (i == matcher.last) {
171.4413 + break;
171.4414 + }
171.4415 + i = matcher.last;
171.4416 + }
171.4417 + return next.match(matcher, i, seq);
171.4418 + }
171.4419 + boolean study(TreeInfo info) {
171.4420 + // Save original info
171.4421 + int minL = info.minLength;
171.4422 + int maxL = info.maxLength;
171.4423 + boolean maxV = info.maxValid;
171.4424 + boolean detm = info.deterministic;
171.4425 + info.reset();
171.4426 +
171.4427 + atom.study(info);
171.4428 +
171.4429 + int temp = info.minLength * cmin + minL;
171.4430 + if (temp < minL) {
171.4431 + temp = 0xFFFFFFF; // Arbitrary large number
171.4432 + }
171.4433 + info.minLength = temp;
171.4434 +
171.4435 + if (maxV & info.maxValid) {
171.4436 + temp = info.maxLength * cmax + maxL;
171.4437 + info.maxLength = temp;
171.4438 + if (temp < maxL) {
171.4439 + info.maxValid = false;
171.4440 + }
171.4441 + } else {
171.4442 + info.maxValid = false;
171.4443 + }
171.4444 +
171.4445 + if (info.deterministic && cmin == cmax) {
171.4446 + info.deterministic = detm;
171.4447 + } else {
171.4448 + info.deterministic = false;
171.4449 + }
171.4450 +
171.4451 + return next.study(info);
171.4452 + }
171.4453 + }
171.4454 +
171.4455 + /**
171.4456 + * A Guard node at the end of each atom node in a Branch. It
171.4457 + * serves the purpose of chaining the "match" operation to
171.4458 + * "next" but not the "study", so we can collect the TreeInfo
171.4459 + * of each atom node without including the TreeInfo of the
171.4460 + * "next".
171.4461 + */
171.4462 + static final class BranchConn extends Node {
171.4463 + BranchConn() {};
171.4464 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4465 + return next.match(matcher, i, seq);
171.4466 + }
171.4467 + boolean study(TreeInfo info) {
171.4468 + return info.deterministic;
171.4469 + }
171.4470 + }
171.4471 +
171.4472 + /**
171.4473 + * Handles the branching of alternations. Note this is also used for
171.4474 + * the ? quantifier to branch between the case where it matches once
171.4475 + * and where it does not occur.
171.4476 + */
171.4477 + static final class Branch extends Node {
171.4478 + Node[] atoms = new Node[2];
171.4479 + int size = 2;
171.4480 + Node conn;
171.4481 + Branch(Node first, Node second, Node branchConn) {
171.4482 + conn = branchConn;
171.4483 + atoms[0] = first;
171.4484 + atoms[1] = second;
171.4485 + }
171.4486 +
171.4487 + void add(Node node) {
171.4488 + if (size >= atoms.length) {
171.4489 + Node[] tmp = new Node[atoms.length*2];
171.4490 + System.arraycopy(atoms, 0, tmp, 0, atoms.length);
171.4491 + atoms = tmp;
171.4492 + }
171.4493 + atoms[size++] = node;
171.4494 + }
171.4495 +
171.4496 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4497 + for (int n = 0; n < size; n++) {
171.4498 + if (atoms[n] == null) {
171.4499 + if (conn.next.match(matcher, i, seq))
171.4500 + return true;
171.4501 + } else if (atoms[n].match(matcher, i, seq)) {
171.4502 + return true;
171.4503 + }
171.4504 + }
171.4505 + return false;
171.4506 + }
171.4507 +
171.4508 + boolean study(TreeInfo info) {
171.4509 + int minL = info.minLength;
171.4510 + int maxL = info.maxLength;
171.4511 + boolean maxV = info.maxValid;
171.4512 +
171.4513 + int minL2 = Integer.MAX_VALUE; //arbitrary large enough num
171.4514 + int maxL2 = -1;
171.4515 + for (int n = 0; n < size; n++) {
171.4516 + info.reset();
171.4517 + if (atoms[n] != null)
171.4518 + atoms[n].study(info);
171.4519 + minL2 = Math.min(minL2, info.minLength);
171.4520 + maxL2 = Math.max(maxL2, info.maxLength);
171.4521 + maxV = (maxV & info.maxValid);
171.4522 + }
171.4523 +
171.4524 + minL += minL2;
171.4525 + maxL += maxL2;
171.4526 +
171.4527 + info.reset();
171.4528 + conn.next.study(info);
171.4529 +
171.4530 + info.minLength += minL;
171.4531 + info.maxLength += maxL;
171.4532 + info.maxValid &= maxV;
171.4533 + info.deterministic = false;
171.4534 + return false;
171.4535 + }
171.4536 + }
171.4537 +
171.4538 + /**
171.4539 + * The GroupHead saves the location where the group begins in the locals
171.4540 + * and restores them when the match is done.
171.4541 + *
171.4542 + * The matchRef is used when a reference to this group is accessed later
171.4543 + * in the expression. The locals will have a negative value in them to
171.4544 + * indicate that we do not want to unset the group if the reference
171.4545 + * doesn't match.
171.4546 + */
171.4547 + static final class GroupHead extends Node {
171.4548 + int localIndex;
171.4549 + GroupHead(int localCount) {
171.4550 + localIndex = localCount;
171.4551 + }
171.4552 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4553 + int save = matcher.locals[localIndex];
171.4554 + matcher.locals[localIndex] = i;
171.4555 + boolean ret = next.match(matcher, i, seq);
171.4556 + matcher.locals[localIndex] = save;
171.4557 + return ret;
171.4558 + }
171.4559 + boolean matchRef(Matcher matcher, int i, CharSequence seq) {
171.4560 + int save = matcher.locals[localIndex];
171.4561 + matcher.locals[localIndex] = ~i; // HACK
171.4562 + boolean ret = next.match(matcher, i, seq);
171.4563 + matcher.locals[localIndex] = save;
171.4564 + return ret;
171.4565 + }
171.4566 + }
171.4567 +
171.4568 + /**
171.4569 + * Recursive reference to a group in the regular expression. It calls
171.4570 + * matchRef because if the reference fails to match we would not unset
171.4571 + * the group.
171.4572 + */
171.4573 + static final class GroupRef extends Node {
171.4574 + GroupHead head;
171.4575 + GroupRef(GroupHead head) {
171.4576 + this.head = head;
171.4577 + }
171.4578 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4579 + return head.matchRef(matcher, i, seq)
171.4580 + && next.match(matcher, matcher.last, seq);
171.4581 + }
171.4582 + boolean study(TreeInfo info) {
171.4583 + info.maxValid = false;
171.4584 + info.deterministic = false;
171.4585 + return next.study(info);
171.4586 + }
171.4587 + }
171.4588 +
171.4589 + /**
171.4590 + * The GroupTail handles the setting of group beginning and ending
171.4591 + * locations when groups are successfully matched. It must also be able to
171.4592 + * unset groups that have to be backed off of.
171.4593 + *
171.4594 + * The GroupTail node is also used when a previous group is referenced,
171.4595 + * and in that case no group information needs to be set.
171.4596 + */
171.4597 + static final class GroupTail extends Node {
171.4598 + int localIndex;
171.4599 + int groupIndex;
171.4600 + GroupTail(int localCount, int groupCount) {
171.4601 + localIndex = localCount;
171.4602 + groupIndex = groupCount + groupCount;
171.4603 + }
171.4604 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4605 + int tmp = matcher.locals[localIndex];
171.4606 + if (tmp >= 0) { // This is the normal group case.
171.4607 + // Save the group so we can unset it if it
171.4608 + // backs off of a match.
171.4609 + int groupStart = matcher.groups[groupIndex];
171.4610 + int groupEnd = matcher.groups[groupIndex+1];
171.4611 +
171.4612 + matcher.groups[groupIndex] = tmp;
171.4613 + matcher.groups[groupIndex+1] = i;
171.4614 + if (next.match(matcher, i, seq)) {
171.4615 + return true;
171.4616 + }
171.4617 + matcher.groups[groupIndex] = groupStart;
171.4618 + matcher.groups[groupIndex+1] = groupEnd;
171.4619 + return false;
171.4620 + } else {
171.4621 + // This is a group reference case. We don't need to save any
171.4622 + // group info because it isn't really a group.
171.4623 + matcher.last = i;
171.4624 + return true;
171.4625 + }
171.4626 + }
171.4627 + }
171.4628 +
171.4629 + /**
171.4630 + * This sets up a loop to handle a recursive quantifier structure.
171.4631 + */
171.4632 + static final class Prolog extends Node {
171.4633 + Loop loop;
171.4634 + Prolog(Loop loop) {
171.4635 + this.loop = loop;
171.4636 + }
171.4637 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4638 + return loop.matchInit(matcher, i, seq);
171.4639 + }
171.4640 + boolean study(TreeInfo info) {
171.4641 + return loop.study(info);
171.4642 + }
171.4643 + }
171.4644 +
171.4645 + /**
171.4646 + * Handles the repetition count for a greedy Curly. The matchInit
171.4647 + * is called from the Prolog to save the index of where the group
171.4648 + * beginning is stored. A zero length group check occurs in the
171.4649 + * normal match but is skipped in the matchInit.
171.4650 + */
171.4651 + static class Loop extends Node {
171.4652 + Node body;
171.4653 + int countIndex; // local count index in matcher locals
171.4654 + int beginIndex; // group beginning index
171.4655 + int cmin, cmax;
171.4656 + Loop(int countIndex, int beginIndex) {
171.4657 + this.countIndex = countIndex;
171.4658 + this.beginIndex = beginIndex;
171.4659 + }
171.4660 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4661 + // Avoid infinite loop in zero-length case.
171.4662 + if (i > matcher.locals[beginIndex]) {
171.4663 + int count = matcher.locals[countIndex];
171.4664 +
171.4665 + // This block is for before we reach the minimum
171.4666 + // iterations required for the loop to match
171.4667 + if (count < cmin) {
171.4668 + matcher.locals[countIndex] = count + 1;
171.4669 + boolean b = body.match(matcher, i, seq);
171.4670 + // If match failed we must backtrack, so
171.4671 + // the loop count should NOT be incremented
171.4672 + if (!b)
171.4673 + matcher.locals[countIndex] = count;
171.4674 + // Return success or failure since we are under
171.4675 + // minimum
171.4676 + return b;
171.4677 + }
171.4678 + // This block is for after we have the minimum
171.4679 + // iterations required for the loop to match
171.4680 + if (count < cmax) {
171.4681 + matcher.locals[countIndex] = count + 1;
171.4682 + boolean b = body.match(matcher, i, seq);
171.4683 + // If match failed we must backtrack, so
171.4684 + // the loop count should NOT be incremented
171.4685 + if (!b)
171.4686 + matcher.locals[countIndex] = count;
171.4687 + else
171.4688 + return true;
171.4689 + }
171.4690 + }
171.4691 + return next.match(matcher, i, seq);
171.4692 + }
171.4693 + boolean matchInit(Matcher matcher, int i, CharSequence seq) {
171.4694 + int save = matcher.locals[countIndex];
171.4695 + boolean ret = false;
171.4696 + if (0 < cmin) {
171.4697 + matcher.locals[countIndex] = 1;
171.4698 + ret = body.match(matcher, i, seq);
171.4699 + } else if (0 < cmax) {
171.4700 + matcher.locals[countIndex] = 1;
171.4701 + ret = body.match(matcher, i, seq);
171.4702 + if (ret == false)
171.4703 + ret = next.match(matcher, i, seq);
171.4704 + } else {
171.4705 + ret = next.match(matcher, i, seq);
171.4706 + }
171.4707 + matcher.locals[countIndex] = save;
171.4708 + return ret;
171.4709 + }
171.4710 + boolean study(TreeInfo info) {
171.4711 + info.maxValid = false;
171.4712 + info.deterministic = false;
171.4713 + return false;
171.4714 + }
171.4715 + }
171.4716 +
171.4717 + /**
171.4718 + * Handles the repetition count for a reluctant Curly. The matchInit
171.4719 + * is called from the Prolog to save the index of where the group
171.4720 + * beginning is stored. A zero length group check occurs in the
171.4721 + * normal match but is skipped in the matchInit.
171.4722 + */
171.4723 + static final class LazyLoop extends Loop {
171.4724 + LazyLoop(int countIndex, int beginIndex) {
171.4725 + super(countIndex, beginIndex);
171.4726 + }
171.4727 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4728 + // Check for zero length group
171.4729 + if (i > matcher.locals[beginIndex]) {
171.4730 + int count = matcher.locals[countIndex];
171.4731 + if (count < cmin) {
171.4732 + matcher.locals[countIndex] = count + 1;
171.4733 + boolean result = body.match(matcher, i, seq);
171.4734 + // If match failed we must backtrack, so
171.4735 + // the loop count should NOT be incremented
171.4736 + if (!result)
171.4737 + matcher.locals[countIndex] = count;
171.4738 + return result;
171.4739 + }
171.4740 + if (next.match(matcher, i, seq))
171.4741 + return true;
171.4742 + if (count < cmax) {
171.4743 + matcher.locals[countIndex] = count + 1;
171.4744 + boolean result = body.match(matcher, i, seq);
171.4745 + // If match failed we must backtrack, so
171.4746 + // the loop count should NOT be incremented
171.4747 + if (!result)
171.4748 + matcher.locals[countIndex] = count;
171.4749 + return result;
171.4750 + }
171.4751 + return false;
171.4752 + }
171.4753 + return next.match(matcher, i, seq);
171.4754 + }
171.4755 + boolean matchInit(Matcher matcher, int i, CharSequence seq) {
171.4756 + int save = matcher.locals[countIndex];
171.4757 + boolean ret = false;
171.4758 + if (0 < cmin) {
171.4759 + matcher.locals[countIndex] = 1;
171.4760 + ret = body.match(matcher, i, seq);
171.4761 + } else if (next.match(matcher, i, seq)) {
171.4762 + ret = true;
171.4763 + } else if (0 < cmax) {
171.4764 + matcher.locals[countIndex] = 1;
171.4765 + ret = body.match(matcher, i, seq);
171.4766 + }
171.4767 + matcher.locals[countIndex] = save;
171.4768 + return ret;
171.4769 + }
171.4770 + boolean study(TreeInfo info) {
171.4771 + info.maxValid = false;
171.4772 + info.deterministic = false;
171.4773 + return false;
171.4774 + }
171.4775 + }
171.4776 +
171.4777 + /**
171.4778 + * Refers to a group in the regular expression. Attempts to match
171.4779 + * whatever the group referred to last matched.
171.4780 + */
171.4781 + static class BackRef extends Node {
171.4782 + int groupIndex;
171.4783 + BackRef(int groupCount) {
171.4784 + super();
171.4785 + groupIndex = groupCount + groupCount;
171.4786 + }
171.4787 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4788 + int j = matcher.groups[groupIndex];
171.4789 + int k = matcher.groups[groupIndex+1];
171.4790 +
171.4791 + int groupSize = k - j;
171.4792 +
171.4793 + // If the referenced group didn't match, neither can this
171.4794 + if (j < 0)
171.4795 + return false;
171.4796 +
171.4797 + // If there isn't enough input left no match
171.4798 + if (i + groupSize > matcher.to) {
171.4799 + matcher.hitEnd = true;
171.4800 + return false;
171.4801 + }
171.4802 +
171.4803 + // Check each new char to make sure it matches what the group
171.4804 + // referenced matched last time around
171.4805 + for (int index=0; index<groupSize; index++)
171.4806 + if (seq.charAt(i+index) != seq.charAt(j+index))
171.4807 + return false;
171.4808 +
171.4809 + return next.match(matcher, i+groupSize, seq);
171.4810 + }
171.4811 + boolean study(TreeInfo info) {
171.4812 + info.maxValid = false;
171.4813 + return next.study(info);
171.4814 + }
171.4815 + }
171.4816 +
171.4817 + static class CIBackRef extends Node {
171.4818 + int groupIndex;
171.4819 + boolean doUnicodeCase;
171.4820 + CIBackRef(int groupCount, boolean doUnicodeCase) {
171.4821 + super();
171.4822 + groupIndex = groupCount + groupCount;
171.4823 + this.doUnicodeCase = doUnicodeCase;
171.4824 + }
171.4825 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4826 + int j = matcher.groups[groupIndex];
171.4827 + int k = matcher.groups[groupIndex+1];
171.4828 +
171.4829 + int groupSize = k - j;
171.4830 +
171.4831 + // If the referenced group didn't match, neither can this
171.4832 + if (j < 0)
171.4833 + return false;
171.4834 +
171.4835 + // If there isn't enough input left no match
171.4836 + if (i + groupSize > matcher.to) {
171.4837 + matcher.hitEnd = true;
171.4838 + return false;
171.4839 + }
171.4840 +
171.4841 + // Check each new char to make sure it matches what the group
171.4842 + // referenced matched last time around
171.4843 + int x = i;
171.4844 + for (int index=0; index<groupSize; index++) {
171.4845 + int c1 = Character.codePointAt(seq, x);
171.4846 + int c2 = Character.codePointAt(seq, j);
171.4847 + if (c1 != c2) {
171.4848 + if (doUnicodeCase) {
171.4849 + int cc1 = Character.toUpperCase(c1);
171.4850 + int cc2 = Character.toUpperCase(c2);
171.4851 + if (cc1 != cc2 &&
171.4852 + Character.toLowerCase(cc1) !=
171.4853 + Character.toLowerCase(cc2))
171.4854 + return false;
171.4855 + } else {
171.4856 + if (ASCII.toLower(c1) != ASCII.toLower(c2))
171.4857 + return false;
171.4858 + }
171.4859 + }
171.4860 + x += Character.charCount(c1);
171.4861 + j += Character.charCount(c2);
171.4862 + }
171.4863 +
171.4864 + return next.match(matcher, i+groupSize, seq);
171.4865 + }
171.4866 + boolean study(TreeInfo info) {
171.4867 + info.maxValid = false;
171.4868 + return next.study(info);
171.4869 + }
171.4870 + }
171.4871 +
171.4872 + /**
171.4873 + * Searches until the next instance of its atom. This is useful for
171.4874 + * finding the atom efficiently without passing an instance of it
171.4875 + * (greedy problem) and without a lot of wasted search time (reluctant
171.4876 + * problem).
171.4877 + */
171.4878 + static final class First extends Node {
171.4879 + Node atom;
171.4880 + First(Node node) {
171.4881 + this.atom = BnM.optimize(node);
171.4882 + }
171.4883 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4884 + if (atom instanceof BnM) {
171.4885 + return atom.match(matcher, i, seq)
171.4886 + && next.match(matcher, matcher.last, seq);
171.4887 + }
171.4888 + for (;;) {
171.4889 + if (i > matcher.to) {
171.4890 + matcher.hitEnd = true;
171.4891 + return false;
171.4892 + }
171.4893 + if (atom.match(matcher, i, seq)) {
171.4894 + return next.match(matcher, matcher.last, seq);
171.4895 + }
171.4896 + i += countChars(seq, i, 1);
171.4897 + matcher.first++;
171.4898 + }
171.4899 + }
171.4900 + boolean study(TreeInfo info) {
171.4901 + atom.study(info);
171.4902 + info.maxValid = false;
171.4903 + info.deterministic = false;
171.4904 + return next.study(info);
171.4905 + }
171.4906 + }
171.4907 +
171.4908 + static final class Conditional extends Node {
171.4909 + Node cond, yes, not;
171.4910 + Conditional(Node cond, Node yes, Node not) {
171.4911 + this.cond = cond;
171.4912 + this.yes = yes;
171.4913 + this.not = not;
171.4914 + }
171.4915 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4916 + if (cond.match(matcher, i, seq)) {
171.4917 + return yes.match(matcher, i, seq);
171.4918 + } else {
171.4919 + return not.match(matcher, i, seq);
171.4920 + }
171.4921 + }
171.4922 + boolean study(TreeInfo info) {
171.4923 + int minL = info.minLength;
171.4924 + int maxL = info.maxLength;
171.4925 + boolean maxV = info.maxValid;
171.4926 + info.reset();
171.4927 + yes.study(info);
171.4928 +
171.4929 + int minL2 = info.minLength;
171.4930 + int maxL2 = info.maxLength;
171.4931 + boolean maxV2 = info.maxValid;
171.4932 + info.reset();
171.4933 + not.study(info);
171.4934 +
171.4935 + info.minLength = minL + Math.min(minL2, info.minLength);
171.4936 + info.maxLength = maxL + Math.max(maxL2, info.maxLength);
171.4937 + info.maxValid = (maxV & maxV2 & info.maxValid);
171.4938 + info.deterministic = false;
171.4939 + return next.study(info);
171.4940 + }
171.4941 + }
171.4942 +
171.4943 + /**
171.4944 + * Zero width positive lookahead.
171.4945 + */
171.4946 + static final class Pos extends Node {
171.4947 + Node cond;
171.4948 + Pos(Node cond) {
171.4949 + this.cond = cond;
171.4950 + }
171.4951 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4952 + int savedTo = matcher.to;
171.4953 + boolean conditionMatched = false;
171.4954 +
171.4955 + // Relax transparent region boundaries for lookahead
171.4956 + if (matcher.transparentBounds)
171.4957 + matcher.to = matcher.getTextLength();
171.4958 + try {
171.4959 + conditionMatched = cond.match(matcher, i, seq);
171.4960 + } finally {
171.4961 + // Reinstate region boundaries
171.4962 + matcher.to = savedTo;
171.4963 + }
171.4964 + return conditionMatched && next.match(matcher, i, seq);
171.4965 + }
171.4966 + }
171.4967 +
171.4968 + /**
171.4969 + * Zero width negative lookahead.
171.4970 + */
171.4971 + static final class Neg extends Node {
171.4972 + Node cond;
171.4973 + Neg(Node cond) {
171.4974 + this.cond = cond;
171.4975 + }
171.4976 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.4977 + int savedTo = matcher.to;
171.4978 + boolean conditionMatched = false;
171.4979 +
171.4980 + // Relax transparent region boundaries for lookahead
171.4981 + if (matcher.transparentBounds)
171.4982 + matcher.to = matcher.getTextLength();
171.4983 + try {
171.4984 + if (i < matcher.to) {
171.4985 + conditionMatched = !cond.match(matcher, i, seq);
171.4986 + } else {
171.4987 + // If a negative lookahead succeeds then more input
171.4988 + // could cause it to fail!
171.4989 + matcher.requireEnd = true;
171.4990 + conditionMatched = !cond.match(matcher, i, seq);
171.4991 + }
171.4992 + } finally {
171.4993 + // Reinstate region boundaries
171.4994 + matcher.to = savedTo;
171.4995 + }
171.4996 + return conditionMatched && next.match(matcher, i, seq);
171.4997 + }
171.4998 + }
171.4999 +
171.5000 + /**
171.5001 + * For use with lookbehinds; matches the position where the lookbehind
171.5002 + * was encountered.
171.5003 + */
171.5004 + static Node lookbehindEnd = new Node() {
171.5005 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5006 + return i == matcher.lookbehindTo;
171.5007 + }
171.5008 + };
171.5009 +
171.5010 + /**
171.5011 + * Zero width positive lookbehind.
171.5012 + */
171.5013 + static class Behind extends Node {
171.5014 + Node cond;
171.5015 + int rmax, rmin;
171.5016 + Behind(Node cond, int rmax, int rmin) {
171.5017 + this.cond = cond;
171.5018 + this.rmax = rmax;
171.5019 + this.rmin = rmin;
171.5020 + }
171.5021 +
171.5022 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5023 + int savedFrom = matcher.from;
171.5024 + boolean conditionMatched = false;
171.5025 + int startIndex = (!matcher.transparentBounds) ?
171.5026 + matcher.from : 0;
171.5027 + int from = Math.max(i - rmax, startIndex);
171.5028 + // Set end boundary
171.5029 + int savedLBT = matcher.lookbehindTo;
171.5030 + matcher.lookbehindTo = i;
171.5031 + // Relax transparent region boundaries for lookbehind
171.5032 + if (matcher.transparentBounds)
171.5033 + matcher.from = 0;
171.5034 + for (int j = i - rmin; !conditionMatched && j >= from; j--) {
171.5035 + conditionMatched = cond.match(matcher, j, seq);
171.5036 + }
171.5037 + matcher.from = savedFrom;
171.5038 + matcher.lookbehindTo = savedLBT;
171.5039 + return conditionMatched && next.match(matcher, i, seq);
171.5040 + }
171.5041 + }
171.5042 +
171.5043 + /**
171.5044 + * Zero width positive lookbehind, including supplementary
171.5045 + * characters or unpaired surrogates.
171.5046 + */
171.5047 + static final class BehindS extends Behind {
171.5048 + BehindS(Node cond, int rmax, int rmin) {
171.5049 + super(cond, rmax, rmin);
171.5050 + }
171.5051 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5052 + int rmaxChars = countChars(seq, i, -rmax);
171.5053 + int rminChars = countChars(seq, i, -rmin);
171.5054 + int savedFrom = matcher.from;
171.5055 + int startIndex = (!matcher.transparentBounds) ?
171.5056 + matcher.from : 0;
171.5057 + boolean conditionMatched = false;
171.5058 + int from = Math.max(i - rmaxChars, startIndex);
171.5059 + // Set end boundary
171.5060 + int savedLBT = matcher.lookbehindTo;
171.5061 + matcher.lookbehindTo = i;
171.5062 + // Relax transparent region boundaries for lookbehind
171.5063 + if (matcher.transparentBounds)
171.5064 + matcher.from = 0;
171.5065 +
171.5066 + for (int j = i - rminChars;
171.5067 + !conditionMatched && j >= from;
171.5068 + j -= j>from ? countChars(seq, j, -1) : 1) {
171.5069 + conditionMatched = cond.match(matcher, j, seq);
171.5070 + }
171.5071 + matcher.from = savedFrom;
171.5072 + matcher.lookbehindTo = savedLBT;
171.5073 + return conditionMatched && next.match(matcher, i, seq);
171.5074 + }
171.5075 + }
171.5076 +
171.5077 + /**
171.5078 + * Zero width negative lookbehind.
171.5079 + */
171.5080 + static class NotBehind extends Node {
171.5081 + Node cond;
171.5082 + int rmax, rmin;
171.5083 + NotBehind(Node cond, int rmax, int rmin) {
171.5084 + this.cond = cond;
171.5085 + this.rmax = rmax;
171.5086 + this.rmin = rmin;
171.5087 + }
171.5088 +
171.5089 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5090 + int savedLBT = matcher.lookbehindTo;
171.5091 + int savedFrom = matcher.from;
171.5092 + boolean conditionMatched = false;
171.5093 + int startIndex = (!matcher.transparentBounds) ?
171.5094 + matcher.from : 0;
171.5095 + int from = Math.max(i - rmax, startIndex);
171.5096 + matcher.lookbehindTo = i;
171.5097 + // Relax transparent region boundaries for lookbehind
171.5098 + if (matcher.transparentBounds)
171.5099 + matcher.from = 0;
171.5100 + for (int j = i - rmin; !conditionMatched && j >= from; j--) {
171.5101 + conditionMatched = cond.match(matcher, j, seq);
171.5102 + }
171.5103 + // Reinstate region boundaries
171.5104 + matcher.from = savedFrom;
171.5105 + matcher.lookbehindTo = savedLBT;
171.5106 + return !conditionMatched && next.match(matcher, i, seq);
171.5107 + }
171.5108 + }
171.5109 +
171.5110 + /**
171.5111 + * Zero width negative lookbehind, including supplementary
171.5112 + * characters or unpaired surrogates.
171.5113 + */
171.5114 + static final class NotBehindS extends NotBehind {
171.5115 + NotBehindS(Node cond, int rmax, int rmin) {
171.5116 + super(cond, rmax, rmin);
171.5117 + }
171.5118 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5119 + int rmaxChars = countChars(seq, i, -rmax);
171.5120 + int rminChars = countChars(seq, i, -rmin);
171.5121 + int savedFrom = matcher.from;
171.5122 + int savedLBT = matcher.lookbehindTo;
171.5123 + boolean conditionMatched = false;
171.5124 + int startIndex = (!matcher.transparentBounds) ?
171.5125 + matcher.from : 0;
171.5126 + int from = Math.max(i - rmaxChars, startIndex);
171.5127 + matcher.lookbehindTo = i;
171.5128 + // Relax transparent region boundaries for lookbehind
171.5129 + if (matcher.transparentBounds)
171.5130 + matcher.from = 0;
171.5131 + for (int j = i - rminChars;
171.5132 + !conditionMatched && j >= from;
171.5133 + j -= j>from ? countChars(seq, j, -1) : 1) {
171.5134 + conditionMatched = cond.match(matcher, j, seq);
171.5135 + }
171.5136 + //Reinstate region boundaries
171.5137 + matcher.from = savedFrom;
171.5138 + matcher.lookbehindTo = savedLBT;
171.5139 + return !conditionMatched && next.match(matcher, i, seq);
171.5140 + }
171.5141 + }
171.5142 +
171.5143 + /**
171.5144 + * Returns the set union of two CharProperty nodes.
171.5145 + */
171.5146 + private static CharProperty union(final CharProperty lhs,
171.5147 + final CharProperty rhs) {
171.5148 + return new CharProperty() {
171.5149 + boolean isSatisfiedBy(int ch) {
171.5150 + return lhs.isSatisfiedBy(ch) || rhs.isSatisfiedBy(ch);}};
171.5151 + }
171.5152 +
171.5153 + /**
171.5154 + * Returns the set intersection of two CharProperty nodes.
171.5155 + */
171.5156 + private static CharProperty intersection(final CharProperty lhs,
171.5157 + final CharProperty rhs) {
171.5158 + return new CharProperty() {
171.5159 + boolean isSatisfiedBy(int ch) {
171.5160 + return lhs.isSatisfiedBy(ch) && rhs.isSatisfiedBy(ch);}};
171.5161 + }
171.5162 +
171.5163 + /**
171.5164 + * Returns the set difference of two CharProperty nodes.
171.5165 + */
171.5166 + private static CharProperty setDifference(final CharProperty lhs,
171.5167 + final CharProperty rhs) {
171.5168 + return new CharProperty() {
171.5169 + boolean isSatisfiedBy(int ch) {
171.5170 + return ! rhs.isSatisfiedBy(ch) && lhs.isSatisfiedBy(ch);}};
171.5171 + }
171.5172 +
171.5173 + /**
171.5174 + * Handles word boundaries. Includes a field to allow this one class to
171.5175 + * deal with the different types of word boundaries we can match. The word
171.5176 + * characters include underscores, letters, and digits. Non spacing marks
171.5177 + * can are also part of a word if they have a base character, otherwise
171.5178 + * they are ignored for purposes of finding word boundaries.
171.5179 + */
171.5180 + static final class Bound extends Node {
171.5181 + static int LEFT = 0x1;
171.5182 + static int RIGHT= 0x2;
171.5183 + static int BOTH = 0x3;
171.5184 + static int NONE = 0x4;
171.5185 + int type;
171.5186 + boolean useUWORD;
171.5187 + Bound(int n, boolean useUWORD) {
171.5188 + type = n;
171.5189 + this.useUWORD = useUWORD;
171.5190 + }
171.5191 +
171.5192 + boolean isWord(int ch) {
171.5193 + return useUWORD ? UnicodeProp.WORD.is(ch)
171.5194 + : (ch == '_' || Character.isLetterOrDigit(ch));
171.5195 + }
171.5196 +
171.5197 + int check(Matcher matcher, int i, CharSequence seq) {
171.5198 + int ch;
171.5199 + boolean left = false;
171.5200 + int startIndex = matcher.from;
171.5201 + int endIndex = matcher.to;
171.5202 + if (matcher.transparentBounds) {
171.5203 + startIndex = 0;
171.5204 + endIndex = matcher.getTextLength();
171.5205 + }
171.5206 + if (i > startIndex) {
171.5207 + ch = Character.codePointBefore(seq, i);
171.5208 + left = (isWord(ch) ||
171.5209 + ((Character.getType(ch) == Character.NON_SPACING_MARK)
171.5210 + && hasBaseCharacter(matcher, i-1, seq)));
171.5211 + }
171.5212 + boolean right = false;
171.5213 + if (i < endIndex) {
171.5214 + ch = Character.codePointAt(seq, i);
171.5215 + right = (isWord(ch) ||
171.5216 + ((Character.getType(ch) == Character.NON_SPACING_MARK)
171.5217 + && hasBaseCharacter(matcher, i, seq)));
171.5218 + } else {
171.5219 + // Tried to access char past the end
171.5220 + matcher.hitEnd = true;
171.5221 + // The addition of another char could wreck a boundary
171.5222 + matcher.requireEnd = true;
171.5223 + }
171.5224 + return ((left ^ right) ? (right ? LEFT : RIGHT) : NONE);
171.5225 + }
171.5226 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5227 + return (check(matcher, i, seq) & type) > 0
171.5228 + && next.match(matcher, i, seq);
171.5229 + }
171.5230 + }
171.5231 +
171.5232 + /**
171.5233 + * Non spacing marks only count as word characters in bounds calculations
171.5234 + * if they have a base character.
171.5235 + */
171.5236 + private static boolean hasBaseCharacter(Matcher matcher, int i,
171.5237 + CharSequence seq)
171.5238 + {
171.5239 + int start = (!matcher.transparentBounds) ?
171.5240 + matcher.from : 0;
171.5241 + for (int x=i; x >= start; x--) {
171.5242 + int ch = Character.codePointAt(seq, x);
171.5243 + if (Character.isLetterOrDigit(ch))
171.5244 + return true;
171.5245 + if (Character.getType(ch) == Character.NON_SPACING_MARK)
171.5246 + continue;
171.5247 + return false;
171.5248 + }
171.5249 + return false;
171.5250 + }
171.5251 +
171.5252 + /**
171.5253 + * Attempts to match a slice in the input using the Boyer-Moore string
171.5254 + * matching algorithm. The algorithm is based on the idea that the
171.5255 + * pattern can be shifted farther ahead in the search text if it is
171.5256 + * matched right to left.
171.5257 + * <p>
171.5258 + * The pattern is compared to the input one character at a time, from
171.5259 + * the rightmost character in the pattern to the left. If the characters
171.5260 + * all match the pattern has been found. If a character does not match,
171.5261 + * the pattern is shifted right a distance that is the maximum of two
171.5262 + * functions, the bad character shift and the good suffix shift. This
171.5263 + * shift moves the attempted match position through the input more
171.5264 + * quickly than a naive one position at a time check.
171.5265 + * <p>
171.5266 + * The bad character shift is based on the character from the text that
171.5267 + * did not match. If the character does not appear in the pattern, the
171.5268 + * pattern can be shifted completely beyond the bad character. If the
171.5269 + * character does occur in the pattern, the pattern can be shifted to
171.5270 + * line the pattern up with the next occurrence of that character.
171.5271 + * <p>
171.5272 + * The good suffix shift is based on the idea that some subset on the right
171.5273 + * side of the pattern has matched. When a bad character is found, the
171.5274 + * pattern can be shifted right by the pattern length if the subset does
171.5275 + * not occur again in pattern, or by the amount of distance to the
171.5276 + * next occurrence of the subset in the pattern.
171.5277 + *
171.5278 + * Boyer-Moore search methods adapted from code by Amy Yu.
171.5279 + */
171.5280 + static class BnM extends Node {
171.5281 + int[] buffer;
171.5282 + int[] lastOcc;
171.5283 + int[] optoSft;
171.5284 +
171.5285 + /**
171.5286 + * Pre calculates arrays needed to generate the bad character
171.5287 + * shift and the good suffix shift. Only the last seven bits
171.5288 + * are used to see if chars match; This keeps the tables small
171.5289 + * and covers the heavily used ASCII range, but occasionally
171.5290 + * results in an aliased match for the bad character shift.
171.5291 + */
171.5292 + static Node optimize(Node node) {
171.5293 + if (!(node instanceof Slice)) {
171.5294 + return node;
171.5295 + }
171.5296 +
171.5297 + int[] src = ((Slice) node).buffer;
171.5298 + int patternLength = src.length;
171.5299 + // The BM algorithm requires a bit of overhead;
171.5300 + // If the pattern is short don't use it, since
171.5301 + // a shift larger than the pattern length cannot
171.5302 + // be used anyway.
171.5303 + if (patternLength < 4) {
171.5304 + return node;
171.5305 + }
171.5306 + int i, j, k;
171.5307 + int[] lastOcc = new int[128];
171.5308 + int[] optoSft = new int[patternLength];
171.5309 + // Precalculate part of the bad character shift
171.5310 + // It is a table for where in the pattern each
171.5311 + // lower 7-bit value occurs
171.5312 + for (i = 0; i < patternLength; i++) {
171.5313 + lastOcc[src[i]&0x7F] = i + 1;
171.5314 + }
171.5315 + // Precalculate the good suffix shift
171.5316 + // i is the shift amount being considered
171.5317 +NEXT: for (i = patternLength; i > 0; i--) {
171.5318 + // j is the beginning index of suffix being considered
171.5319 + for (j = patternLength - 1; j >= i; j--) {
171.5320 + // Testing for good suffix
171.5321 + if (src[j] == src[j-i]) {
171.5322 + // src[j..len] is a good suffix
171.5323 + optoSft[j-1] = i;
171.5324 + } else {
171.5325 + // No match. The array has already been
171.5326 + // filled up with correct values before.
171.5327 + continue NEXT;
171.5328 + }
171.5329 + }
171.5330 + // This fills up the remaining of optoSft
171.5331 + // any suffix can not have larger shift amount
171.5332 + // then its sub-suffix. Why???
171.5333 + while (j > 0) {
171.5334 + optoSft[--j] = i;
171.5335 + }
171.5336 + }
171.5337 + // Set the guard value because of unicode compression
171.5338 + optoSft[patternLength-1] = 1;
171.5339 + if (node instanceof SliceS)
171.5340 + return new BnMS(src, lastOcc, optoSft, node.next);
171.5341 + return new BnM(src, lastOcc, optoSft, node.next);
171.5342 + }
171.5343 + BnM(int[] src, int[] lastOcc, int[] optoSft, Node next) {
171.5344 + this.buffer = src;
171.5345 + this.lastOcc = lastOcc;
171.5346 + this.optoSft = optoSft;
171.5347 + this.next = next;
171.5348 + }
171.5349 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5350 + int[] src = buffer;
171.5351 + int patternLength = src.length;
171.5352 + int last = matcher.to - patternLength;
171.5353 +
171.5354 + // Loop over all possible match positions in text
171.5355 +NEXT: while (i <= last) {
171.5356 + // Loop over pattern from right to left
171.5357 + for (int j = patternLength - 1; j >= 0; j--) {
171.5358 + int ch = seq.charAt(i+j);
171.5359 + if (ch != src[j]) {
171.5360 + // Shift search to the right by the maximum of the
171.5361 + // bad character shift and the good suffix shift
171.5362 + i += Math.max(j + 1 - lastOcc[ch&0x7F], optoSft[j]);
171.5363 + continue NEXT;
171.5364 + }
171.5365 + }
171.5366 + // Entire pattern matched starting at i
171.5367 + matcher.first = i;
171.5368 + boolean ret = next.match(matcher, i + patternLength, seq);
171.5369 + if (ret) {
171.5370 + matcher.first = i;
171.5371 + matcher.groups[0] = matcher.first;
171.5372 + matcher.groups[1] = matcher.last;
171.5373 + return true;
171.5374 + }
171.5375 + i++;
171.5376 + }
171.5377 + // BnM is only used as the leading node in the unanchored case,
171.5378 + // and it replaced its Start() which always searches to the end
171.5379 + // if it doesn't find what it's looking for, so hitEnd is true.
171.5380 + matcher.hitEnd = true;
171.5381 + return false;
171.5382 + }
171.5383 + boolean study(TreeInfo info) {
171.5384 + info.minLength += buffer.length;
171.5385 + info.maxValid = false;
171.5386 + return next.study(info);
171.5387 + }
171.5388 + }
171.5389 +
171.5390 + /**
171.5391 + * Supplementary support version of BnM(). Unpaired surrogates are
171.5392 + * also handled by this class.
171.5393 + */
171.5394 + static final class BnMS extends BnM {
171.5395 + int lengthInChars;
171.5396 +
171.5397 + BnMS(int[] src, int[] lastOcc, int[] optoSft, Node next) {
171.5398 + super(src, lastOcc, optoSft, next);
171.5399 + for (int x = 0; x < buffer.length; x++) {
171.5400 + lengthInChars += Character.charCount(buffer[x]);
171.5401 + }
171.5402 + }
171.5403 + boolean match(Matcher matcher, int i, CharSequence seq) {
171.5404 + int[] src = buffer;
171.5405 + int patternLength = src.length;
171.5406 + int last = matcher.to - lengthInChars;
171.5407 +
171.5408 + // Loop over all possible match positions in text
171.5409 +NEXT: while (i <= last) {
171.5410 + // Loop over pattern from right to left
171.5411 + int ch;
171.5412 + for (int j = countChars(seq, i, patternLength), x = patternLength - 1;
171.5413 + j > 0; j -= Character.charCount(ch), x--) {
171.5414 + ch = Character.codePointBefore(seq, i+j);
171.5415 + if (ch != src[x]) {
171.5416 + // Shift search to the right by the maximum of the
171.5417 + // bad character shift and the good suffix shift
171.5418 + int n = Math.max(x + 1 - lastOcc[ch&0x7F], optoSft[x]);
171.5419 + i += countChars(seq, i, n);
171.5420 + continue NEXT;
171.5421 + }
171.5422 + }
171.5423 + // Entire pattern matched starting at i
171.5424 + matcher.first = i;
171.5425 + boolean ret = next.match(matcher, i + lengthInChars, seq);
171.5426 + if (ret) {
171.5427 + matcher.first = i;
171.5428 + matcher.groups[0] = matcher.first;
171.5429 + matcher.groups[1] = matcher.last;
171.5430 + return true;
171.5431 + }
171.5432 + i += countChars(seq, i, 1);
171.5433 + }
171.5434 + matcher.hitEnd = true;
171.5435 + return false;
171.5436 + }
171.5437 + }
171.5438 +
171.5439 +///////////////////////////////////////////////////////////////////////////////
171.5440 +///////////////////////////////////////////////////////////////////////////////
171.5441 +
171.5442 + /**
171.5443 + * This must be the very first initializer.
171.5444 + */
171.5445 + static Node accept = new Node();
171.5446 +
171.5447 + static Node lastAccept = new LastNode();
171.5448 +
171.5449 + private static class CharPropertyNames {
171.5450 +
171.5451 + static CharProperty charPropertyFor(String name) {
171.5452 + CharPropertyFactory m = map.get(name);
171.5453 + return m == null ? null : m.make();
171.5454 + }
171.5455 +
171.5456 + private static abstract class CharPropertyFactory {
171.5457 + abstract CharProperty make();
171.5458 + }
171.5459 +
171.5460 + private static void defCategory(String name,
171.5461 + final int typeMask) {
171.5462 + map.put(name, new CharPropertyFactory() {
171.5463 + CharProperty make() { return new Category(typeMask);}});
171.5464 + }
171.5465 +
171.5466 + private static void defRange(String name,
171.5467 + final int lower, final int upper) {
171.5468 + map.put(name, new CharPropertyFactory() {
171.5469 + CharProperty make() { return rangeFor(lower, upper);}});
171.5470 + }
171.5471 +
171.5472 + private static void defCtype(String name,
171.5473 + final int ctype) {
171.5474 + map.put(name, new CharPropertyFactory() {
171.5475 + CharProperty make() { return new Ctype(ctype);}});
171.5476 + }
171.5477 +
171.5478 + private static abstract class CloneableProperty
171.5479 + extends CharProperty implements Cloneable
171.5480 + {
171.5481 + public CloneableProperty clone() {
171.5482 + try {
171.5483 + return (CloneableProperty) super.clone();
171.5484 + } catch (CloneNotSupportedException e) {
171.5485 + throw new AssertionError(e);
171.5486 + }
171.5487 + }
171.5488 + }
171.5489 +
171.5490 + private static void defClone(String name,
171.5491 + final CloneableProperty p) {
171.5492 + map.put(name, new CharPropertyFactory() {
171.5493 + CharProperty make() { return p.clone();}});
171.5494 + }
171.5495 +
171.5496 + private static final HashMap<String, CharPropertyFactory> map
171.5497 + = new HashMap<>();
171.5498 +
171.5499 + static {
171.5500 + // Unicode character property aliases, defined in
171.5501 + // http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt
171.5502 + defCategory("Cn", 1<<Character.UNASSIGNED);
171.5503 + defCategory("Lu", 1<<Character.UPPERCASE_LETTER);
171.5504 + defCategory("Ll", 1<<Character.LOWERCASE_LETTER);
171.5505 + defCategory("Lt", 1<<Character.TITLECASE_LETTER);
171.5506 + defCategory("Lm", 1<<Character.MODIFIER_LETTER);
171.5507 + defCategory("Lo", 1<<Character.OTHER_LETTER);
171.5508 + defCategory("Mn", 1<<Character.NON_SPACING_MARK);
171.5509 + defCategory("Me", 1<<Character.ENCLOSING_MARK);
171.5510 + defCategory("Mc", 1<<Character.COMBINING_SPACING_MARK);
171.5511 + defCategory("Nd", 1<<Character.DECIMAL_DIGIT_NUMBER);
171.5512 + defCategory("Nl", 1<<Character.LETTER_NUMBER);
171.5513 + defCategory("No", 1<<Character.OTHER_NUMBER);
171.5514 + defCategory("Zs", 1<<Character.SPACE_SEPARATOR);
171.5515 + defCategory("Zl", 1<<Character.LINE_SEPARATOR);
171.5516 + defCategory("Zp", 1<<Character.PARAGRAPH_SEPARATOR);
171.5517 + defCategory("Cc", 1<<Character.CONTROL);
171.5518 + defCategory("Cf", 1<<Character.FORMAT);
171.5519 + defCategory("Co", 1<<Character.PRIVATE_USE);
171.5520 + defCategory("Cs", 1<<Character.SURROGATE);
171.5521 + defCategory("Pd", 1<<Character.DASH_PUNCTUATION);
171.5522 + defCategory("Ps", 1<<Character.START_PUNCTUATION);
171.5523 + defCategory("Pe", 1<<Character.END_PUNCTUATION);
171.5524 + defCategory("Pc", 1<<Character.CONNECTOR_PUNCTUATION);
171.5525 + defCategory("Po", 1<<Character.OTHER_PUNCTUATION);
171.5526 + defCategory("Sm", 1<<Character.MATH_SYMBOL);
171.5527 + defCategory("Sc", 1<<Character.CURRENCY_SYMBOL);
171.5528 + defCategory("Sk", 1<<Character.MODIFIER_SYMBOL);
171.5529 + defCategory("So", 1<<Character.OTHER_SYMBOL);
171.5530 + defCategory("Pi", 1<<Character.INITIAL_QUOTE_PUNCTUATION);
171.5531 + defCategory("Pf", 1<<Character.FINAL_QUOTE_PUNCTUATION);
171.5532 + defCategory("L", ((1<<Character.UPPERCASE_LETTER) |
171.5533 + (1<<Character.LOWERCASE_LETTER) |
171.5534 + (1<<Character.TITLECASE_LETTER) |
171.5535 + (1<<Character.MODIFIER_LETTER) |
171.5536 + (1<<Character.OTHER_LETTER)));
171.5537 + defCategory("M", ((1<<Character.NON_SPACING_MARK) |
171.5538 + (1<<Character.ENCLOSING_MARK) |
171.5539 + (1<<Character.COMBINING_SPACING_MARK)));
171.5540 + defCategory("N", ((1<<Character.DECIMAL_DIGIT_NUMBER) |
171.5541 + (1<<Character.LETTER_NUMBER) |
171.5542 + (1<<Character.OTHER_NUMBER)));
171.5543 + defCategory("Z", ((1<<Character.SPACE_SEPARATOR) |
171.5544 + (1<<Character.LINE_SEPARATOR) |
171.5545 + (1<<Character.PARAGRAPH_SEPARATOR)));
171.5546 + defCategory("C", ((1<<Character.CONTROL) |
171.5547 + (1<<Character.FORMAT) |
171.5548 + (1<<Character.PRIVATE_USE) |
171.5549 + (1<<Character.SURROGATE))); // Other
171.5550 + defCategory("P", ((1<<Character.DASH_PUNCTUATION) |
171.5551 + (1<<Character.START_PUNCTUATION) |
171.5552 + (1<<Character.END_PUNCTUATION) |
171.5553 + (1<<Character.CONNECTOR_PUNCTUATION) |
171.5554 + (1<<Character.OTHER_PUNCTUATION) |
171.5555 + (1<<Character.INITIAL_QUOTE_PUNCTUATION) |
171.5556 + (1<<Character.FINAL_QUOTE_PUNCTUATION)));
171.5557 + defCategory("S", ((1<<Character.MATH_SYMBOL) |
171.5558 + (1<<Character.CURRENCY_SYMBOL) |
171.5559 + (1<<Character.MODIFIER_SYMBOL) |
171.5560 + (1<<Character.OTHER_SYMBOL)));
171.5561 + defCategory("LC", ((1<<Character.UPPERCASE_LETTER) |
171.5562 + (1<<Character.LOWERCASE_LETTER) |
171.5563 + (1<<Character.TITLECASE_LETTER)));
171.5564 + defCategory("LD", ((1<<Character.UPPERCASE_LETTER) |
171.5565 + (1<<Character.LOWERCASE_LETTER) |
171.5566 + (1<<Character.TITLECASE_LETTER) |
171.5567 + (1<<Character.MODIFIER_LETTER) |
171.5568 + (1<<Character.OTHER_LETTER) |
171.5569 + (1<<Character.DECIMAL_DIGIT_NUMBER)));
171.5570 + defRange("L1", 0x00, 0xFF); // Latin-1
171.5571 + map.put("all", new CharPropertyFactory() {
171.5572 + CharProperty make() { return new All(); }});
171.5573 +
171.5574 + // Posix regular expression character classes, defined in
171.5575 + // http://www.unix.org/onlinepubs/009695399/basedefs/xbd_chap09.html
171.5576 + defRange("ASCII", 0x00, 0x7F); // ASCII
171.5577 + defCtype("Alnum", ASCII.ALNUM); // Alphanumeric characters
171.5578 + defCtype("Alpha", ASCII.ALPHA); // Alphabetic characters
171.5579 + defCtype("Blank", ASCII.BLANK); // Space and tab characters
171.5580 + defCtype("Cntrl", ASCII.CNTRL); // Control characters
171.5581 + defRange("Digit", '0', '9'); // Numeric characters
171.5582 + defCtype("Graph", ASCII.GRAPH); // printable and visible
171.5583 + defRange("Lower", 'a', 'z'); // Lower-case alphabetic
171.5584 + defRange("Print", 0x20, 0x7E); // Printable characters
171.5585 + defCtype("Punct", ASCII.PUNCT); // Punctuation characters
171.5586 + defCtype("Space", ASCII.SPACE); // Space characters
171.5587 + defRange("Upper", 'A', 'Z'); // Upper-case alphabetic
171.5588 + defCtype("XDigit",ASCII.XDIGIT); // hexadecimal digits
171.5589 +
171.5590 + // Java character properties, defined by methods in Character.java
171.5591 + defClone("javaLowerCase", new CloneableProperty() {
171.5592 + boolean isSatisfiedBy(int ch) {
171.5593 + return Character.isLowerCase(ch);}});
171.5594 + defClone("javaUpperCase", new CloneableProperty() {
171.5595 + boolean isSatisfiedBy(int ch) {
171.5596 + return Character.isUpperCase(ch);}});
171.5597 + defClone("javaAlphabetic", new CloneableProperty() {
171.5598 + boolean isSatisfiedBy(int ch) {
171.5599 + return Character.isAlphabetic(ch);}});
171.5600 + defClone("javaIdeographic", new CloneableProperty() {
171.5601 + boolean isSatisfiedBy(int ch) {
171.5602 + return Character.isIdeographic(ch);}});
171.5603 + defClone("javaTitleCase", new CloneableProperty() {
171.5604 + boolean isSatisfiedBy(int ch) {
171.5605 + return Character.isTitleCase(ch);}});
171.5606 + defClone("javaDigit", new CloneableProperty() {
171.5607 + boolean isSatisfiedBy(int ch) {
171.5608 + return Character.isDigit(ch);}});
171.5609 + defClone("javaDefined", new CloneableProperty() {
171.5610 + boolean isSatisfiedBy(int ch) {
171.5611 + return Character.isDefined(ch);}});
171.5612 + defClone("javaLetter", new CloneableProperty() {
171.5613 + boolean isSatisfiedBy(int ch) {
171.5614 + return Character.isLetter(ch);}});
171.5615 + defClone("javaLetterOrDigit", new CloneableProperty() {
171.5616 + boolean isSatisfiedBy(int ch) {
171.5617 + return Character.isLetterOrDigit(ch);}});
171.5618 + defClone("javaJavaIdentifierStart", new CloneableProperty() {
171.5619 + boolean isSatisfiedBy(int ch) {
171.5620 + return Character.isJavaIdentifierStart(ch);}});
171.5621 + defClone("javaJavaIdentifierPart", new CloneableProperty() {
171.5622 + boolean isSatisfiedBy(int ch) {
171.5623 + return Character.isJavaIdentifierPart(ch);}});
171.5624 + defClone("javaUnicodeIdentifierStart", new CloneableProperty() {
171.5625 + boolean isSatisfiedBy(int ch) {
171.5626 + return Character.isUnicodeIdentifierStart(ch);}});
171.5627 + defClone("javaUnicodeIdentifierPart", new CloneableProperty() {
171.5628 + boolean isSatisfiedBy(int ch) {
171.5629 + return Character.isUnicodeIdentifierPart(ch);}});
171.5630 + defClone("javaIdentifierIgnorable", new CloneableProperty() {
171.5631 + boolean isSatisfiedBy(int ch) {
171.5632 + return Character.isIdentifierIgnorable(ch);}});
171.5633 + defClone("javaSpaceChar", new CloneableProperty() {
171.5634 + boolean isSatisfiedBy(int ch) {
171.5635 + return Character.isSpaceChar(ch);}});
171.5636 + defClone("javaWhitespace", new CloneableProperty() {
171.5637 + boolean isSatisfiedBy(int ch) {
171.5638 + return Character.isWhitespace(ch);}});
171.5639 + defClone("javaISOControl", new CloneableProperty() {
171.5640 + boolean isSatisfiedBy(int ch) {
171.5641 + return Character.isISOControl(ch);}});
171.5642 + defClone("javaMirrored", new CloneableProperty() {
171.5643 + boolean isSatisfiedBy(int ch) {
171.5644 + return Character.isMirrored(ch);}});
171.5645 + }
171.5646 + }
171.5647 +
171.5648 + private static final class Normalizer {
171.5649 + public static final int NFD = 1;
171.5650 + public static final int NFC = 2;
171.5651 +
171.5652 + static String normalize(String pattern, int NFD) {
171.5653 + return pattern;
171.5654 + }
171.5655 +
171.5656 + private static int getCombiningClass(int c) {
171.5657 + return 1;
171.5658 + }
171.5659 + }
171.5660 +}
172.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
172.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/PatternSyntaxException.java Tue Feb 11 13:31:42 2014 +0100
172.3 @@ -0,0 +1,119 @@
172.4 +/*
172.5 + * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
172.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
172.7 + *
172.8 + * This code is free software; you can redistribute it and/or modify it
172.9 + * under the terms of the GNU General Public License version 2 only, as
172.10 + * published by the Free Software Foundation. Oracle designates this
172.11 + * particular file as subject to the "Classpath" exception as provided
172.12 + * by Oracle in the LICENSE file that accompanied this code.
172.13 + *
172.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
172.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
172.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
172.17 + * version 2 for more details (a copy is included in the LICENSE file that
172.18 + * accompanied this code).
172.19 + *
172.20 + * You should have received a copy of the GNU General Public License version
172.21 + * 2 along with this work; if not, write to the Free Software Foundation,
172.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
172.23 + *
172.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
172.25 + * or visit www.oracle.com if you need additional information or have any
172.26 + * questions.
172.27 + */
172.28 +
172.29 +package java.util.regex;
172.30 +
172.31 +/**
172.32 + * Unchecked exception thrown to indicate a syntax error in a
172.33 + * regular-expression pattern.
172.34 + *
172.35 + * @author unascribed
172.36 + * @since 1.4
172.37 + * @spec JSR-51
172.38 + */
172.39 +
172.40 +public class PatternSyntaxException
172.41 + extends IllegalArgumentException
172.42 +{
172.43 + private static final long serialVersionUID = -3864639126226059218L;
172.44 +
172.45 + private final String desc;
172.46 + private final String pattern;
172.47 + private final int index;
172.48 +
172.49 + /**
172.50 + * Constructs a new instance of this class.
172.51 + *
172.52 + * @param desc
172.53 + * A description of the error
172.54 + *
172.55 + * @param regex
172.56 + * The erroneous pattern
172.57 + *
172.58 + * @param index
172.59 + * The approximate index in the pattern of the error,
172.60 + * or <tt>-1</tt> if the index is not known
172.61 + */
172.62 + public PatternSyntaxException(String desc, String regex, int index) {
172.63 + this.desc = desc;
172.64 + this.pattern = regex;
172.65 + this.index = index;
172.66 + }
172.67 +
172.68 + /**
172.69 + * Retrieves the error index.
172.70 + *
172.71 + * @return The approximate index in the pattern of the error,
172.72 + * or <tt>-1</tt> if the index is not known
172.73 + */
172.74 + public int getIndex() {
172.75 + return index;
172.76 + }
172.77 +
172.78 + /**
172.79 + * Retrieves the description of the error.
172.80 + *
172.81 + * @return The description of the error
172.82 + */
172.83 + public String getDescription() {
172.84 + return desc;
172.85 + }
172.86 +
172.87 + /**
172.88 + * Retrieves the erroneous regular-expression pattern.
172.89 + *
172.90 + * @return The erroneous pattern
172.91 + */
172.92 + public String getPattern() {
172.93 + return pattern;
172.94 + }
172.95 +
172.96 + private static final String nl = System.lineSeparator();
172.97 +
172.98 + /**
172.99 + * Returns a multi-line string containing the description of the syntax
172.100 + * error and its index, the erroneous regular-expression pattern, and a
172.101 + * visual indication of the error index within the pattern.
172.102 + *
172.103 + * @return The full detail message
172.104 + */
172.105 + public String getMessage() {
172.106 + StringBuffer sb = new StringBuffer();
172.107 + sb.append(desc);
172.108 + if (index >= 0) {
172.109 + sb.append(" near index ");
172.110 + sb.append(index);
172.111 + }
172.112 + sb.append(nl);
172.113 + sb.append(pattern);
172.114 + if (index >= 0) {
172.115 + sb.append(nl);
172.116 + for (int i = 0; i < index; i++) sb.append(' ');
172.117 + sb.append('^');
172.118 + }
172.119 + return sb.toString();
172.120 + }
172.121 +
172.122 +}
173.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
173.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/UnicodeProp.java Tue Feb 11 13:31:42 2014 +0100
173.3 @@ -0,0 +1,236 @@
173.4 +/*
173.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
173.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
173.7 + *
173.8 + * This code is free software; you can redistribute it and/or modify it
173.9 + * under the terms of the GNU General Public License version 2 only, as
173.10 + * published by the Free Software Foundation. Oracle designates this
173.11 + * particular file as subject to the "Classpath" exception as provided
173.12 + * by Oracle in the LICENSE file that accompanied this code.
173.13 + *
173.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
173.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
173.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
173.17 + * version 2 for more details (a copy is included in the LICENSE file that
173.18 + * accompanied this code).
173.19 + *
173.20 + * You should have received a copy of the GNU General Public License version
173.21 + * 2 along with this work; if not, write to the Free Software Foundation,
173.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
173.23 + *
173.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
173.25 + * or visit www.oracle.com if you need additional information or have any
173.26 + * questions.
173.27 + */
173.28 +
173.29 +package java.util.regex;
173.30 +
173.31 +import java.util.HashMap;
173.32 +import java.util.Locale;
173.33 +
173.34 +enum UnicodeProp {
173.35 +
173.36 + ALPHABETIC {
173.37 + public boolean is(int ch) {
173.38 + return Character.isAlphabetic(ch);
173.39 + }
173.40 + },
173.41 +
173.42 + LETTER {
173.43 + public boolean is(int ch) {
173.44 + return Character.isLetter(ch);
173.45 + }
173.46 + },
173.47 +
173.48 + IDEOGRAPHIC {
173.49 + public boolean is(int ch) {
173.50 + return Character.isIdeographic(ch);
173.51 + }
173.52 + },
173.53 +
173.54 + LOWERCASE {
173.55 + public boolean is(int ch) {
173.56 + return Character.isLowerCase(ch);
173.57 + }
173.58 + },
173.59 +
173.60 + UPPERCASE {
173.61 + public boolean is(int ch) {
173.62 + return Character.isUpperCase(ch);
173.63 + }
173.64 + },
173.65 +
173.66 + TITLECASE {
173.67 + public boolean is(int ch) {
173.68 + return Character.isTitleCase(ch);
173.69 + }
173.70 + },
173.71 +
173.72 + WHITE_SPACE {
173.73 + // \p{Whitespace}
173.74 + public boolean is(int ch) {
173.75 + return ((((1 << Character.SPACE_SEPARATOR) |
173.76 + (1 << Character.LINE_SEPARATOR) |
173.77 + (1 << Character.PARAGRAPH_SEPARATOR)) >> Character.getType(ch)) & 1)
173.78 + != 0 || (ch >= 0x9 && ch <= 0xd) || (ch == 0x85);
173.79 + }
173.80 + },
173.81 +
173.82 + CONTROL {
173.83 + // \p{gc=Control}
173.84 + public boolean is(int ch) {
173.85 + return Character.getType(ch) == Character.CONTROL;
173.86 + }
173.87 + },
173.88 +
173.89 + PUNCTUATION {
173.90 + // \p{gc=Punctuation}
173.91 + public boolean is(int ch) {
173.92 + return ((((1 << Character.CONNECTOR_PUNCTUATION) |
173.93 + (1 << Character.DASH_PUNCTUATION) |
173.94 + (1 << Character.START_PUNCTUATION) |
173.95 + (1 << Character.END_PUNCTUATION) |
173.96 + (1 << Character.OTHER_PUNCTUATION) |
173.97 + (1 << Character.INITIAL_QUOTE_PUNCTUATION) |
173.98 + (1 << Character.FINAL_QUOTE_PUNCTUATION)) >> Character.getType(ch)) & 1)
173.99 + != 0;
173.100 + }
173.101 + },
173.102 +
173.103 + HEX_DIGIT {
173.104 + // \p{gc=Decimal_Number}
173.105 + // \p{Hex_Digit} -> PropList.txt: Hex_Digit
173.106 + public boolean is(int ch) {
173.107 + return DIGIT.is(ch) ||
173.108 + (ch >= 0x0030 && ch <= 0x0039) ||
173.109 + (ch >= 0x0041 && ch <= 0x0046) ||
173.110 + (ch >= 0x0061 && ch <= 0x0066) ||
173.111 + (ch >= 0xFF10 && ch <= 0xFF19) ||
173.112 + (ch >= 0xFF21 && ch <= 0xFF26) ||
173.113 + (ch >= 0xFF41 && ch <= 0xFF46);
173.114 + }
173.115 + },
173.116 +
173.117 + ASSIGNED {
173.118 + public boolean is(int ch) {
173.119 + return Character.getType(ch) != Character.UNASSIGNED;
173.120 + }
173.121 + },
173.122 +
173.123 + NONCHARACTER_CODE_POINT {
173.124 + // PropList.txt:Noncharacter_Code_Point
173.125 + public boolean is(int ch) {
173.126 + return (ch & 0xfffe) == 0xfffe || (ch >= 0xfdd0 && ch <= 0xfdef);
173.127 + }
173.128 + },
173.129 +
173.130 + DIGIT {
173.131 + // \p{gc=Decimal_Number}
173.132 + public boolean is(int ch) {
173.133 + return Character.isDigit(ch);
173.134 + }
173.135 + },
173.136 +
173.137 + ALNUM {
173.138 + // \p{alpha}
173.139 + // \p{digit}
173.140 + public boolean is(int ch) {
173.141 + return ALPHABETIC.is(ch) || DIGIT.is(ch);
173.142 + }
173.143 + },
173.144 +
173.145 + BLANK {
173.146 + // \p{Whitespace} --
173.147 + // [\N{LF} \N{VT} \N{FF} \N{CR} \N{NEL} -> 0xa, 0xb, 0xc, 0xd, 0x85
173.148 + // \p{gc=Line_Separator}
173.149 + // \p{gc=Paragraph_Separator}]
173.150 + public boolean is(int ch) {
173.151 + return Character.getType(ch) == Character.SPACE_SEPARATOR ||
173.152 + ch == 0x9; // \N{HT}
173.153 + }
173.154 + },
173.155 +
173.156 + GRAPH {
173.157 + // [^
173.158 + // \p{space}
173.159 + // \p{gc=Control}
173.160 + // \p{gc=Surrogate}
173.161 + // \p{gc=Unassigned}]
173.162 + public boolean is(int ch) {
173.163 + return ((((1 << Character.SPACE_SEPARATOR) |
173.164 + (1 << Character.LINE_SEPARATOR) |
173.165 + (1 << Character.PARAGRAPH_SEPARATOR) |
173.166 + (1 << Character.CONTROL) |
173.167 + (1 << Character.SURROGATE) |
173.168 + (1 << Character.UNASSIGNED)) >> Character.getType(ch)) & 1)
173.169 + == 0;
173.170 + }
173.171 + },
173.172 +
173.173 + PRINT {
173.174 + // \p{graph}
173.175 + // \p{blank}
173.176 + // -- \p{cntrl}
173.177 + public boolean is(int ch) {
173.178 + return (GRAPH.is(ch) || BLANK.is(ch)) && !CONTROL.is(ch);
173.179 + }
173.180 + },
173.181 +
173.182 + WORD {
173.183 + // \p{alpha}
173.184 + // \p{gc=Mark}
173.185 + // \p{digit}
173.186 + // \p{gc=Connector_Punctuation}
173.187 +
173.188 + public boolean is(int ch) {
173.189 + return ALPHABETIC.is(ch) ||
173.190 + ((((1 << Character.NON_SPACING_MARK) |
173.191 + (1 << Character.ENCLOSING_MARK) |
173.192 + (1 << Character.COMBINING_SPACING_MARK) |
173.193 + (1 << Character.DECIMAL_DIGIT_NUMBER) |
173.194 + (1 << Character.CONNECTOR_PUNCTUATION)) >> Character.getType(ch)) & 1)
173.195 + != 0;
173.196 + }
173.197 + };
173.198 +
173.199 + private final static HashMap<String, String> posix = new HashMap<>();
173.200 + private final static HashMap<String, String> aliases = new HashMap<>();
173.201 + static {
173.202 + posix.put("ALPHA", "ALPHABETIC");
173.203 + posix.put("LOWER", "LOWERCASE");
173.204 + posix.put("UPPER", "UPPERCASE");
173.205 + posix.put("SPACE", "WHITE_SPACE");
173.206 + posix.put("PUNCT", "PUNCTUATION");
173.207 + posix.put("XDIGIT","HEX_DIGIT");
173.208 + posix.put("ALNUM", "ALNUM");
173.209 + posix.put("CNTRL", "CONTROL");
173.210 + posix.put("DIGIT", "DIGIT");
173.211 + posix.put("BLANK", "BLANK");
173.212 + posix.put("GRAPH", "GRAPH");
173.213 + posix.put("PRINT", "PRINT");
173.214 +
173.215 + aliases.put("WHITESPACE", "WHITE_SPACE");
173.216 + aliases.put("HEXDIGIT","HEX_DIGIT");
173.217 + aliases.put("NONCHARACTERCODEPOINT", "NONCHARACTER_CODE_POINT");
173.218 + }
173.219 +
173.220 + public static UnicodeProp forName(String propName) {
173.221 + propName = propName.toUpperCase(Locale.ENGLISH);
173.222 + String alias = aliases.get(propName);
173.223 + if (alias != null)
173.224 + propName = alias;
173.225 + try {
173.226 + return valueOf (propName);
173.227 + } catch (IllegalArgumentException x) {}
173.228 + return null;
173.229 + }
173.230 +
173.231 + public static UnicodeProp forPOSIXName(String propName) {
173.232 + propName = posix.get(propName.toUpperCase(Locale.ENGLISH));
173.233 + if (propName == null)
173.234 + return null;
173.235 + return valueOf (propName);
173.236 + }
173.237 +
173.238 + public abstract boolean is(int ch);
173.239 +}
174.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
174.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/package.html Tue Feb 11 13:31:42 2014 +0100
174.3 @@ -0,0 +1,66 @@
174.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
174.5 +<html>
174.6 +<head>
174.7 +<!--
174.8 +Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
174.9 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
174.10 +
174.11 +This code is free software; you can redistribute it and/or modify it
174.12 +under the terms of the GNU General Public License version 2 only, as
174.13 +published by the Free Software Foundation. Oracle designates this
174.14 +particular file as subject to the "Classpath" exception as provided
174.15 +by Oracle in the LICENSE file that accompanied this code.
174.16 +
174.17 +This code is distributed in the hope that it will be useful, but WITHOUT
174.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
174.19 +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
174.20 +version 2 for more details (a copy is included in the LICENSE file that
174.21 +accompanied this code).
174.22 +
174.23 +You should have received a copy of the GNU General Public License version
174.24 +2 along with this work; if not, write to the Free Software Foundation,
174.25 +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
174.26 +
174.27 +Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
174.28 +or visit www.oracle.com if you need additional information or have any
174.29 +questions.
174.30 +-->
174.31 +
174.32 +</head>
174.33 +<body bgcolor="white">
174.34 +
174.35 +Classes for matching character sequences against patterns specified by regular
174.36 +expressions.
174.37 +
174.38 +<p> An instance of the {@link java.util.regex.Pattern} class represents a
174.39 +regular expression that is specified in string form in a syntax similar to
174.40 +that used by Perl.
174.41 +
174.42 +<p> Instances of the {@link java.util.regex.Matcher} class are used to match
174.43 +character sequences against a given pattern. Input is provided to matchers via
174.44 +the {@link java.lang.CharSequence} interface in order to support matching
174.45 +against characters from a wide variety of input sources. </p>
174.46 +
174.47 +<p> Unless otherwise noted, passing a <tt>null</tt> argument to a method
174.48 +in any class or interface in this package will cause a
174.49 +{@link java.lang.NullPointerException NullPointerException} to be thrown.
174.50 +
174.51 +<h2>Related Documentation</h2>
174.52 +
174.53 +<p> An excellent tutorial and overview of regular expressions is <a
174.54 +href="http://www.oreilly.com/catalog/regex/"><i>Mastering Regular
174.55 +Expressions</i>, Jeffrey E. F. Friedl, O'Reilly and Associates, 1997.</a> </p>
174.56 +
174.57 +<!--
174.58 +For overviews, tutorials, examples, guides, and tool documentation, please see:
174.59 +<ul>
174.60 + <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
174.61 +</ul>
174.62 +-->
174.63 +
174.64 +@since 1.4
174.65 +@author Mike McCloskey
174.66 +@author Mark Reinhold
174.67 +
174.68 +</body>
174.69 +</html>
175.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
175.2 +++ b/rt/emul/compact/src/main/java/org/apidesign/bck2brwsr/emul/reflect/ProxyImpl.java Tue Feb 11 13:31:42 2014 +0100
175.3 @@ -0,0 +1,1620 @@
175.4 +/*
175.5 + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
175.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
175.7 + *
175.8 + * This code is free software; you can redistribute it and/or modify it
175.9 + * under the terms of the GNU General Public License version 2 only, as
175.10 + * published by the Free Software Foundation. Oracle designates this
175.11 + * particular file as subject to the "Classpath" exception as provided
175.12 + * by Oracle in the LICENSE file that accompanied this code.
175.13 + *
175.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
175.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
175.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
175.17 + * version 2 for more details (a copy is included in the LICENSE file that
175.18 + * accompanied this code).
175.19 + *
175.20 + * You should have received a copy of the GNU General Public License version
175.21 + * 2 along with this work; if not, write to the Free Software Foundation,
175.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
175.23 + *
175.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
175.25 + * or visit www.oracle.com if you need additional information or have any
175.26 + * questions.
175.27 + */
175.28 +
175.29 +package org.apidesign.bck2brwsr.emul.reflect;
175.30 +
175.31 +import java.io.ByteArrayOutputStream;
175.32 +import java.io.DataOutputStream;
175.33 +import java.io.IOException;
175.34 +import java.io.OutputStream;
175.35 +import java.lang.ref.Reference;
175.36 +import java.lang.ref.WeakReference;
175.37 +import java.lang.reflect.Array;
175.38 +import java.lang.reflect.Constructor;
175.39 +import java.lang.reflect.InvocationHandler;
175.40 +import java.lang.reflect.InvocationTargetException;
175.41 +import java.lang.reflect.Method;
175.42 +import java.lang.reflect.Modifier;
175.43 +import java.util.ArrayList;
175.44 +import java.util.Arrays;
175.45 +import java.util.Collections;
175.46 +import java.util.HashMap;
175.47 +import java.util.HashSet;
175.48 +import java.util.LinkedList;
175.49 +import java.util.Map;
175.50 +import java.util.Set;
175.51 +import java.util.List;
175.52 +import java.util.ListIterator;
175.53 +import java.util.WeakHashMap;
175.54 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
175.55 +import org.apidesign.bck2brwsr.emul.reflect.MethodImpl;
175.56 +
175.57 +/**
175.58 + * {@code Proxy} provides static methods for creating dynamic proxy
175.59 + * classes and instances, and it is also the superclass of all
175.60 + * dynamic proxy classes created by those methods.
175.61 + *
175.62 + * <p>To create a proxy for some interface {@code Foo}:
175.63 + * <pre>
175.64 + * InvocationHandler handler = new MyInvocationHandler(...);
175.65 + * Class proxyClass = Proxy.getProxyClass(
175.66 + * Foo.class.getClassLoader(), new Class[] { Foo.class });
175.67 + * Foo f = (Foo) proxyClass.
175.68 + * getConstructor(new Class[] { InvocationHandler.class }).
175.69 + * newInstance(new Object[] { handler });
175.70 + * </pre>
175.71 + * or more simply:
175.72 + * <pre>
175.73 + * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
175.74 + * new Class[] { Foo.class },
175.75 + * handler);
175.76 + * </pre>
175.77 + *
175.78 + * <p>A <i>dynamic proxy class</i> (simply referred to as a <i>proxy
175.79 + * class</i> below) is a class that implements a list of interfaces
175.80 + * specified at runtime when the class is created, with behavior as
175.81 + * described below.
175.82 + *
175.83 + * A <i>proxy interface</i> is such an interface that is implemented
175.84 + * by a proxy class.
175.85 + *
175.86 + * A <i>proxy instance</i> is an instance of a proxy class.
175.87 + *
175.88 + * Each proxy instance has an associated <i>invocation handler</i>
175.89 + * object, which implements the interface {@link InvocationHandler}.
175.90 + * A method invocation on a proxy instance through one of its proxy
175.91 + * interfaces will be dispatched to the {@link InvocationHandler#invoke
175.92 + * invoke} method of the instance's invocation handler, passing the proxy
175.93 + * instance, a {@code java.lang.reflect.Method} object identifying
175.94 + * the method that was invoked, and an array of type {@code Object}
175.95 + * containing the arguments. The invocation handler processes the
175.96 + * encoded method invocation as appropriate and the result that it
175.97 + * returns will be returned as the result of the method invocation on
175.98 + * the proxy instance.
175.99 + *
175.100 + * <p>A proxy class has the following properties:
175.101 + *
175.102 + * <ul>
175.103 + * <li>Proxy classes are public, final, and not abstract.
175.104 + *
175.105 + * <li>The unqualified name of a proxy class is unspecified. The space
175.106 + * of class names that begin with the string {@code "$Proxy"}
175.107 + * should be, however, reserved for proxy classes.
175.108 + *
175.109 + * <li>A proxy class extends {@code java.lang.reflect.Proxy}.
175.110 + *
175.111 + * <li>A proxy class implements exactly the interfaces specified at its
175.112 + * creation, in the same order.
175.113 + *
175.114 + * <li>If a proxy class implements a non-public interface, then it will
175.115 + * be defined in the same package as that interface. Otherwise, the
175.116 + * package of a proxy class is also unspecified. Note that package
175.117 + * sealing will not prevent a proxy class from being successfully defined
175.118 + * in a particular package at runtime, and neither will classes already
175.119 + * defined by the same class loader and the same package with particular
175.120 + * signers.
175.121 + *
175.122 + * <li>Since a proxy class implements all of the interfaces specified at
175.123 + * its creation, invoking {@code getInterfaces} on its
175.124 + * {@code Class} object will return an array containing the same
175.125 + * list of interfaces (in the order specified at its creation), invoking
175.126 + * {@code getMethods} on its {@code Class} object will return
175.127 + * an array of {@code Method} objects that include all of the
175.128 + * methods in those interfaces, and invoking {@code getMethod} will
175.129 + * find methods in the proxy interfaces as would be expected.
175.130 + *
175.131 + * <li>The {@link Proxy#isProxyClass Proxy.isProxyClass} method will
175.132 + * return true if it is passed a proxy class-- a class returned by
175.133 + * {@code Proxy.getProxyClass} or the class of an object returned by
175.134 + * {@code Proxy.newProxyInstance}-- and false otherwise.
175.135 + *
175.136 + * <li>The {@code java.security.ProtectionDomain} of a proxy class
175.137 + * is the same as that of system classes loaded by the bootstrap class
175.138 + * loader, such as {@code java.lang.Object}, because the code for a
175.139 + * proxy class is generated by trusted system code. This protection
175.140 + * domain will typically be granted
175.141 + * {@code java.security.AllPermission}.
175.142 + *
175.143 + * <li>Each proxy class has one public constructor that takes one argument,
175.144 + * an implementation of the interface {@link InvocationHandler}, to set
175.145 + * the invocation handler for a proxy instance. Rather than having to use
175.146 + * the reflection API to access the public constructor, a proxy instance
175.147 + * can be also be created by calling the {@link Proxy#newProxyInstance
175.148 + * Proxy.newProxyInstance} method, which combines the actions of calling
175.149 + * {@link Proxy#getProxyClass Proxy.getProxyClass} with invoking the
175.150 + * constructor with an invocation handler.
175.151 + * </ul>
175.152 + *
175.153 + * <p>A proxy instance has the following properties:
175.154 + *
175.155 + * <ul>
175.156 + * <li>Given a proxy instance {@code proxy} and one of the
175.157 + * interfaces implemented by its proxy class {@code Foo}, the
175.158 + * following expression will return true:
175.159 + * <pre>
175.160 + * {@code proxy instanceof Foo}
175.161 + * </pre>
175.162 + * and the following cast operation will succeed (rather than throwing
175.163 + * a {@code ClassCastException}):
175.164 + * <pre>
175.165 + * {@code (Foo) proxy}
175.166 + * </pre>
175.167 + *
175.168 + * <li>Each proxy instance has an associated invocation handler, the one
175.169 + * that was passed to its constructor. The static
175.170 + * {@link Proxy#getInvocationHandler Proxy.getInvocationHandler} method
175.171 + * will return the invocation handler associated with the proxy instance
175.172 + * passed as its argument.
175.173 + *
175.174 + * <li>An interface method invocation on a proxy instance will be
175.175 + * encoded and dispatched to the invocation handler's {@link
175.176 + * InvocationHandler#invoke invoke} method as described in the
175.177 + * documentation for that method.
175.178 + *
175.179 + * <li>An invocation of the {@code hashCode},
175.180 + * {@code equals}, or {@code toString} methods declared in
175.181 + * {@code java.lang.Object} on a proxy instance will be encoded and
175.182 + * dispatched to the invocation handler's {@code invoke} method in
175.183 + * the same manner as interface method invocations are encoded and
175.184 + * dispatched, as described above. The declaring class of the
175.185 + * {@code Method} object passed to {@code invoke} will be
175.186 + * {@code java.lang.Object}. Other public methods of a proxy
175.187 + * instance inherited from {@code java.lang.Object} are not
175.188 + * overridden by a proxy class, so invocations of those methods behave
175.189 + * like they do for instances of {@code java.lang.Object}.
175.190 + * </ul>
175.191 + *
175.192 + * <h3>Methods Duplicated in Multiple Proxy Interfaces</h3>
175.193 + *
175.194 + * <p>When two or more interfaces of a proxy class contain a method with
175.195 + * the same name and parameter signature, the order of the proxy class's
175.196 + * interfaces becomes significant. When such a <i>duplicate method</i>
175.197 + * is invoked on a proxy instance, the {@code Method} object passed
175.198 + * to the invocation handler will not necessarily be the one whose
175.199 + * declaring class is assignable from the reference type of the interface
175.200 + * that the proxy's method was invoked through. This limitation exists
175.201 + * because the corresponding method implementation in the generated proxy
175.202 + * class cannot determine which interface it was invoked through.
175.203 + * Therefore, when a duplicate method is invoked on a proxy instance,
175.204 + * the {@code Method} object for the method in the foremost interface
175.205 + * that contains the method (either directly or inherited through a
175.206 + * superinterface) in the proxy class's list of interfaces is passed to
175.207 + * the invocation handler's {@code invoke} method, regardless of the
175.208 + * reference type through which the method invocation occurred.
175.209 + *
175.210 + * <p>If a proxy interface contains a method with the same name and
175.211 + * parameter signature as the {@code hashCode}, {@code equals},
175.212 + * or {@code toString} methods of {@code java.lang.Object},
175.213 + * when such a method is invoked on a proxy instance, the
175.214 + * {@code Method} object passed to the invocation handler will have
175.215 + * {@code java.lang.Object} as its declaring class. In other words,
175.216 + * the public, non-final methods of {@code java.lang.Object}
175.217 + * logically precede all of the proxy interfaces for the determination of
175.218 + * which {@code Method} object to pass to the invocation handler.
175.219 + *
175.220 + * <p>Note also that when a duplicate method is dispatched to an
175.221 + * invocation handler, the {@code invoke} method may only throw
175.222 + * checked exception types that are assignable to one of the exception
175.223 + * types in the {@code throws} clause of the method in <i>all</i> of
175.224 + * the proxy interfaces that it can be invoked through. If the
175.225 + * {@code invoke} method throws a checked exception that is not
175.226 + * assignable to any of the exception types declared by the method in one
175.227 + * of the proxy interfaces that it can be invoked through, then an
175.228 + * unchecked {@code UndeclaredThrowableException} will be thrown by
175.229 + * the invocation on the proxy instance. This restriction means that not
175.230 + * all of the exception types returned by invoking
175.231 + * {@code getExceptionTypes} on the {@code Method} object
175.232 + * passed to the {@code invoke} method can necessarily be thrown
175.233 + * successfully by the {@code invoke} method.
175.234 + *
175.235 + * @author Peter Jones
175.236 + * @see InvocationHandler
175.237 + * @since 1.3
175.238 + */
175.239 +public final class ProxyImpl implements java.io.Serializable {
175.240 +
175.241 + private static final long serialVersionUID = -2222568056686623797L;
175.242 +
175.243 + /** prefix for all proxy class names */
175.244 + private final static String proxyClassNamePrefix = "$Proxy";
175.245 +
175.246 + /** parameter types of a proxy class constructor */
175.247 + private final static Class[] constructorParams =
175.248 + { InvocationHandler.class };
175.249 +
175.250 + /** maps a class loader to the proxy class cache for that loader */
175.251 + private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
175.252 + = new WeakHashMap<>();
175.253 +
175.254 + /** marks that a particular proxy class is currently being generated */
175.255 + private static Object pendingGenerationMarker = new Object();
175.256 +
175.257 + /** next number to use for generation of unique proxy class names */
175.258 + private static long nextUniqueNumber = 0;
175.259 + private static Object nextUniqueNumberLock = new Object();
175.260 +
175.261 + /** set of all generated proxy classes, for isProxyClass implementation */
175.262 + private static Map<Class<?>, Void> proxyClasses =
175.263 + Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
175.264 +
175.265 + /**
175.266 + * the invocation handler for this proxy instance.
175.267 + * @serial
175.268 + */
175.269 + protected InvocationHandler h;
175.270 +
175.271 + /**
175.272 + * Prohibits instantiation.
175.273 + */
175.274 + private ProxyImpl() {
175.275 + }
175.276 +
175.277 + /**
175.278 + * Constructs a new {@code Proxy} instance from a subclass
175.279 + * (typically, a dynamic proxy class) with the specified value
175.280 + * for its invocation handler.
175.281 + *
175.282 + * @param h the invocation handler for this proxy instance
175.283 + */
175.284 + protected ProxyImpl(InvocationHandler h) {
175.285 + this.h = h;
175.286 + }
175.287 +
175.288 + /**
175.289 + * Returns the {@code java.lang.Class} object for a proxy class
175.290 + * given a class loader and an array of interfaces. The proxy class
175.291 + * will be defined by the specified class loader and will implement
175.292 + * all of the supplied interfaces. If a proxy class for the same
175.293 + * permutation of interfaces has already been defined by the class
175.294 + * loader, then the existing proxy class will be returned; otherwise,
175.295 + * a proxy class for those interfaces will be generated dynamically
175.296 + * and defined by the class loader.
175.297 + *
175.298 + * <p>There are several restrictions on the parameters that may be
175.299 + * passed to {@code Proxy.getProxyClass}:
175.300 + *
175.301 + * <ul>
175.302 + * <li>All of the {@code Class} objects in the
175.303 + * {@code interfaces} array must represent interfaces, not
175.304 + * classes or primitive types.
175.305 + *
175.306 + * <li>No two elements in the {@code interfaces} array may
175.307 + * refer to identical {@code Class} objects.
175.308 + *
175.309 + * <li>All of the interface types must be visible by name through the
175.310 + * specified class loader. In other words, for class loader
175.311 + * {@code cl} and every interface {@code i}, the following
175.312 + * expression must be true:
175.313 + * <pre>
175.314 + * Class.forName(i.getName(), false, cl) == i
175.315 + * </pre>
175.316 + *
175.317 + * <li>All non-public interfaces must be in the same package;
175.318 + * otherwise, it would not be possible for the proxy class to
175.319 + * implement all of the interfaces, regardless of what package it is
175.320 + * defined in.
175.321 + *
175.322 + * <li>For any set of member methods of the specified interfaces
175.323 + * that have the same signature:
175.324 + * <ul>
175.325 + * <li>If the return type of any of the methods is a primitive
175.326 + * type or void, then all of the methods must have that same
175.327 + * return type.
175.328 + * <li>Otherwise, one of the methods must have a return type that
175.329 + * is assignable to all of the return types of the rest of the
175.330 + * methods.
175.331 + * </ul>
175.332 + *
175.333 + * <li>The resulting proxy class must not exceed any limits imposed
175.334 + * on classes by the virtual machine. For example, the VM may limit
175.335 + * the number of interfaces that a class may implement to 65535; in
175.336 + * that case, the size of the {@code interfaces} array must not
175.337 + * exceed 65535.
175.338 + * </ul>
175.339 + *
175.340 + * <p>If any of these restrictions are violated,
175.341 + * {@code Proxy.getProxyClass} will throw an
175.342 + * {@code IllegalArgumentException}. If the {@code interfaces}
175.343 + * array argument or any of its elements are {@code null}, a
175.344 + * {@code NullPointerException} will be thrown.
175.345 + *
175.346 + * <p>Note that the order of the specified proxy interfaces is
175.347 + * significant: two requests for a proxy class with the same combination
175.348 + * of interfaces but in a different order will result in two distinct
175.349 + * proxy classes.
175.350 + *
175.351 + * @param loader the class loader to define the proxy class
175.352 + * @param interfaces the list of interfaces for the proxy class
175.353 + * to implement
175.354 + * @return a proxy class that is defined in the specified class loader
175.355 + * and that implements the specified interfaces
175.356 + * @throws IllegalArgumentException if any of the restrictions on the
175.357 + * parameters that may be passed to {@code getProxyClass}
175.358 + * are violated
175.359 + * @throws NullPointerException if the {@code interfaces} array
175.360 + * argument or any of its elements are {@code null}
175.361 + */
175.362 + public static Class<?> getProxyClass(ClassLoader loader,
175.363 + Class<?>... interfaces)
175.364 + throws IllegalArgumentException
175.365 + {
175.366 + if (interfaces.length > 65535) {
175.367 + throw new IllegalArgumentException("interface limit exceeded");
175.368 + }
175.369 +
175.370 + Class<?> proxyClass = null;
175.371 +
175.372 + /* collect interface names to use as key for proxy class cache */
175.373 + String[] interfaceNames = new String[interfaces.length];
175.374 +
175.375 + // for detecting duplicates
175.376 + Set<Class<?>> interfaceSet = new HashSet<>();
175.377 +
175.378 + for (int i = 0; i < interfaces.length; i++) {
175.379 + /*
175.380 + * Verify that the class loader resolves the name of this
175.381 + * interface to the same Class object.
175.382 + */
175.383 + String interfaceName = interfaces[i].getName();
175.384 + Class<?> interfaceClass = null;
175.385 + try {
175.386 + interfaceClass = Class.forName(interfaceName, false, loader);
175.387 + } catch (ClassNotFoundException e) {
175.388 + }
175.389 + if (interfaceClass != interfaces[i]) {
175.390 + throw new IllegalArgumentException(
175.391 + interfaces[i] + " is not visible from class loader");
175.392 + }
175.393 +
175.394 + /*
175.395 + * Verify that the Class object actually represents an
175.396 + * interface.
175.397 + */
175.398 + if (!interfaceClass.isInterface()) {
175.399 + throw new IllegalArgumentException(
175.400 + interfaceClass.getName() + " is not an interface");
175.401 + }
175.402 +
175.403 + /*
175.404 + * Verify that this interface is not a duplicate.
175.405 + */
175.406 + if (interfaceSet.contains(interfaceClass)) {
175.407 + throw new IllegalArgumentException(
175.408 + "repeated interface: " + interfaceClass.getName());
175.409 + }
175.410 + interfaceSet.add(interfaceClass);
175.411 +
175.412 + interfaceNames[i] = interfaceName;
175.413 + }
175.414 +
175.415 + /*
175.416 + * Using string representations of the proxy interfaces as
175.417 + * keys in the proxy class cache (instead of their Class
175.418 + * objects) is sufficient because we require the proxy
175.419 + * interfaces to be resolvable by name through the supplied
175.420 + * class loader, and it has the advantage that using a string
175.421 + * representation of a class makes for an implicit weak
175.422 + * reference to the class.
175.423 + */
175.424 + List<String> key = Arrays.asList(interfaceNames);
175.425 +
175.426 + /*
175.427 + * Find or create the proxy class cache for the class loader.
175.428 + */
175.429 + Map<List<String>, Object> cache;
175.430 + synchronized (loaderToCache) {
175.431 + cache = loaderToCache.get(loader);
175.432 + if (cache == null) {
175.433 + cache = new HashMap<>();
175.434 + loaderToCache.put(loader, cache);
175.435 + }
175.436 + /*
175.437 + * This mapping will remain valid for the duration of this
175.438 + * method, without further synchronization, because the mapping
175.439 + * will only be removed if the class loader becomes unreachable.
175.440 + */
175.441 + }
175.442 +
175.443 + /*
175.444 + * Look up the list of interfaces in the proxy class cache using
175.445 + * the key. This lookup will result in one of three possible
175.446 + * kinds of values:
175.447 + * null, if there is currently no proxy class for the list of
175.448 + * interfaces in the class loader,
175.449 + * the pendingGenerationMarker object, if a proxy class for the
175.450 + * list of interfaces is currently being generated,
175.451 + * or a weak reference to a Class object, if a proxy class for
175.452 + * the list of interfaces has already been generated.
175.453 + */
175.454 + synchronized (cache) {
175.455 + /*
175.456 + * Note that we need not worry about reaping the cache for
175.457 + * entries with cleared weak references because if a proxy class
175.458 + * has been garbage collected, its class loader will have been
175.459 + * garbage collected as well, so the entire cache will be reaped
175.460 + * from the loaderToCache map.
175.461 + */
175.462 + do {
175.463 + Object value = cache.get(key);
175.464 + if (value instanceof Reference) {
175.465 + proxyClass = (Class<?>) ((Reference) value).get();
175.466 + }
175.467 + if (proxyClass != null) {
175.468 + // proxy class already generated: return it
175.469 + return proxyClass;
175.470 + } else if (value == pendingGenerationMarker) {
175.471 + // proxy class being generated: wait for it
175.472 + try {
175.473 + cache.wait();
175.474 + } catch (InterruptedException e) {
175.475 + /*
175.476 + * The class generation that we are waiting for should
175.477 + * take a small, bounded time, so we can safely ignore
175.478 + * thread interrupts here.
175.479 + */
175.480 + }
175.481 + continue;
175.482 + } else {
175.483 + /*
175.484 + * No proxy class for this list of interfaces has been
175.485 + * generated or is being generated, so we will go and
175.486 + * generate it now. Mark it as pending generation.
175.487 + */
175.488 + cache.put(key, pendingGenerationMarker);
175.489 + break;
175.490 + }
175.491 + } while (true);
175.492 + }
175.493 +
175.494 + try {
175.495 + String proxyPkg = null; // package to define proxy class in
175.496 +
175.497 + /*
175.498 + * Record the package of a non-public proxy interface so that the
175.499 + * proxy class will be defined in the same package. Verify that
175.500 + * all non-public proxy interfaces are in the same package.
175.501 + */
175.502 + for (int i = 0; i < interfaces.length; i++) {
175.503 + int flags = interfaces[i].getModifiers();
175.504 + if (!Modifier.isPublic(flags)) {
175.505 + String name = interfaces[i].getName();
175.506 + int n = name.lastIndexOf('.');
175.507 + String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
175.508 + if (proxyPkg == null) {
175.509 + proxyPkg = pkg;
175.510 + } else if (!pkg.equals(proxyPkg)) {
175.511 + throw new IllegalArgumentException(
175.512 + "non-public interfaces from different packages");
175.513 + }
175.514 + }
175.515 + }
175.516 +
175.517 + if (proxyPkg == null) { // if no non-public proxy interfaces,
175.518 + proxyPkg = ""; // use the unnamed package
175.519 + }
175.520 +
175.521 + {
175.522 + /*
175.523 + * Choose a name for the proxy class to generate.
175.524 + */
175.525 + long num;
175.526 + synchronized (nextUniqueNumberLock) {
175.527 + num = nextUniqueNumber++;
175.528 + }
175.529 + String proxyName = proxyPkg + proxyClassNamePrefix + num;
175.530 + /*
175.531 + * Verify that the class loader hasn't already
175.532 + * defined a class with the chosen name.
175.533 + */
175.534 +
175.535 + /*
175.536 + * Generate the specified proxy class.
175.537 + */
175.538 + Generator gen = new Generator(proxyName, interfaces);
175.539 + final byte[] proxyClassFile = gen.generateClassFile();
175.540 + try {
175.541 + proxyClass = defineClass0(loader, proxyName,
175.542 + proxyClassFile);
175.543 + } catch (ClassFormatError e) {
175.544 + /*
175.545 + * A ClassFormatError here means that (barring bugs in the
175.546 + * proxy class generation code) there was some other
175.547 + * invalid aspect of the arguments supplied to the proxy
175.548 + * class creation (such as virtual machine limitations
175.549 + * exceeded).
175.550 + */
175.551 + throw new IllegalArgumentException(e.toString());
175.552 + }
175.553 + gen.fillInMethods(proxyClass);
175.554 + }
175.555 + // add to set of all generated proxy classes, for isProxyClass
175.556 + proxyClasses.put(proxyClass, null);
175.557 +
175.558 + } finally {
175.559 + /*
175.560 + * We must clean up the "pending generation" state of the proxy
175.561 + * class cache entry somehow. If a proxy class was successfully
175.562 + * generated, store it in the cache (with a weak reference);
175.563 + * otherwise, remove the reserved entry. In all cases, notify
175.564 + * all waiters on reserved entries in this cache.
175.565 + */
175.566 + synchronized (cache) {
175.567 + if (proxyClass != null) {
175.568 + cache.put(key, new WeakReference<Class<?>>(proxyClass));
175.569 + } else {
175.570 + cache.remove(key);
175.571 + }
175.572 + cache.notifyAll();
175.573 + }
175.574 + }
175.575 + return proxyClass;
175.576 + }
175.577 +
175.578 + /**
175.579 + * Returns an instance of a proxy class for the specified interfaces
175.580 + * that dispatches method invocations to the specified invocation
175.581 + * handler. This method is equivalent to:
175.582 + * <pre>
175.583 + * Proxy.getProxyClass(loader, interfaces).
175.584 + * getConstructor(new Class[] { InvocationHandler.class }).
175.585 + * newInstance(new Object[] { handler });
175.586 + * </pre>
175.587 + *
175.588 + * <p>{@code Proxy.newProxyInstance} throws
175.589 + * {@code IllegalArgumentException} for the same reasons that
175.590 + * {@code Proxy.getProxyClass} does.
175.591 + *
175.592 + * @param loader the class loader to define the proxy class
175.593 + * @param interfaces the list of interfaces for the proxy class
175.594 + * to implement
175.595 + * @param h the invocation handler to dispatch method invocations to
175.596 + * @return a proxy instance with the specified invocation handler of a
175.597 + * proxy class that is defined by the specified class loader
175.598 + * and that implements the specified interfaces
175.599 + * @throws IllegalArgumentException if any of the restrictions on the
175.600 + * parameters that may be passed to {@code getProxyClass}
175.601 + * are violated
175.602 + * @throws NullPointerException if the {@code interfaces} array
175.603 + * argument or any of its elements are {@code null}, or
175.604 + * if the invocation handler, {@code h}, is
175.605 + * {@code null}
175.606 + */
175.607 + public static Object newProxyInstance(ClassLoader loader,
175.608 + Class<?>[] interfaces,
175.609 + InvocationHandler h)
175.610 + throws IllegalArgumentException
175.611 + {
175.612 + if (h == null) {
175.613 + throw new NullPointerException();
175.614 + }
175.615 +
175.616 + /*
175.617 + * Look up or generate the designated proxy class.
175.618 + */
175.619 + Class<?> cl = getProxyClass(loader, interfaces);
175.620 +
175.621 + /*
175.622 + * Invoke its constructor with the designated invocation handler.
175.623 + */
175.624 + try {
175.625 + Constructor cons = cl.getConstructor(constructorParams);
175.626 + return cons.newInstance(new Object[] { h });
175.627 + } catch (NoSuchMethodException e) {
175.628 + throw new InternalError(e.toString());
175.629 + } catch (IllegalAccessException e) {
175.630 + throw new InternalError(e.toString());
175.631 + } catch (InstantiationException e) {
175.632 + throw new InternalError(e.toString());
175.633 + } catch (InvocationTargetException e) {
175.634 + throw new InternalError(e.toString());
175.635 + }
175.636 + }
175.637 +
175.638 + /**
175.639 + * Returns true if and only if the specified class was dynamically
175.640 + * generated to be a proxy class using the {@code getProxyClass}
175.641 + * method or the {@code newProxyInstance} method.
175.642 + *
175.643 + * <p>The reliability of this method is important for the ability
175.644 + * to use it to make security decisions, so its implementation should
175.645 + * not just test if the class in question extends {@code Proxy}.
175.646 + *
175.647 + * @param cl the class to test
175.648 + * @return {@code true} if the class is a proxy class and
175.649 + * {@code false} otherwise
175.650 + * @throws NullPointerException if {@code cl} is {@code null}
175.651 + */
175.652 + public static boolean isProxyClass(Class<?> cl) {
175.653 + if (cl == null) {
175.654 + throw new NullPointerException();
175.655 + }
175.656 +
175.657 + return proxyClasses.containsKey(cl);
175.658 + }
175.659 +
175.660 + /**
175.661 + * Returns the invocation handler for the specified proxy instance.
175.662 + *
175.663 + * @param proxy the proxy instance to return the invocation handler for
175.664 + * @return the invocation handler for the proxy instance
175.665 + * @throws IllegalArgumentException if the argument is not a
175.666 + * proxy instance
175.667 + */
175.668 + public static InvocationHandler getInvocationHandler(Object proxy)
175.669 + throws IllegalArgumentException
175.670 + {
175.671 + /*
175.672 + * Verify that the object is actually a proxy instance.
175.673 + */
175.674 + if (!isProxyClass(proxy.getClass())) {
175.675 + throw new IllegalArgumentException("not a proxy instance");
175.676 + }
175.677 +
175.678 + ProxyImpl p = (ProxyImpl) proxy;
175.679 + return p.h;
175.680 + }
175.681 +
175.682 + @JavaScriptBody(args = { "ignore", "name", "byteCode" },
175.683 + body = "return vm._reload(name, byteCode).constructor.$class;"
175.684 + )
175.685 + private static native Class defineClass0(
175.686 + ClassLoader loader, String name, byte[] b
175.687 + );
175.688 +
175.689 + private static class Generator {
175.690 + /*
175.691 + * In the comments below, "JVMS" refers to The Java Virtual Machine
175.692 + * Specification Second Edition and "JLS" refers to the original
175.693 + * version of The Java Language Specification, unless otherwise
175.694 + * specified.
175.695 + */
175.696 +
175.697 + /* need 1.6 bytecode */
175.698 + private static final int CLASSFILE_MAJOR_VERSION = 50;
175.699 + private static final int CLASSFILE_MINOR_VERSION = 0;
175.700 +
175.701 + /*
175.702 + * beginning of constants copied from
175.703 + * sun.tools.java.RuntimeConstants (which no longer exists):
175.704 + */
175.705 +
175.706 + /* constant pool tags */
175.707 + private static final int CONSTANT_UTF8 = 1;
175.708 + private static final int CONSTANT_UNICODE = 2;
175.709 + private static final int CONSTANT_INTEGER = 3;
175.710 + private static final int CONSTANT_FLOAT = 4;
175.711 + private static final int CONSTANT_LONG = 5;
175.712 + private static final int CONSTANT_DOUBLE = 6;
175.713 + private static final int CONSTANT_CLASS = 7;
175.714 + private static final int CONSTANT_STRING = 8;
175.715 + private static final int CONSTANT_FIELD = 9;
175.716 + private static final int CONSTANT_METHOD = 10;
175.717 + private static final int CONSTANT_INTERFACEMETHOD = 11;
175.718 + private static final int CONSTANT_NAMEANDTYPE = 12;
175.719 +
175.720 + /* access and modifier flags */
175.721 + private static final int ACC_PUBLIC = 0x00000001;
175.722 + private static final int ACC_FINAL = 0x00000010;
175.723 + private static final int ACC_SUPER = 0x00000020;
175.724 +
175.725 + // end of constants copied from sun.tools.java.RuntimeConstants
175.726 + /**
175.727 + * name of the superclass of proxy classes
175.728 + */
175.729 + private final static String superclassName = "java/lang/reflect/Proxy";
175.730 +
175.731 + /**
175.732 + * name of field for storing a proxy instance's invocation handler
175.733 + */
175.734 + private final static String handlerFieldName = "h";
175.735 +
175.736 + /* preloaded Method objects for methods in java.lang.Object */
175.737 + private static Method hashCodeMethod;
175.738 + private static Method equalsMethod;
175.739 + private static Method toStringMethod;
175.740 +
175.741 + static {
175.742 + try {
175.743 + hashCodeMethod = Object.class.getMethod("hashCode");
175.744 + equalsMethod
175.745 + = Object.class.getMethod("equals", new Class[]{Object.class});
175.746 + toStringMethod = Object.class.getMethod("toString");
175.747 + } catch (NoSuchMethodException e) {
175.748 + throw new IllegalStateException(e.getMessage());
175.749 + }
175.750 + }
175.751 +
175.752 + /**
175.753 + * name of proxy class
175.754 + */
175.755 + private String className;
175.756 +
175.757 + /**
175.758 + * proxy interfaces
175.759 + */
175.760 + private Class[] interfaces;
175.761 +
175.762 + /**
175.763 + * constant pool of class being generated
175.764 + */
175.765 + private ConstantPool cp = new ConstantPool();
175.766 +
175.767 + /**
175.768 + * maps method signature string to list of ProxyMethod objects for proxy
175.769 + * methods with that signature
175.770 + */
175.771 + private Map<String, List<ProxyMethod>> proxyMethods
175.772 + = new HashMap<String, List<ProxyMethod>>();
175.773 +
175.774 + /**
175.775 + * count of ProxyMethod objects added to proxyMethods
175.776 + */
175.777 + private int proxyMethodCount = 0;
175.778 +
175.779 + /**
175.780 + * Construct a ProxyGenerator to generate a proxy class with the
175.781 + * specified name and for the given interfaces.
175.782 + *
175.783 + * A ProxyGenerator object contains the state for the ongoing generation
175.784 + * of a particular proxy class.
175.785 + */
175.786 + private Generator(String className, Class[] interfaces) {
175.787 + this.className = className;
175.788 + this.interfaces = interfaces;
175.789 + }
175.790 +
175.791 + /**
175.792 + * Generate a class file for the proxy class. This method drives the
175.793 + * class file generation process.
175.794 + */
175.795 + private byte[] generateClassFile() {
175.796 +
175.797 + /* ============================================================
175.798 + * Step 1: Assemble ProxyMethod objects for all methods to
175.799 + * generate proxy dispatching code for.
175.800 + */
175.801 +
175.802 + /*
175.803 + * Record that proxy methods are needed for the hashCode, equals,
175.804 + * and toString methods of java.lang.Object. This is done before
175.805 + * the methods from the proxy interfaces so that the methods from
175.806 + * java.lang.Object take precedence over duplicate methods in the
175.807 + * proxy interfaces.
175.808 + */
175.809 + addProxyMethod(hashCodeMethod, Object.class);
175.810 + addProxyMethod(equalsMethod, Object.class);
175.811 + addProxyMethod(toStringMethod, Object.class);
175.812 +
175.813 + /*
175.814 + * Now record all of the methods from the proxy interfaces, giving
175.815 + * earlier interfaces precedence over later ones with duplicate
175.816 + * methods.
175.817 + */
175.818 + for (int i = 0; i < interfaces.length; i++) {
175.819 + Method[] methods = interfaces[i].getMethods();
175.820 + for (int j = 0; j < methods.length; j++) {
175.821 + addProxyMethod(methods[j], interfaces[i]);
175.822 + }
175.823 + }
175.824 +
175.825 + /*
175.826 + * For each set of proxy methods with the same signature,
175.827 + * verify that the methods' return types are compatible.
175.828 + */
175.829 + for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
175.830 + checkReturnTypes(sigmethods);
175.831 + }
175.832 +
175.833 + /* ============================================================
175.834 + * Step 2: Assemble FieldInfo and MethodInfo structs for all of
175.835 + * fields and methods in the class we are generating.
175.836 + */
175.837 +
175.838 + // will be done in fillInMethods
175.839 +
175.840 + /* ============================================================
175.841 + * Step 3: Write the final class file.
175.842 + */
175.843 +
175.844 + /*
175.845 + * Make sure that constant pool indexes are reserved for the
175.846 + * following items before starting to write the final class file.
175.847 + */
175.848 + cp.getClass(dotToSlash(className));
175.849 + cp.getClass(superclassName);
175.850 + for (int i = 0; i < interfaces.length; i++) {
175.851 + cp.getClass(dotToSlash(interfaces[i].getName()));
175.852 + }
175.853 +
175.854 + /*
175.855 + * Disallow new constant pool additions beyond this point, since
175.856 + * we are about to write the final constant pool table.
175.857 + */
175.858 + cp.setReadOnly();
175.859 +
175.860 + ByteArrayOutputStream bout = new ByteArrayOutputStream();
175.861 + DataOutputStream dout = new DataOutputStream(bout);
175.862 +
175.863 + try {
175.864 + /*
175.865 + * Write all the items of the "ClassFile" structure.
175.866 + * See JVMS section 4.1.
175.867 + */
175.868 + // u4 magic;
175.869 + dout.writeInt(0xCAFEBABE);
175.870 + // u2 minor_version;
175.871 + dout.writeShort(CLASSFILE_MINOR_VERSION);
175.872 + // u2 major_version;
175.873 + dout.writeShort(CLASSFILE_MAJOR_VERSION);
175.874 +
175.875 + cp.write(dout); // (write constant pool)
175.876 +
175.877 + // u2 access_flags;
175.878 + dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
175.879 + // u2 this_class;
175.880 + dout.writeShort(cp.getClass(dotToSlash(className)));
175.881 + // u2 super_class;
175.882 + dout.writeShort(cp.getClass(superclassName));
175.883 +
175.884 + // u2 interfaces_count;
175.885 + dout.writeShort(interfaces.length);
175.886 + // u2 interfaces[interfaces_count];
175.887 + for (int i = 0; i < interfaces.length; i++) {
175.888 + dout.writeShort(cp.getClass(
175.889 + dotToSlash(interfaces[i].getName())));
175.890 + }
175.891 +
175.892 + // u2 fields_count;
175.893 + dout.writeShort(0);
175.894 +
175.895 + // u2 methods_count;
175.896 + dout.writeShort(0);
175.897 +
175.898 + // u2 attributes_count;
175.899 + dout.writeShort(0); // (no ClassFile attributes for proxy classes)
175.900 +
175.901 + } catch (IOException e) {
175.902 + throw new InternalError("unexpected I/O Exception");
175.903 + }
175.904 +
175.905 + return bout.toByteArray();
175.906 + }
175.907 +
175.908 + @JavaScriptBody(args = { "c", "sig", "method", "primitive" }, body =
175.909 + "var p = c.cnstr.prototype;\n" +
175.910 + "p[sig] = function() {\n" +
175.911 + " var h = this._h();\n" +
175.912 + " var res = h.invoke__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_reflect_Method_2_3Ljava_lang_Object_2(this, method, arguments);\n" +
175.913 + " \n" +
175.914 + " \n" +
175.915 + " return res;\n" +
175.916 + "};"
175.917 + )
175.918 + private static native void defineMethod(Class<?> proxyClass, String sig, Method method, boolean primitive);
175.919 +
175.920 + @JavaScriptBody(args = "c", body =
175.921 + "var h = c.cnstr.cons__VLjava_lang_reflect_InvocationHandler_2 = function(h) {\n"
175.922 + + " c.superclass.cnstr.cons__VLjava_lang_reflect_InvocationHandler_2.call(this, h);\n"
175.923 + + "}\n"
175.924 + + "h.cls = c.cnstr;\n"
175.925 + )
175.926 + private static native void defineConstructor(Class<?> proxyClass);
175.927 +
175.928 + final void fillInMethods(Class<?> proxyClass) {
175.929 + for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
175.930 + for (ProxyMethod pm : sigmethods) {
175.931 + String sig = MethodImpl.toSignature(pm.method);
175.932 + defineMethod(proxyClass, sig, pm.method, pm.method.getReturnType().isPrimitive());
175.933 + }
175.934 + }
175.935 + defineConstructor(proxyClass);
175.936 + }
175.937 +
175.938 + /**
175.939 + * Add another method to be proxied, either by creating a new
175.940 + * ProxyMethod object or augmenting an old one for a duplicate method.
175.941 + *
175.942 + * "fromClass" indicates the proxy interface that the method was found
175.943 + * through, which may be different from (a subinterface of) the method's
175.944 + * "declaring class". Note that the first Method object passed for a
175.945 + * given name and descriptor identifies the Method object (and thus the
175.946 + * declaring class) that will be passed to the invocation handler's
175.947 + * "invoke" method for a given set of duplicate methods.
175.948 + */
175.949 + private void addProxyMethod(Method m, Class fromClass) {
175.950 + String name = m.getName();
175.951 + Class[] parameterTypes = m.getParameterTypes();
175.952 + Class returnType = m.getReturnType();
175.953 + Class[] exceptionTypes = m.getExceptionTypes();
175.954 +
175.955 + String sig = MethodImpl.toSignature(m);
175.956 + List<ProxyMethod> sigmethods = proxyMethods.get(sig);
175.957 + if (sigmethods != null) {
175.958 + for (ProxyMethod pm : sigmethods) {
175.959 + if (returnType == pm.returnType) {
175.960 + /*
175.961 + * Found a match: reduce exception types to the
175.962 + * greatest set of exceptions that can thrown
175.963 + * compatibly with the throws clauses of both
175.964 + * overridden methods.
175.965 + */
175.966 + List<Class<?>> legalExceptions = new ArrayList<Class<?>>();
175.967 + collectCompatibleTypes(
175.968 + exceptionTypes, pm.exceptionTypes, legalExceptions);
175.969 + collectCompatibleTypes(
175.970 + pm.exceptionTypes, exceptionTypes, legalExceptions);
175.971 + pm.exceptionTypes = new Class[legalExceptions.size()];
175.972 + pm.exceptionTypes
175.973 + = legalExceptions.toArray(pm.exceptionTypes);
175.974 + return;
175.975 + }
175.976 + }
175.977 + } else {
175.978 + sigmethods = new ArrayList<ProxyMethod>(3);
175.979 + proxyMethods.put(sig, sigmethods);
175.980 + }
175.981 + sigmethods.add(new ProxyMethod(m, name, parameterTypes, returnType,
175.982 + exceptionTypes, fromClass));
175.983 + }
175.984 +
175.985 + /**
175.986 + * For a given set of proxy methods with the same signature, check that
175.987 + * their return types are compatible according to the Proxy
175.988 + * specification.
175.989 + *
175.990 + * Specifically, if there is more than one such method, then all of the
175.991 + * return types must be reference types, and there must be one return
175.992 + * type that is assignable to each of the rest of them.
175.993 + */
175.994 + private static void checkReturnTypes(List<ProxyMethod> methods) {
175.995 + /*
175.996 + * If there is only one method with a given signature, there
175.997 + * cannot be a conflict. This is the only case in which a
175.998 + * primitive (or void) return type is allowed.
175.999 + */
175.1000 + if (methods.size() < 2) {
175.1001 + return;
175.1002 + }
175.1003 +
175.1004 + /*
175.1005 + * List of return types that are not yet known to be
175.1006 + * assignable from ("covered" by) any of the others.
175.1007 + */
175.1008 + LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<Class<?>>();
175.1009 +
175.1010 + nextNewReturnType:
175.1011 + for (ProxyMethod pm : methods) {
175.1012 + Class<?> newReturnType = pm.returnType;
175.1013 + if (newReturnType.isPrimitive()) {
175.1014 + throw new IllegalArgumentException(
175.1015 + "methods with same signature "
175.1016 + + getFriendlyMethodSignature(pm.methodName,
175.1017 + pm.parameterTypes)
175.1018 + + " but incompatible return types: "
175.1019 + + newReturnType.getName() + " and others");
175.1020 + }
175.1021 + boolean added = false;
175.1022 +
175.1023 + /*
175.1024 + * Compare the new return type to the existing uncovered
175.1025 + * return types.
175.1026 + */
175.1027 + ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
175.1028 + while (liter.hasNext()) {
175.1029 + Class<?> uncoveredReturnType = liter.next();
175.1030 +
175.1031 + /*
175.1032 + * If an existing uncovered return type is assignable
175.1033 + * to this new one, then we can forget the new one.
175.1034 + */
175.1035 + if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
175.1036 + assert !added;
175.1037 + continue nextNewReturnType;
175.1038 + }
175.1039 +
175.1040 + /*
175.1041 + * If the new return type is assignable to an existing
175.1042 + * uncovered one, then should replace the existing one
175.1043 + * with the new one (or just forget the existing one,
175.1044 + * if the new one has already be put in the list).
175.1045 + */
175.1046 + if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
175.1047 + // (we can assume that each return type is unique)
175.1048 + if (!added) {
175.1049 + liter.set(newReturnType);
175.1050 + added = true;
175.1051 + } else {
175.1052 + liter.remove();
175.1053 + }
175.1054 + }
175.1055 + }
175.1056 +
175.1057 + /*
175.1058 + * If we got through the list of existing uncovered return
175.1059 + * types without an assignability relationship, then add
175.1060 + * the new return type to the list of uncovered ones.
175.1061 + */
175.1062 + if (!added) {
175.1063 + uncoveredReturnTypes.add(newReturnType);
175.1064 + }
175.1065 + }
175.1066 +
175.1067 + /*
175.1068 + * We shouldn't end up with more than one return type that is
175.1069 + * not assignable from any of the others.
175.1070 + */
175.1071 + if (uncoveredReturnTypes.size() > 1) {
175.1072 + ProxyMethod pm = methods.get(0);
175.1073 + throw new IllegalArgumentException(
175.1074 + "methods with same signature "
175.1075 + + getFriendlyMethodSignature(pm.methodName, pm.parameterTypes)
175.1076 + + " but incompatible return types: " + uncoveredReturnTypes);
175.1077 + }
175.1078 + }
175.1079 +
175.1080 +
175.1081 + /**
175.1082 + * A ProxyMethod object represents a proxy method in the proxy class
175.1083 + * being generated: a method whose implementation will encode and
175.1084 + * dispatch invocations to the proxy instance's invocation handler.
175.1085 + */
175.1086 + private class ProxyMethod {
175.1087 +
175.1088 + private final Method method;
175.1089 + public String methodName;
175.1090 + public Class[] parameterTypes;
175.1091 + public Class returnType;
175.1092 + public Class[] exceptionTypes;
175.1093 + public Class fromClass;
175.1094 + public String methodFieldName;
175.1095 +
175.1096 + private ProxyMethod(Method m,
175.1097 + String methodName, Class[] parameterTypes,
175.1098 + Class returnType, Class[] exceptionTypes,
175.1099 + Class fromClass
175.1100 + ) {
175.1101 + this.method = m;
175.1102 + this.methodName = methodName;
175.1103 + this.parameterTypes = parameterTypes;
175.1104 + this.returnType = returnType;
175.1105 + this.exceptionTypes = exceptionTypes;
175.1106 + this.fromClass = fromClass;
175.1107 + this.methodFieldName = "m" + proxyMethodCount++;
175.1108 + }
175.1109 +
175.1110 + }
175.1111 +
175.1112 + /*
175.1113 + * ==================== General Utility Methods ====================
175.1114 + */
175.1115 + /**
175.1116 + * Convert a fully qualified class name that uses '.' as the package
175.1117 + * separator, the external representation used by the Java language and
175.1118 + * APIs, to a fully qualified class name that uses '/' as the package
175.1119 + * separator, the representation used in the class file format (see JVMS
175.1120 + * section 4.2).
175.1121 + */
175.1122 + private static String dotToSlash(String name) {
175.1123 + return name.replace('.', '/');
175.1124 + }
175.1125 +
175.1126 + /**
175.1127 + * Return the list of "parameter descriptor" strings enclosed in
175.1128 + * parentheses corresponding to the given parameter types (in other
175.1129 + * words, a method descriptor without a return descriptor). This string
175.1130 + * is useful for constructing string keys for methods without regard to
175.1131 + * their return type.
175.1132 + */
175.1133 + private static String getParameterDescriptors(Class[] parameterTypes) {
175.1134 + StringBuilder desc = new StringBuilder("(");
175.1135 + for (int i = 0; i < parameterTypes.length; i++) {
175.1136 + desc.append(getFieldType(parameterTypes[i]));
175.1137 + }
175.1138 + desc.append(')');
175.1139 + return desc.toString();
175.1140 + }
175.1141 +
175.1142 + /**
175.1143 + * Return the "field type" string for the given type, appropriate for a
175.1144 + * field descriptor, a parameter descriptor, or a return descriptor
175.1145 + * other than "void". See JVMS section 4.3.2.
175.1146 + */
175.1147 + private static String getFieldType(Class type) {
175.1148 + if (type.isPrimitive()) {
175.1149 + return PrimitiveTypeInfo.get(type).baseTypeString;
175.1150 + } else if (type.isArray()) {
175.1151 + /*
175.1152 + * According to JLS 20.3.2, the getName() method on Class does
175.1153 + * return the VM type descriptor format for array classes (only);
175.1154 + * using that should be quicker than the otherwise obvious code:
175.1155 + *
175.1156 + * return "[" + getTypeDescriptor(type.getComponentType());
175.1157 + */
175.1158 + return type.getName().replace('.', '/');
175.1159 + } else {
175.1160 + return "L" + dotToSlash(type.getName()) + ";";
175.1161 + }
175.1162 + }
175.1163 +
175.1164 + /**
175.1165 + * Returns a human-readable string representing the signature of a
175.1166 + * method with the given name and parameter types.
175.1167 + */
175.1168 + private static String getFriendlyMethodSignature(String name,
175.1169 + Class[] parameterTypes) {
175.1170 + StringBuilder sig = new StringBuilder(name);
175.1171 + sig.append('(');
175.1172 + for (int i = 0; i < parameterTypes.length; i++) {
175.1173 + if (i > 0) {
175.1174 + sig.append(',');
175.1175 + }
175.1176 + Class parameterType = parameterTypes[i];
175.1177 + int dimensions = 0;
175.1178 + while (parameterType.isArray()) {
175.1179 + parameterType = parameterType.getComponentType();
175.1180 + dimensions++;
175.1181 + }
175.1182 + sig.append(parameterType.getName());
175.1183 + while (dimensions-- > 0) {
175.1184 + sig.append("[]");
175.1185 + }
175.1186 + }
175.1187 + sig.append(')');
175.1188 + return sig.toString();
175.1189 + }
175.1190 +
175.1191 + /**
175.1192 + * Add to the given list all of the types in the "from" array that are
175.1193 + * not already contained in the list and are assignable to at least one
175.1194 + * of the types in the "with" array.
175.1195 + *
175.1196 + * This method is useful for computing the greatest common set of
175.1197 + * declared exceptions from duplicate methods inherited from different
175.1198 + * interfaces.
175.1199 + */
175.1200 + private static void collectCompatibleTypes(Class<?>[] from,
175.1201 + Class<?>[] with,
175.1202 + List<Class<?>> list) {
175.1203 + for (int i = 0; i < from.length; i++) {
175.1204 + if (!list.contains(from[i])) {
175.1205 + for (int j = 0; j < with.length; j++) {
175.1206 + if (with[j].isAssignableFrom(from[i])) {
175.1207 + list.add(from[i]);
175.1208 + break;
175.1209 + }
175.1210 + }
175.1211 + }
175.1212 + }
175.1213 + }
175.1214 +
175.1215 +
175.1216 + /**
175.1217 + * A PrimitiveTypeInfo object contains assorted information about a
175.1218 + * primitive type in its public fields. The struct for a particular
175.1219 + * primitive type can be obtained using the static "get" method.
175.1220 + */
175.1221 + private static class PrimitiveTypeInfo {
175.1222 +
175.1223 + /**
175.1224 + * "base type" used in various descriptors (see JVMS section 4.3.2)
175.1225 + */
175.1226 + public String baseTypeString;
175.1227 +
175.1228 + /**
175.1229 + * name of corresponding wrapper class
175.1230 + */
175.1231 + public String wrapperClassName;
175.1232 +
175.1233 + /**
175.1234 + * method descriptor for wrapper class "valueOf" factory method
175.1235 + */
175.1236 + public String wrapperValueOfDesc;
175.1237 +
175.1238 + /**
175.1239 + * name of wrapper class method for retrieving primitive value
175.1240 + */
175.1241 + public String unwrapMethodName;
175.1242 +
175.1243 + /**
175.1244 + * descriptor of same method
175.1245 + */
175.1246 + public String unwrapMethodDesc;
175.1247 +
175.1248 + private static Map<Class, PrimitiveTypeInfo> table
175.1249 + = new HashMap<Class, PrimitiveTypeInfo>();
175.1250 +
175.1251 + static {
175.1252 + add(byte.class, Byte.class);
175.1253 + add(char.class, Character.class);
175.1254 + add(double.class, Double.class);
175.1255 + add(float.class, Float.class);
175.1256 + add(int.class, Integer.class);
175.1257 + add(long.class, Long.class);
175.1258 + add(short.class, Short.class);
175.1259 + add(boolean.class, Boolean.class);
175.1260 + }
175.1261 +
175.1262 + private static void add(Class primitiveClass, Class wrapperClass) {
175.1263 + table.put(primitiveClass,
175.1264 + new PrimitiveTypeInfo(primitiveClass, wrapperClass));
175.1265 + }
175.1266 +
175.1267 + private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) {
175.1268 + assert primitiveClass.isPrimitive();
175.1269 +
175.1270 + baseTypeString
175.1271 + = Array.newInstance(primitiveClass, 0)
175.1272 + .getClass().getName().substring(1);
175.1273 + wrapperClassName = dotToSlash(wrapperClass.getName());
175.1274 + wrapperValueOfDesc
175.1275 + = "(" + baseTypeString + ")L" + wrapperClassName + ";";
175.1276 + unwrapMethodName = primitiveClass.getName() + "Value";
175.1277 + unwrapMethodDesc = "()" + baseTypeString;
175.1278 + }
175.1279 +
175.1280 + public static PrimitiveTypeInfo get(Class cl) {
175.1281 + return table.get(cl);
175.1282 + }
175.1283 + }
175.1284 +
175.1285 + /**
175.1286 + * A ConstantPool object represents the constant pool of a class file
175.1287 + * being generated. This representation of a constant pool is designed
175.1288 + * specifically for use by ProxyGenerator; in particular, it assumes
175.1289 + * that constant pool entries will not need to be resorted (for example,
175.1290 + * by their type, as the Java compiler does), so that the final index
175.1291 + * value can be assigned and used when an entry is first created.
175.1292 + *
175.1293 + * Note that new entries cannot be created after the constant pool has
175.1294 + * been written to a class file. To prevent such logic errors, a
175.1295 + * ConstantPool instance can be marked "read only", so that further
175.1296 + * attempts to add new entries will fail with a runtime exception.
175.1297 + *
175.1298 + * See JVMS section 4.4 for more information about the constant pool of
175.1299 + * a class file.
175.1300 + */
175.1301 + private static class ConstantPool {
175.1302 +
175.1303 + /**
175.1304 + * list of constant pool entries, in constant pool index order.
175.1305 + *
175.1306 + * This list is used when writing the constant pool to a stream and
175.1307 + * for assigning the next index value. Note that element 0 of this
175.1308 + * list corresponds to constant pool index 1.
175.1309 + */
175.1310 + private List<Entry> pool = new ArrayList<Entry>(32);
175.1311 +
175.1312 + /**
175.1313 + * maps constant pool data of all types to constant pool indexes.
175.1314 + *
175.1315 + * This map is used to look up the index of an existing entry for
175.1316 + * values of all types.
175.1317 + */
175.1318 + private Map<Object, Short> map = new HashMap<Object, Short>(16);
175.1319 +
175.1320 + /**
175.1321 + * true if no new constant pool entries may be added
175.1322 + */
175.1323 + private boolean readOnly = false;
175.1324 +
175.1325 + /**
175.1326 + * Get or assign the index for a CONSTANT_Utf8 entry.
175.1327 + */
175.1328 + public short getUtf8(String s) {
175.1329 + if (s == null) {
175.1330 + throw new NullPointerException();
175.1331 + }
175.1332 + return getValue(s);
175.1333 + }
175.1334 +
175.1335 + /**
175.1336 + * Get or assign the index for a CONSTANT_Integer entry.
175.1337 + */
175.1338 + public short getInteger(int i) {
175.1339 + return getValue(new Integer(i));
175.1340 + }
175.1341 +
175.1342 + /**
175.1343 + * Get or assign the index for a CONSTANT_Float entry.
175.1344 + */
175.1345 + public short getFloat(float f) {
175.1346 + return getValue(new Float(f));
175.1347 + }
175.1348 +
175.1349 + /**
175.1350 + * Get or assign the index for a CONSTANT_Class entry.
175.1351 + */
175.1352 + public short getClass(String name) {
175.1353 + short utf8Index = getUtf8(name);
175.1354 + return getIndirect(new IndirectEntry(
175.1355 + CONSTANT_CLASS, utf8Index));
175.1356 + }
175.1357 +
175.1358 + /**
175.1359 + * Get or assign the index for a CONSTANT_String entry.
175.1360 + */
175.1361 + public short getString(String s) {
175.1362 + short utf8Index = getUtf8(s);
175.1363 + return getIndirect(new IndirectEntry(
175.1364 + CONSTANT_STRING, utf8Index));
175.1365 + }
175.1366 +
175.1367 + /**
175.1368 + * Get or assign the index for a CONSTANT_FieldRef entry.
175.1369 + */
175.1370 + public short getFieldRef(String className,
175.1371 + String name, String descriptor) {
175.1372 + short classIndex = getClass(className);
175.1373 + short nameAndTypeIndex = getNameAndType(name, descriptor);
175.1374 + return getIndirect(new IndirectEntry(
175.1375 + CONSTANT_FIELD, classIndex, nameAndTypeIndex));
175.1376 + }
175.1377 +
175.1378 + /**
175.1379 + * Get or assign the index for a CONSTANT_MethodRef entry.
175.1380 + */
175.1381 + public short getMethodRef(String className,
175.1382 + String name, String descriptor) {
175.1383 + short classIndex = getClass(className);
175.1384 + short nameAndTypeIndex = getNameAndType(name, descriptor);
175.1385 + return getIndirect(new IndirectEntry(
175.1386 + CONSTANT_METHOD, classIndex, nameAndTypeIndex));
175.1387 + }
175.1388 +
175.1389 + /**
175.1390 + * Get or assign the index for a CONSTANT_InterfaceMethodRef entry.
175.1391 + */
175.1392 + public short getInterfaceMethodRef(String className, String name,
175.1393 + String descriptor) {
175.1394 + short classIndex = getClass(className);
175.1395 + short nameAndTypeIndex = getNameAndType(name, descriptor);
175.1396 + return getIndirect(new IndirectEntry(
175.1397 + CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex));
175.1398 + }
175.1399 +
175.1400 + /**
175.1401 + * Get or assign the index for a CONSTANT_NameAndType entry.
175.1402 + */
175.1403 + public short getNameAndType(String name, String descriptor) {
175.1404 + short nameIndex = getUtf8(name);
175.1405 + short descriptorIndex = getUtf8(descriptor);
175.1406 + return getIndirect(new IndirectEntry(
175.1407 + CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex));
175.1408 + }
175.1409 +
175.1410 + /**
175.1411 + * Set this ConstantPool instance to be "read only".
175.1412 + *
175.1413 + * After this method has been called, further requests to get an
175.1414 + * index for a non-existent entry will cause an InternalError to be
175.1415 + * thrown instead of creating of the entry.
175.1416 + */
175.1417 + public void setReadOnly() {
175.1418 + readOnly = true;
175.1419 + }
175.1420 +
175.1421 + /**
175.1422 + * Write this constant pool to a stream as part of the class file
175.1423 + * format.
175.1424 + *
175.1425 + * This consists of writing the "constant_pool_count" and
175.1426 + * "constant_pool[]" items of the "ClassFile" structure, as
175.1427 + * described in JVMS section 4.1.
175.1428 + */
175.1429 + public void write(OutputStream out) throws IOException {
175.1430 + DataOutputStream dataOut = new DataOutputStream(out);
175.1431 +
175.1432 + // constant_pool_count: number of entries plus one
175.1433 + dataOut.writeShort(pool.size() + 1);
175.1434 +
175.1435 + for (Entry e : pool) {
175.1436 + e.write(dataOut);
175.1437 + }
175.1438 + }
175.1439 +
175.1440 + /**
175.1441 + * Add a new constant pool entry and return its index.
175.1442 + */
175.1443 + private short addEntry(Entry entry) {
175.1444 + pool.add(entry);
175.1445 + /*
175.1446 + * Note that this way of determining the index of the
175.1447 + * added entry is wrong if this pool supports
175.1448 + * CONSTANT_Long or CONSTANT_Double entries.
175.1449 + */
175.1450 + if (pool.size() >= 65535) {
175.1451 + throw new IllegalArgumentException(
175.1452 + "constant pool size limit exceeded");
175.1453 + }
175.1454 + return (short) pool.size();
175.1455 + }
175.1456 +
175.1457 + /**
175.1458 + * Get or assign the index for an entry of a type that contains a
175.1459 + * direct value. The type of the given object determines the type of
175.1460 + * the desired entry as follows:
175.1461 + *
175.1462 + * java.lang.String CONSTANT_Utf8 java.lang.Integer CONSTANT_Integer
175.1463 + * java.lang.Float CONSTANT_Float java.lang.Long CONSTANT_Long
175.1464 + * java.lang.Double CONSTANT_DOUBLE
175.1465 + */
175.1466 + private short getValue(Object key) {
175.1467 + Short index = map.get(key);
175.1468 + if (index != null) {
175.1469 + return index.shortValue();
175.1470 + } else {
175.1471 + if (readOnly) {
175.1472 + throw new InternalError(
175.1473 + "late constant pool addition: " + key);
175.1474 + }
175.1475 + short i = addEntry(new ValueEntry(key));
175.1476 + map.put(key, new Short(i));
175.1477 + return i;
175.1478 + }
175.1479 + }
175.1480 +
175.1481 + /**
175.1482 + * Get or assign the index for an entry of a type that contains
175.1483 + * references to other constant pool entries.
175.1484 + */
175.1485 + private short getIndirect(IndirectEntry e) {
175.1486 + Short index = map.get(e);
175.1487 + if (index != null) {
175.1488 + return index.shortValue();
175.1489 + } else {
175.1490 + if (readOnly) {
175.1491 + throw new InternalError("late constant pool addition");
175.1492 + }
175.1493 + short i = addEntry(e);
175.1494 + map.put(e, new Short(i));
175.1495 + return i;
175.1496 + }
175.1497 + }
175.1498 +
175.1499 + /**
175.1500 + * Entry is the abstact superclass of all constant pool entry types
175.1501 + * that can be stored in the "pool" list; its purpose is to define a
175.1502 + * common method for writing constant pool entries to a class file.
175.1503 + */
175.1504 + private static abstract class Entry {
175.1505 +
175.1506 + public abstract void write(DataOutputStream out)
175.1507 + throws IOException;
175.1508 + }
175.1509 +
175.1510 + /**
175.1511 + * ValueEntry represents a constant pool entry of a type that
175.1512 + * contains a direct value (see the comments for the "getValue"
175.1513 + * method for a list of such types).
175.1514 + *
175.1515 + * ValueEntry objects are not used as keys for their entries in the
175.1516 + * Map "map", so no useful hashCode or equals methods are defined.
175.1517 + */
175.1518 + private static class ValueEntry extends Entry {
175.1519 +
175.1520 + private Object value;
175.1521 +
175.1522 + public ValueEntry(Object value) {
175.1523 + this.value = value;
175.1524 + }
175.1525 +
175.1526 + public void write(DataOutputStream out) throws IOException {
175.1527 + if (value instanceof String) {
175.1528 + out.writeByte(CONSTANT_UTF8);
175.1529 + out.writeUTF((String) value);
175.1530 + } else if (value instanceof Integer) {
175.1531 + out.writeByte(CONSTANT_INTEGER);
175.1532 + out.writeInt(((Integer) value).intValue());
175.1533 + } else if (value instanceof Float) {
175.1534 + out.writeByte(CONSTANT_FLOAT);
175.1535 + out.writeFloat(((Float) value).floatValue());
175.1536 + } else if (value instanceof Long) {
175.1537 + out.writeByte(CONSTANT_LONG);
175.1538 + out.writeLong(((Long) value).longValue());
175.1539 + } else if (value instanceof Double) {
175.1540 + out.writeDouble(CONSTANT_DOUBLE);
175.1541 + out.writeDouble(((Double) value).doubleValue());
175.1542 + } else {
175.1543 + throw new InternalError("bogus value entry: " + value);
175.1544 + }
175.1545 + }
175.1546 + }
175.1547 +
175.1548 + /**
175.1549 + * IndirectEntry represents a constant pool entry of a type that
175.1550 + * references other constant pool entries, i.e., the following
175.1551 + * types:
175.1552 + *
175.1553 + * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref,
175.1554 + * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and
175.1555 + * CONSTANT_NameAndType.
175.1556 + *
175.1557 + * Each of these entry types contains either one or two indexes of
175.1558 + * other constant pool entries.
175.1559 + *
175.1560 + * IndirectEntry objects are used as the keys for their entries in
175.1561 + * the Map "map", so the hashCode and equals methods are overridden
175.1562 + * to allow matching.
175.1563 + */
175.1564 + private static class IndirectEntry extends Entry {
175.1565 +
175.1566 + private int tag;
175.1567 + private short index0;
175.1568 + private short index1;
175.1569 +
175.1570 + /**
175.1571 + * Construct an IndirectEntry for a constant pool entry type
175.1572 + * that contains one index of another entry.
175.1573 + */
175.1574 + public IndirectEntry(int tag, short index) {
175.1575 + this.tag = tag;
175.1576 + this.index0 = index;
175.1577 + this.index1 = 0;
175.1578 + }
175.1579 +
175.1580 + /**
175.1581 + * Construct an IndirectEntry for a constant pool entry type
175.1582 + * that contains two indexes for other entries.
175.1583 + */
175.1584 + public IndirectEntry(int tag, short index0, short index1) {
175.1585 + this.tag = tag;
175.1586 + this.index0 = index0;
175.1587 + this.index1 = index1;
175.1588 + }
175.1589 +
175.1590 + public void write(DataOutputStream out) throws IOException {
175.1591 + out.writeByte(tag);
175.1592 + out.writeShort(index0);
175.1593 + /*
175.1594 + * If this entry type contains two indexes, write
175.1595 + * out the second, too.
175.1596 + */
175.1597 + if (tag == CONSTANT_FIELD
175.1598 + || tag == CONSTANT_METHOD
175.1599 + || tag == CONSTANT_INTERFACEMETHOD
175.1600 + || tag == CONSTANT_NAMEANDTYPE) {
175.1601 + out.writeShort(index1);
175.1602 + }
175.1603 + }
175.1604 +
175.1605 + public int hashCode() {
175.1606 + return tag + index0 + index1;
175.1607 + }
175.1608 +
175.1609 + public boolean equals(Object obj) {
175.1610 + if (obj instanceof IndirectEntry) {
175.1611 + IndirectEntry other = (IndirectEntry) obj;
175.1612 + if (tag == other.tag
175.1613 + && index0 == other.index0 && index1 == other.index1) {
175.1614 + return true;
175.1615 + }
175.1616 + }
175.1617 + return false;
175.1618 + }
175.1619 + }
175.1620 + }
175.1621 + }
175.1622 +
175.1623 +}
176.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java Tue Feb 11 10:48:24 2014 +0100
176.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java Tue Feb 11 13:31:42 2014 +0100
176.3 @@ -20,6 +20,7 @@
176.4 import java.util.ArrayList;
176.5 import java.util.Arrays;
176.6 import java.util.Collection;
176.7 +import java.util.Collections;
176.8 import java.util.Comparator;
176.9 import java.util.HashMap;
176.10 import java.util.HashSet;
176.11 @@ -96,6 +97,7 @@
176.12
176.13 List<Entry<String,Integer>> arr = new Vector<>();
176.14 arr.addAll(map.entrySet());
176.15 + Collections.sort(arr, new C());
176.16 return arr.toString();
176.17 }
176.18
177.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
177.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/AtomicTest.java Tue Feb 11 13:31:42 2014 +0100
177.3 @@ -0,0 +1,54 @@
177.4 +/**
177.5 + * Back 2 Browser Bytecode Translator
177.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
177.7 + *
177.8 + * This program is free software: you can redistribute it and/or modify
177.9 + * it under the terms of the GNU General Public License as published by
177.10 + * the Free Software Foundation, version 2 of the License.
177.11 + *
177.12 + * This program is distributed in the hope that it will be useful,
177.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
177.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
177.15 + * GNU General Public License for more details.
177.16 + *
177.17 + * You should have received a copy of the GNU General Public License
177.18 + * along with this program. Look for COPYING file in the top folder.
177.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
177.20 + */
177.21 +package org.apidesign.bck2brwsr.tck;
177.22 +
177.23 +import java.util.concurrent.atomic.AtomicBoolean;
177.24 +import java.util.concurrent.atomic.AtomicInteger;
177.25 +import java.util.concurrent.atomic.AtomicReference;
177.26 +import org.apidesign.bck2brwsr.vmtest.Compare;
177.27 +import org.apidesign.bck2brwsr.vmtest.VMTest;
177.28 +import org.testng.annotations.Factory;
177.29 +
177.30 +/**
177.31 + *
177.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
177.33 + */
177.34 +public class AtomicTest {
177.35 + @Compare public boolean atomicBoolean() {
177.36 + AtomicBoolean ab = new AtomicBoolean();
177.37 + ab.set(true);
177.38 + return ab.compareAndSet(true, false);
177.39 + }
177.40 +
177.41 + @Compare public int atomicInt() {
177.42 + AtomicInteger ab = new AtomicInteger();
177.43 + ab.set(30);
177.44 + assert ab.compareAndSet(30, 10);
177.45 + return ab.get();
177.46 + }
177.47 +
177.48 + @Compare public String atomicRef() {
177.49 + AtomicReference<String> ar = new AtomicReference<String>("Ahoj");
177.50 + assert ar.compareAndSet("Ahoj", "Hello");
177.51 + return ar.getAndSet("Other");
177.52 + }
177.53 +
177.54 + @Factory public static Object[] create() {
177.55 + return VMTest.create(AtomicTest.class);
177.56 + }
177.57 +}
178.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
178.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CharacterTest.java Tue Feb 11 13:31:42 2014 +0100
178.3 @@ -0,0 +1,51 @@
178.4 +/**
178.5 + * Back 2 Browser Bytecode Translator
178.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
178.7 + *
178.8 + * This program is free software: you can redistribute it and/or modify
178.9 + * it under the terms of the GNU General Public License as published by
178.10 + * the Free Software Foundation, version 2 of the License.
178.11 + *
178.12 + * This program is distributed in the hope that it will be useful,
178.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
178.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
178.15 + * GNU General Public License for more details.
178.16 + *
178.17 + * You should have received a copy of the GNU General Public License
178.18 + * along with this program. Look for COPYING file in the top folder.
178.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
178.20 + */
178.21 +package org.apidesign.bck2brwsr.tck;
178.22 +
178.23 +import org.apidesign.bck2brwsr.vmtest.Compare;
178.24 +import org.apidesign.bck2brwsr.vmtest.VMTest;
178.25 +import org.testng.annotations.Factory;
178.26 +
178.27 +/**
178.28 + *
178.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
178.30 + */
178.31 +public class CharacterTest {
178.32 + @Compare public boolean dolarJavaStart() {
178.33 + return Character.isJavaIdentifierStart('$');
178.34 + }
178.35 +
178.36 + @Compare public boolean dolarJavaPart() {
178.37 + return Character.isJavaIdentifierPart('$');
178.38 + }
178.39 +
178.40 + @Compare public boolean numberJavaStart() {
178.41 + return Character.isJavaIdentifierStart('3');
178.42 + }
178.43 +
178.44 + @Compare public boolean numberJavaPart() {
178.45 + return Character.isJavaIdentifierPart('3');
178.46 + }
178.47 +
178.48 +
178.49 + @Factory
178.50 + public static Object[] create() {
178.51 + return VMTest.create(CharacterTest.class);
178.52 + }
178.53 +
178.54 +}
179.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
179.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ClassLoaderTest.java Tue Feb 11 13:31:42 2014 +0100
179.3 @@ -0,0 +1,41 @@
179.4 +/**
179.5 + * Back 2 Browser Bytecode Translator
179.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
179.7 + *
179.8 + * This program is free software: you can redistribute it and/or modify
179.9 + * it under the terms of the GNU General Public License as published by
179.10 + * the Free Software Foundation, version 2 of the License.
179.11 + *
179.12 + * This program is distributed in the hope that it will be useful,
179.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
179.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
179.15 + * GNU General Public License for more details.
179.16 + *
179.17 + * You should have received a copy of the GNU General Public License
179.18 + * along with this program. Look for COPYING file in the top folder.
179.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
179.20 + */
179.21 +package org.apidesign.bck2brwsr.tck;
179.22 +
179.23 +import org.apidesign.bck2brwsr.vmtest.Compare;
179.24 +import org.apidesign.bck2brwsr.vmtest.VMTest;
179.25 +import org.testng.annotations.Factory;
179.26 +
179.27 +/**
179.28 + *
179.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
179.30 + */
179.31 +public class ClassLoaderTest {
179.32 + @Compare public Object unknownResource() {
179.33 + return ClassLoader.getSystemResource("really/unknown/resource.txt");
179.34 + }
179.35 +
179.36 + @Compare public boolean indenpotentSetOfClassloaderIsOK() {
179.37 + Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
179.38 + return Thread.currentThread().getContextClassLoader() == ClassLoader.getSystemClassLoader();
179.39 + }
179.40 +
179.41 + @Factory public static Object[] create() {
179.42 + return VMTest.create(ClassLoaderTest.class);
179.43 + }
179.44 +}
180.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Tue Feb 11 10:48:24 2014 +0100
180.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Tue Feb 11 13:31:42 2014 +0100
180.3 @@ -19,7 +19,9 @@
180.4
180.5 import java.io.UnsupportedEncodingException;
180.6 import java.net.MalformedURLException;
180.7 +import java.net.URISyntaxException;
180.8 import java.net.URL;
180.9 +import java.util.Locale;
180.10 import org.apidesign.bck2brwsr.vmtest.Compare;
180.11 import org.apidesign.bck2brwsr.vmtest.VMTest;
180.12 import org.testng.annotations.Factory;
180.13 @@ -47,6 +49,14 @@
180.14 return "Ahoj".equals(null);
180.15 }
180.16
180.17 + @Compare public boolean internIsSame() {
180.18 + return new String("Ahoj").intern() == another();
180.19 + }
180.20 +
180.21 + private static String another() {
180.22 + return new String("Ahoj").intern();
180.23 + }
180.24 +
180.25 @Compare public int highByteLenght() {
180.26 byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 };
180.27 return new String(arr, 0).length();
180.28 @@ -63,6 +73,10 @@
180.29 @Compare public static Object compareURLs() throws MalformedURLException {
180.30 return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString();
180.31 }
180.32 +
180.33 + @Compare public static Object compareURLsViaURIs() throws Exception {
180.34 + return new URL("http://apidesign.org:8080/wiki/").toURI().toString();
180.35 + }
180.36
180.37 @Compare public String deleteLastTwoCharacters() {
180.38 StringBuilder sb = new StringBuilder();
180.39 @@ -161,7 +175,28 @@
180.40 assert res.equals("ba") : "Expecting ba: " + res;
180.41 return res;
180.42 }
180.43 +
180.44 + @Compare public String localeUS() {
180.45 + return Locale.US.toString();
180.46 + }
180.47 +
180.48 + @Compare public String localeFrench() {
180.49 + return Locale.FRENCH.toString();
180.50 + }
180.51 +
180.52 +
180.53 + @Compare public String formatSimple() {
180.54 + return String.format((Locale)null, "Hello %s!", "World");
180.55 + }
180.56
180.57 + @Compare public String replaceWithItself() {
180.58 + return "org.apidesign.bck2brwsr.core.JavaScriptBody".replace(".", "\\.");
180.59 + }
180.60 +
180.61 + @Compare public boolean matchWithComplicatedRegExp() {
180.62 + return "Activates this model instance.".matches("(?sm).*^\\s*@deprecated( |$).*");
180.63 + }
180.64 +
180.65 @Factory
180.66 public static Object[] create() {
180.67 return VMTest.create(CompareStringsTest.class);
181.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
181.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ConcurrentTest.java Tue Feb 11 13:31:42 2014 +0100
181.3 @@ -0,0 +1,40 @@
181.4 +/**
181.5 + * Back 2 Browser Bytecode Translator
181.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
181.7 + *
181.8 + * This program is free software: you can redistribute it and/or modify
181.9 + * it under the terms of the GNU General Public License as published by
181.10 + * the Free Software Foundation, version 2 of the License.
181.11 + *
181.12 + * This program is distributed in the hope that it will be useful,
181.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
181.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
181.15 + * GNU General Public License for more details.
181.16 + *
181.17 + * You should have received a copy of the GNU General Public License
181.18 + * along with this program. Look for COPYING file in the top folder.
181.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
181.20 + */
181.21 +package org.apidesign.bck2brwsr.tck;
181.22 +
181.23 +import java.util.concurrent.ConcurrentHashMap;
181.24 +import org.apidesign.bck2brwsr.vmtest.Compare;
181.25 +import org.apidesign.bck2brwsr.vmtest.VMTest;
181.26 +import org.testng.annotations.Factory;
181.27 +
181.28 +/**
181.29 + *
181.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
181.31 + */
181.32 +public class ConcurrentTest {
181.33 + @Compare public String mapIfAbsent() {
181.34 + ConcurrentHashMap<String,String> m = new ConcurrentHashMap<>();
181.35 + m.putIfAbsent("Ahoj", "Jardo");
181.36 + m.putIfAbsent("Ahoj", "Dardo");
181.37 + return m.get("Ahoj");
181.38 + }
181.39 +
181.40 + @Factory public static Object[] create() {
181.41 + return VMTest.create(ConcurrentTest.class);
181.42 + }
181.43 +}
182.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java Tue Feb 11 10:48:24 2014 +0100
182.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java Tue Feb 11 13:31:42 2014 +0100
182.3 @@ -29,6 +29,10 @@
182.4 @Compare public boolean parsedDoubleIsDouble() {
182.5 return Double.valueOf("1.1") instanceof Double;
182.6 }
182.7 +
182.8 + @Compare public boolean parsedFloatIsFloat() {
182.9 + return Float.valueOf("1.1") instanceof Float;
182.10 + }
182.11
182.12 @Compare public String integerToString() {
182.13 return toStr(1);
183.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
183.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/EnumsTest.java Tue Feb 11 13:31:42 2014 +0100
183.3 @@ -0,0 +1,76 @@
183.4 +/**
183.5 + * Back 2 Browser Bytecode Translator
183.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
183.7 + *
183.8 + * This program is free software: you can redistribute it and/or modify
183.9 + * it under the terms of the GNU General Public License as published by
183.10 + * the Free Software Foundation, version 2 of the License.
183.11 + *
183.12 + * This program is distributed in the hope that it will be useful,
183.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
183.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
183.15 + * GNU General Public License for more details.
183.16 + *
183.17 + * You should have received a copy of the GNU General Public License
183.18 + * along with this program. Look for COPYING file in the top folder.
183.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
183.20 + */
183.21 +package org.apidesign.bck2brwsr.tck;
183.22 +
183.23 +import java.util.EnumMap;
183.24 +import java.util.EnumSet;
183.25 +import org.apidesign.bck2brwsr.vmtest.Compare;
183.26 +import org.apidesign.bck2brwsr.vmtest.VMTest;
183.27 +import org.testng.annotations.Factory;
183.28 +
183.29 +/**
183.30 + *
183.31 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
183.32 + */
183.33 +public class EnumsTest {
183.34 + enum Color {
183.35 + B, W;
183.36 + }
183.37 +
183.38 + @Compare public String enumSet() {
183.39 + try { throw new Exception(); } catch (Exception ex) {}
183.40 + EnumSet<Color> c = EnumSet.allOf(Color.class);
183.41 + return c.toString();
183.42 + }
183.43 +
183.44 + @Compare public String enumSetOneByOne() {
183.45 + EnumSet<Color> c = EnumSet.of(Color.B, Color.W);
183.46 + return c.toString();
183.47 + }
183.48 +
183.49 + @Compare public boolean enumFirstContains() {
183.50 + EnumSet<Color> c = EnumSet.of(Color.B);
183.51 + return c.contains(Color.B);
183.52 + }
183.53 +
183.54 + @Compare public boolean enumFirstDoesNotContains() {
183.55 + EnumSet<Color> c = EnumSet.of(Color.B);
183.56 + return c.contains(Color.W);
183.57 + }
183.58 +
183.59 + @Compare public boolean enumSndContains() {
183.60 + EnumSet<Color> c = EnumSet.of(Color.W);
183.61 + return c.contains(Color.W);
183.62 + }
183.63 +
183.64 + @Compare public boolean enumSecondDoesNotContains() {
183.65 + EnumSet<Color> c = EnumSet.of(Color.W);
183.66 + return c.contains(Color.B);
183.67 + }
183.68 +
183.69 + @Compare public String enumMap() {
183.70 + EnumMap<Color,String> c = new EnumMap(Color.class);
183.71 + c.put(Color.B, "Black");
183.72 + c.put(Color.W, "White");
183.73 + return c.toString();
183.74 + }
183.75 +
183.76 + @Factory public static Object[] create() {
183.77 + return VMTest.create(EnumsTest.class);
183.78 + }
183.79 +}
184.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
184.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ExceptionsTest.java Tue Feb 11 13:31:42 2014 +0100
184.3 @@ -0,0 +1,68 @@
184.4 +/**
184.5 + * Back 2 Browser Bytecode Translator
184.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
184.7 + *
184.8 + * This program is free software: you can redistribute it and/or modify
184.9 + * it under the terms of the GNU General Public License as published by
184.10 + * the Free Software Foundation, version 2 of the License.
184.11 + *
184.12 + * This program is distributed in the hope that it will be useful,
184.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
184.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
184.15 + * GNU General Public License for more details.
184.16 + *
184.17 + * You should have received a copy of the GNU General Public License
184.18 + * along with this program. Look for COPYING file in the top folder.
184.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
184.20 + */
184.21 +package org.apidesign.bck2brwsr.tck;
184.22 +
184.23 +import java.io.ByteArrayOutputStream;
184.24 +import java.io.PrintStream;
184.25 +import java.io.PrintWriter;
184.26 +import java.io.StringWriter;
184.27 +import java.io.UnsupportedEncodingException;
184.28 +import org.apidesign.bck2brwsr.vmtest.Compare;
184.29 +import org.apidesign.bck2brwsr.vmtest.VMTest;
184.30 +import org.testng.annotations.Factory;
184.31 +
184.32 +/**
184.33 + *
184.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
184.35 + */
184.36 +public class ExceptionsTest {
184.37 + @Compare public String firstLineIsTheSame() throws UnsupportedEncodingException {
184.38 + MyException ex = new MyException("Hello");
184.39 + ByteArrayOutputStream out = new ByteArrayOutputStream();
184.40 + PrintStream ps = new PrintStream(out);
184.41 + ex.printStackTrace(ps);
184.42 + ps.flush();
184.43 +
184.44 + String s = new String(out.toByteArray(), "UTF-8");
184.45 + int newLine = s.indexOf('\n');
184.46 + return s.substring(0, newLine);
184.47 + }
184.48 +
184.49 + @Compare public String firstLineIsTheSameWithWriter() throws UnsupportedEncodingException {
184.50 + MyException ex = new MyException("Hello");
184.51 + StringWriter sw = new StringWriter();
184.52 + PrintWriter pw = new PrintWriter(sw);
184.53 + ex.printStackTrace(pw);
184.54 + pw.flush();
184.55 +
184.56 + String s = sw.toString();
184.57 + int newLine = s.indexOf('\n');
184.58 + return s.substring(0, newLine);
184.59 + }
184.60 +
184.61 + static class MyException extends Exception {
184.62 + public MyException(String message) {
184.63 + super(message);
184.64 + }
184.65 + }
184.66 +
184.67 +
184.68 + @Factory public static Object[] create() {
184.69 + return VMTest.create(ExceptionsTest.class);
184.70 + }
184.71 +}
185.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java Tue Feb 11 10:48:24 2014 +0100
185.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java Tue Feb 11 13:31:42 2014 +0100
185.3 @@ -304,6 +304,14 @@
185.4 @Compare public long shiftL3() {
185.5 return shl(0x00fa37d7763e0ca1l, 45);
185.6 }
185.7 +
185.8 + @Compare public long shiftL4() {
185.9 + return shl(0x00fa37d7763e0ca1l, 0);
185.10 + }
185.11 +
185.12 + @Compare public long shiftL5() {
185.13 + return shl(0x00fa37d7763e0ca1l, 70);
185.14 + }
185.15
185.16 @Compare public long shiftR1() {
185.17 return shr(0x00fa37d7763e0ca1l, 5);
185.18 @@ -316,6 +324,14 @@
185.19 @Compare public long shiftR3() {
185.20 return shr(0x00fa37d7763e0ca1l, 45);
185.21 }
185.22 +
185.23 + @Compare public long shiftR4() {
185.24 + return shr(0x00fa37d7763e0ca1l, 0);
185.25 + }
185.26 +
185.27 + @Compare public long shiftR5() {
185.28 + return shr(0x00fa37d7763e0ca1l, 70);
185.29 + }
185.30
185.31 @Compare public long uShiftR1() {
185.32 return ushr(0x00fa37d7763e0ca1l, 5);
185.33 @@ -324,14 +340,30 @@
185.34 @Compare public long uShiftR2() {
185.35 return ushr(0x00fa37d7763e0ca1l, 45);
185.36 }
185.37 +
185.38 + @Compare public long uShiftR3() {
185.39 + return ushr(0x00fa37d7763e0ca1l, 0);
185.40 + }
185.41 +
185.42 + @Compare public long uShiftR4() {
185.43 + return ushr(0x00fa37d7763e0ca1l, 70);
185.44 + }
185.45
185.46 - @Compare public long uShiftR3() {
185.47 + @Compare public long uShiftR5() {
185.48 return ushr(0xf0fa37d7763e0ca1l, 5);
185.49 }
185.50
185.51 - @Compare public long uShiftR4() {
185.52 + @Compare public long uShiftR6() {
185.53 return ushr(0xf0fa37d7763e0ca1l, 45);
185.54 }
185.55 +
185.56 + @Compare public long uShiftR7() {
185.57 + return ushr(0xf0fa37d7763e0ca1l, 0);
185.58 + }
185.59 +
185.60 + @Compare public long uShiftR8() {
185.61 + return ushr(0xf0fa37d7763e0ca1l, 70);
185.62 + }
185.63
185.64 @Compare public long and1() {
185.65 return and(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el);
186.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
186.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ProxyTest.java Tue Feb 11 13:31:42 2014 +0100
186.3 @@ -0,0 +1,71 @@
186.4 +/**
186.5 + * Back 2 Browser Bytecode Translator
186.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
186.7 + *
186.8 + * This program is free software: you can redistribute it and/or modify
186.9 + * it under the terms of the GNU General Public License as published by
186.10 + * the Free Software Foundation, version 2 of the License.
186.11 + *
186.12 + * This program is distributed in the hope that it will be useful,
186.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
186.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
186.15 + * GNU General Public License for more details.
186.16 + *
186.17 + * You should have received a copy of the GNU General Public License
186.18 + * along with this program. Look for COPYING file in the top folder.
186.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
186.20 + */
186.21 +package org.apidesign.bck2brwsr.tck;
186.22 +
186.23 +import java.lang.reflect.InvocationHandler;
186.24 +import java.lang.reflect.Method;
186.25 +import java.lang.reflect.Proxy;
186.26 +import org.apidesign.bck2brwsr.vmtest.Compare;
186.27 +import org.apidesign.bck2brwsr.vmtest.VMTest;
186.28 +import org.testng.annotations.Factory;
186.29 +
186.30 +/**
186.31 + *
186.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
186.33 + */
186.34 +public class ProxyTest {
186.35 + @Compare public String generateAnnotation() throws Exception {
186.36 + class InvHandler implements InvocationHandler {
186.37 + @Override
186.38 + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
186.39 + return "Joe Hacker";
186.40 + }
186.41 + }
186.42 + Anno anno = (Anno) Proxy.newProxyInstance(
186.43 + Anno.class.getClassLoader(),
186.44 + new Class[] { Anno.class },
186.45 + new InvHandler()
186.46 + );
186.47 + return anno.name();
186.48 + }
186.49 +
186.50 + @Compare public int getPrimitiveType() throws Exception {
186.51 + class InvHandler implements InvocationHandler {
186.52 + @Override
186.53 + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
186.54 + return 40;
186.55 + }
186.56 + }
186.57 + Anno anno = (Anno) Proxy.newProxyInstance(
186.58 + Anno.class.getClassLoader(),
186.59 + new Class[] { Anno.class },
186.60 + new InvHandler()
186.61 + );
186.62 + return 2 + anno.age();
186.63 + }
186.64 +
186.65 + public static @interface Anno {
186.66 + public String name();
186.67 + public int age();
186.68 + }
186.69 +
186.70 + @Factory
186.71 + public static Object[] create() {
186.72 + return VMTest.create(ProxyTest.class);
186.73 + }
186.74 +}
187.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java Tue Feb 11 10:48:24 2014 +0100
187.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java Tue Feb 11 13:31:42 2014 +0100
187.3 @@ -34,6 +34,11 @@
187.4 return arr.length;
187.5 }
187.6
187.7 + @Compare public String indexOutOfBounds() {
187.8 + String[] arr = { null, null };
187.9 + return arr[2];
187.10 + }
187.11 +
187.12 @Compare public int reflectiveLengthOfStringArray() {
187.13 Object arr = Array.newInstance(String.class, 10);
187.14 return Array.getLength(arr);
188.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Tue Feb 11 10:48:24 2014 +0100
188.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Tue Feb 11 13:31:42 2014 +0100
188.3 @@ -19,7 +19,9 @@
188.4
188.5 import java.lang.annotation.Retention;
188.6 import java.lang.annotation.RetentionPolicy;
188.7 +import java.lang.reflect.Constructor;
188.8 import java.lang.reflect.Method;
188.9 +import java.lang.reflect.Proxy;
188.10 import java.util.Arrays;
188.11 import java.util.Collections;
188.12 import java.util.List;
188.13 @@ -72,6 +74,18 @@
188.14 @Compare public String isRunnableHasRunMethod() throws NoSuchMethodException {
188.15 return Runnable.class.getMethod("run").getName();
188.16 }
188.17 +
188.18 + @Compare public String isRunnableDeclaresRunMethod() throws NoSuchMethodException {
188.19 + return Runnable.class.getDeclaredMethod("run").getName();
188.20 + }
188.21 +
188.22 + @Compare public String intValue() throws Exception {
188.23 + return Integer.class.getConstructor(int.class).newInstance(10).toString();
188.24 + }
188.25 +
188.26 + @Compare public String getMethodWithArray() throws Exception {
188.27 + return Proxy.class.getMethod("getProxyClass", ClassLoader.class, Class[].class).getName();
188.28 + }
188.29
188.30 @Compare public String namesOfMethods() {
188.31 StringBuilder sb = new StringBuilder();
188.32 @@ -86,6 +100,19 @@
188.33 return sb.toString();
188.34 }
188.35
188.36 + @Compare public String paramsOfConstructors() {
188.37 + StringBuilder sb = new StringBuilder();
188.38 + String[] arr = new String[20];
188.39 + int i = 0;
188.40 + for (Constructor<?> m : StaticUse.class.getConstructors()) {
188.41 + arr[i++] = m.getName();
188.42 + }
188.43 + for (String s : sort(arr, i)) {
188.44 + sb.append(s).append("\n");
188.45 + }
188.46 + return sb.toString();
188.47 + }
188.48 +
188.49 @Compare public String namesOfDeclaringClassesOfMethods() {
188.50 StringBuilder sb = new StringBuilder();
188.51 String[] arr = new String[20];
189.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
189.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/RegExpReplaceAllTest.java Tue Feb 11 13:31:42 2014 +0100
189.3 @@ -0,0 +1,54 @@
189.4 +/**
189.5 + * Back 2 Browser Bytecode Translator
189.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
189.7 + *
189.8 + * This program is free software: you can redistribute it and/or modify
189.9 + * it under the terms of the GNU General Public License as published by
189.10 + * the Free Software Foundation, version 2 of the License.
189.11 + *
189.12 + * This program is distributed in the hope that it will be useful,
189.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
189.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
189.15 + * GNU General Public License for more details.
189.16 + *
189.17 + * You should have received a copy of the GNU General Public License
189.18 + * along with this program. Look for COPYING file in the top folder.
189.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
189.20 + */
189.21 +package org.apidesign.bck2brwsr.tck;
189.22 +
189.23 +import org.apidesign.bck2brwsr.vmtest.Compare;
189.24 +import org.apidesign.bck2brwsr.vmtest.VMTest;
189.25 +import org.testng.annotations.Factory;
189.26 +
189.27 +/**
189.28 + *
189.29 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
189.30 + */
189.31 +public class RegExpReplaceAllTest {
189.32 +
189.33 + @Compare public String replaceAll() {
189.34 + return "JavaScript".replaceAll("Script", "One");
189.35 + }
189.36 +
189.37 + @Compare public String replaceAllTwice() {
189.38 + return "Script JavaScript!".replaceAll("Script", "One");
189.39 + }
189.40 +
189.41 +
189.42 + @Compare public String replaceAllRegexp() {
189.43 + return "JavaScript".replaceAll("S....t", "One");
189.44 + }
189.45 +
189.46 + @Compare public String replaceAllRegexpTwice() {
189.47 + return "Script JavaScript!".replaceAll("S....t", "One");
189.48 + }
189.49 +
189.50 + @Compare public String replaceFirstRegexpOnly() {
189.51 + return "Script JavaScript!".replaceFirst("S....t", "One");
189.52 + }
189.53 +
189.54 + @Factory public static Object[] create() {
189.55 + return VMTest.create(RegExpReplaceAllTest.class);
189.56 + }
189.57 +}
190.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/RegExpSplitTest.java Tue Feb 11 10:48:24 2014 +0100
190.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/RegExpSplitTest.java Tue Feb 11 13:31:42 2014 +0100
190.3 @@ -32,6 +32,10 @@
190.4 return Arrays.asList("How are you today?".split(" "));
190.5 }
190.6
190.7 + public @Compare String splitNewline() {
190.8 + return Arrays.toString("initializer must be able to complete normally".split("\n"));
190.9 + }
190.10 +
190.11 public @Compare Object splitSpaceTrimMinusOne() {
190.12 return Arrays.asList(" How are you today? ".split(" ", -1));
190.13 }
191.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
191.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourceBundleTest.java Tue Feb 11 13:31:42 2014 +0100
191.3 @@ -0,0 +1,45 @@
191.4 +/**
191.5 + * Back 2 Browser Bytecode Translator
191.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
191.7 + *
191.8 + * This program is free software: you can redistribute it and/or modify
191.9 + * it under the terms of the GNU General Public License as published by
191.10 + * the Free Software Foundation, version 2 of the License.
191.11 + *
191.12 + * This program is distributed in the hope that it will be useful,
191.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
191.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
191.15 + * GNU General Public License for more details.
191.16 + *
191.17 + * You should have received a copy of the GNU General Public License
191.18 + * along with this program. Look for COPYING file in the top folder.
191.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
191.20 + */
191.21 +package org.apidesign.bck2brwsr.tck;
191.22 +
191.23 +import java.net.URL;
191.24 +import java.util.ResourceBundle;
191.25 +import org.apidesign.bck2brwsr.vmtest.Compare;
191.26 +import org.apidesign.bck2brwsr.vmtest.VMTest;
191.27 +import org.testng.annotations.Factory;
191.28 +
191.29 +/**
191.30 + *
191.31 + * @author Jaroslav Tulach <jtulach@netbeans.org>
191.32 + */
191.33 +public class ResourceBundleTest {
191.34 +
191.35 + @Compare public String readFromBundle() throws Exception {
191.36 + ResourceBundle b = ResourceBundle.getBundle("org/apidesign/bck2brwsr/tck/Bundle");
191.37 + return b.getString("KEY");
191.38 + }
191.39 +
191.40 + @Compare public String toURIFromURL() throws Exception {
191.41 + URL u = new URL("http://apidesign.org");
191.42 + return u.toURI().toString();
191.43 + }
191.44 +
191.45 + @Factory public static Object[] create() {
191.46 + return VMTest.create(ResourceBundleTest.class);
191.47 + }
191.48 +}
192.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Tue Feb 11 10:48:24 2014 +0100
192.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Tue Feb 11 13:31:42 2014 +0100
192.3 @@ -17,7 +17,10 @@
192.4 */
192.5 package org.apidesign.bck2brwsr.tck;
192.6
192.7 +import java.io.IOException;
192.8 import java.io.InputStream;
192.9 +import java.net.URL;
192.10 +import java.util.Enumeration;
192.11 import org.apidesign.bck2brwsr.vmtest.Compare;
192.12 import org.apidesign.bck2brwsr.vmtest.VMTest;
192.13 import org.testng.annotations.Factory;
192.14 @@ -27,16 +30,62 @@
192.15 * @author Jaroslav Tulach <jtulach@netbeans.org>
192.16 */
192.17 public class ResourcesTest {
192.18 + @Compare public String allManifests() throws Exception {
192.19 + Enumeration<URL> en = ClassLoader.getSystemResources("META-INF/MANIFEST.MF");
192.20 + assert en.hasMoreElements() : "Should have at least one manifest";
192.21 + String first = readString(en.nextElement().openStream());
192.22 + boolean different = false;
192.23 + int cnt = 1;
192.24 + while (en.hasMoreElements()) {
192.25 + URL url = en.nextElement();
192.26 + String now = readString(url.openStream());
192.27 + if (!first.equals(now)) {
192.28 + different = true;
192.29 + }
192.30 + cnt++;
192.31 + if (cnt > 500) {
192.32 + throw new IllegalStateException(
192.33 + "Giving up. First manifest:\n" + first +
192.34 + "\nLast manifest:\n" + now
192.35 + );
192.36 + }
192.37 + }
192.38 + assert different : "Not all manifests should look like first one:\n" + first;
192.39 + return "" + cnt;
192.40 + }
192.41
192.42 @Compare public String readResourceAsStream() throws Exception {
192.43 InputStream is = getClass().getResourceAsStream("Resources.txt");
192.44 - byte[] b = new byte[30];
192.45 - int len = is.read(b);
192.46 + return readString(is);
192.47 + }
192.48 +
192.49 + @Compare public String readResourceViaConnection() throws Exception {
192.50 + InputStream is = getClass().getResource("Resources.txt").openConnection().getInputStream();
192.51 + return readString(is);
192.52 + }
192.53 +
192.54 + private String readString(InputStream is) throws IOException {
192.55 StringBuilder sb = new StringBuilder();
192.56 - for (int i = 0; i < len; i++) {
192.57 - sb.append((char)b[i]);
192.58 + byte[] b = new byte[512];
192.59 + for (;;) {
192.60 + int len = is.read(b);
192.61 + if (len == -1) {
192.62 + return sb.toString();
192.63 + }
192.64 + for (int i = 0; i < len; i++) {
192.65 + sb.append((char)b[i]);
192.66 + }
192.67 }
192.68 - return sb.toString();
192.69 + }
192.70 +
192.71 + @Compare public String readResourceAsStreamFromClassLoader() throws Exception {
192.72 + InputStream is = getClass().getClassLoader().getResourceAsStream("org/apidesign/bck2brwsr/tck/Resources.txt");
192.73 + return readString(is);
192.74 + }
192.75 +
192.76 + @Compare public String toURIFromURL() throws Exception {
192.77 + URL u = new URL("http://apidesign.org");
192.78 + return u.toURI().toString();
192.79 }
192.80
192.81 @Factory public static Object[] create() {
193.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
193.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/SystemTest.java Tue Feb 11 13:31:42 2014 +0100
193.3 @@ -0,0 +1,65 @@
193.4 +/**
193.5 + * Back 2 Browser Bytecode Translator
193.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
193.7 + *
193.8 + * This program is free software: you can redistribute it and/or modify
193.9 + * it under the terms of the GNU General Public License as published by
193.10 + * the Free Software Foundation, version 2 of the License.
193.11 + *
193.12 + * This program is distributed in the hope that it will be useful,
193.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
193.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
193.15 + * GNU General Public License for more details.
193.16 + *
193.17 + * You should have received a copy of the GNU General Public License
193.18 + * along with this program. Look for COPYING file in the top folder.
193.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
193.20 + */
193.21 +package org.apidesign.bck2brwsr.tck;
193.22 +
193.23 +import java.io.ByteArrayOutputStream;
193.24 +import java.io.PrintStream;
193.25 +import org.apidesign.bck2brwsr.core.ExtraJavaScript;
193.26 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
193.27 +import org.apidesign.bck2brwsr.vmtest.Compare;
193.28 +import org.apidesign.bck2brwsr.vmtest.VMTest;
193.29 +import org.testng.annotations.Factory;
193.30 +
193.31 +/**
193.32 + *
193.33 + * @author Jaroslav Tulach <jtulach@netbeans.org>
193.34 + */
193.35 +@ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/tck/console.js")
193.36 +public class SystemTest {
193.37 + @Compare public boolean nonNullOSName() {
193.38 + return System.getProperty("os.name") != null;
193.39 + }
193.40 +
193.41 + @Compare public String captureStdOut() throws Exception {
193.42 + Object capture = initCapture();
193.43 + System.out.println("Ahoj");
193.44 + return textCapture(capture);
193.45 + }
193.46 +
193.47 + @JavaScriptBody(args = {}, body = ""
193.48 + + "var lines = [];"
193.49 + + "console.log = function(l) { lines.push(l); };"
193.50 + + "return lines;")
193.51 + Object initCapture() {
193.52 + ByteArrayOutputStream os = new ByteArrayOutputStream();
193.53 + PrintStream ps = new PrintStream(os);
193.54 +
193.55 + System.setOut(ps);
193.56 + return os;
193.57 + }
193.58 +
193.59 + @JavaScriptBody(args = { "o" }, body = "return o.join('');")
193.60 + String textCapture(Object o) throws java.io.IOException {
193.61 + ByteArrayOutputStream b = (ByteArrayOutputStream) o;
193.62 + return new String(b.toByteArray(), "UTF-8");
193.63 + }
193.64 +
193.65 + @Factory public static Object[] create() {
193.66 + return VMTest.create(SystemTest.class);
193.67 + }
193.68 +}
194.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
194.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/TimerTest.java Tue Feb 11 13:31:42 2014 +0100
194.3 @@ -0,0 +1,81 @@
194.4 +/**
194.5 + * Back 2 Browser Bytecode Translator
194.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
194.7 + *
194.8 + * This program is free software: you can redistribute it and/or modify
194.9 + * it under the terms of the GNU General Public License as published by
194.10 + * the Free Software Foundation, version 2 of the License.
194.11 + *
194.12 + * This program is distributed in the hope that it will be useful,
194.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
194.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
194.15 + * GNU General Public License for more details.
194.16 + *
194.17 + * You should have received a copy of the GNU General Public License
194.18 + * along with this program. Look for COPYING file in the top folder.
194.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
194.20 + */
194.21 +package org.apidesign.bck2brwsr.tck;
194.22 +
194.23 +import java.util.Timer;
194.24 +import java.util.TimerTask;
194.25 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
194.26 +import org.apidesign.bck2brwsr.vmtest.VMTest;
194.27 +import org.testng.annotations.Factory;
194.28 +
194.29 +/**
194.30 + *
194.31 + * @author Jaroslav Tulach <jtulach@netbeans.org>
194.32 + */
194.33 +public class TimerTest {
194.34 + int miss;
194.35 + int exec;
194.36 +
194.37 + public TimerTest() {
194.38 + }
194.39 +
194.40 + @BrwsrTest public void scheduleTick() throws Exception {
194.41 + Timer t = new Timer("MyTest");
194.42 + class TT extends TimerTask {
194.43 + @Override
194.44 + public void run() {
194.45 + exec++;
194.46 + }
194.47 + }
194.48 + TT task = new TT();
194.49 + t.schedule(task, 15);
194.50 +
194.51 + if (exec == 0) {
194.52 + miss++;
194.53 + throw new InterruptedException();
194.54 + }
194.55 +
194.56 + assert exec == 1 : "One exec: " + exec;
194.57 + assert miss == 1 : "One miss: " + miss;
194.58 + }
194.59 +
194.60 + @BrwsrTest public void repeatedTicks() throws Exception {
194.61 + Timer t = new Timer("MyTest");
194.62 + class TT extends TimerTask {
194.63 + @Override
194.64 + public void run() {
194.65 + exec++;
194.66 + }
194.67 + }
194.68 + TT task = new TT();
194.69 + t.scheduleAtFixedRate(task, 15, 10);
194.70 +
194.71 + if (exec != 2) {
194.72 + miss++;
194.73 + throw new InterruptedException();
194.74 + }
194.75 +
194.76 + assert exec == 2 : "Two execs: " + exec;
194.77 + assert miss == 2 : "Two misses: " + miss;
194.78 + }
194.79 +
194.80 + @Factory public static Object[] create() {
194.81 + return VMTest.create(TimerTest.class);
194.82 + }
194.83 +
194.84 +}
195.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
195.2 +++ b/rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/tck/Bundle.properties Tue Feb 11 13:31:42 2014 +0100
195.3 @@ -0,0 +1,2 @@
195.4 +KEY=Value
195.5 +
196.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
196.2 +++ b/rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/tck/console.js Tue Feb 11 13:31:42 2014 +0100
196.3 @@ -0,0 +1,22 @@
196.4 +/**
196.5 + * Back 2 Browser Bytecode Translator
196.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
196.7 + *
196.8 + * This program is free software: you can redistribute it and/or modify
196.9 + * it under the terms of the GNU General Public License as published by
196.10 + * the Free Software Foundation, version 2 of the License.
196.11 + *
196.12 + * This program is distributed in the hope that it will be useful,
196.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
196.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
196.15 + * GNU General Public License for more details.
196.16 + *
196.17 + * You should have received a copy of the GNU General Public License
196.18 + * along with this program. Look for COPYING file in the top folder.
196.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
196.20 + */
196.21 +
196.22 +if (typeof console === 'undefined') {
196.23 + console = {};
196.24 +}
196.25 +
197.1 --- a/rt/emul/mini/pom.xml Tue Feb 11 10:48:24 2014 +0100
197.2 +++ b/rt/emul/mini/pom.xml Tue Feb 11 13:31:42 2014 +0100
197.3 @@ -31,12 +31,49 @@
197.4 <build>
197.5 <plugins>
197.6 <plugin>
197.7 + <artifactId>maven-antrun-plugin</artifactId>
197.8 + <version>1.7</version>
197.9 + <executions>
197.10 + <execution>
197.11 + <phase>generate-sources</phase>
197.12 + <configuration>
197.13 + <target>
197.14 + <mkdir dir="${project.build.directory}/bootcp" />
197.15 + <copy todir="${project.build.directory}/bootcp">
197.16 + <resources>
197.17 + <!-- copy all resources that are in
197.18 + compact profile, but are referenced from
197.19 + the mini profile
197.20 + -->
197.21 + <javaresource name="java/net/URI.class" />
197.22 + <javaresource name="java/net/URISyntaxException.class" />
197.23 + <javaresource name="java/net/URLConnection.class" />
197.24 + <javaresource name="java/util/Locale.class" />
197.25 + <javaresource name="java/io/OutputStream.class" />
197.26 + <javaresource name="java/io/FilterOutputStream.class" />
197.27 + <javaresource name="java/io/PrintStream.class" />
197.28 + <javaresource name="java/io/PrintWriter.class" />
197.29 + <javaresource name="java/io/Writer.class" />
197.30 + </resources>
197.31 + </copy>
197.32 + </target>
197.33 + </configuration>
197.34 + <goals>
197.35 + <goal>run</goal>
197.36 + </goals>
197.37 + </execution>
197.38 + </executions>
197.39 + </plugin>
197.40 + <plugin>
197.41 <groupId>org.apache.maven.plugins</groupId>
197.42 <artifactId>maven-compiler-plugin</artifactId>
197.43 <version>2.5.1</version>
197.44 <configuration>
197.45 <compilerArguments>
197.46 - <bootclasspath>netbeans.ignore.jdk.bootsclasspath</bootclasspath>
197.47 + <!--
197.48 + <bootclasspath>netbeans.ignore.jdk.bootsclasspath</bootclasspath>
197.49 + -->
197.50 + <bootclasspath>${project.build.directory}/bootcp/</bootclasspath>
197.51 </compilerArguments>
197.52 <source>1.7</source>
197.53 <target>1.7</target>
198.1 --- a/rt/emul/mini/src/main/java/java/lang/Character.java Tue Feb 11 10:48:24 2014 +0100
198.2 +++ b/rt/emul/mini/src/main/java/java/lang/Character.java Tue Feb 11 13:31:42 2014 +0100
198.3 @@ -572,6 +572,46 @@
198.4 */
198.5 public static final int MAX_CODE_POINT = 0X10FFFF;
198.6
198.7 + public static boolean isAlphabetic(int ch) {
198.8 + throw new UnsupportedOperationException("isAlphabetic: " + (char)ch);
198.9 + }
198.10 +
198.11 + public static boolean isIdeographic(int ch) {
198.12 + throw new UnsupportedOperationException("isIdeographic: " + (char)ch);
198.13 + }
198.14 +
198.15 + public static boolean isLowerCase(int ch) {
198.16 + throw new UnsupportedOperationException("isLowerCase: " + (char)ch);
198.17 + }
198.18 +
198.19 + public static boolean isUpperCase(int ch) {
198.20 + throw new UnsupportedOperationException("isUpperCase: " + (char)ch);
198.21 + }
198.22 +
198.23 + public static boolean isMirrored(int ch) {
198.24 + throw new UnsupportedOperationException("isMirrored: " + (char)ch);
198.25 + }
198.26 +
198.27 + public static boolean isIdentifierIgnorable(int ch) {
198.28 + throw new UnsupportedOperationException("isIdentifierIgnorable: " + (char)ch);
198.29 + }
198.30 +
198.31 + public static boolean isUnicodeIdentifierPart(int ch) {
198.32 + throw new UnsupportedOperationException("isUnicodeIdentifierPart: " + (char)ch);
198.33 + }
198.34 +
198.35 + public static boolean isUnicodeIdentifierStart(int ch) {
198.36 + throw new UnsupportedOperationException("isUnicodeIdentifierStart: " + (char)ch);
198.37 + }
198.38 +
198.39 + public static char toUpperCase(int ch) {
198.40 + throw new UnsupportedOperationException("toUpperCase: " + (char)ch);
198.41 + }
198.42 +
198.43 + public static int toLowerCase(int ch) {
198.44 + throw new UnsupportedOperationException("toLowerCase: " + (char)ch);
198.45 + }
198.46 +
198.47
198.48 /**
198.49 * Instances of this class represent particular subsets of the Unicode
198.50 @@ -1892,8 +1932,8 @@
198.51 return fromCodeChars(codePoint).matches("\\w");
198.52 }
198.53
198.54 - static int getType(int x) {
198.55 - throw new UnsupportedOperationException();
198.56 + public static int getType(int x) {
198.57 + throw new UnsupportedOperationException("getType: " + (char)x);
198.58 }
198.59
198.60 /**
198.61 @@ -1955,7 +1995,8 @@
198.62 public static boolean isJavaIdentifierStart(int codePoint) {
198.63 return
198.64 ('A' <= codePoint && codePoint <= 'Z') ||
198.65 - ('a' <= codePoint && codePoint <= 'z');
198.66 + ('a' <= codePoint && codePoint <= 'z') ||
198.67 + codePoint == '$';
198.68 }
198.69
198.70 /**
198.71 @@ -2375,7 +2416,14 @@
198.72 * @since 1.5
198.73 */
198.74 public static boolean isWhitespace(int codePoint) {
198.75 - throw new UnsupportedOperationException();
198.76 + if (
198.77 + codePoint == SPACE_SEPARATOR ||
198.78 + codePoint == LINE_SEPARATOR ||
198.79 + codePoint == PARAGRAPH_SEPARATOR
198.80 + ) {
198.81 + return true;
198.82 + }
198.83 + return false;
198.84 }
198.85
198.86 /**
199.1 --- a/rt/emul/mini/src/main/java/java/lang/Class.java Tue Feb 11 10:48:24 2014 +0100
199.2 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Tue Feb 11 13:31:42 2014 +0100
199.3 @@ -29,6 +29,7 @@
199.4 import org.apidesign.bck2brwsr.emul.reflect.AnnotationImpl;
199.5 import java.io.InputStream;
199.6 import java.lang.annotation.Annotation;
199.7 +import java.lang.reflect.Constructor;
199.8 import java.lang.reflect.Field;
199.9 import java.lang.reflect.Method;
199.10 import java.lang.reflect.TypeVariable;
199.11 @@ -631,6 +632,20 @@
199.12 return getAccess();
199.13 }
199.14
199.15 + /**
199.16 + * If the class or interface represented by this {@code Class} object
199.17 + * is a member of another class, returns the {@code Class} object
199.18 + * representing the class in which it was declared. This method returns
199.19 + * null if this class or interface is not a member of any other class. If
199.20 + * this {@code Class} object represents an array class, a primitive
199.21 + * type, or void,then this method returns null.
199.22 + *
199.23 + * @return the declaring class for this class
199.24 + * @since JDK1.1
199.25 + */
199.26 + public Class<?> getDeclaringClass() {
199.27 + throw new SecurityException();
199.28 + }
199.29
199.30 /**
199.31 * Returns the simple name of the underlying class as given in the
199.32 @@ -975,6 +990,319 @@
199.33 }
199.34
199.35 /**
199.36 + * Returns an array of {@code Field} objects reflecting all the fields
199.37 + * declared by the class or interface represented by this
199.38 + * {@code Class} object. This includes public, protected, default
199.39 + * (package) access, and private fields, but excludes inherited fields.
199.40 + * The elements in the array returned are not sorted and are not in any
199.41 + * particular order. This method returns an array of length 0 if the class
199.42 + * or interface declares no fields, or if this {@code Class} object
199.43 + * represents a primitive type, an array class, or void.
199.44 + *
199.45 + * <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
199.46 + *
199.47 + * @return the array of {@code Field} objects representing all the
199.48 + * declared fields of this class
199.49 + * @exception SecurityException
199.50 + * If a security manager, <i>s</i>, is present and any of the
199.51 + * following conditions is met:
199.52 + *
199.53 + * <ul>
199.54 + *
199.55 + * <li> invocation of
199.56 + * {@link SecurityManager#checkMemberAccess
199.57 + * s.checkMemberAccess(this, Member.DECLARED)} denies
199.58 + * access to the declared fields within this class
199.59 + *
199.60 + * <li> the caller's class loader is not the same as or an
199.61 + * ancestor of the class loader for the current class and
199.62 + * invocation of {@link SecurityManager#checkPackageAccess
199.63 + * s.checkPackageAccess()} denies access to the package
199.64 + * of this class
199.65 + *
199.66 + * </ul>
199.67 + *
199.68 + * @since JDK1.1
199.69 + */
199.70 + public Field[] getDeclaredFields() throws SecurityException {
199.71 + throw new SecurityException();
199.72 + }
199.73 +
199.74 + /**
199.75 + * <b>Bck2Brwsr</b> emulation can only seek public methods, otherwise it
199.76 + * throws a {@code SecurityException}.
199.77 + * <p>
199.78 + * Returns a {@code Method} object that reflects the specified
199.79 + * declared method of the class or interface represented by this
199.80 + * {@code Class} object. The {@code name} parameter is a
199.81 + * {@code String} that specifies the simple name of the desired
199.82 + * method, and the {@code parameterTypes} parameter is an array of
199.83 + * {@code Class} objects that identify the method's formal parameter
199.84 + * types, in declared order. If more than one method with the same
199.85 + * parameter types is declared in a class, and one of these methods has a
199.86 + * return type that is more specific than any of the others, that method is
199.87 + * returned; otherwise one of the methods is chosen arbitrarily. If the
199.88 + * name is "<init>"or "<clinit>" a {@code NoSuchMethodException}
199.89 + * is raised.
199.90 + *
199.91 + * @param name the name of the method
199.92 + * @param parameterTypes the parameter array
199.93 + * @return the {@code Method} object for the method of this class
199.94 + * matching the specified name and parameters
199.95 + * @exception NoSuchMethodException if a matching method is not found.
199.96 + * @exception NullPointerException if {@code name} is {@code null}
199.97 + * @exception SecurityException
199.98 + * If a security manager, <i>s</i>, is present and any of the
199.99 + * following conditions is met:
199.100 + *
199.101 + * <ul>
199.102 + *
199.103 + * <li> invocation of
199.104 + * {@link SecurityManager#checkMemberAccess
199.105 + * s.checkMemberAccess(this, Member.DECLARED)} denies
199.106 + * access to the declared method
199.107 + *
199.108 + * <li> the caller's class loader is not the same as or an
199.109 + * ancestor of the class loader for the current class and
199.110 + * invocation of {@link SecurityManager#checkPackageAccess
199.111 + * s.checkPackageAccess()} denies access to the package
199.112 + * of this class
199.113 + *
199.114 + * </ul>
199.115 + *
199.116 + * @since JDK1.1
199.117 + */
199.118 + public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
199.119 + throws NoSuchMethodException, SecurityException {
199.120 + try {
199.121 + return getMethod(name, parameterTypes);
199.122 + } catch (NoSuchMethodException ex) {
199.123 + throw new SecurityException();
199.124 + }
199.125 + }
199.126 +
199.127 + /**
199.128 + * Returns a {@code Field} object that reflects the specified declared
199.129 + * field of the class or interface represented by this {@code Class}
199.130 + * object. The {@code name} parameter is a {@code String} that
199.131 + * specifies the simple name of the desired field. Note that this method
199.132 + * will not reflect the {@code length} field of an array class.
199.133 + *
199.134 + * @param name the name of the field
199.135 + * @return the {@code Field} object for the specified field in this
199.136 + * class
199.137 + * @exception NoSuchFieldException if a field with the specified name is
199.138 + * not found.
199.139 + * @exception NullPointerException if {@code name} is {@code null}
199.140 + * @exception SecurityException
199.141 + * If a security manager, <i>s</i>, is present and any of the
199.142 + * following conditions is met:
199.143 + *
199.144 + * <ul>
199.145 + *
199.146 + * <li> invocation of
199.147 + * {@link SecurityManager#checkMemberAccess
199.148 + * s.checkMemberAccess(this, Member.DECLARED)} denies
199.149 + * access to the declared field
199.150 + *
199.151 + * <li> the caller's class loader is not the same as or an
199.152 + * ancestor of the class loader for the current class and
199.153 + * invocation of {@link SecurityManager#checkPackageAccess
199.154 + * s.checkPackageAccess()} denies access to the package
199.155 + * of this class
199.156 + *
199.157 + * </ul>
199.158 + *
199.159 + * @since JDK1.1
199.160 + */
199.161 + public Field getDeclaredField(String name)
199.162 + throws SecurityException {
199.163 + throw new SecurityException();
199.164 + }
199.165 +
199.166 + /**
199.167 + * Returns an array containing {@code Constructor} objects reflecting
199.168 + * all the public constructors of the class represented by this
199.169 + * {@code Class} object. An array of length 0 is returned if the
199.170 + * class has no public constructors, or if the class is an array class, or
199.171 + * if the class reflects a primitive type or void.
199.172 + *
199.173 + * Note that while this method returns an array of {@code
199.174 + * Constructor<T>} objects (that is an array of constructors from
199.175 + * this class), the return type of this method is {@code
199.176 + * Constructor<?>[]} and <em>not</em> {@code Constructor<T>[]} as
199.177 + * might be expected. This less informative return type is
199.178 + * necessary since after being returned from this method, the
199.179 + * array could be modified to hold {@code Constructor} objects for
199.180 + * different classes, which would violate the type guarantees of
199.181 + * {@code Constructor<T>[]}.
199.182 + *
199.183 + * @return the array of {@code Constructor} objects representing the
199.184 + * public constructors of this class
199.185 + * @exception SecurityException
199.186 + * If a security manager, <i>s</i>, is present and any of the
199.187 + * following conditions is met:
199.188 + *
199.189 + * <ul>
199.190 + *
199.191 + * <li> invocation of
199.192 + * {@link SecurityManager#checkMemberAccess
199.193 + * s.checkMemberAccess(this, Member.PUBLIC)} denies
199.194 + * access to the constructors within this class
199.195 + *
199.196 + * <li> the caller's class loader is not the same as or an
199.197 + * ancestor of the class loader for the current class and
199.198 + * invocation of {@link SecurityManager#checkPackageAccess
199.199 + * s.checkPackageAccess()} denies access to the package
199.200 + * of this class
199.201 + *
199.202 + * </ul>
199.203 + *
199.204 + * @since JDK1.1
199.205 + */
199.206 + public Constructor<?>[] getConstructors() throws SecurityException {
199.207 + return MethodImpl.findConstructors(this, 0x01);
199.208 + }
199.209 +
199.210 + /**
199.211 + * Returns a {@code Constructor} object that reflects the specified
199.212 + * public constructor of the class represented by this {@code Class}
199.213 + * object. The {@code parameterTypes} parameter is an array of
199.214 + * {@code Class} objects that identify the constructor's formal
199.215 + * parameter types, in declared order.
199.216 + *
199.217 + * If this {@code Class} object represents an inner class
199.218 + * declared in a non-static context, the formal parameter types
199.219 + * include the explicit enclosing instance as the first parameter.
199.220 + *
199.221 + * <p> The constructor to reflect is the public constructor of the class
199.222 + * represented by this {@code Class} object whose formal parameter
199.223 + * types match those specified by {@code parameterTypes}.
199.224 + *
199.225 + * @param parameterTypes the parameter array
199.226 + * @return the {@code Constructor} object of the public constructor that
199.227 + * matches the specified {@code parameterTypes}
199.228 + * @exception NoSuchMethodException if a matching method is not found.
199.229 + * @exception SecurityException
199.230 + * If a security manager, <i>s</i>, is present and any of the
199.231 + * following conditions is met:
199.232 + *
199.233 + * <ul>
199.234 + *
199.235 + * <li> invocation of
199.236 + * {@link SecurityManager#checkMemberAccess
199.237 + * s.checkMemberAccess(this, Member.PUBLIC)} denies
199.238 + * access to the constructor
199.239 + *
199.240 + * <li> the caller's class loader is not the same as or an
199.241 + * ancestor of the class loader for the current class and
199.242 + * invocation of {@link SecurityManager#checkPackageAccess
199.243 + * s.checkPackageAccess()} denies access to the package
199.244 + * of this class
199.245 + *
199.246 + * </ul>
199.247 + *
199.248 + * @since JDK1.1
199.249 + */
199.250 + public Constructor<T> getConstructor(Class<?>... parameterTypes)
199.251 + throws NoSuchMethodException, SecurityException {
199.252 + Constructor c = MethodImpl.findConstructor(this, parameterTypes);
199.253 + if (c == null) {
199.254 + StringBuilder sb = new StringBuilder();
199.255 + sb.append(getName()).append('(');
199.256 + String sep = "";
199.257 + for (int i = 0; i < parameterTypes.length; i++) {
199.258 + sb.append(sep).append(parameterTypes[i].getName());
199.259 + sep = ", ";
199.260 + }
199.261 + sb.append(')');
199.262 + throw new NoSuchMethodException(sb.toString());
199.263 + }
199.264 + return c;
199.265 + }
199.266 +
199.267 + /**
199.268 + * Returns an array of {@code Constructor} objects reflecting all the
199.269 + * constructors declared by the class represented by this
199.270 + * {@code Class} object. These are public, protected, default
199.271 + * (package) access, and private constructors. The elements in the array
199.272 + * returned are not sorted and are not in any particular order. If the
199.273 + * class has a default constructor, it is included in the returned array.
199.274 + * This method returns an array of length 0 if this {@code Class}
199.275 + * object represents an interface, a primitive type, an array class, or
199.276 + * void.
199.277 + *
199.278 + * <p> See <em>The Java Language Specification</em>, section 8.2.
199.279 + *
199.280 + * @return the array of {@code Constructor} objects representing all the
199.281 + * declared constructors of this class
199.282 + * @exception SecurityException
199.283 + * If a security manager, <i>s</i>, is present and any of the
199.284 + * following conditions is met:
199.285 + *
199.286 + * <ul>
199.287 + *
199.288 + * <li> invocation of
199.289 + * {@link SecurityManager#checkMemberAccess
199.290 + * s.checkMemberAccess(this, Member.DECLARED)} denies
199.291 + * access to the declared constructors within this class
199.292 + *
199.293 + * <li> the caller's class loader is not the same as or an
199.294 + * ancestor of the class loader for the current class and
199.295 + * invocation of {@link SecurityManager#checkPackageAccess
199.296 + * s.checkPackageAccess()} denies access to the package
199.297 + * of this class
199.298 + *
199.299 + * </ul>
199.300 + *
199.301 + * @since JDK1.1
199.302 + */
199.303 + public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
199.304 + throw new SecurityException();
199.305 + }
199.306 + /**
199.307 + * Returns a {@code Constructor} object that reflects the specified
199.308 + * constructor of the class or interface represented by this
199.309 + * {@code Class} object. The {@code parameterTypes} parameter is
199.310 + * an array of {@code Class} objects that identify the constructor's
199.311 + * formal parameter types, in declared order.
199.312 + *
199.313 + * If this {@code Class} object represents an inner class
199.314 + * declared in a non-static context, the formal parameter types
199.315 + * include the explicit enclosing instance as the first parameter.
199.316 + *
199.317 + * @param parameterTypes the parameter array
199.318 + * @return The {@code Constructor} object for the constructor with the
199.319 + * specified parameter list
199.320 + * @exception NoSuchMethodException if a matching method is not found.
199.321 + * @exception SecurityException
199.322 + * If a security manager, <i>s</i>, is present and any of the
199.323 + * following conditions is met:
199.324 + *
199.325 + * <ul>
199.326 + *
199.327 + * <li> invocation of
199.328 + * {@link SecurityManager#checkMemberAccess
199.329 + * s.checkMemberAccess(this, Member.DECLARED)} denies
199.330 + * access to the declared constructor
199.331 + *
199.332 + * <li> the caller's class loader is not the same as or an
199.333 + * ancestor of the class loader for the current class and
199.334 + * invocation of {@link SecurityManager#checkPackageAccess
199.335 + * s.checkPackageAccess()} denies access to the package
199.336 + * of this class
199.337 + *
199.338 + * </ul>
199.339 + *
199.340 + * @since JDK1.1
199.341 + */
199.342 + public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
199.343 + throws NoSuchMethodException, SecurityException {
199.344 + return getConstructor(parameterTypes);
199.345 + }
199.346 +
199.347 +
199.348 + /**
199.349 * Character.isDigit answers {@code true} to some non-ascii
199.350 * digits. This one does not.
199.351 */
199.352 @@ -1051,15 +1379,10 @@
199.353 */
199.354 public InputStream getResourceAsStream(String name) {
199.355 name = resolveName(name);
199.356 - byte[] arr = getResourceAsStream0(name);
199.357 + byte[] arr = ClassLoader.getResourceAsStream0(name, 0);
199.358 return arr == null ? null : new ByteArrayInputStream(arr);
199.359 }
199.360 -
199.361 - @JavaScriptBody(args = "name", body =
199.362 - "return (vm.loadBytes) ? vm.loadBytes(name) : null;"
199.363 - )
199.364 - private static native byte[] getResourceAsStream0(String name);
199.365 -
199.366 +
199.367 /**
199.368 * Finds a resource with a given name. The rules for searching resources
199.369 * associated with a given class are implemented by the defining
199.370 @@ -1095,8 +1418,11 @@
199.371 * @since JDK1.1
199.372 */
199.373 public java.net.URL getResource(String name) {
199.374 - InputStream is = getResourceAsStream(name);
199.375 - return is == null ? null : newResourceURL(URL.class, "res:/" + name, is);
199.376 + return newResourceURL(name, getResourceAsStream(name));
199.377 + }
199.378 +
199.379 + static URL newResourceURL(String name, InputStream is) {
199.380 + return is == null ? null : newResourceURL0(URL.class, "res:/" + name, is);
199.381 }
199.382
199.383 @JavaScriptBody(args = { "url", "spec", "is" }, body =
199.384 @@ -1104,7 +1430,7 @@
199.385 + "u.constructor.cons__VLjava_lang_String_2Ljava_io_InputStream_2.call(u, spec, is);\n"
199.386 + "return u;"
199.387 )
199.388 - private static native URL newResourceURL(Class<URL> url, String spec, InputStream is);
199.389 + private static native URL newResourceURL0(Class<URL> url, String spec, InputStream is);
199.390
199.391 /**
199.392 * Add a package name prefix if the name is not absolute Remove leading "/"
199.393 @@ -1158,7 +1484,7 @@
199.394 * @see java.lang.RuntimePermission
199.395 */
199.396 public ClassLoader getClassLoader() {
199.397 - throw new SecurityException();
199.398 + return ClassLoader.getSystemClassLoader();
199.399 }
199.400
199.401 /**
199.402 @@ -1335,9 +1661,11 @@
199.403
199.404 @JavaScriptBody(args = { "ac" },
199.405 body =
199.406 - "if (this.anno) {"
199.407 - + " return this.anno['L' + ac.jvmName + ';'];"
199.408 - + "} else return null;"
199.409 + "if (this.anno) {\n"
199.410 + + " var r = this.anno['L' + ac.jvmName + ';'];\n"
199.411 + + " if (typeof r === 'undefined') r = null;\n"
199.412 + + " return r;\n"
199.413 + + "} else return null;\n"
199.414 )
199.415 private Object getAnnotationData(Class<?> annotationClass) {
199.416 throw new UnsupportedOperationException();
200.1 --- a/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Tue Feb 11 10:48:24 2014 +0100
200.2 +++ b/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Tue Feb 11 13:31:42 2014 +0100
200.3 @@ -24,6 +24,7 @@
200.4 */
200.5 package java.lang;
200.6
200.7 +import java.io.ByteArrayInputStream;
200.8 import java.io.InputStream;
200.9 import java.io.IOException;
200.10 import java.net.URL;
200.11 @@ -180,7 +181,7 @@
200.12 * @since 1.2
200.13 */
200.14 protected ClassLoader(ClassLoader parent) {
200.15 - throw new SecurityException();
200.16 + this.parent = parent;
200.17 }
200.18
200.19 /**
200.20 @@ -199,7 +200,7 @@
200.21 * of a new class loader.
200.22 */
200.23 protected ClassLoader() {
200.24 - throw new SecurityException();
200.25 + this.parent = null;
200.26 }
200.27
200.28 // -- Class --
200.29 @@ -845,8 +846,27 @@
200.30 * @revised 1.4
200.31 */
200.32 public static ClassLoader getSystemClassLoader() {
200.33 - throw new SecurityException();
200.34 + if (SYSTEM == null) {
200.35 + SYSTEM = new ClassLoader() {
200.36 + @Override
200.37 + protected Enumeration<URL> findResources(String name) throws IOException {
200.38 + return getBootstrapResources(name);
200.39 + }
200.40 +
200.41 + @Override
200.42 + protected URL findResource(String name) {
200.43 + return getBootstrapResource(name);
200.44 + }
200.45 +
200.46 + @Override
200.47 + protected Class<?> findClass(String name) throws ClassNotFoundException {
200.48 + return Class.forName(name);
200.49 + }
200.50 + };
200.51 + }
200.52 + return SYSTEM;
200.53 }
200.54 + private static ClassLoader SYSTEM;
200.55
200.56 // Returns true if the specified class loader can be found in this class
200.57 // loader's delegation chain.
200.58 @@ -870,12 +890,48 @@
200.59 }
200.60
200.61 private static URL getBootstrapResource(String name) {
200.62 - throw new UnsupportedOperationException();
200.63 + return Object.class.getResource("/" + name);
200.64 }
200.65
200.66 + @JavaScriptBody(args = { "name", "skip" }, body
200.67 + = "return (vm.loadBytes) ? vm.loadBytes(name, skip) : null;"
200.68 + )
200.69 + static native byte[] getResourceAsStream0(String name, int skip);
200.70 +
200.71 private static Enumeration<URL> getBootstrapResources(String name) {
200.72 - URL u = Object.class.getResource("/" + name);
200.73 - return new OneOrZeroEnum(u);
200.74 + return new ResEnum(name);
200.75 + }
200.76 +
200.77 + private static class ResEnum implements Enumeration<URL> {
200.78 + private final String name;
200.79 + private URL next;
200.80 + private int skip;
200.81 +
200.82 + public ResEnum(String name) {
200.83 + this.name = name;
200.84 + }
200.85 +
200.86 +
200.87 + public boolean hasMoreElements() {
200.88 + if (next == null && skip >= 0) {
200.89 + byte[] arr = getResourceAsStream0(name, skip++);
200.90 + if (arr != null) {
200.91 + next = Class.newResourceURL(name, new ByteArrayInputStream(arr));
200.92 + } else {
200.93 + skip = -1;
200.94 + }
200.95 + }
200.96 + return next != null;
200.97 + }
200.98 +
200.99 + public URL nextElement() {
200.100 + URL r = next;
200.101 + if (r == null) {
200.102 + throw new NoSuchElementException();
200.103 + }
200.104 + next = null;
200.105 + return r;
200.106 + }
200.107 }
200.108
200.109 private static class OneOrZeroEnum implements Enumeration<URL> {
200.110 @@ -910,7 +966,7 @@
200.111 }
200.112
200.113 public boolean hasMoreElements() {
200.114 - if (next == null) {
200.115 + if (next == null && index < arr.length) {
200.116 if (arr[index].hasMoreElements()) {
200.117 next = (URL) arr[index].nextElement();
200.118 } else {
201.1 --- a/rt/emul/mini/src/main/java/java/lang/Double.java Tue Feb 11 10:48:24 2014 +0100
201.2 +++ b/rt/emul/mini/src/main/java/java/lang/Double.java Tue Feb 11 13:31:42 2014 +0100
201.3 @@ -540,8 +540,7 @@
201.4 */
201.5 @JavaScriptBody(args="s", body="return parseFloat(s);")
201.6 public static double parseDouble(String s) throws NumberFormatException {
201.7 - throw new UnsupportedOperationException();
201.8 -// return FloatingDecimal.readJavaFormatString(s).doubleValue();
201.9 + return 0;
201.10 }
201.11
201.12 /**
202.1 --- a/rt/emul/mini/src/main/java/java/lang/Enum.java Tue Feb 11 10:48:24 2014 +0100
202.2 +++ b/rt/emul/mini/src/main/java/java/lang/Enum.java Tue Feb 11 13:31:42 2014 +0100
202.3 @@ -235,7 +235,7 @@
202.4 throw new IllegalArgumentException();
202.5 }
202.6
202.7 - @JavaScriptBody(args = { "enumType" }, body = "return enumType.cnstr.$VALUES;")
202.8 + @JavaScriptBody(args = { "enumType" }, body = "return enumType.cnstr.fld_$VALUES;")
202.9 private static native Object[] values(Class<?> enumType);
202.10
202.11 /**
203.1 --- a/rt/emul/mini/src/main/java/java/lang/Float.java Tue Feb 11 10:48:24 2014 +0100
203.2 +++ b/rt/emul/mini/src/main/java/java/lang/Float.java Tue Feb 11 13:31:42 2014 +0100
203.3 @@ -412,8 +412,7 @@
203.4 * parsable number.
203.5 */
203.6 public static Float valueOf(String s) throws NumberFormatException {
203.7 - throw new UnsupportedOperationException();
203.8 -// return new Float(FloatingDecimal.readJavaFormatString(s).floatValue());
203.9 + return new Float(parseFloat(s));
203.10 }
203.11
203.12 /**
203.13 @@ -447,9 +446,9 @@
203.14 * @see java.lang.Float#valueOf(String)
203.15 * @since 1.2
203.16 */
203.17 + @JavaScriptBody(args="s", body="return parseFloat(s);")
203.18 public static float parseFloat(String s) throws NumberFormatException {
203.19 - throw new UnsupportedOperationException();
203.20 -// return FloatingDecimal.readJavaFormatString(s).floatValue();
203.21 + return 0;
203.22 }
203.23
203.24 /**
204.1 --- a/rt/emul/mini/src/main/java/java/lang/String.java Tue Feb 11 10:48:24 2014 +0100
204.2 +++ b/rt/emul/mini/src/main/java/java/lang/String.java Tue Feb 11 13:31:42 2014 +0100
204.3 @@ -26,7 +26,10 @@
204.4 package java.lang;
204.5
204.6 import java.io.UnsupportedEncodingException;
204.7 +import java.lang.reflect.InvocationTargetException;
204.8 +import java.lang.reflect.Method;
204.9 import java.util.Comparator;
204.10 +import java.util.Locale;
204.11 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
204.12 import org.apidesign.bck2brwsr.core.JavaScriptBody;
204.13 import org.apidesign.bck2brwsr.core.JavaScriptOnly;
204.14 @@ -984,12 +987,12 @@
204.15 continue;
204.16 }
204.17 if (v < 0x0800) {
204.18 - arr = System.expandArray(arr, i + 1);
204.19 + arr = System.expandArray(arr, arr.length + 1);
204.20 arr[i++] = (byte) (0xC0 | (v >> 6));
204.21 arr[i++] = (byte) (0x80 | (0x3F & v));
204.22 continue;
204.23 }
204.24 - arr = System.expandArray(arr, i + 2);
204.25 + arr = System.expandArray(arr, arr.length + 2);
204.26 arr[i++] = (byte) (0xE0 | (v >> 12));
204.27 arr[i++] = (byte) (0x80 | ((v >> 6) & 0x7F));
204.28 arr[i++] = (byte) (0x80 | (0x3F & v));
204.29 @@ -2097,13 +2100,31 @@
204.30 * @since 1.4
204.31 * @spec JSR-51
204.32 */
204.33 + public boolean matches(String regex) {
204.34 + try {
204.35 + return matchesViaJS(regex);
204.36 + } catch (Throwable t) {
204.37 + // fallback to classical behavior
204.38 + try {
204.39 + Method m = Class.forName("java.util.regex.Pattern").getMethod("matches", String.class, CharSequence.class);
204.40 + return (Boolean)m.invoke(null, regex, this);
204.41 + } catch (InvocationTargetException ex) {
204.42 + if (ex.getTargetException() instanceof RuntimeException) {
204.43 + throw (RuntimeException)ex.getTargetException();
204.44 + }
204.45 + } catch (Throwable another) {
204.46 + // will report the old one
204.47 + }
204.48 + throw new RuntimeException(t);
204.49 + }
204.50 + }
204.51 @JavaScriptBody(args = { "regex" }, body =
204.52 "var self = this.toString();\n"
204.53 + "var re = new RegExp(regex.toString());\n"
204.54 + "var r = re.exec(self);\n"
204.55 + "return r != null && r.length > 0 && self.length == r[0].length;"
204.56 )
204.57 - public boolean matches(String regex) {
204.58 + private boolean matchesViaJS(String regex) {
204.59 throw new UnsupportedOperationException();
204.60 }
204.61
204.62 @@ -2159,6 +2180,14 @@
204.63 * @since 1.4
204.64 * @spec JSR-51
204.65 */
204.66 + @JavaScriptBody(args = { "regex", "newText" }, body =
204.67 + "var self = this.toString();\n"
204.68 + + "var re = new RegExp(regex.toString());\n"
204.69 + + "var r = re.exec(self);\n"
204.70 + + "if (r === null || r.length === 0) return this;\n"
204.71 + + "var from = self.indexOf(r[0]);\n"
204.72 + + "return this.substring(0, from) + newText + this.substring(from + r[0].length);\n"
204.73 + )
204.74 public String replaceFirst(String regex, String replacement) {
204.75 throw new UnsupportedOperationException();
204.76 }
204.77 @@ -2203,7 +2232,14 @@
204.78 * @spec JSR-51
204.79 */
204.80 public String replaceAll(String regex, String replacement) {
204.81 - throw new UnsupportedOperationException();
204.82 + String p = this;
204.83 + for (;;) {
204.84 + String n = p.replaceFirst(regex, replacement);
204.85 + if (n == p) {
204.86 + return n;
204.87 + }
204.88 + p = n;
204.89 + }
204.90 }
204.91
204.92 /**
204.93 @@ -2224,12 +2260,14 @@
204.94 "var s = this.toString();\n"
204.95 + "target = target.toString();\n"
204.96 + "replacement = replacement.toString();\n"
204.97 + + "var pos = 0;\n"
204.98 + "for (;;) {\n"
204.99 - + " var ret = s.replace(target, replacement);\n"
204.100 - + " if (ret === s) {\n"
204.101 - + " return ret;\n"
204.102 + + " var indx = s.indexOf(target, pos);\n"
204.103 + + " if (indx === -1) {\n"
204.104 + + " return s;\n"
204.105 + " }\n"
204.106 - + " s = ret;\n"
204.107 + + " pos = indx + replacement.length;\n"
204.108 + + " s = s.substring(0, indx) + replacement + s.substring(indx + target.length);\n"
204.109 + "}"
204.110 )
204.111 public native String replace(CharSequence target, CharSequence replacement);
204.112 @@ -2318,8 +2356,8 @@
204.113 if (limit <= 0) {
204.114 Object[] arr = splitImpl(this, regex, Integer.MAX_VALUE);
204.115 int to = arr.length;
204.116 - if (limit == 0) {
204.117 - while (to > 1 && ((String)arr[--to]).isEmpty()) {
204.118 + if (limit == 0 && to > 0) {
204.119 + while (to > 0 && ((String)arr[--to]).isEmpty()) {
204.120 }
204.121 to++;
204.122 }
204.123 @@ -2439,7 +2477,9 @@
204.124 * @see java.lang.String#toUpperCase(Locale)
204.125 * @since 1.1
204.126 */
204.127 -// public String toLowerCase(Locale locale) {
204.128 + public String toLowerCase(java.util.Locale locale) {
204.129 + return toLowerCase();
204.130 + }
204.131 // if (locale == null) {
204.132 // throw new NullPointerException();
204.133 // }
204.134 @@ -2554,7 +2594,7 @@
204.135 */
204.136 @JavaScriptBody(args = {}, body = "return this.toLowerCase();")
204.137 public String toLowerCase() {
204.138 - throw new UnsupportedOperationException("Should be supported but without connection to locale");
204.139 + return null;
204.140 }
204.141
204.142 /**
204.143 @@ -2605,8 +2645,10 @@
204.144 * @see java.lang.String#toLowerCase(Locale)
204.145 * @since 1.1
204.146 */
204.147 + public String toUpperCase(Locale locale) {
204.148 + return toUpperCase();
204.149 + }
204.150 /* not for javascript
204.151 - public String toUpperCase(Locale locale) {
204.152 if (locale == null) {
204.153 throw new NullPointerException();
204.154 }
204.155 @@ -2720,7 +2762,7 @@
204.156 */
204.157 @JavaScriptBody(args = {}, body = "return this.toUpperCase();")
204.158 public String toUpperCase() {
204.159 - throw new UnsupportedOperationException();
204.160 + return null;
204.161 }
204.162
204.163 /**
204.164 @@ -2831,7 +2873,7 @@
204.165 * @since 1.5
204.166 */
204.167 public static String format(String format, Object ... args) {
204.168 - throw new UnsupportedOperationException();
204.169 + return format((Locale)null, format, args);
204.170 }
204.171
204.172 /**
204.173 @@ -2874,9 +2916,15 @@
204.174 * @see java.util.Formatter
204.175 * @since 1.5
204.176 */
204.177 -// public static String format(Locale l, String format, Object ... args) {
204.178 -// return new Formatter(l).format(format, args).toString();
204.179 -// }
204.180 + public static String format(Locale l, String format, Object ... args) {
204.181 + String p = format;
204.182 + for (int i = 0; i < args.length; i++) {
204.183 + String v = args[i] == null ? "null" : args[i].toString();
204.184 + p = p.replaceFirst("%s", v);
204.185 + }
204.186 + return p;
204.187 + // return new Formatter(l).format(format, args).toString();
204.188 + }
204.189
204.190 /**
204.191 * Returns the string representation of the <code>Object</code> argument.
204.192 @@ -3061,6 +3109,14 @@
204.193 * @return a string that has the same contents as this string, but is
204.194 * guaranteed to be from a pool of unique strings.
204.195 */
204.196 + @JavaScriptBody(args = {}, body =
204.197 + "var s = this.toString().toString();\n" +
204.198 + "var i = String.intern || (String.intern = {})\n" +
204.199 + "if (!i[s]) {\n" +
204.200 + " i[s] = s;\n" +
204.201 + "}\n" +
204.202 + "return i[s];"
204.203 + )
204.204 public native String intern();
204.205
204.206
205.1 --- a/rt/emul/mini/src/main/java/java/lang/Throwable.java Tue Feb 11 10:48:24 2014 +0100
205.2 +++ b/rt/emul/mini/src/main/java/java/lang/Throwable.java Tue Feb 11 13:31:42 2014 +0100
205.3 @@ -638,93 +638,34 @@
205.4 * ... 2 more
205.5 * </pre>
205.6 */
205.7 - @JavaScriptBody(args = { }, body = "console.warn(this.toString());")
205.8 - public native void printStackTrace();
205.9 + public void printStackTrace() {
205.10 + warn(getClass().getName() + ": " + getMessage());
205.11 + }
205.12 + @JavaScriptBody(args = { "msg" }, body = "if (console) console.warn(msg.toString());")
205.13 + private native void warn(String msg);
205.14
205.15 -// /**
205.16 -// * Prints this throwable and its backtrace to the specified print stream.
205.17 -// *
205.18 -// * @param s {@code PrintStream} to use for output
205.19 -// */
205.20 -// public void printStackTrace(PrintStream s) {
205.21 -// printStackTrace(new WrappedPrintStream(s));
205.22 -// }
205.23 -//
205.24 -// private void printStackTrace(PrintStreamOrWriter s) {
205.25 -// // Guard against malicious overrides of Throwable.equals by
205.26 -// // using a Set with identity equality semantics.
205.27 -//// Set<Throwable> dejaVu =
205.28 -//// Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
205.29 -//// dejaVu.add(this);
205.30 -//
205.31 -// synchronized (s.lock()) {
205.32 -// // Print our stack trace
205.33 -// s.println(this);
205.34 -// StackTraceElement[] trace = getOurStackTrace();
205.35 -// for (StackTraceElement traceElement : trace)
205.36 -// s.println("\tat " + traceElement);
205.37 -//
205.38 -// // Print suppressed exceptions, if any
205.39 -//// for (Throwable se : getSuppressed())
205.40 -//// se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
205.41 -//
205.42 -// // Print cause, if any
205.43 -// Throwable ourCause = getCause();
205.44 -//// if (ourCause != null)
205.45 -//// ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
205.46 -// }
205.47 -// }
205.48 -//
205.49 -// /**
205.50 -// * Print our stack trace as an enclosed exception for the specified
205.51 -// * stack trace.
205.52 -// */
205.53 -// private void printEnclosedStackTrace(PrintStreamOrWriter s,
205.54 -// StackTraceElement[] enclosingTrace,
205.55 -// String caption,
205.56 -// String prefix,
205.57 -// Object dejaVu) {
205.58 -// assert Thread.holdsLock(s.lock());
205.59 -// {
205.60 -// // Compute number of frames in common between this and enclosing trace
205.61 -// StackTraceElement[] trace = getOurStackTrace();
205.62 -// int m = trace.length - 1;
205.63 -// int n = enclosingTrace.length - 1;
205.64 -// while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
205.65 -// m--; n--;
205.66 -// }
205.67 -// int framesInCommon = trace.length - 1 - m;
205.68 -//
205.69 -// // Print our stack trace
205.70 -// s.println(prefix + caption + this);
205.71 -// for (int i = 0; i <= m; i++)
205.72 -// s.println(prefix + "\tat " + trace[i]);
205.73 -// if (framesInCommon != 0)
205.74 -// s.println(prefix + "\t... " + framesInCommon + " more");
205.75 -//
205.76 -// // Print suppressed exceptions, if any
205.77 -// for (Throwable se : getSuppressed())
205.78 -// se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
205.79 -// prefix +"\t", dejaVu);
205.80 -//
205.81 -// // Print cause, if any
205.82 -// Throwable ourCause = getCause();
205.83 -// if (ourCause != null)
205.84 -// ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu);
205.85 -// }
205.86 -// }
205.87 -//
205.88 -// /**
205.89 -// * Prints this throwable and its backtrace to the specified
205.90 -// * print writer.
205.91 -// *
205.92 -// * @param s {@code PrintWriter} to use for output
205.93 -// * @since JDK1.1
205.94 -// */
205.95 -// public void printStackTrace(PrintWriter s) {
205.96 -// printStackTrace(new WrappedPrintWriter(s));
205.97 -// }
205.98 -//
205.99 + /**
205.100 + * Prints this throwable and its backtrace to the specified print stream.
205.101 + *
205.102 + * @param s {@code PrintStream} to use for output
205.103 + */
205.104 + public void printStackTrace(PrintStream s) {
205.105 + s.print(getClass().getName());
205.106 + s.print(": ");
205.107 + s.println(getMessage());
205.108 + }
205.109 +
205.110 + /**
205.111 + * Prints this throwable and its backtrace to the specified
205.112 + * print writer.
205.113 + *
205.114 + * @param s {@code PrintWriter} to use for output
205.115 + * @since JDK1.1
205.116 + */
205.117 + public void printStackTrace(PrintWriter s) {
205.118 + s.append(getClass().getName()).append(": ").println(getMessage());
205.119 + }
205.120 +
205.121 // /**
205.122 // * Wrapper class for PrintStream and PrintWriter to enable a single
205.123 // * implementation of printStackTrace.
206.1 --- a/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Tue Feb 11 10:48:24 2014 +0100
206.2 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Tue Feb 11 13:31:42 2014 +0100
206.3 @@ -106,7 +106,7 @@
206.4 if (type.getName().equals("void")) {
206.5 throw new IllegalStateException("Can't create array for " + type);
206.6 }
206.7 - return "[L" + type.getName() + ";";
206.8 + return "[L" + type.getName().replace('.', '/') + ";";
206.9 }
206.10 /**
206.11 * Creates a new array
207.1 --- a/rt/emul/mini/src/main/java/java/lang/reflect/Constructor.java Tue Feb 11 10:48:24 2014 +0100
207.2 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Constructor.java Tue Feb 11 13:31:42 2014 +0100
207.3 @@ -26,6 +26,10 @@
207.4 package java.lang.reflect;
207.5
207.6 import java.lang.annotation.Annotation;
207.7 +import static java.lang.reflect.Method.fromPrimitive;
207.8 +import static java.lang.reflect.Method.getAccess;
207.9 +import static java.lang.reflect.Method.getParameterTypes;
207.10 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
207.11 import org.apidesign.bck2brwsr.emul.reflect.TypeProvider;
207.12
207.13 /**
207.14 @@ -53,44 +57,20 @@
207.15 GenericDeclaration,
207.16 Member {
207.17
207.18 - private Class<T> clazz;
207.19 - private int slot;
207.20 - private Class<?>[] parameterTypes;
207.21 - private Class<?>[] exceptionTypes;
207.22 - private int modifiers;
207.23 - // Generics and annotations support
207.24 - private transient String signature;
207.25 - private byte[] annotations;
207.26 - private byte[] parameterAnnotations;
207.27 -
207.28 -
207.29 - // For sharing of ConstructorAccessors. This branching structure
207.30 - // is currently only two levels deep (i.e., one root Constructor
207.31 - // and potentially many Constructor objects pointing to it.)
207.32 - private Constructor<T> root;
207.33 + private final Class<T> clazz;
207.34 + private final Object data;
207.35 + private final String sig;
207.36
207.37 /**
207.38 * Package-private constructor used by ReflectAccess to enable
207.39 * instantiation of these objects in Java code from the java.lang
207.40 * package via sun.reflect.LangReflectAccess.
207.41 */
207.42 - Constructor(Class<T> declaringClass,
207.43 - Class<?>[] parameterTypes,
207.44 - Class<?>[] checkedExceptions,
207.45 - int modifiers,
207.46 - int slot,
207.47 - String signature,
207.48 - byte[] annotations,
207.49 - byte[] parameterAnnotations)
207.50 + Constructor(Class<T> declaringClass, Object data, String sig)
207.51 {
207.52 this.clazz = declaringClass;
207.53 - this.parameterTypes = parameterTypes;
207.54 - this.exceptionTypes = checkedExceptions;
207.55 - this.modifiers = modifiers;
207.56 - this.slot = slot;
207.57 - this.signature = signature;
207.58 - this.annotations = annotations;
207.59 - this.parameterAnnotations = parameterAnnotations;
207.60 + this.data = data;
207.61 + this.sig = sig;
207.62 }
207.63
207.64 /**
207.65 @@ -126,7 +106,7 @@
207.66 * @see Modifier
207.67 */
207.68 public int getModifiers() {
207.69 - return modifiers;
207.70 + return getAccess(data);
207.71 }
207.72
207.73 /**
207.74 @@ -159,7 +139,7 @@
207.75 * represents
207.76 */
207.77 public Class<?>[] getParameterTypes() {
207.78 - return (Class<?>[]) parameterTypes.clone();
207.79 + return Method.getParameterTypes(sig);
207.80 }
207.81
207.82
207.83 @@ -205,7 +185,7 @@
207.84 * constructor this object represents
207.85 */
207.86 public Class<?>[] getExceptionTypes() {
207.87 - return (Class<?>[])exceptionTypes.clone();
207.88 + return new Class[0];
207.89 }
207.90
207.91
207.92 @@ -242,20 +222,9 @@
207.93 * same formal parameter types.
207.94 */
207.95 public boolean equals(Object obj) {
207.96 - if (obj != null && obj instanceof Constructor) {
207.97 - Constructor<?> other = (Constructor<?>)obj;
207.98 - if (getDeclaringClass() == other.getDeclaringClass()) {
207.99 - /* Avoid unnecessary cloning */
207.100 - Class<?>[] params1 = parameterTypes;
207.101 - Class<?>[] params2 = other.parameterTypes;
207.102 - if (params1.length == params2.length) {
207.103 - for (int i = 0; i < params1.length; i++) {
207.104 - if (params1[i] != params2[i])
207.105 - return false;
207.106 - }
207.107 - return true;
207.108 - }
207.109 - }
207.110 + if (obj instanceof Constructor) {
207.111 + Constructor other = (Constructor)obj;
207.112 + return data == other.data;
207.113 }
207.114 return false;
207.115 }
207.116 @@ -293,13 +262,14 @@
207.117 }
207.118 sb.append(Field.getTypeName(getDeclaringClass()));
207.119 sb.append("(");
207.120 - Class<?>[] params = parameterTypes; // avoid clone
207.121 + Class<?>[] params = getParameterTypes(); // avoid clone
207.122 for (int j = 0; j < params.length; j++) {
207.123 sb.append(Field.getTypeName(params[j]));
207.124 if (j < (params.length - 1))
207.125 sb.append(",");
207.126 }
207.127 sb.append(")");
207.128 + /*
207.129 Class<?>[] exceptions = exceptionTypes; // avoid clone
207.130 if (exceptions.length > 0) {
207.131 sb.append(" throws ");
207.132 @@ -309,6 +279,7 @@
207.133 sb.append(",");
207.134 }
207.135 }
207.136 + */
207.137 return sb.toString();
207.138 } catch (Exception e) {
207.139 return "<" + e + ">";
207.140 @@ -452,9 +423,29 @@
207.141 throws InstantiationException, IllegalAccessException,
207.142 IllegalArgumentException, InvocationTargetException
207.143 {
207.144 - throw new SecurityException();
207.145 + Class[] types = getParameterTypes();
207.146 + if (types.length != initargs.length) {
207.147 + throw new IllegalArgumentException("Types len " + types.length + " args: " + initargs.length);
207.148 + } else {
207.149 + initargs = initargs.clone();
207.150 + for (int i = 0; i < types.length; i++) {
207.151 + Class c = types[i];
207.152 + if (c.isPrimitive() && initargs[i] != null) {
207.153 + initargs[i] = Method.toPrimitive(initargs[i]);
207.154 + }
207.155 + }
207.156 + }
207.157 + return (T) newInstance0(this.getDeclaringClass(), "cons__" + sig, initargs);
207.158 }
207.159
207.160 + @JavaScriptBody(args = { "self", "sig", "args" }, body =
207.161 + "\nvar c = self.cnstr;"
207.162 + + "\nvar inst = c();"
207.163 + + "\nc[sig].apply(inst, args);"
207.164 + + "\nreturn inst;"
207.165 + )
207.166 + private static native Object newInstance0(Class<?> self, String sig, Object[] args);
207.167 +
207.168 /**
207.169 * Returns {@code true} if this constructor was declared to take
207.170 * a variable number of arguments; returns {@code false}
207.171 @@ -481,22 +472,6 @@
207.172 return Modifier.isSynthetic(getModifiers());
207.173 }
207.174
207.175 - int getSlot() {
207.176 - return slot;
207.177 - }
207.178 -
207.179 - String getSignature() {
207.180 - return signature;
207.181 - }
207.182 -
207.183 - byte[] getRawAnnotations() {
207.184 - return annotations;
207.185 - }
207.186 -
207.187 - byte[] getRawParameterAnnotations() {
207.188 - return parameterAnnotations;
207.189 - }
207.190 -
207.191 /**
207.192 * @throws NullPointerException {@inheritDoc}
207.193 * @since 1.5
207.194 @@ -532,11 +507,11 @@
207.195 * @since 1.5
207.196 */
207.197 public Annotation[][] getParameterAnnotations() {
207.198 - int numParameters = parameterTypes.length;
207.199 - if (parameterAnnotations == null)
207.200 - return new Annotation[numParameters][0];
207.201 +// int numParameters = parameterTypes.length;
207.202 +// if (parameterAnnotations == null)
207.203 +// return new Annotation[numParameters][0];
207.204
207.205 - return new Annotation[numParameters][0]; // XXX
207.206 + return new Annotation[0][0]; // XXX
207.207 /*
207.208 Annotation[][] result = AnnotationParser.parseParameterAnnotations(
207.209 parameterAnnotations,
208.1 --- a/rt/emul/mini/src/main/java/java/lang/reflect/Method.java Tue Feb 11 10:48:24 2014 +0100
208.2 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Method.java Tue Feb 11 13:31:42 2014 +0100
208.3 @@ -113,7 +113,7 @@
208.4 }
208.5
208.6 @JavaScriptBody(args = "self", body = "return self.access;")
208.7 - private static native int getAccess(Object self);
208.8 + static native int getAccess(Object self);
208.9
208.10 /**
208.11 * Returns an array of {@code TypeVariable} objects that represent the
208.12 @@ -183,6 +183,10 @@
208.13 * represents
208.14 */
208.15 public Class<?>[] getParameterTypes() {
208.16 + return getParameterTypes(sig);
208.17 + }
208.18 +
208.19 + static Class<?>[] getParameterTypes(String sig) {
208.20 Class[] arr = new Class[MethodImpl.signatureElements(sig) - 1];
208.21 Enumeration<Class> en = MethodImpl.signatureParser(sig);
208.22 en.nextElement(); // return type
208.23 @@ -235,8 +239,7 @@
208.24 * method this object represents
208.25 */
208.26 public Class<?>[] getExceptionTypes() {
208.27 - throw new UnsupportedOperationException();
208.28 - //return (Class<?>[]) exceptionTypes.clone();
208.29 + return new Class[0];
208.30 }
208.31
208.32 /**
208.33 @@ -583,7 +586,7 @@
208.34 private static native Integer fromRaw(Class<?> cls, String m, Object o);
208.35
208.36 @JavaScriptBody(args = { "o" }, body = "return o.valueOf();")
208.37 - private static native Object toPrimitive(Object o);
208.38 + static native Object toPrimitive(Object o);
208.39
208.40 /**
208.41 * Returns {@code true} if this method is a bridge
208.42 @@ -692,6 +695,11 @@
208.43 protected Method create(Class<?> declaringClass, String name, Object data, String sig) {
208.44 return new Method(declaringClass, name, data, sig);
208.45 }
208.46 +
208.47 + @Override
208.48 + protected Constructor create(Class<?> declaringClass, Object data, String sig) {
208.49 + return new Constructor(declaringClass, data, sig);
208.50 + }
208.51 };
208.52 }
208.53 }
209.1 --- a/rt/emul/mini/src/main/java/java/lang/reflect/Proxy.java Tue Feb 11 10:48:24 2014 +0100
209.2 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Proxy.java Tue Feb 11 13:31:42 2014 +0100
209.3 @@ -212,7 +212,16 @@
209.4
209.5 private static final long serialVersionUID = -2222568056686623797L;
209.6
209.7 -
209.8 + private final static Method getProxyClass;
209.9 + static {
209.10 + Class<?> pg;
209.11 + try {
209.12 + pg = Class.forName("org.apidesign.bck2brwsr.emul.reflect.ProxyImpl");
209.13 + getProxyClass = pg.getMethod("getProxyClass", ClassLoader.class, Class[].class);
209.14 + } catch (Exception ex) {
209.15 + throw new IllegalStateException(ex);
209.16 + }
209.17 + }
209.18
209.19 /**
209.20 * the invocation handler for this proxy instance.
209.21 @@ -315,7 +324,13 @@
209.22 Class<?>... interfaces)
209.23 throws IllegalArgumentException
209.24 {
209.25 - throw new IllegalArgumentException();
209.26 + try {
209.27 + return (Class<?>) getProxyClass.invoke(null, loader, interfaces);
209.28 + } catch (IllegalAccessException ex) {
209.29 + throw new IllegalStateException(ex);
209.30 + } catch (InvocationTargetException ex) {
209.31 + throw (RuntimeException)ex.getTargetException();
209.32 + }
209.33 }
209.34
209.35 /**
209.36 @@ -355,7 +370,27 @@
209.37 if (h == null) {
209.38 throw new NullPointerException();
209.39 }
209.40 - throw new IllegalArgumentException();
209.41 +
209.42 + /*
209.43 + * Look up or generate the designated proxy class.
209.44 + */
209.45 + Class<?> cl = getProxyClass(loader, interfaces);
209.46 +
209.47 + /*
209.48 + * Invoke its constructor with the designated invocation handler.
209.49 + */
209.50 + try {
209.51 + Constructor cons = cl.getConstructor(InvocationHandler.class);
209.52 + return cons.newInstance(new Object[] { h });
209.53 + } catch (NoSuchMethodException e) {
209.54 + throw new IllegalStateException(e.toString());
209.55 + } catch (IllegalAccessException e) {
209.56 + throw new IllegalStateException(e.toString());
209.57 + } catch (InstantiationException e) {
209.58 + throw new IllegalStateException(e.toString());
209.59 + } catch (InvocationTargetException e) {
209.60 + throw new IllegalStateException(e.toString());
209.61 + }
209.62 }
209.63
209.64 /**
209.65 @@ -376,8 +411,7 @@
209.66 if (cl == null) {
209.67 throw new NullPointerException();
209.68 }
209.69 -
209.70 - return false;
209.71 + return Proxy.class.isAssignableFrom(cl);
209.72 }
209.73
209.74 /**
209.75 @@ -401,7 +435,4 @@
209.76 Proxy p = (Proxy) proxy;
209.77 return p.h;
209.78 }
209.79 -
209.80 - private static native Class defineClass0(ClassLoader loader, String name,
209.81 - byte[] b, int off, int len);
209.82 }
210.1 --- a/rt/emul/mini/src/main/java/java/net/URL.java Tue Feb 11 10:48:24 2014 +0100
210.2 +++ b/rt/emul/mini/src/main/java/java/net/URL.java Tue Feb 11 13:31:42 2014 +0100
210.3 @@ -920,6 +920,23 @@
210.4 }
210.5
210.6 /**
210.7 + * Returns a {@link java.net.URI} equivalent to this URL.
210.8 + * This method functions in the same way as <code>new URI (this.toString())</code>.
210.9 + * <p>Note, any URL instance that complies with RFC 2396 can be converted
210.10 + * to a URI. However, some URLs that are not strictly in compliance
210.11 + * can not be converted to a URI.
210.12 + *
210.13 + * @exception URISyntaxException if this URL is not formatted strictly according to
210.14 + * to RFC2396 and cannot be converted to a URI.
210.15 + *
210.16 + * @return a URI instance equivalent to this URL.
210.17 + * @since 1.5
210.18 + */
210.19 + public URI toURI() throws URISyntaxException {
210.20 + return new URI (toString());
210.21 + }
210.22 +
210.23 + /**
210.24 * Returns a {@link java.net.URLConnection URLConnection} instance that
210.25 * represents a connection to the remote object referred to by the
210.26 * {@code URL}.
210.27 @@ -948,9 +965,9 @@
210.28 * @see java.net.URL#URL(java.lang.String, java.lang.String,
210.29 * int, java.lang.String)
210.30 */
210.31 -// public URLConnection openConnection() throws java.io.IOException {
210.32 -// return handler.openConnection(this);
210.33 -// }
210.34 + public URLConnection openConnection() throws java.io.IOException {
210.35 + return handler.openConnection(this);
210.36 + }
210.37
210.38
210.39 /**
210.40 @@ -1027,18 +1044,53 @@
210.41 public final Object getContent(Class[] classes)
210.42 throws java.io.IOException {
210.43 for (Class<?> c : classes) {
210.44 - if (c == String.class) {
210.45 - return loadText(toExternalForm());
210.46 - }
210.47 - if (c == byte[].class) {
210.48 - return loadBytes(toExternalForm(), new byte[0]);
210.49 + try {
210.50 + if (c == String.class) {
210.51 + return loadText(toExternalForm());
210.52 + }
210.53 + if (c == byte[].class) {
210.54 + return loadBytes(toExternalForm(), new byte[0]);
210.55 + }
210.56 + } catch (Throwable t) {
210.57 + throw new IOException(t);
210.58 }
210.59 }
210.60 return null;
210.61 }
210.62
210.63 - static URLStreamHandler getURLStreamHandler(String protocol) {
210.64 - URLStreamHandler universal = new URLStreamHandler() {};
210.65 + static URLStreamHandler getURLStreamHandler(final String protocol) {
210.66 + URLStreamHandler universal = new URLStreamHandler() {
210.67 + @Override
210.68 + protected URLConnection openConnection(URL u) throws IOException {
210.69 + return new URLConnection(u) {
210.70 + Object stream = url.is;
210.71 +
210.72 + @Override
210.73 + public void connect() throws IOException {
210.74 + if (stream == null) {
210.75 + try {
210.76 + byte[] arr = (byte[]) url.getContent(new Class[]{byte[].class});
210.77 + stream = new ByteArrayInputStream(arr);
210.78 + } catch (IOException ex) {
210.79 + stream = ex;
210.80 + throw ex;
210.81 + }
210.82 + }
210.83 + }
210.84 +
210.85 + @Override
210.86 + public InputStream getInputStream() throws IOException {
210.87 + connect();
210.88 + if (stream instanceof IOException) {
210.89 + throw (IOException)stream;
210.90 + }
210.91 + return (InputStream)stream;
210.92 + }
210.93 +
210.94 +
210.95 + };
210.96 + }
210.97 + };
210.98 return universal;
210.99 }
210.100
210.101 @@ -1053,10 +1105,16 @@
210.102 }
210.103
210.104 @JavaScriptBody(args = {}, body =
210.105 - "if (typeof window !== 'object') return null;\n"
210.106 - + "if (!window.location) return null;\n"
210.107 - + "if (!window.location.href) return null;\n"
210.108 - + "return window.location.href;\n"
210.109 + "var l;\n"
210.110 + + "if (typeof location !== 'object') {"
210.111 + + " if (typeof window !== 'object') return null;\n"
210.112 + + " if (!window.location) return null;\n"
210.113 + + " l = window.location;\n"
210.114 + + "} else {\n"
210.115 + + " l = location;\n"
210.116 + + "}\n"
210.117 + + "if (!l.href) return null;\n"
210.118 + + "return l.href;\n"
210.119 )
210.120 private static native String findBaseURL();
210.121 }
211.1 --- a/rt/emul/mini/src/main/java/java/net/URLStreamHandler.java Tue Feb 11 10:48:24 2014 +0100
211.2 +++ b/rt/emul/mini/src/main/java/java/net/URLStreamHandler.java Tue Feb 11 13:31:42 2014 +0100
211.3 @@ -25,6 +25,8 @@
211.4
211.5 package java.net;
211.6
211.7 +import java.io.IOException;
211.8 +
211.9
211.10 /**
211.11 * The abstract class <code>URLStreamHandler</code> is the common
211.12 @@ -62,7 +64,7 @@
211.13 * @exception IOException if an I/O error occurs while opening the
211.14 * connection.
211.15 */
211.16 -// abstract protected URLConnection openConnection(URL u) throws IOException;
211.17 + abstract protected URLConnection openConnection(URL u) throws IOException;
211.18
211.19 /**
211.20 * Same as openConnection(URL), except that the connection will be
212.1 --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Tue Feb 11 10:48:24 2014 +0100
212.2 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Tue Feb 11 13:31:42 2014 +0100
212.3 @@ -19,6 +19,7 @@
212.4
212.5 import java.lang.reflect.Method;
212.6 import org.apidesign.bck2brwsr.core.JavaScriptBody;
212.7 +import org.apidesign.bck2brwsr.core.JavaScriptOnly;
212.8
212.9 /**
212.10 *
212.11 @@ -71,4 +72,27 @@
212.12 }
212.13 @JavaScriptBody(args = { "obj" }, body="return vm.java_lang_Object(false).hashCode__I.call(obj);")
212.14 public static native int identityHashCode(Object obj);
212.15 +
212.16 + @JavaScriptOnly(name = "toJS", value = "function(v) {\n" +
212.17 + " if (v === null) return null;\n" +
212.18 + " if (Object.prototype.toString.call(v) === '[object Array]') {\n" +
212.19 + " return vm.org_apidesign_bck2brwsr_emul_lang_System(false).convArray__Ljava_lang_Object_2Ljava_lang_Object_2(v);\n" +
212.20 + " }\n" +
212.21 + " return v.valueOf();\n" +
212.22 + "}\n"
212.23 + )
212.24 + public static native int toJS();
212.25 +
212.26 + private static Object convArray(Object o) {
212.27 + if (o instanceof Object[]) {
212.28 + Object[] arr = (Object[]) o;
212.29 + final int l = arr.length;
212.30 + Object[] ret = new Object[l];
212.31 + for (int i = 0; i < l; i++) {
212.32 + ret[i] = convArray(arr[i]);
212.33 + }
212.34 + return ret;
212.35 + }
212.36 + return o;
212.37 + }
212.38 }
213.1 --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Tue Feb 11 10:48:24 2014 +0100
213.2 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Tue Feb 11 13:31:42 2014 +0100
213.3 @@ -18,6 +18,7 @@
213.4 package org.apidesign.bck2brwsr.emul.reflect;
213.5
213.6 import java.lang.reflect.Array;
213.7 +import java.lang.reflect.Constructor;
213.8 import java.lang.reflect.Method;
213.9 import java.util.Enumeration;
213.10 import org.apidesign.bck2brwsr.core.JavaScriptBody;
213.11 @@ -37,15 +38,17 @@
213.12 }
213.13
213.14 protected abstract Method create(Class<?> declaringClass, String name, Object data, String sig);
213.15 + protected abstract Constructor create(Class<?> declaringClass, Object data, String sig);
213.16
213.17
213.18 //
213.19 // bck2brwsr implementation
213.20 //
213.21
213.22 - @JavaScriptBody(args = {"clazz", "prefix"},
213.23 + @JavaScriptBody(args = {"clazz", "prefix", "cnstr"},
213.24 body = ""
213.25 - + "var c = clazz.cnstr.prototype;"
213.26 + + "var c = clazz.cnstr;\n"
213.27 + + "if (!cnstr) c = c.prototype;"
213.28 + "var arr = new Array();\n"
213.29 + "for (m in c) {\n"
213.30 + " if (m.indexOf(prefix) === 0) {\n"
213.31 @@ -57,11 +60,55 @@
213.32 + "}\n"
213.33 + "return arr;")
213.34 private static native Object[] findMethodData(
213.35 - Class<?> clazz, String prefix);
213.36 + Class<?> clazz, String prefix, boolean cnstr);
213.37
213.38 + public static Constructor findConstructor(
213.39 + Class<?> clazz, Class<?>... parameterTypes) {
213.40 + Object[] data = findMethodData(clazz, "cons__", true);
213.41 + BIG: for (int i = 0; i < data.length; i += 3) {
213.42 + String sig = ((String) data[i]).substring(6);
213.43 + Class<?> cls = (Class<?>) data[i + 2];
213.44 + Constructor tmp = INSTANCE.create(cls, data[i + 1], sig);
213.45 + Class<?>[] tmpParms = tmp.getParameterTypes();
213.46 + if (parameterTypes.length != tmpParms.length) {
213.47 + continue;
213.48 + }
213.49 + for (int j = 0; j < tmpParms.length; j++) {
213.50 + if (!parameterTypes[j].equals(tmpParms[j])) {
213.51 + continue BIG;
213.52 + }
213.53 + }
213.54 + return tmp;
213.55 + }
213.56 + return null;
213.57 + }
213.58 +
213.59 + public static Constructor[] findConstructors(Class<?> clazz, int mask) {
213.60 + Object[] namesAndData = findMethodData(clazz, "", true);
213.61 + int cnt = 0;
213.62 + for (int i = 0; i < namesAndData.length; i += 3) {
213.63 + String sig = (String) namesAndData[i];
213.64 + Object data = namesAndData[i + 1];
213.65 + if (!sig.startsWith("cons__")) {
213.66 + continue;
213.67 + }
213.68 + sig = sig.substring(6);
213.69 + Class<?> cls = (Class<?>) namesAndData[i + 2];
213.70 + final Constructor m = INSTANCE.create(cls, data, sig);
213.71 + if ((m.getModifiers() & mask) == 0) {
213.72 + continue;
213.73 + }
213.74 + namesAndData[cnt++] = m;
213.75 + }
213.76 + Constructor[] arr = new Constructor[cnt];
213.77 + for (int i = 0; i < cnt; i++) {
213.78 + arr[i] = (Constructor) namesAndData[i];
213.79 + }
213.80 + return arr;
213.81 + }
213.82 public static Method findMethod(
213.83 Class<?> clazz, String name, Class<?>... parameterTypes) {
213.84 - Object[] data = findMethodData(clazz, name + "__");
213.85 + Object[] data = findMethodData(clazz, name + "__", false);
213.86 BIG: for (int i = 0; i < data.length; i += 3) {
213.87 String sig = ((String) data[i]).substring(name.length() + 2);
213.88 Class<?> cls = (Class<?>) data[i + 2];
213.89 @@ -81,7 +128,7 @@
213.90 }
213.91
213.92 public static Method[] findMethods(Class<?> clazz, int mask) {
213.93 - Object[] namesAndData = findMethodData(clazz, "");
213.94 + Object[] namesAndData = findMethodData(clazz, "", false);
213.95 int cnt = 0;
213.96 for (int i = 0; i < namesAndData.length; i += 3) {
213.97 String sig = (String) namesAndData[i];
214.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
214.2 +++ b/rt/emul/mini/src/main/java/org/apidesign/vm4brwsr/api/VM.java Tue Feb 11 13:31:42 2014 +0100
214.3 @@ -0,0 +1,47 @@
214.4 +/**
214.5 + * Back 2 Browser Bytecode Translator
214.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
214.7 + *
214.8 + * This program is free software: you can redistribute it and/or modify
214.9 + * it under the terms of the GNU General Public License as published by
214.10 + * the Free Software Foundation, version 2 of the License.
214.11 + *
214.12 + * This program is distributed in the hope that it will be useful,
214.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
214.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
214.15 + * GNU General Public License for more details.
214.16 + *
214.17 + * You should have received a copy of the GNU General Public License
214.18 + * along with this program. Look for COPYING file in the top folder.
214.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
214.20 + */
214.21 +package org.apidesign.vm4brwsr.api;
214.22 +
214.23 +import java.io.IOException;
214.24 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
214.25 +
214.26 +/** Utility methods to talk to the Bck2Brwsr virtual machine.
214.27 + *
214.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
214.29 + * @since 0.9
214.30 + */
214.31 +public final class VM {
214.32 + private VM() {
214.33 + }
214.34 +
214.35 + /** Takes an existing class and replaces its existing byte code
214.36 + * with new one.
214.37 + *
214.38 + * @param clazz existing class to reload
214.39 + * @param byteCode new bytecode
214.40 + * @throws IOException an exception is something goes wrong
214.41 + */
214.42 + public static void reload(Class<?> clazz, byte[] byteCode) throws IOException {
214.43 + reloadImpl(clazz.getName(), byteCode);
214.44 + }
214.45 +
214.46 + @JavaScriptBody(args = { "name", "byteCode" }, body = "vm._reload(name, byteCode);")
214.47 + private static void reloadImpl(String name, byte[] byteCode) throws IOException {
214.48 + throw new IOException();
214.49 + }
214.50 +}
215.1 --- a/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js Tue Feb 11 10:48:24 2014 +0100
215.2 +++ b/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js Tue Feb 11 13:31:42 2014 +0100
215.3 @@ -176,6 +176,8 @@
215.4 };
215.5
215.6 numberPrototype.shl64 = function(x) {
215.7 + x &= 0x3f;
215.8 + if (x == 0) return this;
215.9 if (x >= 32) {
215.10 var hi = this << (x - 32);
215.11 return hi.next32(0);
215.12 @@ -190,6 +192,8 @@
215.13 };
215.14
215.15 numberPrototype.shr64 = function(x) {
215.16 + x &= 0x3f;
215.17 + if (x == 0) return this;
215.18 if (x >= 32) {
215.19 var low = this.high32() >> (x - 32);
215.20 low += (low < 0) ? (__m32 + 1) : 0;
215.21 @@ -205,6 +209,8 @@
215.22 };
215.23
215.24 numberPrototype.ushr64 = function(x) {
215.25 + x &= 0x3f;
215.26 + if (x == 0) return this;
215.27 if (x >= 32) {
215.28 var low = this.high32() >>> (x - 32);
215.29 low += (low < 0) ? (__m32 + 1) : 0;
216.1 --- a/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js Tue Feb 11 10:48:24 2014 +0100
216.2 +++ b/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js Tue Feb 11 13:31:42 2014 +0100
216.3 @@ -2,16 +2,16 @@
216.4 vm.java_lang_reflect_Array(false);
216.5 vm.java_lang_String(false);
216.6
216.7 -Array.prototype.at = function(indx, value) {
216.8 - if (indx < 0 || indx > this.length) {
216.9 +Array.at = function(arr, indx, value) {
216.10 + if (indx < 0 || indx >= arr.length) {
216.11 var e = vm.java_lang_ArrayIndexOutOfBoundsException(true);
216.12 e.constructor.cons__VLjava_lang_String_2.call(e, indx.toString());
216.13 throw e;
216.14 }
216.15 - if (arguments.length === 2) {
216.16 - this[indx] = value;
216.17 + if (arguments.length === 3) {
216.18 + arr[indx] = value;
216.19 }
216.20 - return this[indx];
216.21 + return arr[indx];
216.22 };
216.23 Array.prototype.getClass__Ljava_lang_Class_2 = function() {
216.24 return vm.java_lang_Class(false).defineArray__Ljava_lang_Class_2Ljava_lang_String_2(this.jvmName);
217.1 --- a/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrwsrMojo.java Tue Feb 11 10:48:24 2014 +0100
217.2 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrwsrMojo.java Tue Feb 11 13:31:42 2014 +0100
217.3 @@ -70,12 +70,18 @@
217.4 /** Root of all pages, and files, etc. */
217.5 @Parameter
217.6 private File directory;
217.7 +
217.8 + @Parameter(defaultValue="${project.build.directory}/bck2brwsr.js")
217.9 + private File javascript;
217.10
217.11 @Override
217.12 public void execute() throws MojoExecutionException {
217.13 if (startpage == null) {
217.14 throw new MojoExecutionException("You have to provide a start page");
217.15 }
217.16 + if (javascript != null && javascript.isFile()) {
217.17 + System.setProperty("bck2brwsr.js", javascript.toURI().toString());
217.18 + }
217.19 try {
217.20 Closeable httpServer;
217.21 if (directory != null) {
218.1 --- a/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Tue Feb 11 10:48:24 2014 +0100
218.2 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Tue Feb 11 13:31:42 2014 +0100
218.3 @@ -50,7 +50,7 @@
218.4 @Parameter(defaultValue="${project.build.directory}/classes")
218.5 private File classes;
218.6 /** JavaScript file to generate */
218.7 - @Parameter
218.8 + @Parameter(defaultValue="${project.build.directory}/bck2brwsr.js")
218.9 private File javascript;
218.10
218.11 /** Additional classes that should be pre-compiled into the javascript
218.12 @@ -70,12 +70,19 @@
218.13 */
218.14 @Parameter(defaultValue="NONE")
218.15 private ObfuscationLevel obfuscation;
218.16 +
218.17 + /** Should classes from rt.jar be included? */
218.18 + @Parameter(defaultValue = "false")
218.19 + private boolean ignoreBootClassPath;
218.20
218.21 @Override
218.22 public void execute() throws MojoExecutionException {
218.23 if (!classes.isDirectory()) {
218.24 throw new MojoExecutionException("Can't find " + classes);
218.25 }
218.26 + if (javascript == null) {
218.27 + throw new MojoExecutionException("Need to define 'javascript' attribute with a path to file to generate");
218.28 + }
218.29
218.30 List<String> arr = new ArrayList<String>();
218.31 long newest = collectAllClasses("", classes, arr);
218.32 @@ -94,7 +101,7 @@
218.33 FileWriter w = new FileWriter(javascript);
218.34 Bck2Brwsr.newCompiler().
218.35 obfuscation(obfuscation).
218.36 - resources(url).
218.37 + resources(url, ignoreBootClassPath).
218.38 addRootClasses(arr.toArray(new String[0])).
218.39 generate(w);
218.40 w.close();
219.1 --- a/rt/vm/pom.xml Tue Feb 11 10:48:24 2014 +0100
219.2 +++ b/rt/vm/pom.xml Tue Feb 11 13:31:42 2014 +0100
219.3 @@ -18,6 +18,7 @@
219.4 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
219.5 <author.name>Jaroslav Tulach</author.name>
219.6 <author.email>jaroslav.tulach@apidesign.org</author.email>
219.7 + <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
219.8 </properties>
219.9
219.10 <repositories>
219.11 @@ -91,7 +92,7 @@
219.12 <classpath />
219.13 <argument>org.apidesign.vm4brwsr.Main</argument>
219.14 <argument>--obfuscatelevel</argument>
219.15 - <argument>MINIMAL</argument>
219.16 + <argument>${bck2brwsr.obfuscationlevel}</argument>
219.17 <argument>${project.build.directory}/bck2brwsr.js</argument>
219.18 <argument>org/apidesign/vm4brwsr/Bck2Brwsr</argument>
219.19 </arguments>
219.20 @@ -153,7 +154,7 @@
219.21 <scope>compile</scope>
219.22 </dependency>
219.23 <dependency>
219.24 - <groupId>org.apidesign.html</groupId>
219.25 + <groupId>org.netbeans.html</groupId>
219.26 <artifactId>net.java.html.boot</artifactId>
219.27 <scope>test</scope>
219.28 <version>${net.java.html.version}</version>
220.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Tue Feb 11 10:48:24 2014 +0100
220.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Tue Feb 11 13:31:42 2014 +0100
220.3 @@ -19,8 +19,6 @@
220.4
220.5 import java.io.IOException;
220.6 import java.io.InputStream;
220.7 -import java.net.URL;
220.8 -import java.util.Enumeration;
220.9
220.10 /** Build your own virtual machine! Use methods in this class to generate
220.11 * a skeleton JVM in JavaScript that contains pre-compiled classes of your
220.12 @@ -151,7 +149,21 @@
220.13 * @since 0.5
220.14 */
220.15 public Bck2Brwsr resources(final ClassLoader loader) {
220.16 - return resources(new LdrRsrcs(loader));
220.17 + return resources(loader, false);
220.18 + }
220.19 +
220.20 + /** A way to change the provider of additional resources (classes) for the
220.21 + * compiler by specifying classloader to use for loading them.
220.22 + *
220.23 + * @param loader class loader to load the resources from
220.24 + * @param ignoreBootClassPath <code>true</code> if classes loaded
220.25 + * from <code>rt.jar</code>
220.26 + * @return new instance of the compiler with all values being the same, just
220.27 + * different resources provider
220.28 + * @since 0.9
220.29 + */
220.30 + public Bck2Brwsr resources(final ClassLoader loader, boolean ignoreBootClassPath) {
220.31 + return resources(new LdrRsrcs(loader, ignoreBootClassPath));
220.32 }
220.33
220.34 /** Generates virtual machine based on previous configuration of the
220.35 @@ -161,7 +173,7 @@
220.36 * @since 0.5
220.37 */
220.38 public void generate(Appendable out) throws IOException {
220.39 - Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader());
220.40 + Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader(), false);
220.41 if (level != ObfuscationLevel.NONE) {
220.42 try {
220.43 ClosureWrapper.produceTo(out, level, r, classes);
221.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Tue Feb 11 10:48:24 2014 +0100
221.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Tue Feb 11 13:31:42 2014 +0100
221.3 @@ -1845,6 +1845,12 @@
221.4 case '\"':
221.5 sb.append('\\').append('\"');
221.6 break;
221.7 + case '\u2028':
221.8 + sb.append("\\u2028");
221.9 + break;
221.10 + case '\u2029':
221.11 + sb.append("\\u2029");
221.12 + break;
221.13 default:
221.14 sb.append(c);
221.15 }
222.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue Feb 11 10:48:24 2014 +0100
222.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue Feb 11 13:31:42 2014 +0100
222.3 @@ -139,7 +139,7 @@
222.4 if (proto == null) {
222.5 String sc = jc.getSuperClassName(); // with _
222.6 out.append("\n var pp = ").
222.7 - append(accessClass(sc.replace('/', '_'))).append("(true);");
222.8 + append(accessClass(mangleClassName(sc))).append("(true);");
222.9 out.append("\n var p = CLS.prototype = pp;");
222.10 out.append("\n var c = p;");
222.11 out.append("\n var sprcls = pp.constructor.$class;");
222.12 @@ -153,10 +153,10 @@
222.13 }
222.14 for (FieldData v : jc.getFields()) {
222.15 if (v.isStatic()) {
222.16 - out.append("\n CLS.").append(v.getName()).append(initField(v));
222.17 + out.append("\n CLS.fld_").append(v.getName()).append(initField(v));
222.18 out.append("\n c._").append(v.getName()).append(" = function (v) {")
222.19 - .append(" if (arguments.length == 1) CLS.").append(v.getName())
222.20 - .append(" = v; return CLS.").
222.21 + .append(" if (arguments.length == 1) CLS.fld_").append(v.getName())
222.22 + .append(" = v; return CLS.fld_").
222.23 append(v.getName()).append("; };");
222.24 } else {
222.25 out.append("\n c._").append(v.getName()).append(" = function (v) {")
222.26 @@ -975,7 +975,7 @@
222.27 int indx = readUShortArg(byteCodes, i);
222.28 String ci = jc.getClassName(indx);
222.29 emit(out, "var @1 = new @2;",
222.30 - smapper.pushA(), accessClass(ci.replace('/', '_')));
222.31 + smapper.pushA(), accessClass(mangleClassName(ci)));
222.32 addReference(ci);
222.33 i += 2;
222.34 break;
222.35 @@ -1001,49 +1001,49 @@
222.36 smapper.popA(), smapper.pushI());
222.37 break;
222.38 case opc_lastore:
222.39 - emit(out, "@3.at(@2, @1);",
222.40 + emit(out, "Array.at(@3, @2, @1);",
222.41 smapper.popL(), smapper.popI(), smapper.popA());
222.42 break;
222.43 case opc_fastore:
222.44 - emit(out, "@3.at(@2, @1);",
222.45 + emit(out, "Array.at(@3, @2, @1);",
222.46 smapper.popF(), smapper.popI(), smapper.popA());
222.47 break;
222.48 case opc_dastore:
222.49 - emit(out, "@3.at(@2, @1);",
222.50 + emit(out, "Array.at(@3, @2, @1);",
222.51 smapper.popD(), smapper.popI(), smapper.popA());
222.52 break;
222.53 case opc_aastore:
222.54 - emit(out, "@3.at(@2, @1);",
222.55 + emit(out, "Array.at(@3, @2, @1);",
222.56 smapper.popA(), smapper.popI(), smapper.popA());
222.57 break;
222.58 case opc_iastore:
222.59 case opc_bastore:
222.60 case opc_castore:
222.61 case opc_sastore:
222.62 - emit(out, "@3.at(@2, @1);",
222.63 + emit(out, "Array.at(@3, @2, @1);",
222.64 smapper.popI(), smapper.popI(), smapper.popA());
222.65 break;
222.66 case opc_laload:
222.67 - emit(out, "var @3 = @2.at(@1);",
222.68 + emit(out, "var @3 = Array.at(@2, @1);",
222.69 smapper.popI(), smapper.popA(), smapper.pushL());
222.70 break;
222.71 case opc_faload:
222.72 - emit(out, "var @3 = @2.at(@1);",
222.73 + emit(out, "var @3 = Array.at(@2, @1);",
222.74 smapper.popI(), smapper.popA(), smapper.pushF());
222.75 break;
222.76 case opc_daload:
222.77 - emit(out, "var @3 = @2.at(@1);",
222.78 + emit(out, "var @3 = Array.at(@2, @1);",
222.79 smapper.popI(), smapper.popA(), smapper.pushD());
222.80 break;
222.81 case opc_aaload:
222.82 - emit(out, "var @3 = @2.at(@1);",
222.83 + emit(out, "var @3 = Array.at(@2, @1);",
222.84 smapper.popI(), smapper.popA(), smapper.pushA());
222.85 break;
222.86 case opc_iaload:
222.87 case opc_baload:
222.88 case opc_caload:
222.89 case opc_saload:
222.90 - emit(out, "var @3 = @2.at(@1);",
222.91 + emit(out, "var @3 = Array.at(@2, @1);",
222.92 smapper.popI(), smapper.popA(), smapper.pushI());
222.93 break;
222.94 case opc_pop:
222.95 @@ -1214,7 +1214,7 @@
222.96 int indx = readUShortArg(byteCodes, i);
222.97 String[] fi = jc.getFieldInfoName(indx);
222.98 final int type = VarType.fromFieldType(fi[2].charAt(0));
222.99 - final String mangleClass = mangleSig(fi[0]);
222.100 + final String mangleClass = mangleClassName(fi[0]);
222.101 final String mangleClassAccess = accessClass(mangleClass);
222.102 emit(out, "var @2 = @4(false)._@3.call(@1);",
222.103 smapper.popA(),
222.104 @@ -1227,7 +1227,7 @@
222.105 int indx = readUShortArg(byteCodes, i);
222.106 String[] fi = jc.getFieldInfoName(indx);
222.107 final int type = VarType.fromFieldType(fi[2].charAt(0));
222.108 - final String mangleClass = mangleSig(fi[0]);
222.109 + final String mangleClass = mangleClassName(fi[0]);
222.110 final String mangleClassAccess = accessClass(mangleClass);
222.111 emit(out, "@4(false)._@3.call(@2, @1);",
222.112 smapper.popT(type),
222.113 @@ -1243,7 +1243,7 @@
222.114 final int type = VarType.fromFieldType(fi[2].charAt(0));
222.115 emit(out, "var @1 = @2(false)._@3();",
222.116 smapper.pushT(type),
222.117 - accessClass(fi[0].replace('/', '_')), fi[1]);
222.118 + accessClass(mangleClassName(fi[0])), fi[1]);
222.119 i += 2;
222.120 addReference(fi[0]);
222.121 break;
222.122 @@ -1253,7 +1253,7 @@
222.123 String[] fi = jc.getFieldInfoName(indx);
222.124 final int type = VarType.fromFieldType(fi[2].charAt(0));
222.125 emit(out, "@1(false)._@2(@3);",
222.126 - accessClass(fi[0].replace('/', '_')), fi[1],
222.127 + accessClass(mangleClassName(fi[0])), fi[1],
222.128 smapper.popT(type));
222.129 i += 2;
222.130 addReference(fi[0]);
222.131 @@ -1435,8 +1435,20 @@
222.132 return mangleSig(sig, 0, sig.length());
222.133 }
222.134
222.135 + private static String mangleMethodName(String name) {
222.136 + StringBuilder sb = new StringBuilder(name.length() * 2);
222.137 + int last = name.length();
222.138 + for (int i = 0; i < last; i++) {
222.139 + final char ch = name.charAt(i);
222.140 + switch (ch) {
222.141 + case '_': sb.append("_1"); break;
222.142 + default: sb.append(ch); break;
222.143 + }
222.144 + }
222.145 + return sb.toString();
222.146 + }
222.147 private static String mangleSig(String txt, int first, int last) {
222.148 - StringBuilder sb = new StringBuilder();
222.149 + StringBuilder sb = new StringBuilder((last - first) * 2);
222.150 for (int i = first; i < last; i++) {
222.151 final char ch = txt.charAt(i);
222.152 switch (ch) {
222.153 @@ -1449,6 +1461,10 @@
222.154 }
222.155 return sb.toString();
222.156 }
222.157 +
222.158 + private static String mangleClassName(String name) {
222.159 + return mangleSig(name);
222.160 + }
222.161
222.162 private static String findMethodName(MethodData m, StringBuilder cnt) {
222.163 StringBuilder name = new StringBuilder();
222.164 @@ -1457,7 +1473,7 @@
222.165 } else if ("<clinit>".equals(m.getName())) { // NOI18N
222.166 name.append("class"); // NOI18N
222.167 } else {
222.168 - name.append(m.getName());
222.169 + name.append(mangleMethodName(m.getName()));
222.170 }
222.171
222.172 countArgs(m.getInternalSig(), new char[1], name, cnt);
222.173 @@ -1471,7 +1487,7 @@
222.174 if ("<init>".equals(nm)) { // NOI18N
222.175 name.append("cons"); // NOI18N
222.176 } else {
222.177 - name.append(nm);
222.178 + name.append(mangleMethodName(nm));
222.179 }
222.180 countArgs(descr, returnType, name, cnt);
222.181 return name.toString();
222.182 @@ -1499,7 +1515,7 @@
222.183 }
222.184
222.185 final String in = mi[0];
222.186 - out.append(accessClass(in.replace('/', '_')));
222.187 + out.append(accessClass(mangleClassName(in)));
222.188 out.append("(false).");
222.189 if (mn.startsWith("cons_")) {
222.190 out.append("constructor.");
222.191 @@ -1585,7 +1601,7 @@
222.192 s = accessClass("java_lang_Class") + "(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('" + classRef[0] + "');";
222.193 } else {
222.194 addReference(classRef[0]);
222.195 - s = accessClass(s.replace('/', '_')) + "(false).constructor.$class";
222.196 + s = accessClass(mangleClassName(s)) + "(false).constructor.$class";
222.197 }
222.198 }
222.199 return s;
222.200 @@ -1607,6 +1623,7 @@
222.201 String[] args = new String[30];
222.202 String body;
222.203 boolean javacall;
222.204 + boolean html4j;
222.205
222.206 @Override
222.207 protected void visitAttr(String type, String attr, String at, String value) {
222.208 @@ -1620,6 +1637,7 @@
222.209 }
222.210 }
222.211 if (type.equals(htmlType)) {
222.212 + html4j = true;
222.213 if ("body".equals(attr)) {
222.214 body = value;
222.215 } else if ("args".equals(attr)) {
222.216 @@ -1643,12 +1661,18 @@
222.217 out.append(" = function(");
222.218 String space = "";
222.219 int index = 0;
222.220 + StringBuilder toValue = new StringBuilder();
222.221 for (int i = 0; i < cnt.length(); i++) {
222.222 out.append(space);
222.223 space = outputArg(out, p.args, index);
222.224 + if (p.html4j && space.length() > 0) {
222.225 + toValue.append("\n ").append(p.args[index]).append(" = vm.org_apidesign_bck2brwsr_emul_lang_System(false).toJS(").
222.226 + append(p.args[index]).append(");");
222.227 + }
222.228 index++;
222.229 }
222.230 out.append(") {").append("\n");
222.231 + out.append(toValue.toString());
222.232 if (p.javacall) {
222.233 int lastSlash = jc.getClassName().lastIndexOf('/');
222.234 final String pkg = jc.getClassName().substring(0, lastSlash);
222.235 @@ -1695,7 +1719,7 @@
222.236 int paramBeg = body.indexOf('(', sigEnd + 1);
222.237
222.238 sb.append("vm.").append(pkgName.replace('/', '_')).append("_$JsCallbacks$(false)._VM().");
222.239 - sb.append(mangle(fqn, method, params, false));
222.240 + sb.append(mangleJsCallbacks(fqn, method, params, false));
222.241 sb.append("(").append(refId);
222.242 if (body.charAt(paramBeg + 1) != ')') {
222.243 sb.append(",");
222.244 @@ -1732,12 +1756,13 @@
222.245 int paramBeg = body.indexOf('(', sigEnd + 1);
222.246
222.247 sb.append("vm.").append(pkgName.replace('/', '_')).append("_$JsCallbacks$(false)._VM().");
222.248 - sb.append(mangle(fqn, method, params, true));
222.249 + sb.append(mangleJsCallbacks(fqn, method, params, true));
222.250 sb.append("(");
222.251 pos = paramBeg + 1;
222.252 }
222.253 }
222.254 - private static String mangle(String fqn, String method, String params, boolean isStatic) {
222.255 +
222.256 + static String mangleJsCallbacks(String fqn, String method, String params, boolean isStatic) {
222.257 if (params.startsWith("(")) {
222.258 params = params.substring(1);
222.259 }
222.260 @@ -1745,28 +1770,40 @@
222.261 params = params.substring(0, params.length() - 1);
222.262 }
222.263 StringBuilder sb = new StringBuilder();
222.264 - final String rfqn = replace(fqn);
222.265 - final String rm = replace(method);
222.266 - final String rp = replace(params);
222.267 + final String fqnu = fqn.replace('.', '_');
222.268 + final String rfqn = mangleClassName(fqnu);
222.269 + final String rm = mangleMethodName(method);
222.270 + final String srp;
222.271 + {
222.272 + StringBuilder pb = new StringBuilder();
222.273 + int len = params.length();
222.274 + int indx = 0;
222.275 + while (indx < len) {
222.276 + char ch = params.charAt(indx);
222.277 + if (ch == '[' || ch == 'L') {
222.278 + pb.append("Ljava/lang/Object;");
222.279 + indx = params.indexOf(';', indx) + 1;
222.280 + } else {
222.281 + pb.append(ch);
222.282 + indx++;
222.283 + }
222.284 + }
222.285 + srp = mangleSig(pb.toString());
222.286 + }
222.287 + final String rp = mangleSig(params);
222.288 + final String mrp = mangleMethodName(rp);
222.289 sb.append(rfqn).append("$").append(rm).
222.290 - append('$').append(rp).append("__Ljava_lang_Object_2");
222.291 + append('$').append(mrp).append("__Ljava_lang_Object_2");
222.292 if (!isStatic) {
222.293 - sb.append('L').append(rfqn).append("_2");
222.294 + sb.append('L').append(fqnu).append("_2");
222.295 }
222.296 - sb.append(rp);
222.297 + sb.append(srp);
222.298 return sb.toString();
222.299 }
222.300
222.301 - private static String replace(String orig) {
222.302 - return orig.replace("_", "_1").
222.303 - replace(";", "_2").
222.304 - replace("[", "_3").
222.305 - replace('.', '_').replace('/', '_');
222.306 - }
222.307 -
222.308 private static String className(ClassData jc) {
222.309 //return jc.getName().getInternalName().replace('/', '_');
222.310 - return jc.getClassName().replace('/', '_');
222.311 + return mangleClassName(jc.getClassName());
222.312 }
222.313
222.314 private static String[] findAnnotation(
222.315 @@ -1879,8 +1916,8 @@
222.316 final String slashType = attrType.substring(1, attrType.length() - 1);
222.317 requireReference(slashType);
222.318
222.319 - out.append(accessClass(slashType.replace('/', '_')))
222.320 - .append("(false).constructor.").append(value);
222.321 + out.append(accessClass(mangleClassName(slashType)))
222.322 + .append("(false).constructor.fld_").append(value);
222.323 }
222.324 };
222.325 ap.parse(data, cd);
223.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/LdrRsrcs.java Tue Feb 11 10:48:24 2014 +0100
223.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/LdrRsrcs.java Tue Feb 11 13:31:42 2014 +0100
223.3 @@ -28,9 +28,11 @@
223.4 */
223.5 final class LdrRsrcs implements Bck2Brwsr.Resources {
223.6 private final ClassLoader loader;
223.7 + private final boolean skipRtJar;
223.8
223.9 - LdrRsrcs(ClassLoader loader) {
223.10 + LdrRsrcs(ClassLoader loader, boolean skipRtJar) {
223.11 this.loader = loader;
223.12 + this.skipRtJar = skipRtJar;
223.13 }
223.14
223.15 @Override
223.16 @@ -43,6 +45,9 @@
223.17 if (u == null) {
223.18 throw new IOException("Can't find " + name);
223.19 }
223.20 + if (skipRtJar && u.toExternalForm().contains("lib/rt.jar!")) {
223.21 + return null;
223.22 + }
223.23 return u.openStream();
223.24 }
223.25 }
224.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Tue Feb 11 10:48:24 2014 +0100
224.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Tue Feb 11 13:31:42 2014 +0100
224.3 @@ -93,7 +93,7 @@
224.4 Bck2Brwsr.newCompiler().
224.5 obfuscation(obfLevel).
224.6 addRootClasses(classes.toArray()).
224.7 - resources(Main.class.getClassLoader()).
224.8 + resources(new LdrRsrcs(Main.class.getClassLoader(), true)).
224.9 generate(w);
224.10 }
224.11 }
225.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue Feb 11 10:48:24 2014 +0100
225.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue Feb 11 13:31:42 2014 +0100
225.3 @@ -61,11 +61,15 @@
225.4 out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {");
225.5 StringArray processed = new StringArray();
225.6 StringArray initCode = new StringArray();
225.7 + StringArray skipClass = new StringArray();
225.8 for (String baseClass : names.toArray()) {
225.9 references.add(baseClass);
225.10 for (;;) {
225.11 String name = null;
225.12 for (String n : references.toArray()) {
225.13 + if (skipClass.contains(n)) {
225.14 + continue;
225.15 + }
225.16 if (processed.contains(n)) {
225.17 continue;
225.18 }
225.19 @@ -76,7 +80,9 @@
225.20 }
225.21 InputStream is = loadClass(l, name);
225.22 if (is == null) {
225.23 - throw new IOException("Can't find class " + name);
225.24 + lazyReference(out, name);
225.25 + skipClass.add(name);
225.26 + continue;
225.27 }
225.28 try {
225.29 String ic = compile(is);
225.30 @@ -131,13 +137,17 @@
225.31 out.append(
225.32 " return vm;\n"
225.33 + " };\n"
225.34 + + " function mangleClass(name) {\n"
225.35 + + " return name.replace__Ljava_lang_String_2Ljava_lang_CharSequence_2Ljava_lang_CharSequence_2(\n"
225.36 + + " '_', '_1').replace__Ljava_lang_String_2CC('.','_');\n"
225.37 + + " };\n"
225.38 + " global.bck2brwsr = function() {\n"
225.39 + " var args = Array.prototype.slice.apply(arguments);\n"
225.40 + " var vm = fillInVMSkeleton({});\n"
225.41 + " var loader = {};\n"
225.42 + " loader.vm = vm;\n"
225.43 + " loader.loadClass = function(name) {\n"
225.44 - + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n"
225.45 + + " var attr = mangleClass(name);\n"
225.46 + " var fn = vm[attr];\n"
225.47 + " if (fn) return fn(false);\n"
225.48 + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
225.49 @@ -147,13 +157,19 @@
225.50 + " throw 'Cannot initialize the bck2brwsr VM twice!';\n"
225.51 + " }\n"
225.52 + " vm.loadClass = loader.loadClass;\n"
225.53 - + " vm.loadBytes = function(name) {\n"
225.54 + + " vm._reload = function(name, byteCode) {;\n"
225.55 + + " var attr = mangleClass(name);\n"
225.56 + + " delete vm[attr];\n"
225.57 + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
225.58 - + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n"
225.59 + + " reload__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2_3B(loader, name, args, byteCode);\n"
225.60 + + " };\n"
225.61 + + " vm.loadBytes = function(name, skip) {\n"
225.62 + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
225.63 + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2I(loader, name, args, typeof skip == 'number' ? skip : 0);\n"
225.64 + " }\n"
225.65 + " vm.java_lang_reflect_Array(false);\n"
225.66 + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
225.67 - + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, null, args);\n"
225.68 + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2I(loader, null, args, 0);\n"
225.69 + " return loader;\n"
225.70 + " };\n");
225.71 out.append("}(this));");
225.72 @@ -245,4 +261,16 @@
225.73 String getVMObject() {
225.74 return "vm";
225.75 }
225.76 +
225.77 + private static void lazyReference(Appendable out, String n) throws IOException {
225.78 + String cls = n.replace('/', '_');
225.79 + String dot = n.replace('/', '.');
225.80 +
225.81 + out.append("\nvm.").append(cls).append(" = function() {");
225.82 + out.append("\n var instance = arguments.length == 0 || arguments[0] === true;");
225.83 + out.append("\n delete vm.").append(cls).append(";");
225.84 + out.append("\n var c = vm.loadClass('").append(dot).append("');");
225.85 + out.append("\n return vm.").append(cls).append("(instance);");
225.86 + out.append("\n}");
225.87 + }
225.88 }
226.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Tue Feb 11 10:48:24 2014 +0100
226.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Tue Feb 11 13:31:42 2014 +0100
226.3 @@ -20,7 +20,6 @@
226.4 import java.io.ByteArrayInputStream;
226.5 import java.io.IOException;
226.6 import java.io.InputStream;
226.7 -import java.lang.reflect.Array;
226.8 import org.apidesign.bck2brwsr.core.JavaScriptBody;
226.9
226.10 /**
226.11 @@ -43,27 +42,34 @@
226.12 throws IOException, ClassNotFoundException {
226.13 return new VMLazy(loader, arguments).load(name, false);
226.14 }
226.15 +
226.16 + static Object reload(Object loader, String name, Object[] arguments, byte[] arr)
226.17 + throws IOException, ClassNotFoundException {
226.18 + return new VMLazy(loader, arguments).defineClass(arr, name, false);
226.19 + }
226.20
226.21 - static byte[] loadBytes(Object loader, String name, Object[] arguments) throws Exception {
226.22 - return Zips.loadFromCp(arguments, name);
226.23 + static byte[] loadBytes(Object loader, String name, Object[] arguments, int skip) throws Exception {
226.24 + return Zips.loadFromCp(arguments, name, skip);
226.25 }
226.26
226.27 private Object load(String name, boolean instance)
226.28 throws IOException, ClassNotFoundException {
226.29 String res = name.replace('.', '/') + ".class";
226.30 - byte[] arr = Zips.loadFromCp(args, res);
226.31 + byte[] arr = Zips.loadFromCp(args, res, 0);
226.32 if (arr == null) {
226.33 throw new ClassNotFoundException(name);
226.34 }
226.35 -// beingDefined(loader, name);
226.36 +
226.37 + return defineClass(arr, name, instance);
226.38 + }
226.39 +
226.40 + private Object defineClass(byte[] arr, String name, boolean instance) throws IOException {
226.41 StringBuilder out = new StringBuilder(65535);
226.42 out.append("var loader = arguments[0];\n");
226.43 out.append("var vm = loader.vm;\n");
226.44 int prelude = out.length();
226.45 String initCode = new Gen(this, out).compile(new ByteArrayInputStream(arr));
226.46 String code = out.toString().toString();
226.47 -// dump("Loading " + name);
226.48 - dump(code);
226.49 String under = name.replace('.', '_');
226.50 Object fn = applyCode(loader, under, code, instance);
226.51
226.52 @@ -71,26 +77,12 @@
226.53 out.setLength(prelude);
226.54 out.append(initCode);
226.55 code = out.toString().toString();
226.56 - dump(code);
226.57 applyCode(loader, null, code, false);
226.58 }
226.59
226.60 return fn;
226.61 }
226.62
226.63 -// @JavaScriptBody(args = "s", body = "java.lang.System.out.println(s.toString());")
226.64 - static void dump(String s) {
226.65 - }
226.66 -
226.67 -/* possibly not needed:
226.68 - @JavaScriptBody(args = {"loader", "n" }, body =
226.69 - "var cls = n.replace__Ljava_lang_String_2CC(n, '.','_').toString();" +
226.70 - "loader.vm[cls] = true;\n"
226.71 - )
226.72 - private static native void beingDefined(Object loader, String name);
226.73 -*/
226.74 -
226.75 -
226.76 @JavaScriptBody(args = {"loader", "name", "script", "instance" }, body =
226.77 "try {\n" +
226.78 " new Function(script)(loader, name);\n" +
227.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Tue Feb 11 10:48:24 2014 +0100
227.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Tue Feb 11 13:31:42 2014 +0100
227.3 @@ -49,7 +49,7 @@
227.4 @JavaScriptBody(args = { "arr", "index", "value" }, body = "arr[index] = value; return value;")
227.5 private static native Object set(Object arr, int index, Object value);
227.6
227.7 - public static byte[] loadFromCp(Object classpath, String res)
227.8 + public static byte[] loadFromCp(Object classpath, String res, int skip)
227.9 throws IOException, ClassNotFoundException {
227.10 for (int i = 0; i < length(classpath); i++) {
227.11 Object c = at(classpath, i);
227.12 @@ -74,24 +74,27 @@
227.13 byte[] checkRes;
227.14 if (c instanceof Zips) {
227.15 checkRes = ((Zips)c).findRes(res);
227.16 + if (checkRes != null && --skip < 0) {
227.17 + return checkRes;
227.18 + }
227.19 } else {
227.20 - checkRes = callFunction(c, res);
227.21 - }
227.22 - if (checkRes != null) {
227.23 - return checkRes;
227.24 + checkRes = callFunction(c, res, skip);
227.25 + if (checkRes != null) {
227.26 + return checkRes;
227.27 + }
227.28 }
227.29 }
227.30 }
227.31 return null;
227.32 }
227.33
227.34 - @JavaScriptBody(args = { "fn", "res" }, body =
227.35 - "if (typeof fn === 'function') return fn(res);\n"
227.36 + @JavaScriptBody(args = { "fn", "res", "skip" }, body =
227.37 + "if (typeof fn === 'function') return fn(res, skip);\n"
227.38 + "return null;"
227.39 )
227.40 - private static native byte[] callFunction(Object fn, String res);
227.41 + private static native byte[] callFunction(Object fn, String res, int skip);
227.42
227.43 - @JavaScriptBody(args = { "msg" }, body = "console.log(msg.toString());")
227.44 + @JavaScriptBody(args = { "msg" }, body = "if (typeof console !== 'undefined') console.log(msg.toString());")
227.45 private static native void log(String msg);
227.46
227.47 private byte[] findRes(String res) throws IOException {
228.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java Tue Feb 11 10:48:24 2014 +0100
228.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java Tue Feb 11 13:31:42 2014 +0100
228.3 @@ -52,4 +52,15 @@
228.4 assertTrue(returnType[0] != 'V', "Returns string");
228.5 assertEquals(ret, "toJavaScript__Ljava_lang_String_2_3B");
228.6 }
228.7 +
228.8 + @Test public void mangleJsCallbackToAType() throws Exception {
228.9 + String res = ByteCodeToJavaScript.mangleJsCallbacks(
228.10 + "org.apidesign.bck2brwsr.vmtest.impl.HtmlAnnotations",
228.11 + "onError", "Ljava/lang/Object;", false
228.12 + );
228.13 + assertEquals(res,
228.14 + "org_1apidesign_1bck2brwsr_1vmtest_1impl_1HtmlAnnotations$onError$Ljava_1lang_1Object_12__Ljava_lang_Object_2Lorg_apidesign_bck2brwsr_vmtest_impl_HtmlAnnotations_2Ljava_lang_Object_2",
228.15 + "Pretty long method name"
228.16 + );
228.17 + }
228.18 }
229.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Tue Feb 11 10:48:24 2014 +0100
229.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Tue Feb 11 13:31:42 2014 +0100
229.3 @@ -21,20 +21,19 @@
229.4 import java.io.InputStream;
229.5 import java.net.URL;
229.6 import java.util.Enumeration;
229.7 -import java.util.Set;
229.8 -import java.util.TreeSet;
229.9
229.10 /**
229.11 *
229.12 * @author Jaroslav Tulach <jtulach@netbeans.org>
229.13 */
229.14 public final class BytesLoader {
229.15 - private static Set<String> requested = new TreeSet<String>();
229.16 + private static final StringArray requested = new StringArray();
229.17
229.18 public byte[] get(String name) throws IOException {
229.19 - if (!requested.add(name)) {
229.20 + if (requested.contains(name)) {
229.21 throw new IllegalStateException("Requested for second time: " + name);
229.22 }
229.23 + requested.add(name);
229.24 byte[] arr = readClass(name);
229.25 /*
229.26 System.err.print("loader['" + name + "'] = [");
230.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
230.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/DelayedLoading.java Tue Feb 11 13:31:42 2014 +0100
230.3 @@ -0,0 +1,31 @@
230.4 +/**
230.5 + * Back 2 Browser Bytecode Translator
230.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
230.7 + *
230.8 + * This program is free software: you can redistribute it and/or modify
230.9 + * it under the terms of the GNU General Public License as published by
230.10 + * the Free Software Foundation, version 2 of the License.
230.11 + *
230.12 + * This program is distributed in the hope that it will be useful,
230.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
230.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
230.15 + * GNU General Public License for more details.
230.16 + *
230.17 + * You should have received a copy of the GNU General Public License
230.18 + * along with this program. Look for COPYING file in the top folder.
230.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
230.20 + */
230.21 +package org.apidesign.vm4brwsr;
230.22 +
230.23 +import java.net.URL;
230.24 +
230.25 +/**
230.26 + *
230.27 + * @author Jaroslav Tulach <jtulach@netbeans.org>
230.28 + */
230.29 +public class DelayedLoading {
230.30 + public static String toStrViaURI(String url) throws Exception {
230.31 + URL u = new URL(url);
230.32 + return u.toURI().toString();
230.33 + }
230.34 +}
231.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
231.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/DelayedLoadingTest.java Tue Feb 11 13:31:42 2014 +0100
231.3 @@ -0,0 +1,55 @@
231.4 +/**
231.5 + * Back 2 Browser Bytecode Translator
231.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
231.7 + *
231.8 + * This program is free software: you can redistribute it and/or modify
231.9 + * it under the terms of the GNU General Public License as published by
231.10 + * the Free Software Foundation, version 2 of the License.
231.11 + *
231.12 + * This program is distributed in the hope that it will be useful,
231.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
231.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
231.15 + * GNU General Public License for more details.
231.16 + *
231.17 + * You should have received a copy of the GNU General Public License
231.18 + * along with this program. Look for COPYING file in the top folder.
231.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
231.20 + */
231.21 +package org.apidesign.vm4brwsr;
231.22 +
231.23 +import java.net.URL;
231.24 +import org.testng.annotations.BeforeClass;
231.25 +import org.testng.annotations.AfterClass;
231.26 +import org.testng.annotations.Test;
231.27 +
231.28 +/**
231.29 + *
231.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
231.31 + */
231.32 +public class DelayedLoadingTest {
231.33 + private static TestVM code;
231.34 +
231.35 + @Test public void verifyUsageOf() throws Exception {
231.36 + code.register(new BytesLoader());
231.37 +
231.38 + URL u = new URL("http://apidesign.org");
231.39 +
231.40 + Object str = code.execCode("Access URI",
231.41 + DelayedLoading.class, "toStrViaURI__Ljava_lang_String_2Ljava_lang_String_2",
231.42 + u.toExternalForm(), u.toExternalForm()
231.43 + );
231.44 + }
231.45 +
231.46 +
231.47 + @BeforeClass
231.48 + public static void compileTheCode() throws Exception {
231.49 + code = TestVM.compileClass(
231.50 + "org/apidesign/vm4brwsr/DelayedLoading");
231.51 + }
231.52 + @AfterClass
231.53 + public static void releaseTheCode() {
231.54 + code = null;
231.55 + }
231.56 +
231.57 +}
231.58 +
232.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Tue Feb 11 10:48:24 2014 +0100
232.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Tue Feb 11 13:31:42 2014 +0100
232.3 @@ -94,12 +94,14 @@
232.4 {
232.5 // 2nd invocation
232.6 Object ret = code.invokeMethod(clazz, method, "java.lang.String");
232.7 - assertEquals(ret, Double.valueOf(2));
232.8 + assertTrue(ret instanceof Number, "Is number: " + ret);
232.9 + assertEquals(((Number)ret).doubleValue(), 2.0);
232.10 }
232.11 {
232.12 // 3rd invocation
232.13 Object ret = code.invokeMethod(clazz, method, "java.lang.Integer");
232.14 - assertEquals(ret, Double.valueOf(3));
232.15 + assertTrue(ret instanceof Number, "Is number: " + ret);
232.16 + assertEquals(((Number)ret).doubleValue(), 3.0);
232.17 }
232.18 }
232.19
233.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
233.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Hello.java Tue Feb 11 13:31:42 2014 +0100
233.3 @@ -0,0 +1,35 @@
233.4 +/**
233.5 + * Back 2 Browser Bytecode Translator
233.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
233.7 + *
233.8 + * This program is free software: you can redistribute it and/or modify
233.9 + * it under the terms of the GNU General Public License as published by
233.10 + * the Free Software Foundation, version 2 of the License.
233.11 + *
233.12 + * This program is distributed in the hope that it will be useful,
233.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
233.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
233.15 + * GNU General Public License for more details.
233.16 + *
233.17 + * You should have received a copy of the GNU General Public License
233.18 + * along with this program. Look for COPYING file in the top folder.
233.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
233.20 + */
233.21 +package org.apidesign.vm4brwsr;
233.22 +
233.23 +import java.io.IOException;
233.24 +
233.25 +/**
233.26 + *
233.27 + * @author Jaroslav Tulach <jtulach@netbeans.org>
233.28 + */
233.29 +public class Hello {
233.30 + public static String hello() {
233.31 + return "Hello World!";
233.32 + }
233.33 +
233.34 + public static Object reloadYourSelf(byte[] arr) throws IOException {
233.35 + org.apidesign.vm4brwsr.api.VM.reload(Hello.class, arr);
233.36 + return null;
233.37 + }
233.38 +}
234.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
234.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ReloadingTest.java Tue Feb 11 13:31:42 2014 +0100
234.3 @@ -0,0 +1,72 @@
234.4 +/**
234.5 + * Back 2 Browser Bytecode Translator
234.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
234.7 + *
234.8 + * This program is free software: you can redistribute it and/or modify
234.9 + * it under the terms of the GNU General Public License as published by
234.10 + * the Free Software Foundation, version 2 of the License.
234.11 + *
234.12 + * This program is distributed in the hope that it will be useful,
234.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
234.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
234.15 + * GNU General Public License for more details.
234.16 + *
234.17 + * You should have received a copy of the GNU General Public License
234.18 + * along with this program. Look for COPYING file in the top folder.
234.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
234.20 + */
234.21 +package org.apidesign.vm4brwsr;
234.22 +
234.23 +import org.testng.annotations.BeforeClass;
234.24 +import org.testng.annotations.AfterClass;
234.25 +import org.testng.annotations.Test;
234.26 +
234.27 +/**
234.28 + *
234.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
234.30 + */
234.31 +public class ReloadingTest {
234.32 + private static TestVM code;
234.33 +
234.34 + @Test public void verifyUsageOf() throws Exception {
234.35 + code.execCode("First hello",
234.36 + Hello.class, "hello__Ljava_lang_String_2",
234.37 + "Hello World!"
234.38 + );
234.39 +
234.40 + byte[] arr = BytesLoader.readClass("org/apidesign/vm4brwsr/Hello.class");
234.41 + for (int i = 0; i < arr.length; i++) {
234.42 + if (arr[i] == 'H' && arr[i + 1] == 'e' && arr[i + 2] == 'l') {
234.43 + arr[i] = 'A';
234.44 + arr[i + 1] = 'h';
234.45 + arr[i + 2] = 'o';
234.46 + arr[i + 3] = 'y';
234.47 + arr[i + 4] = ' ';
234.48 + break;
234.49 + }
234.50 + }
234.51 +
234.52 + code.execCode("Redefine class",
234.53 + Hello.class, "reloadYourSelf__Ljava_lang_Object_2_3B",
234.54 + null, arr
234.55 + );
234.56 +
234.57 + code.execCode("Second hello",
234.58 + Hello.class, "hello__Ljava_lang_String_2",
234.59 + "Ahoy World!"
234.60 + );
234.61 + }
234.62 +
234.63 +
234.64 + @BeforeClass
234.65 + public static void compileTheCode() throws Exception {
234.66 + code = TestVM.compileClass(
234.67 + "org/apidesign/vm4brwsr/Hello");
234.68 + }
234.69 + @AfterClass
234.70 + public static void releaseTheCode() {
234.71 + code = null;
234.72 + }
234.73 +
234.74 +}
234.75 +
235.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Tue Feb 11 10:48:24 2014 +0100
235.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Tue Feb 11 13:31:42 2014 +0100
235.3 @@ -80,6 +80,10 @@
235.4 return sb.toString().toString();
235.5 }
235.6
235.7 + public static String unicode() {
235.8 + return "\r\n\u2028\u2029]";
235.9 + }
235.10 +
235.11 public static String insertBuffer() {
235.12 StringBuilder sb = new StringBuilder();
235.13 sb.append("Jardo!");
236.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Tue Feb 11 10:48:24 2014 +0100
236.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Tue Feb 11 13:31:42 2014 +0100
236.3 @@ -213,6 +213,16 @@
236.4 exp, false, true
236.5 );
236.6 }
236.7 +
236.8 + @Test public void weirdUnicodeCharacters() throws Exception {
236.9 + String exp = StringSample.unicode();
236.10 +
236.11 + assertExec(
236.12 + "Unicode is OK",
236.13 + StringSample.class, "unicode__Ljava_lang_String_2",
236.14 + exp
236.15 + );
236.16 + }
236.17
236.18 @Test public void valueOfOnJSArray() throws Exception {
236.19 assertExec(
237.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Tue Feb 11 10:48:24 2014 +0100
237.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Tue Feb 11 13:31:42 2014 +0100
237.3 @@ -24,23 +24,33 @@
237.4 import java.net.URL;
237.5 import java.util.Enumeration;
237.6 import javax.script.Invocable;
237.7 +import javax.script.ScriptContext;
237.8 import javax.script.ScriptEngine;
237.9 import javax.script.ScriptEngineManager;
237.10 import javax.script.ScriptException;
237.11 import static org.testng.Assert.*;
237.12
237.13 -final class TestVM {
237.14 +public final class TestVM {
237.15 private final Invocable code;
237.16 private final CharSequence codeSeq;
237.17 private final Object bck2brwsr;
237.18 + private BytesLoader resources;
237.19
237.20
237.21 private TestVM(Invocable code, CharSequence codeSeq) throws ScriptException, NoSuchMethodException {
237.22 this.code = code;
237.23 this.codeSeq = codeSeq;
237.24 - this.bck2brwsr = code.invokeFunction("bck2brwsr");
237.25 + this.bck2brwsr = ((ScriptEngine)code).eval("bck2brwsr(function(n) { return loader.get(n); })");
237.26 + ((ScriptEngine)code).getContext().setAttribute("loader", this, ScriptContext.ENGINE_SCOPE);
237.27 }
237.28
237.29 + public void register(BytesLoader res) {
237.30 + this.resources = res;
237.31 + }
237.32 +
237.33 + public byte[] get(String res) throws IOException {
237.34 + return resources != null ? resources.get(res) : null;
237.35 + }
237.36
237.37 public Object execCode(
237.38 String msg, Class<?> clazz, String method,
237.39 @@ -65,7 +75,10 @@
237.40 // in case of Long it is necessary convert it to number
237.41 // since the Long is represented by two numbers in JavaScript
237.42 try {
237.43 - ret = code.invokeMethod(ret, "toFP");
237.44 + final Object toFP = ((ScriptEngine)code).eval("Number.prototype.toFP");
237.45 + if (ret instanceof Long) {
237.46 + ret = code.invokeMethod(toFP, "call", ret);
237.47 + }
237.48 ret = code.invokeFunction("Number", ret);
237.49 } catch (ScriptException ex) {
237.50 fail("Conversion to number failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
237.51 @@ -83,6 +96,9 @@
237.52 if (ret == null) {
237.53 return;
237.54 }
237.55 + if (expRes instanceof Integer && ret instanceof Double) {
237.56 + expRes = ((Integer)expRes).doubleValue();
237.57 + }
237.58 if (expRes != null && expRes.equals(ret)) {
237.59 return;
237.60 }
237.61 @@ -153,6 +169,18 @@
237.62 private static class EmulationResources implements Bck2Brwsr.Resources {
237.63 @Override
237.64 public InputStream get(String name) throws IOException {
237.65 + if ("java/net/URI.class".equals(name)) {
237.66 + // skip
237.67 + return null;
237.68 + }
237.69 + if ("java/net/URLConnection.class".equals(name)) {
237.70 + // skip
237.71 + return null;
237.72 + }
237.73 + if ("java/lang/System.class".equals(name)) {
237.74 + // skip
237.75 + return null;
237.76 + }
237.77 Enumeration<URL> en = StaticMethodTest.class.getClassLoader().getResources(name);
237.78 URL u = null;
237.79 while (en.hasMoreElements()) {
238.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
238.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/UnderTest.java Tue Feb 11 13:31:42 2014 +0100
238.3 @@ -0,0 +1,88 @@
238.4 +/**
238.5 + * Back 2 Browser Bytecode Translator
238.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
238.7 + *
238.8 + * This program is free software: you can redistribute it and/or modify
238.9 + * it under the terms of the GNU General Public License as published by
238.10 + * the Free Software Foundation, version 2 of the License.
238.11 + *
238.12 + * This program is distributed in the hope that it will be useful,
238.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
238.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
238.15 + * GNU General Public License for more details.
238.16 + *
238.17 + * You should have received a copy of the GNU General Public License
238.18 + * along with this program. Look for COPYING file in the top folder.
238.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
238.20 + */
238.21 +package org.apidesign.vm4brwsr;
238.22 +
238.23 +import org.testng.annotations.AfterClass;
238.24 +import org.testng.annotations.BeforeClass;
238.25 +import org.testng.annotations.Test;
238.26 +
238.27 +/** Checks behavior of classes and methods with underscore.
238.28 + *
238.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
238.30 + */
238.31 +public class UnderTest {
238.32 + @Test public void one() throws Exception {
238.33 + assertExec(
238.34 + "Should be one",
238.35 + Under_Score.class, "one__I",
238.36 + Double.valueOf(1)
238.37 + );
238.38 + }
238.39 +
238.40 + @Test public void onePlusOne() throws Exception {
238.41 + assertExec(
238.42 + "Should be two",
238.43 + Under_Score.class, "one_1plus_1one__I",
238.44 + Double.valueOf(2)
238.45 + );
238.46 + }
238.47 +
238.48 + @Test public void two() throws Exception {
238.49 + assertExec(
238.50 + "Should be two",
238.51 + Under_Score.class, "two__I",
238.52 + Double.valueOf(2)
238.53 + );
238.54 + }
238.55 +
238.56 + @Test public void staticField() throws Exception {
238.57 + assertExec(
238.58 + "Should be ten",
238.59 + Under_Score.class, "staticField__I",
238.60 + Double.valueOf(10)
238.61 + );
238.62 + }
238.63 +
238.64 + @Test public void instance() throws Exception {
238.65 + assertExec(
238.66 + "Should be five",
238.67 + Under_Score.class, "instance__I",
238.68 + Double.valueOf(5)
238.69 + );
238.70 + }
238.71 +
238.72 +
238.73 + private static TestVM code;
238.74 +
238.75 + @BeforeClass
238.76 + public static void compileTheCode() throws Exception {
238.77 + StringBuilder sb = new StringBuilder();
238.78 + code = TestVM.compileClass(sb, "org/apidesign/vm4brwsr/Under_Score");
238.79 + }
238.80 + @AfterClass
238.81 + public static void releaseTheCode() {
238.82 + code = null;
238.83 + }
238.84 +
238.85 + private void assertExec(
238.86 + String msg, Class<?> clazz, String method,
238.87 + Object ret, Object... args
238.88 + ) throws Exception {
238.89 + code.assertExec(msg, clazz, method, ret, args);
238.90 + }
238.91 +}
239.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
239.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Under_Score.java Tue Feb 11 13:31:42 2014 +0100
239.3 @@ -0,0 +1,51 @@
239.4 +/**
239.5 + * Back 2 Browser Bytecode Translator
239.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
239.7 + *
239.8 + * This program is free software: you can redistribute it and/or modify
239.9 + * it under the terms of the GNU General Public License as published by
239.10 + * the Free Software Foundation, version 2 of the License.
239.11 + *
239.12 + * This program is distributed in the hope that it will be useful,
239.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
239.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
239.15 + * GNU General Public License for more details.
239.16 + *
239.17 + * You should have received a copy of the GNU General Public License
239.18 + * along with this program. Look for COPYING file in the top folder.
239.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
239.20 + */
239.21 +package org.apidesign.vm4brwsr;
239.22 +
239.23 +/**
239.24 + *
239.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
239.26 + */
239.27 +public class Under_Score {
239.28 + public static int under_field = 10;
239.29 + public int instance_field = 5;
239.30 +
239.31 + public static int one() {
239.32 + return 1;
239.33 + }
239.34 +
239.35 + public static int one_plus_one() {
239.36 + return 1 + 1;
239.37 + }
239.38 +
239.39 + public static int two() {
239.40 + return one_plus_one();
239.41 + }
239.42 +
239.43 + public static int staticField() {
239.44 + return under_field;
239.45 + }
239.46 +
239.47 + public static int instance() {
239.48 + return new Under_Score().get_fld();
239.49 + }
239.50 +
239.51 + private int get_fld() {
239.52 + return instance_field;
239.53 + }
239.54 +}
240.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Tue Feb 11 10:48:24 2014 +0100
240.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Tue Feb 11 13:31:42 2014 +0100
240.3 @@ -90,6 +90,9 @@
240.4 if (ret == null && expRes == null) {
240.5 return;
240.6 }
240.7 + if (expRes instanceof Double && ret instanceof Number) {
240.8 + ret = ((Number)ret).doubleValue();
240.9 + }
240.10 if (expRes.equals(ret)) {
240.11 return;
240.12 }