# HG changeset patch # User Jaroslav Tulach # Date 1360959365 -3600 # Node ID 3883d08b8cb317266dde16c644c19cea01550153 # Parent cb5ac4ada10dfc305945f2d498cdf28b5fef689a# Parent ae352b76395918f66a1a838357b1c69c0570857f Time to merge emul branch to default: Archetype now generates static ZIP on deploy diff -r cb5ac4ada10d -r 3883d08b8cb3 dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java --- a/dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java Fri Feb 15 11:54:45 2013 +0100 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java Fri Feb 15 21:16:05 2013 +0100 @@ -96,7 +96,9 @@ } if (r.equals("/result.html")) { response.setContentType("text/html"); - response.getOutputBuffer().write(data.getHtml()); + if (data != null) { + response.getOutputBuffer().write(data.getHtml()); + } response.setStatus(HttpStatus.OK_200); return; } diff -r cb5ac4ada10d -r 3883d08b8cb3 dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js Fri Feb 15 11:54:45 2013 +0100 +++ b/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js Fri Feb 15 21:16:05 2013 +0100 @@ -112,7 +112,7 @@ " var arr = eval('(' + request.responseText + ')');\n" + " return arr;\n" + " }\n" + -" var vm = new bck2brwsr(ldCls);\n" + +" var vm = bck2brwsr(ldCls);\n" + " vm.loadClass('${fqn}');\n" + " \n" + ""; diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/pom.xml --- a/emul/compact/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/compact/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -10,7 +10,7 @@ org.apidesign.bck2brwsr emul 0.3-SNAPSHOT - Compact API Profile + Bck2Brwsr API Profile http://maven.apache.org UTF-8 @@ -20,6 +20,7 @@ ${project.groupId} emul.mini ${project.version} + provided ${project.groupId} @@ -47,6 +48,25 @@ 1.7 + + maven-assembly-plugin + 2.4 + + + rt + package + + single + + + + src/main/assembly/rt.xml + + bck2brwsr-${project.version} + + + + diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/main/assembly/rt.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/assembly/rt.xml Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,26 @@ + + + rt + + jar + + false + + + true + true + provided + + + META-INF/maven/** + + + + + + + ${project.build.outputDirectory} + / + + + \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/main/java/java/io/BufferedOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/BufferedOutputStream.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * The class implements a buffered output stream. By setting up such + * an output stream, an application can write bytes to the underlying + * output stream without necessarily causing a call to the underlying + * system for each byte written. + * + * @author Arthur van Hoff + * @since JDK1.0 + */ +public +class BufferedOutputStream extends FilterOutputStream { + /** + * The internal buffer where data is stored. + */ + protected byte buf[]; + + /** + * The number of valid bytes in the buffer. This value is always + * in the range 0 through buf.length; elements + * buf[0] through buf[count-1] contain valid + * byte data. + */ + protected int count; + + /** + * Creates a new buffered output stream to write data to the + * specified underlying output stream. + * + * @param out the underlying output stream. + */ + public BufferedOutputStream(OutputStream out) { + this(out, 8192); + } + + /** + * Creates a new buffered output stream to write data to the + * specified underlying output stream with the specified buffer + * size. + * + * @param out the underlying output stream. + * @param size the buffer size. + * @exception IllegalArgumentException if size <= 0. + */ + public BufferedOutputStream(OutputStream out, int size) { + super(out); + if (size <= 0) { + throw new IllegalArgumentException("Buffer size <= 0"); + } + buf = new byte[size]; + } + + /** Flush the internal buffer */ + private void flushBuffer() throws IOException { + if (count > 0) { + out.write(buf, 0, count); + count = 0; + } + } + + /** + * Writes the specified byte to this buffered output stream. + * + * @param b the byte to be written. + * @exception IOException if an I/O error occurs. + */ + public synchronized void write(int b) throws IOException { + if (count >= buf.length) { + flushBuffer(); + } + buf[count++] = (byte)b; + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this buffered output stream. + * + *

Ordinarily this method stores bytes from the given array into this + * stream's buffer, flushing the buffer to the underlying output stream as + * needed. If the requested length is at least as large as this stream's + * buffer, however, then this method will flush the buffer and write the + * bytes directly to the underlying output stream. Thus redundant + * BufferedOutputStreams will not copy data unnecessarily. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + */ + public synchronized void write(byte b[], int off, int len) throws IOException { + if (len >= buf.length) { + /* If the request length exceeds the size of the output buffer, + flush the output buffer and then write the data directly. + In this way buffered streams will cascade harmlessly. */ + flushBuffer(); + out.write(b, off, len); + return; + } + if (len > buf.length - count) { + flushBuffer(); + } + System.arraycopy(b, off, buf, count, len); + count += len; + } + + /** + * Flushes this buffered output stream. This forces any buffered + * output bytes to be written out to the underlying output stream. + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public synchronized void flush() throws IOException { + flushBuffer(); + out.flush(); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/main/java/java/io/ByteArrayOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ByteArrayOutputStream.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.util.Arrays; + +/** + * This class implements an output stream in which the data is + * written into a byte array. The buffer automatically grows as data + * is written to it. + * The data can be retrieved using toByteArray() and + * toString(). + *

+ * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + * + * @author Arthur van Hoff + * @since JDK1.0 + */ + +public class ByteArrayOutputStream extends OutputStream { + + /** + * The buffer where data is stored. + */ + protected byte buf[]; + + /** + * The number of valid bytes in the buffer. + */ + protected int count; + + /** + * Creates a new byte array output stream. The buffer capacity is + * initially 32 bytes, though its size increases if necessary. + */ + public ByteArrayOutputStream() { + this(32); + } + + /** + * Creates a new byte array output stream, with a buffer capacity of + * the specified size, in bytes. + * + * @param size the initial size. + * @exception IllegalArgumentException if size is negative. + */ + public ByteArrayOutputStream(int size) { + if (size < 0) { + throw new IllegalArgumentException("Negative initial size: " + + size); + } + buf = new byte[size]; + } + + /** + * Increases the capacity if necessary to ensure that it can hold + * at least the number of elements specified by the minimum + * capacity argument. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if {@code minCapacity < 0}. This is + * interpreted as a request for the unsatisfiably large capacity + * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}. + */ + private void ensureCapacity(int minCapacity) { + // overflow-conscious code + if (minCapacity - buf.length > 0) + grow(minCapacity); + } + + /** + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + */ + private void grow(int minCapacity) { + // overflow-conscious code + int oldCapacity = buf.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + if (newCapacity < 0) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + newCapacity = Integer.MAX_VALUE; + } + buf = Arrays.copyOf(buf, newCapacity); + } + + /** + * Writes the specified byte to this byte array output stream. + * + * @param b the byte to be written. + */ + public synchronized void write(int b) { + ensureCapacity(count + 1); + buf[count] = (byte) b; + count += 1; + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this byte array output stream. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + */ + public synchronized void write(byte b[], int off, int len) { + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) - b.length > 0)) { + throw new IndexOutOfBoundsException(); + } + ensureCapacity(count + len); + System.arraycopy(b, off, buf, count, len); + count += len; + } + + /** + * Writes the complete contents of this byte array output stream to + * the specified output stream argument, as if by calling the output + * stream's write method using out.write(buf, 0, count). + * + * @param out the output stream to which to write the data. + * @exception IOException if an I/O error occurs. + */ + public synchronized void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); + } + + /** + * Resets the count field of this byte array output + * stream to zero, so that all currently accumulated output in the + * output stream is discarded. The output stream can be used again, + * reusing the already allocated buffer space. + * + * @see java.io.ByteArrayInputStream#count + */ + public synchronized void reset() { + count = 0; + } + + /** + * Creates a newly allocated byte array. Its size is the current + * size of this output stream and the valid contents of the buffer + * have been copied into it. + * + * @return the current contents of this output stream, as a byte array. + * @see java.io.ByteArrayOutputStream#size() + */ + public synchronized byte toByteArray()[] { + return Arrays.copyOf(buf, count); + } + + /** + * Returns the current size of the buffer. + * + * @return the value of the count field, which is the number + * of valid bytes in this output stream. + * @see java.io.ByteArrayOutputStream#count + */ + public synchronized int size() { + return count; + } + + /** + * Converts the buffer's contents into a string decoding bytes using the + * platform's default character set. The length of the new String + * is a function of the character set, and hence may not be equal to the + * size of the buffer. + * + *

This method always replaces malformed-input and unmappable-character + * sequences with the default replacement string for the platform's + * default character set. The {@linkplain java.nio.charset.CharsetDecoder} + * class should be used when more control over the decoding process is + * required. + * + * @return String decoded from the buffer's contents. + * @since JDK1.1 + */ + public synchronized String toString() { + return new String(buf, 0, count); + } + + /** + * Converts the buffer's contents into a string by decoding the bytes using + * the specified {@link java.nio.charset.Charset charsetName}. The length of + * the new String is a function of the charset, and hence may not be + * equal to the length of the byte array. + * + *

This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + * + * @param charsetName the name of a supported + * {@linkplain java.nio.charset.Charset charset} + * @return String decoded from the buffer's contents. + * @exception UnsupportedEncodingException + * If the named charset is not supported + * @since JDK1.1 + */ + public synchronized String toString(String charsetName) + throws UnsupportedEncodingException + { + return new String(buf, 0, count, charsetName); + } + + /** + * Creates a newly allocated string. Its size is the current size of + * the output stream and the valid contents of the buffer have been + * copied into it. Each character c in the resulting string is + * constructed from the corresponding element b in the byte + * array such that: + *

+     *     c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
+     * 
+ * + * @deprecated This method does not properly convert bytes into characters. + * As of JDK 1.1, the preferred way to do this is via the + * toString(String enc) method, which takes an encoding-name + * argument, or the toString() method, which uses the + * platform's default character encoding. + * + * @param hibyte the high byte of each resulting Unicode character. + * @return the current contents of the output stream, as a string. + * @see java.io.ByteArrayOutputStream#size() + * @see java.io.ByteArrayOutputStream#toString(String) + * @see java.io.ByteArrayOutputStream#toString() + */ + @Deprecated + public synchronized String toString(int hibyte) { + return new String(buf, hibyte, 0, count); + } + + /** + * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + *

+ * + */ + public void close() throws IOException { + } + +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/main/java/java/util/Objects.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Objects.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * This class consists of {@code static} utility methods for operating + * on objects. These utilities include {@code null}-safe or {@code + * null}-tolerant methods for computing the hash code of an object, + * returning a string for an object, and comparing two objects. + * + * @since 1.7 + */ +public final class Objects { + private Objects() { + throw new AssertionError("No java.util.Objects instances for you!"); + } + + /** + * Returns {@code true} if the arguments are equal to each other + * and {@code false} otherwise. + * Consequently, if both arguments are {@code null}, {@code true} + * is returned and if exactly one argument is {@code null}, {@code + * false} is returned. Otherwise, equality is determined by using + * the {@link Object#equals equals} method of the first + * argument. + * + * @param a an object + * @param b an object to be compared with {@code a} for equality + * @return {@code true} if the arguments are equal to each other + * and {@code false} otherwise + * @see Object#equals(Object) + */ + public static boolean equals(Object a, Object b) { + return (a == b) || (a != null && a.equals(b)); + } + + /** + * Returns {@code true} if the arguments are deeply equal to each other + * and {@code false} otherwise. + * + * Two {@code null} values are deeply equal. If both arguments are + * arrays, the algorithm in {@link Arrays#deepEquals(Object[], + * Object[]) Arrays.deepEquals} is used to determine equality. + * Otherwise, equality is determined by using the {@link + * Object#equals equals} method of the first argument. + * + * @param a an object + * @param b an object to be compared with {@code a} for deep equality + * @return {@code true} if the arguments are deeply equal to each other + * and {@code false} otherwise + * @see Arrays#deepEquals(Object[], Object[]) + * @see Objects#equals(Object, Object) + */ + public static boolean deepEquals(Object a, Object b) { + if (a == b) + return true; + else if (a == null || b == null) + return false; + else + return Arrays.deepEquals0(a, b); + } + + /** + * Returns the hash code of a non-{@code null} argument and 0 for + * a {@code null} argument. + * + * @param o an object + * @return the hash code of a non-{@code null} argument and 0 for + * a {@code null} argument + * @see Object#hashCode + */ + public static int hashCode(Object o) { + return o != null ? o.hashCode() : 0; + } + + /** + * Generates a hash code for a sequence of input values. The hash + * code is generated as if all the input values were placed into an + * array, and that array were hashed by calling {@link + * Arrays#hashCode(Object[])}. + * + *

This method is useful for implementing {@link + * Object#hashCode()} on objects containing multiple fields. For + * example, if an object that has three fields, {@code x}, {@code + * y}, and {@code z}, one could write: + * + *

+    * @Override public int hashCode() {
+    *     return Objects.hash(x, y, z);
+    * }
+    * 
+ * + * Warning: When a single object reference is supplied, the returned + * value does not equal the hash code of that object reference. This + * value can be computed by calling {@link #hashCode(Object)}. + * + * @param values the values to be hashed + * @return a hash value of the sequence of input values + * @see Arrays#hashCode(Object[]) + * @see List#hashCode + */ + public static int hash(Object... values) { + return Arrays.hashCode(values); + } + + /** + * Returns the result of calling {@code toString} for a non-{@code + * null} argument and {@code "null"} for a {@code null} argument. + * + * @param o an object + * @return the result of calling {@code toString} for a non-{@code + * null} argument and {@code "null"} for a {@code null} argument + * @see Object#toString + * @see String#valueOf(Object) + */ + public static String toString(Object o) { + return String.valueOf(o); + } + + /** + * Returns the result of calling {@code toString} on the first + * argument if the first argument is not {@code null} and returns + * the second argument otherwise. + * + * @param o an object + * @param nullDefault string to return if the first argument is + * {@code null} + * @return the result of calling {@code toString} on the first + * argument if it is not {@code null} and the second argument + * otherwise. + * @see Objects#toString(Object) + */ + public static String toString(Object o, String nullDefault) { + return (o != null) ? o.toString() : nullDefault; + } + + /** + * Returns 0 if the arguments are identical and {@code + * c.compare(a, b)} otherwise. + * Consequently, if both arguments are {@code null} 0 + * is returned. + * + *

Note that if one of the arguments is {@code null}, a {@code + * NullPointerException} may or may not be thrown depending on + * what ordering policy, if any, the {@link Comparator Comparator} + * chooses to have for {@code null} values. + * + * @param the type of the objects being compared + * @param a an object + * @param b an object to be compared with {@code a} + * @param c the {@code Comparator} to compare the first two arguments + * @return 0 if the arguments are identical and {@code + * c.compare(a, b)} otherwise. + * @see Comparable + * @see Comparator + */ + public static int compare(T a, T b, Comparator c) { + return (a == b) ? 0 : c.compare(a, b); + } + + /** + * Checks that the specified object reference is not {@code null}. This + * method is designed primarily for doing parameter validation in methods + * and constructors, as demonstrated below: + *

+     * public Foo(Bar bar) {
+     *     this.bar = Objects.requireNonNull(bar);
+     * }
+     * 
+ * + * @param obj the object reference to check for nullity + * @param the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + */ + public static T requireNonNull(T obj) { + if (obj == null) + throw new NullPointerException(); + return obj; + } + + /** + * Checks that the specified object reference is not {@code null} and + * throws a customized {@link NullPointerException} if it is. This method + * is designed primarily for doing parameter validation in methods and + * constructors with multiple parameters, as demonstrated below: + *
+     * public Foo(Bar bar, Baz baz) {
+     *     this.bar = Objects.requireNonNull(bar, "bar must not be null");
+     *     this.baz = Objects.requireNonNull(baz, "baz must not be null");
+     * }
+     * 
+ * + * @param obj the object reference to check for nullity + * @param message detail message to be used in the event that a {@code + * NullPointerException} is thrown + * @param the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + */ + public static T requireNonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipArchive.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipArchive.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,154 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.zip.ZipEntry; +import org.apidesign.bck2brwsr.emul.zip.ZipInputStream; + +/** + * + * @author Jaroslav Tulach + */ +final class ZipArchive { + private final Map entries = new LinkedHashMap<>(); + + public static ZipArchive createZip(InputStream is) throws IOException { + ZipArchive a = new ZipArchive(); + readZip(is, a); + return a; + } + + public static ZipArchive createReal(InputStream is) throws IOException { + ZipArchive a = new ZipArchive(); + realZip(is, a); + return a; + } + + /** + * Registers entry name and data + */ + final void register(String entry, InputStream is) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + os.write(ch); + } + os.close(); + entries.put(entry, os.toByteArray()); + } + + @Override + public int hashCode() { + return entries.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ZipArchive other = (ZipArchive) obj; + if (!Objects.deepEquals(this.entries, other.entries)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry en : entries.entrySet()) { + String string = en.getKey(); + byte[] bs = en.getValue(); + sb.append(string).append(" = ").append(Arrays.toString(bs)).append("\n"); + } + return sb.toString(); + } + + public void assertEquals(ZipArchive zip, String msg) { + boolean ok = true; + StringBuilder sb = new StringBuilder(); + sb.append(msg); + for (Map.Entry en : entries.entrySet()) { + String string = en.getKey(); + byte[] bs = en.getValue(); + byte[] other = zip.entries.get(string); + sb.append("\n"); + if (other == null) { + sb.append("EXTRA ").append(string).append(" = ").append(Arrays.toString(bs)); + ok = false; + continue; + } + if (Arrays.equals(bs, other)) { + sb.append("OK ").append(string); + continue; + } else { + sb.append("DIFF ").append(string).append(" = ").append(Arrays.toString(bs)).append("\n"); + sb.append(" TO").append(string).append(" = ").append(Arrays.toString(other)).append("\n"); + ok = false; + continue; + } + } + for (Map.Entry entry : zip.entries.entrySet()) { + String string = entry.getKey(); + if (entries.get(string) == null) { + sb.append("MISS ").append(string).append(" = ").append(Arrays.toString(entry.getValue())); + ok = false; + } + } + if (!ok) { + assert false : sb.toString(); + } + } + + public static void readZip(InputStream is, ZipArchive data) throws IOException { + ZipInputStream zip = new org.apidesign.bck2brwsr.emul.zip.ZipInputStream(is); + for (;;) { + ZipEntry en = zip.getNextEntry(); + if (en == null) { + return; + } + data.register(en.getName(), zip); + } + } + + public static void realZip(InputStream is, ZipArchive data) throws IOException { + java.util.zip.ZipInputStream zip = new java.util.zip.ZipInputStream(is); + for (;;) { + ZipEntry en = zip.getNextEntry(); + if (en == null) { + return; + } + data.register(en.getName(), zip); + } + } + +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,42 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ZipCompatibilityTest { + @Compare + public String testDemoStaticCalculator() throws IOException { + InputStream is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + ZipArchive zip = ZipArchive.createZip(is); + return zip.toString(); + } + + @Factory + public static Object[] create() { + return VMTest.create(ZipCompatibilityTest.class); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,39 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.IOException; +import java.io.InputStream; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class ZipVsJzLibTest { + @Test public void r() throws IOException { + InputStream is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + ZipArchive zip = ZipArchive.createZip(is); + + is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + ZipArchive real = ZipArchive.createReal(is); + + real.assertEquals(zip, "Are they the same?"); + } + +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar Binary file emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar has changed diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/pom.xml --- a/emul/mini/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -22,6 +22,12 @@ 0.3-SNAPSHOT jar
+ + org.testng + testng + 6.5.2 + test + diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/java/lang/Class.java --- a/emul/mini/src/main/java/java/lang/Class.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Class.java Fri Feb 15 21:16:05 2013 +0100 @@ -231,12 +231,14 @@ } @JavaScriptBody(args = {"n", "c" }, body = - "if (vm[c]) return vm[c].$class;\n" - + "if (vm.loadClass) {\n" - + " vm.loadClass(n);\n" - + " if (vm[c]) return vm[c].$class;\n" + "if (!vm[c]) {\n" + + " if (vm.loadClass) {\n" + + " vm.loadClass(n);\n" + + " }\n" + + " if (!vm[c]) return null;\n" + "}\n" - + "return null;" + + "vm[c](false);" + + "return vm[c].$class;" ) private static native Class loadCls(String n, String c); diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/java/lang/String.java --- a/emul/mini/src/main/java/java/lang/String.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/String.java Fri Feb 15 21:16:05 2013 +0100 @@ -335,7 +335,7 @@ value[i] = (char) (hibyte | (ascii[i + offset] & 0xff)); } } - this.r = new String(value, 0, count); + initFromCharArray(value, offset, count); } /** diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/java/util/zip/Inflater.java --- a/emul/mini/src/main/java/java/util/zip/Inflater.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/java/util/zip/Inflater.java Fri Feb 15 21:16:05 2013 +0100 @@ -1,44 +1,30 @@ -/* Inflater.java - Decompress a data stream - Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Classpath is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -02111-1307 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ package java.util.zip; -import org.apidesign.bck2brwsr.emul.lang.System; - /** * This class provides support for general purpose decompression using the * popular ZLIB compression library. The ZLIB compression library was @@ -84,1392 +70,241 @@ * @author David Connelly * */ +public +class Inflater { + private final org.apidesign.bck2brwsr.emul.zip.Inflater impl; + + /** + * Creates a new decompressor. If the parameter 'nowrap' is true then + * the ZLIB header and checksum fields will not be used. This provides + * compatibility with the compression format used by both GZIP and PKZIP. + *

+ * Note: When using the 'nowrap' option it is also necessary to provide + * an extra "dummy" byte as input. This is required by the ZLIB native + * library in order to support certain optimizations. + * + * @param nowrap if true then support GZIP compatible compression + */ + public Inflater(boolean nowrap) { + if (getClass() == org.apidesign.bck2brwsr.emul.zip.Inflater.class) { + impl = null; + } else { + impl = new org.apidesign.bck2brwsr.emul.zip.Inflater(nowrap); + } + } -/* Written using on-line Java Platform 1.2 API Specification - * and JCL book. - * Believed complete and correct. - */ + /** + * Creates a new decompressor. + */ + public Inflater() { + this(false); + } -/** - * Inflater is used to decompress data that has been compressed according - * to the "deflate" standard described in rfc1950. - * - * The usage is as following. First you have to set some input with - * setInput(), then inflate() it. If inflate doesn't - * inflate any bytes there may be three reasons: - *

    - *
  • needsInput() returns true because the input buffer is empty. - * You have to provide more input with setInput(). - * NOTE: needsInput() also returns true when, the stream is finished. - *
  • - *
  • needsDictionary() returns true, you have to provide a preset - * dictionary with setDictionary().
  • - *
  • finished() returns true, the inflater has finished.
  • - *
- * Once the first output byte is produced, a dictionary will not be - * needed at a later stage. - * - * @author John Leuner, Jochen Hoenicke - * @author Tom Tromey - * @date May 17, 1999 - * @since JDK 1.1 - */ -public class Inflater -{ - /* Copy lengths for literal codes 257..285 */ - private static final int CPLENS[] = - { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 - }; - - /* Extra bits for literal codes 257..285 */ - private static final int CPLEXT[] = - { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 - }; + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @param off the start offset of the input data + * @param len the length of the input data + * @see Inflater#needsInput + */ + public void setInput(byte[] b, int off, int len) { + impl.setInput(b, off, len); + } - /* Copy offsets for distance codes 0..29 */ - private static final int CPDIST[] = { - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577 - }; - - /* Extra bits for distance codes */ - private static final int CPDEXT[] = { - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13 - }; + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @see Inflater#needsInput + */ + public void setInput(byte[] b) { + impl.setInput(b); + } - /* This are the state in which the inflater can be. */ - private static final int DECODE_HEADER = 0; - private static final int DECODE_DICT = 1; - private static final int DECODE_BLOCKS = 2; - private static final int DECODE_STORED_LEN1 = 3; - private static final int DECODE_STORED_LEN2 = 4; - private static final int DECODE_STORED = 5; - private static final int DECODE_DYN_HEADER = 6; - private static final int DECODE_HUFFMAN = 7; - private static final int DECODE_HUFFMAN_LENBITS = 8; - private static final int DECODE_HUFFMAN_DIST = 9; - private static final int DECODE_HUFFMAN_DISTBITS = 10; - private static final int DECODE_CHKSUM = 11; - private static final int FINISHED = 12; + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @param off the start offset of the data + * @param len the length of the data + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b, int off, int len) { + impl.setDictionary(b, off, len); + } - /** This variable contains the current state. */ - private int mode; + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b) { + impl.setDictionary(b); + } - /** - * The adler checksum of the dictionary or of the decompressed - * stream, as it is written in the header resp. footer of the - * compressed stream.
- * - * Only valid if mode is DECODE_DICT or DECODE_CHKSUM. - */ - private int readAdler; - /** - * The number of bits needed to complete the current state. This - * is valid, if mode is DECODE_DICT, DECODE_CHKSUM, - * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. - */ - private int neededBits; - private int repLength, repDist; - private int uncomprLen; - /** - * True, if the last block flag was set in the last block of the - * inflated stream. This means that the stream ends after the - * current block. - */ - private boolean isLastBlock; + /** + * Returns the total number of bytes remaining in the input buffer. + * This can be used to find out what bytes still remain in the input + * buffer after decompression has finished. + * @return the total number of bytes remaining in the input buffer + */ + public int getRemaining() { + return impl.getRemaining(); + } - /** - * The total number of inflated bytes. - */ - private long totalOut; - /** - * The total number of bytes set with setInput(). This is not the - * value returned by getTotalIn(), since this also includes the - * unprocessed input. - */ - private long totalIn; - /** - * This variable stores the nowrap flag that was given to the constructor. - * True means, that the inflated stream doesn't contain a header nor the - * checksum in the footer. - */ - private boolean nowrap; + /** + * Returns true if no data remains in the input buffer. This can + * be used to determine if #setInput should be called in order + * to provide more input. + * @return true if no data remains in the input buffer + */ + public boolean needsInput() { + return impl.needsInput(); + } - private StreamManipulator input; - private OutputWindow outputWindow; - private InflaterDynHeader dynHeader; - private InflaterHuffmanTree litlenTree, distTree; - private Adler32 adler; + /** + * Returns true if a preset dictionary is needed for decompression. + * @return true if a preset dictionary is needed for decompression + * @see Inflater#setDictionary + */ + public boolean needsDictionary() { + return impl.needsDictionary(); + } - /** - * Creates a new inflater. - */ - public Inflater () - { - this (false); - } + /** + * Returns true if the end of the compressed data stream has been + * reached. + * @return true if the end of the compressed data stream has been + * reached + */ + public boolean finished() { + return impl.finished(); + } - /** - * Creates a new inflater. - * @param nowrap true if no header and checksum field appears in the - * stream. This is used for GZIPed input. For compatibility with - * Sun JDK you should provide one byte of input more than needed in - * this case. - */ - public Inflater (boolean nowrap) - { - this.nowrap = nowrap; - this.adler = new Adler32(); - input = new StreamManipulator(); - outputWindow = new OutputWindow(); - mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; - } + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @param off the start offset of the data + * @param len the maximum number of uncompressed bytes + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b, int off, int len) + throws DataFormatException + { + return impl.inflate(b, off, len); + } - /** - * Finalizes this object. - */ - protected void finalize () - { - /* Exists only for compatibility */ - } + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b) throws DataFormatException { + return impl.inflate(b); + } - /** - * Frees all objects allocated by the inflater. There's no reason - * to call this, since you can just rely on garbage collection (even - * for the Sun implementation). Exists only for compatibility - * with Sun's JDK, where the compressor allocates native memory. - * If you call any method (even reset) afterwards the behaviour is - * undefined. - * @deprecated Just clear all references to inflater instead. - */ - public void end () - { - outputWindow = null; - input = null; - dynHeader = null; - litlenTree = null; - distTree = null; - adler = null; - } + /** + * Returns the ADLER-32 value of the uncompressed data. + * @return the ADLER-32 value of the uncompressed data + */ + public int getAdler() { + return impl.getAdler(); + } - /** - * Returns true, if the inflater has finished. This means, that no - * input is needed and no output can be produced. - */ - public boolean finished() - { - return mode == FINISHED && outputWindow.getAvailable() == 0; - } + /** + * Returns the total number of compressed bytes input so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of compressed bytes input so far + */ + public int getTotalIn() { + return impl.getTotalIn(); + } - /** - * Gets the adler checksum. This is either the checksum of all - * uncompressed bytes returned by inflate(), or if needsDictionary() - * returns true (and thus no output was yet produced) this is the - * adler checksum of the expected dictionary. - * @returns the adler checksum. - */ - public int getAdler() - { - return needsDictionary() ? readAdler : (int) adler.getValue(); - } - - /** - * Gets the number of unprocessed input. Useful, if the end of the - * stream is reached and you want to further process the bytes after - * the deflate stream. - * @return the number of bytes of the input which were not processed. - */ - public int getRemaining() - { - return input.getAvailableBytes(); - } - - /** - * Gets the total number of processed compressed input bytes. - * @return the total number of bytes of processed input bytes. - */ - public int getTotalIn() - { - return (int)getBytesRead(); - } - - /** - * Gets the total number of output bytes returned by inflate(). - * @return the total number of output bytes. - */ - public int getTotalOut() - { - return (int)totalOut; - } - - public long getBytesWritten() { - return totalOut; - } + /** + * Returns the total number of compressed bytes input so far.

+ * + * @return the total (non-negative) number of compressed bytes input so far + * @since 1.5 + */ + public long getBytesRead() { + return impl.getBytesRead(); + } - public long getBytesRead() { - return totalIn - getRemaining(); - } - + /** + * Returns the total number of uncompressed bytes output so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of uncompressed bytes output so far + */ + public int getTotalOut() { + return impl.getTotalOut(); + } - /** - * Inflates the compressed stream to the output buffer. If this - * returns 0, you should check, whether needsDictionary(), - * needsInput() or finished() returns true, to determine why no - * further output is produced. - * @param buffer the output buffer. - * @return the number of bytes written to the buffer, 0 if no further - * output can be produced. - * @exception DataFormatException if deflated stream is invalid. - * @exception IllegalArgumentException if buf has length 0. - */ - public int inflate (byte[] buf) throws DataFormatException - { - return inflate (buf, 0, buf.length); - } + /** + * Returns the total number of uncompressed bytes output so far.

+ * + * @return the total (non-negative) number of uncompressed bytes output so far + * @since 1.5 + */ + public long getBytesWritten() { + return impl.getBytesWritten(); + } - /** - * Inflates the compressed stream to the output buffer. If this - * returns 0, you should check, whether needsDictionary(), - * needsInput() or finished() returns true, to determine why no - * further output is produced. - * @param buffer the output buffer. - * @param off the offset into buffer where the output should start. - * @param len the maximum length of the output. - * @return the number of bytes written to the buffer, 0 if no further - * output can be produced. - * @exception DataFormatException if deflated stream is invalid. - * @exception IndexOutOfBoundsException if the off and/or len are wrong. - */ - public int inflate (byte[] buf, int off, int len) throws DataFormatException - { - /* Special case: len may be zero */ - if (len == 0) - return 0; - /* Check for correct buff, off, len triple */ - if (0 > off || off > off + len || off + len > buf.length) - throw new ArrayIndexOutOfBoundsException(); - int count = 0; - int more; - do - { - if (mode != DECODE_CHKSUM) - { - /* Don't give away any output, if we are waiting for the - * checksum in the input stream. - * - * With this trick we have always: - * needsInput() and not finished() - * implies more output can be produced. - */ - more = outputWindow.copyOutput(buf, off, len); - adler.update(buf, off, more); - off += more; - count += more; - totalOut += more; - len -= more; - if (len == 0) - return count; - } - } - while (decode() || (outputWindow.getAvailable() > 0 - && mode != DECODE_CHKSUM)); - return count; - } + /** + * Resets inflater so that a new set of input data can be processed. + */ + public void reset() { + impl.reset(); + } - /** - * Returns true, if a preset dictionary is needed to inflate the input. - */ - public boolean needsDictionary () - { - return mode == DECODE_DICT && neededBits == 0; - } + /** + * Closes the decompressor and discards any unprocessed input. + * This method should be called when the decompressor is no longer + * being used, but will also be called automatically by the finalize() + * method. Once this method is called, the behavior of the Inflater + * object is undefined. + */ + public void end() { + impl.end(); + } - /** - * Returns true, if the input buffer is empty. - * You should then call setInput().
- * - * NOTE: This method also returns true when the stream is finished. - */ - public boolean needsInput () - { - return input.needsInput (); - } - - /** - * Resets the inflater so that a new stream can be decompressed. All - * pending input and output will be discarded. - */ - public void reset () - { - mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; - totalIn = totalOut = 0; - input.reset(); - outputWindow.reset(); - dynHeader = null; - litlenTree = null; - distTree = null; - isLastBlock = false; - adler.reset(); - } - - /** - * Sets the preset dictionary. This should only be called, if - * needsDictionary() returns true and it should set the same - * dictionary, that was used for deflating. The getAdler() - * function returns the checksum of the dictionary needed. - * @param buffer the dictionary. - * @exception IllegalStateException if no dictionary is needed. - * @exception IllegalArgumentException if the dictionary checksum is - * wrong. - */ - public void setDictionary (byte[] buffer) - { - setDictionary(buffer, 0, buffer.length); - } - - /** - * Sets the preset dictionary. This should only be called, if - * needsDictionary() returns true and it should set the same - * dictionary, that was used for deflating. The getAdler() - * function returns the checksum of the dictionary needed. - * @param buffer the dictionary. - * @param off the offset into buffer where the dictionary starts. - * @param len the length of the dictionary. - * @exception IllegalStateException if no dictionary is needed. - * @exception IllegalArgumentException if the dictionary checksum is - * wrong. - * @exception IndexOutOfBoundsException if the off and/or len are wrong. - */ - public void setDictionary (byte[] buffer, int off, int len) - { - if (!needsDictionary()) - throw new IllegalStateException(); - - adler.update(buffer, off, len); - if ((int) adler.getValue() != readAdler) - throw new IllegalArgumentException("Wrong adler checksum"); - adler.reset(); - outputWindow.copyDict(buffer, off, len); - mode = DECODE_BLOCKS; - } - - /** - * Sets the input. This should only be called, if needsInput() - * returns true. - * @param buffer the input. - * @exception IllegalStateException if no input is needed. - */ - public void setInput (byte[] buf) - { - setInput (buf, 0, buf.length); - } - - /** - * Sets the input. This should only be called, if needsInput() - * returns true. - * @param buffer the input. - * @param off the offset into buffer where the input starts. - * @param len the length of the input. - * @exception IllegalStateException if no input is needed. - * @exception IndexOutOfBoundsException if the off and/or len are wrong. - */ - public void setInput (byte[] buf, int off, int len) - { - input.setInput (buf, off, len); - totalIn += len; - } - private static final int DEFLATED = 8; - /** - * Decodes the deflate header. - * @return false if more input is needed. - * @exception DataFormatException if header is invalid. - */ - private boolean decodeHeader () throws DataFormatException - { - int header = input.peekBits(16); - if (header < 0) - return false; - input.dropBits(16); - - /* The header is written in "wrong" byte order */ - header = ((header << 8) | (header >> 8)) & 0xffff; - if (header % 31 != 0) - throw new DataFormatException("Header checksum illegal"); - - if ((header & 0x0f00) != (DEFLATED << 8)) - throw new DataFormatException("Compression Method unknown"); - - /* Maximum size of the backwards window in bits. - * We currently ignore this, but we could use it to make the - * inflater window more space efficient. On the other hand the - * full window (15 bits) is needed most times, anyway. - int max_wbits = ((header & 0x7000) >> 12) + 8; + /** + * Closes the decompressor when garbage is collected. */ - - if ((header & 0x0020) == 0) // Dictionary flag? - { - mode = DECODE_BLOCKS; - } - else - { - mode = DECODE_DICT; - neededBits = 32; - } - return true; - } - - /** - * Decodes the dictionary checksum after the deflate header. - * @return false if more input is needed. - */ - private boolean decodeDict () - { - while (neededBits > 0) - { - int dictByte = input.peekBits(8); - if (dictByte < 0) - return false; - input.dropBits(8); - readAdler = (readAdler << 8) | dictByte; - neededBits -= 8; - } - return false; - } - - /** - * Decodes the huffman encoded symbols in the input stream. - * @return false if more input is needed, true if output window is - * full or the current block ends. - * @exception DataFormatException if deflated stream is invalid. - */ - private boolean decodeHuffman () throws DataFormatException - { - int free = outputWindow.getFreeSpace(); - while (free >= 258) - { - int symbol; - switch (mode) - { - case DECODE_HUFFMAN: - /* This is the inner loop so it is optimized a bit */ - while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0) - { - outputWindow.write(symbol); - if (--free < 258) - return true; - } - if (symbol < 257) - { - if (symbol < 0) - return false; - else - { - /* symbol == 256: end of block */ - distTree = null; - litlenTree = null; - mode = DECODE_BLOCKS; - return true; - } - } - - try - { - repLength = CPLENS[symbol - 257]; - neededBits = CPLEXT[symbol - 257]; - } - catch (ArrayIndexOutOfBoundsException ex) - { - throw new DataFormatException("Illegal rep length code"); - } - /* fall through */ - case DECODE_HUFFMAN_LENBITS: - if (neededBits > 0) - { - mode = DECODE_HUFFMAN_LENBITS; - int i = input.peekBits(neededBits); - if (i < 0) - return false; - input.dropBits(neededBits); - repLength += i; - } - mode = DECODE_HUFFMAN_DIST; - /* fall through */ - case DECODE_HUFFMAN_DIST: - symbol = distTree.getSymbol(input); - if (symbol < 0) - return false; - try - { - repDist = CPDIST[symbol]; - neededBits = CPDEXT[symbol]; - } - catch (ArrayIndexOutOfBoundsException ex) - { - throw new DataFormatException("Illegal rep dist code"); - } - /* fall through */ - case DECODE_HUFFMAN_DISTBITS: - if (neededBits > 0) - { - mode = DECODE_HUFFMAN_DISTBITS; - int i = input.peekBits(neededBits); - if (i < 0) - return false; - input.dropBits(neededBits); - repDist += i; - } - outputWindow.repeat(repLength, repDist); - free -= repLength; - mode = DECODE_HUFFMAN; - break; - default: - throw new IllegalStateException(); - } - } - return true; - } - - /** - * Decodes the adler checksum after the deflate stream. - * @return false if more input is needed. - * @exception DataFormatException if checksum doesn't match. - */ - private boolean decodeChksum () throws DataFormatException - { - while (neededBits > 0) - { - int chkByte = input.peekBits(8); - if (chkByte < 0) - return false; - input.dropBits(8); - readAdler = (readAdler << 8) | chkByte; - neededBits -= 8; - } - if ((int) adler.getValue() != readAdler) - throw new DataFormatException("Adler chksum doesn't match: " - +Integer.toHexString((int)adler.getValue()) - +" vs. "+Integer.toHexString(readAdler)); - mode = FINISHED; - return false; - } - - /** - * Decodes the deflated stream. - * @return false if more input is needed, or if finished. - * @exception DataFormatException if deflated stream is invalid. - */ - private boolean decode () throws DataFormatException - { - switch (mode) - { - case DECODE_HEADER: - return decodeHeader(); - case DECODE_DICT: - return decodeDict(); - case DECODE_CHKSUM: - return decodeChksum(); - - case DECODE_BLOCKS: - if (isLastBlock) - { - if (nowrap) - { - mode = FINISHED; - return false; - } - else - { - input.skipToByteBoundary(); - neededBits = 32; - mode = DECODE_CHKSUM; - return true; - } - } - - int type = input.peekBits(3); - if (type < 0) - return false; - input.dropBits(3); - - if ((type & 1) != 0) - isLastBlock = true; - switch (type >> 1) - { - case DeflaterConstants.STORED_BLOCK: - input.skipToByteBoundary(); - mode = DECODE_STORED_LEN1; - break; - case DeflaterConstants.STATIC_TREES: - litlenTree = InflaterHuffmanTree.defLitLenTree; - distTree = InflaterHuffmanTree.defDistTree; - mode = DECODE_HUFFMAN; - break; - case DeflaterConstants.DYN_TREES: - dynHeader = new InflaterDynHeader(); - mode = DECODE_DYN_HEADER; - break; - default: - throw new DataFormatException("Unknown block type "+type); - } - return true; - - case DECODE_STORED_LEN1: - { - if ((uncomprLen = input.peekBits(16)) < 0) - return false; - input.dropBits(16); - mode = DECODE_STORED_LEN2; - } - /* fall through */ - case DECODE_STORED_LEN2: - { - int nlen = input.peekBits(16); - if (nlen < 0) - return false; - input.dropBits(16); - if (nlen != (uncomprLen ^ 0xffff)) - throw new DataFormatException("broken uncompressed block"); - mode = DECODE_STORED; - } - /* fall through */ - case DECODE_STORED: - { - int more = outputWindow.copyStored(input, uncomprLen); - uncomprLen -= more; - if (uncomprLen == 0) - { - mode = DECODE_BLOCKS; - return true; - } - return !input.needsInput(); - } - - case DECODE_DYN_HEADER: - if (!dynHeader.decode(input)) - return false; - litlenTree = dynHeader.buildLitLenTree(); - distTree = dynHeader.buildDistTree(); - mode = DECODE_HUFFMAN; - /* fall through */ - case DECODE_HUFFMAN: - case DECODE_HUFFMAN_LENBITS: - case DECODE_HUFFMAN_DIST: - case DECODE_HUFFMAN_DISTBITS: - return decodeHuffman(); - case FINISHED: - return false; - default: - throw new IllegalStateException(); - } - } - - - interface DeflaterConstants { - final static boolean DEBUGGING = false; - - final static int STORED_BLOCK = 0; - final static int STATIC_TREES = 1; - final static int DYN_TREES = 2; - final static int PRESET_DICT = 0x20; - - final static int DEFAULT_MEM_LEVEL = 8; - - final static int MAX_MATCH = 258; - final static int MIN_MATCH = 3; - - final static int MAX_WBITS = 15; - final static int WSIZE = 1 << MAX_WBITS; - final static int WMASK = WSIZE - 1; - - final static int HASH_BITS = DEFAULT_MEM_LEVEL + 7; - final static int HASH_SIZE = 1 << HASH_BITS; - final static int HASH_MASK = HASH_SIZE - 1; - final static int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; - - final static int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; - final static int MAX_DIST = WSIZE - MIN_LOOKAHEAD; - - final static int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); - final static int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5); - - final static int DEFLATE_STORED = 0; - final static int DEFLATE_FAST = 1; - final static int DEFLATE_SLOW = 2; - - final static int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 }; - final static int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 }; - final static int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 }; - final static int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 }; - final static int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 }; + protected void finalize() { + end(); } - private static class InflaterHuffmanTree { - private final static int MAX_BITLEN = 15; - private short[] tree; - - public static InflaterHuffmanTree defLitLenTree, defDistTree; - - static - { - try - { - byte[] codeLengths = new byte[288]; - int i = 0; - while (i < 144) - codeLengths[i++] = 8; - while (i < 256) - codeLengths[i++] = 9; - while (i < 280) - codeLengths[i++] = 7; - while (i < 288) - codeLengths[i++] = 8; - defLitLenTree = new InflaterHuffmanTree(codeLengths); - - codeLengths = new byte[32]; - i = 0; - while (i < 32) - codeLengths[i++] = 5; - defDistTree = new InflaterHuffmanTree(codeLengths); - } - catch (DataFormatException ex) - { - throw new IllegalStateException - ("InflaterHuffmanTree: static tree length illegal"); - } - } - - /** - * Constructs a Huffman tree from the array of code lengths. - * - * @param codeLengths the array of code lengths - */ - public InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException - { - buildTree(codeLengths); - } - - private void buildTree(byte[] codeLengths) throws DataFormatException - { - int[] blCount = new int[MAX_BITLEN+1]; - int[] nextCode = new int[MAX_BITLEN+1]; - for (int i = 0; i < codeLengths.length; i++) - { - int bits = codeLengths[i]; - if (bits > 0) - blCount[bits]++; - } - - int code = 0; - int treeSize = 512; - for (int bits = 1; bits <= MAX_BITLEN; bits++) - { - nextCode[bits] = code; - code += blCount[bits] << (16 - bits); - if (bits >= 10) - { - /* We need an extra table for bit lengths >= 10. */ - int start = nextCode[bits] & 0x1ff80; - int end = code & 0x1ff80; - treeSize += (end - start) >> (16 - bits); - } - } - if (code != 65536) - throw new DataFormatException("Code lengths don't add up properly."); - - /* Now create and fill the extra tables from longest to shortest - * bit len. This way the sub trees will be aligned. - */ - tree = new short[treeSize]; - int treePtr = 512; - for (int bits = MAX_BITLEN; bits >= 10; bits--) - { - int end = code & 0x1ff80; - code -= blCount[bits] << (16 - bits); - int start = code & 0x1ff80; - for (int i = start; i < end; i += 1 << 7) - { - tree[bitReverse(i)] - = (short) ((-treePtr << 4) | bits); - treePtr += 1 << (bits-9); - } - } - - for (int i = 0; i < codeLengths.length; i++) - { - int bits = codeLengths[i]; - if (bits == 0) - continue; - code = nextCode[bits]; - int revcode = bitReverse(code); - if (bits <= 9) - { - do - { - tree[revcode] = (short) ((i << 4) | bits); - revcode += 1 << bits; - } - while (revcode < 512); - } - else - { - int subTree = tree[revcode & 511]; - int treeLen = 1 << (subTree & 15); - subTree = -(subTree >> 4); - do - { - tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits); - revcode += 1 << bits; - } - while (revcode < treeLen); - } - nextCode[bits] = code + (1 << (16 - bits)); - } - } - private final static String bit4Reverse = - "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; - static short bitReverse(int value) { - return (short) (bit4Reverse.charAt(value & 0xf) << 12 - | bit4Reverse.charAt((value >> 4) & 0xf) << 8 - | bit4Reverse.charAt((value >> 8) & 0xf) << 4 - | bit4Reverse.charAt(value >> 12)); - } - - /** - * Reads the next symbol from input. The symbol is encoded using the - * huffman tree. - * @param input the input source. - * @return the next symbol, or -1 if not enough input is available. - */ - public int getSymbol(StreamManipulator input) throws DataFormatException - { - int lookahead, symbol; - if ((lookahead = input.peekBits(9)) >= 0) - { - if ((symbol = tree[lookahead]) >= 0) - { - input.dropBits(symbol & 15); - return symbol >> 4; - } - int subtree = -(symbol >> 4); - int bitlen = symbol & 15; - if ((lookahead = input.peekBits(bitlen)) >= 0) - { - symbol = tree[subtree | (lookahead >> 9)]; - input.dropBits(symbol & 15); - return symbol >> 4; - } - else - { - int bits = input.getAvailableBits(); - lookahead = input.peekBits(bits); - symbol = tree[subtree | (lookahead >> 9)]; - if ((symbol & 15) <= bits) - { - input.dropBits(symbol & 15); - return symbol >> 4; - } - else - return -1; - } - } - else - { - int bits = input.getAvailableBits(); - lookahead = input.peekBits(bits); - symbol = tree[lookahead]; - if (symbol >= 0 && (symbol & 15) <= bits) - { - input.dropBits(symbol & 15); - return symbol >> 4; - } - else - return -1; - } - } - } - private static class InflaterDynHeader - { - private static final int LNUM = 0; - private static final int DNUM = 1; - private static final int BLNUM = 2; - private static final int BLLENS = 3; - private static final int LENS = 4; - private static final int REPS = 5; - - private static final int repMin[] = { 3, 3, 11 }; - private static final int repBits[] = { 2, 3, 7 }; - - - private byte[] blLens; - private byte[] litdistLens; - - private InflaterHuffmanTree blTree; - - private int mode; - private int lnum, dnum, blnum, num; - private int repSymbol; - private byte lastLen; - private int ptr; - - private static final int[] BL_ORDER = - { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - public InflaterDynHeader() - { - } - - public boolean decode(StreamManipulator input) throws DataFormatException - { - decode_loop: - for (;;) - { - switch (mode) - { - case LNUM: - lnum = input.peekBits(5); - if (lnum < 0) - return false; - lnum += 257; - input.dropBits(5); - // System.err.println("LNUM: "+lnum); - mode = DNUM; - /* fall through */ - case DNUM: - dnum = input.peekBits(5); - if (dnum < 0) - return false; - dnum++; - input.dropBits(5); - // System.err.println("DNUM: "+dnum); - num = lnum+dnum; - litdistLens = new byte[num]; - mode = BLNUM; - /* fall through */ - case BLNUM: - blnum = input.peekBits(4); - if (blnum < 0) - return false; - blnum += 4; - input.dropBits(4); - blLens = new byte[19]; - ptr = 0; - // System.err.println("BLNUM: "+blnum); - mode = BLLENS; - /* fall through */ - case BLLENS: - while (ptr < blnum) - { - int len = input.peekBits(3); - if (len < 0) - return false; - input.dropBits(3); - // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); - blLens[BL_ORDER[ptr]] = (byte) len; - ptr++; - } - blTree = new InflaterHuffmanTree(blLens); - blLens = null; - ptr = 0; - mode = LENS; - /* fall through */ - case LENS: - { - int symbol; - while (((symbol = blTree.getSymbol(input)) & ~15) == 0) - { - /* Normal case: symbol in [0..15] */ - - // System.err.println("litdistLens["+ptr+"]: "+symbol); - litdistLens[ptr++] = lastLen = (byte) symbol; - - if (ptr == num) - { - /* Finished */ - return true; - } - } - - /* need more input ? */ - if (symbol < 0) - return false; - - /* otherwise repeat code */ - if (symbol >= 17) - { - /* repeat zero */ - // System.err.println("repeating zero"); - lastLen = 0; - } - else - { - if (ptr == 0) - throw new DataFormatException(); - } - repSymbol = symbol-16; - mode = REPS; - } - /* fall through */ - - case REPS: - { - int bits = repBits[repSymbol]; - int count = input.peekBits(bits); - if (count < 0) - return false; - input.dropBits(bits); - count += repMin[repSymbol]; - // System.err.println("litdistLens repeated: "+count); - - if (ptr + count > num) - throw new DataFormatException(); - while (count-- > 0) - litdistLens[ptr++] = lastLen; - - if (ptr == num) - { - /* Finished */ - return true; - } - } - mode = LENS; - continue decode_loop; - } - } - } - - public InflaterHuffmanTree buildLitLenTree() throws DataFormatException - { - byte[] litlenLens = new byte[lnum]; - System.arraycopy(litdistLens, 0, litlenLens, 0, lnum); - return new InflaterHuffmanTree(litlenLens); - } - - public InflaterHuffmanTree buildDistTree() throws DataFormatException - { - byte[] distLens = new byte[dnum]; - System.arraycopy(litdistLens, lnum, distLens, 0, dnum); - return new InflaterHuffmanTree(distLens); - } - } - /** - * This class allows us to retrieve a specified amount of bits from - * the input buffer, as well as copy big byte blocks. - * - * It uses an int buffer to store up to 31 bits for direct - * manipulation. This guarantees that we can get at least 16 bits, - * but we only need at most 15, so this is all safe. - * - * There are some optimizations in this class, for example, you must - * never peek more then 8 bits more than needed, and you must first - * peek bits before you may drop them. This is not a general purpose - * class but optimized for the behaviour of the Inflater. - * - * @author John Leuner, Jochen Hoenicke - */ - - private static class StreamManipulator - { - private byte[] window; - private int window_start = 0; - private int window_end = 0; - - private int buffer = 0; - private int bits_in_buffer = 0; - - /** - * Get the next n bits but don't increase input pointer. n must be - * less or equal 16 and if you if this call succeeds, you must drop - * at least n-8 bits in the next call. - * - * @return the value of the bits, or -1 if not enough bits available. */ - public final int peekBits(int n) - { - if (bits_in_buffer < n) - { - if (window_start == window_end) - return -1; - buffer |= (window[window_start++] & 0xff - | (window[window_start++] & 0xff) << 8) << bits_in_buffer; - bits_in_buffer += 16; - } - return buffer & ((1 << n) - 1); - } - - /* Drops the next n bits from the input. You should have called peekBits - * with a bigger or equal n before, to make sure that enough bits are in - * the bit buffer. - */ - public final void dropBits(int n) - { - buffer >>>= n; - bits_in_buffer -= n; - } - - /** - * Gets the next n bits and increases input pointer. This is equivalent - * to peekBits followed by dropBits, except for correct error handling. - * @return the value of the bits, or -1 if not enough bits available. - */ - public final int getBits(int n) - { - int bits = peekBits(n); - if (bits >= 0) - dropBits(n); - return bits; - } - /** - * Gets the number of bits available in the bit buffer. This must be - * only called when a previous peekBits() returned -1. - * @return the number of bits available. - */ - public final int getAvailableBits() - { - return bits_in_buffer; - } - - /** - * Gets the number of bytes available. - * @return the number of bytes available. - */ - public final int getAvailableBytes() - { - return window_end - window_start + (bits_in_buffer >> 3); - } - - /** - * Skips to the next byte boundary. - */ - public void skipToByteBoundary() - { - buffer >>= (bits_in_buffer & 7); - bits_in_buffer &= ~7; - } - - public final boolean needsInput() { - return window_start == window_end; - } - - - /* Copies length bytes from input buffer to output buffer starting - * at output[offset]. You have to make sure, that the buffer is - * byte aligned. If not enough bytes are available, copies fewer - * bytes. - * @param length the length to copy, 0 is allowed. - * @return the number of bytes copied, 0 if no byte is available. - */ - public int copyBytes(byte[] output, int offset, int length) - { - if (length < 0) - throw new IllegalArgumentException("length negative"); - if ((bits_in_buffer & 7) != 0) - /* bits_in_buffer may only be 0 or 8 */ - throw new IllegalStateException("Bit buffer is not aligned!"); - - int count = 0; - while (bits_in_buffer > 0 && length > 0) - { - output[offset++] = (byte) buffer; - buffer >>>= 8; - bits_in_buffer -= 8; - length--; - count++; - } - if (length == 0) - return count; - - int avail = window_end - window_start; - if (length > avail) - length = avail; - System.arraycopy(window, window_start, output, offset, length); - window_start += length; - - if (((window_start - window_end) & 1) != 0) - { - /* We always want an even number of bytes in input, see peekBits */ - buffer = (window[window_start++] & 0xff); - bits_in_buffer = 8; - } - return count + length; - } - - public StreamManipulator() - { - } - - public void reset() - { - window_start = window_end = buffer = bits_in_buffer = 0; - } - - public void setInput(byte[] buf, int off, int len) - { - if (window_start < window_end) - throw new IllegalStateException - ("Old input was not completely processed"); - - int end = off + len; - - /* We want to throw an ArrayIndexOutOfBoundsException early. The - * check is very tricky: it also handles integer wrap around. - */ - if (0 > off || off > end || end > buf.length) - throw new ArrayIndexOutOfBoundsException(); - - if ((len & 1) != 0) - { - /* We always want an even number of bytes in input, see peekBits */ - buffer |= (buf[off++] & 0xff) << bits_in_buffer; - bits_in_buffer += 8; - } - - window = buf; - window_start = off; - window_end = end; - } - } - /* - * Contains the output from the Inflation process. - * - * We need to have a window so that we can refer backwards into the output stream - * to repeat stuff. - * - * @author John Leuner - * @since JDK 1.1 - */ - - private static class OutputWindow - { - private final int WINDOW_SIZE = 1 << 15; - private final int WINDOW_MASK = WINDOW_SIZE - 1; - - private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes - private int window_end = 0; - private int window_filled = 0; - - public void write(int abyte) - { - if (window_filled++ == WINDOW_SIZE) - throw new IllegalStateException("Window full"); - window[window_end++] = (byte) abyte; - window_end &= WINDOW_MASK; - } - - - private final void slowRepeat(int rep_start, int len, int dist) - { - while (len-- > 0) - { - window[window_end++] = window[rep_start++]; - window_end &= WINDOW_MASK; - rep_start &= WINDOW_MASK; - } - } - - public void repeat(int len, int dist) - { - if ((window_filled += len) > WINDOW_SIZE) - throw new IllegalStateException("Window full"); - - int rep_start = (window_end - dist) & WINDOW_MASK; - int border = WINDOW_SIZE - len; - if (rep_start <= border && window_end < border) - { - if (len <= dist) - { - System.arraycopy(window, rep_start, window, window_end, len); - window_end += len; - } - else - { - /* We have to copy manually, since the repeat pattern overlaps. - */ - while (len-- > 0) - window[window_end++] = window[rep_start++]; - } - } - else - slowRepeat(rep_start, len, dist); - } - - public int copyStored(StreamManipulator input, int len) - { - len = Math.min(Math.min(len, WINDOW_SIZE - window_filled), - input.getAvailableBytes()); - int copied; - - int tailLen = WINDOW_SIZE - window_end; - if (len > tailLen) - { - copied = input.copyBytes(window, window_end, tailLen); - if (copied == tailLen) - copied += input.copyBytes(window, 0, len - tailLen); - } - else - copied = input.copyBytes(window, window_end, len); - - window_end = (window_end + copied) & WINDOW_MASK; - window_filled += copied; - return copied; - } - - public void copyDict(byte[] dict, int offset, int len) - { - if (window_filled > 0) - throw new IllegalStateException(); - - if (len > WINDOW_SIZE) - { - offset += len - WINDOW_SIZE; - len = WINDOW_SIZE; - } - System.arraycopy(dict, offset, window, 0, len); - window_end = len & WINDOW_MASK; - } - - public int getFreeSpace() - { - return WINDOW_SIZE - window_filled; - } - - public int getAvailable() - { - return window_filled; - } - - public int copyOutput(byte[] output, int offset, int len) - { - int copy_end = window_end; - if (len > window_filled) - len = window_filled; - else - copy_end = (window_end - window_filled + len) & WINDOW_MASK; - - int copied = len; - int tailLen = len - copy_end; - - if (tailLen > 0) - { - System.arraycopy(window, WINDOW_SIZE - tailLen, - output, offset, tailLen); - offset += tailLen; - len = copy_end; - } - System.arraycopy(window, copy_end - len, output, offset, len); - window_filled -= copied; - if (window_filled < 0) - throw new IllegalStateException(); - return copied; - } - - public void reset() { - window_filled = window_end = 0; - } - } - } diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/java/util/zip/ZipConstants64.java --- a/emul/mini/src/main/java/java/util/zip/ZipConstants64.java Fri Feb 15 11:54:45 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * Copyright (c) 1995, 1996, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.util.zip; - -/* - * This class defines the constants that are used by the classes - * which manipulate Zip64 files. - */ - -class ZipConstants64 { - - /* - * ZIP64 constants - */ - static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006" - static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007" - static final int ZIP64_ENDHDR = 56; // ZIP64 end header size - static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size - static final int ZIP64_EXTHDR = 24; // EXT header size - static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID - - static final int ZIP64_MAGICCOUNT = 0xFFFF; - static final long ZIP64_MAGICVAL = 0xFFFFFFFFL; - - /* - * Zip64 End of central directory (END) header field offsets - */ - static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir - static final int ZIP64_ENDVEM = 12; // version made by - static final int ZIP64_ENDVER = 14; // version needed to extract - static final int ZIP64_ENDNMD = 16; // number of this disk - static final int ZIP64_ENDDSK = 20; // disk number of start - static final int ZIP64_ENDTOD = 24; // total number of entries on this disk - static final int ZIP64_ENDTOT = 32; // total number of entries - static final int ZIP64_ENDSIZ = 40; // central directory size in bytes - static final int ZIP64_ENDOFF = 48; // offset of first CEN header - static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector - - /* - * Zip64 End of central directory locator field offsets - */ - static final int ZIP64_LOCDSK = 4; // disk number start - static final int ZIP64_LOCOFF = 8; // offset of zip64 end - static final int ZIP64_LOCTOT = 16; // total number of disks - - /* - * Zip64 Extra local (EXT) header field offsets - */ - static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value - static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte - static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte - - /* - * Language encoding flag EFS - */ - static final int EFS = 0x800; // If this bit is set the filename and - // comment fields for this file must be - // encoded using UTF-8. - - private ZipConstants64() {} -} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/java/util/zip/ZipInputStream.java --- a/emul/mini/src/main/java/java/util/zip/ZipInputStream.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/java/util/zip/ZipInputStream.java Fri Feb 15 21:16:05 2013 +0100 @@ -27,10 +27,6 @@ import java.io.InputStream; import java.io.IOException; -import java.io.EOFException; -import java.io.PushbackInputStream; -import static java.util.zip.ZipConstants64.*; -import org.apidesign.bck2brwsr.core.JavaScriptBody; /** * This class implements an input stream filter for reading files in the @@ -41,28 +37,7 @@ */ public class ZipInputStream extends InflaterInputStream implements ZipConstants { - private ZipEntry entry; - private int flag; - private CRC32 crc = new CRC32(); - private long remaining; - private byte[] tmpbuf = new byte[512]; - - private static final int STORED = ZipEntry.STORED; - private static final int DEFLATED = ZipEntry.DEFLATED; - - private boolean closed = false; - // this flag is set to true after EOF has reached for - // one entry - private boolean entryEOF = false; - - /** - * Check to make sure that this stream has not been closed - */ - private void ensureOpen() throws IOException { - if (closed) { - throw new IOException("Stream closed"); - } - } + private final org.apidesign.bck2brwsr.emul.zip.ZipInputStream impl; /** * Creates a new ZIP input stream. @@ -73,12 +48,8 @@ * @param in the actual input stream */ public ZipInputStream(InputStream in) { -// this(in, "UTF-8"); - super(new PushbackInputStream(in, 512), new Inflater(true), 512); - usesDefaultInflater = true; - if(in == null) { - throw new NullPointerException("in is null"); - } + super(in); + impl = new org.apidesign.bck2brwsr.emul.zip.ZipInputStream(in); } /** @@ -115,20 +86,7 @@ * @exception IOException if an I/O error has occurred */ public ZipEntry getNextEntry() throws IOException { - ensureOpen(); - if (entry != null) { - closeEntry(); - } - crc.reset(); - inf.reset(); - if ((entry = readLOC()) == null) { - return null; - } - if (entry.method == STORED) { - remaining = entry.size; - } - entryEOF = false; - return entry; + return impl.getNextEntry(); } /** @@ -138,9 +96,7 @@ * @exception IOException if an I/O error has occurred */ public void closeEntry() throws IOException { - ensureOpen(); - while (read(tmpbuf, 0, tmpbuf.length) != -1) ; - entryEOF = true; + impl.closeEntry(); } /** @@ -155,12 +111,7 @@ * */ public int available() throws IOException { - ensureOpen(); - if (entryEOF) { - return 0; - } else { - return 1; - } + return impl.available(); } /** @@ -181,51 +132,7 @@ * @exception IOException if an I/O error has occurred */ public int read(byte[] b, int off, int len) throws IOException { - ensureOpen(); - if (off < 0 || len < 0 || off > b.length - len) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - - if (entry == null) { - return -1; - } - switch (entry.method) { - case DEFLATED: - len = super.read(b, off, len); - if (len == -1) { - readEnd(entry); - entryEOF = true; - entry = null; - } else { - crc.update(b, off, len); - } - return len; - case STORED: - if (remaining <= 0) { - entryEOF = true; - entry = null; - return -1; - } - if (len > remaining) { - len = (int)remaining; - } - len = in.read(b, off, len); - if (len == -1) { - throw new ZipException("unexpected EOF"); - } - crc.update(b, off, len); - remaining -= len; - if (remaining == 0 && entry.crc != crc.getValue()) { - throw new ZipException( - "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) + - " but got 0x" + Long.toHexString(crc.getValue()) + ")"); - } - return len; - default: - throw new ZipException("invalid compression method"); - } + return impl.read(b, off, len); } /** @@ -237,25 +144,7 @@ * @exception IllegalArgumentException if n < 0 */ public long skip(long n) throws IOException { - if (n < 0) { - throw new IllegalArgumentException("negative skip length"); - } - ensureOpen(); - int max = (int)Math.min(n, Integer.MAX_VALUE); - int total = 0; - while (total < max) { - int len = max - total; - if (len > tmpbuf.length) { - len = tmpbuf.length; - } - len = read(tmpbuf, 0, len); - if (len == -1) { - entryEOF = true; - break; - } - total += len; - } - return total; + return impl.skip(n); } /** @@ -264,89 +153,7 @@ * @exception IOException if an I/O error has occurred */ public void close() throws IOException { - if (!closed) { - super.close(); - closed = true; - } - } - - private byte[] b = new byte[256]; - - /* - * Reads local file (LOC) header for next entry. - */ - private ZipEntry readLOC() throws IOException { - try { - readFully(tmpbuf, 0, LOCHDR); - } catch (EOFException e) { - return null; - } - if (get32(tmpbuf, 0) != LOCSIG) { - return null; - } - // get flag first, we need check EFS. - flag = get16(tmpbuf, LOCFLG); - // get the entry name and create the ZipEntry first - int len = get16(tmpbuf, LOCNAM); - int blen = b.length; - if (len > blen) { - do - blen = blen * 2; - while (len > blen); - b = new byte[blen]; - } - readFully(b, 0, len); - // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8 - ZipEntry e = createZipEntry(((flag & EFS) != 0) - ? toStringUTF8(b, len) - : toString(b, len)); - // now get the remaining fields for the entry - if ((flag & 1) == 1) { - throw new ZipException("encrypted ZIP entry not supported"); - } - e.method = get16(tmpbuf, LOCHOW); - e.time = get32(tmpbuf, LOCTIM); - if ((flag & 8) == 8) { - /* "Data Descriptor" present */ - if (e.method != DEFLATED) { - throw new ZipException( - "only DEFLATED entries can have EXT descriptor"); - } - } else { - e.crc = get32(tmpbuf, LOCCRC); - e.csize = get32(tmpbuf, LOCSIZ); - e.size = get32(tmpbuf, LOCLEN); - } - len = get16(tmpbuf, LOCEXT); - if (len > 0) { - byte[] bb = new byte[len]; - readFully(bb, 0, len); - e.setExtra(bb); - // extra fields are in "HeaderID(2)DataSize(2)Data... format - if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) { - int off = 0; - while (off + 4 < len) { - int sz = get16(bb, off + 2); - if (get16(bb, off) == ZIP64_EXTID) { - off += 4; - // LOC extra zip64 entry MUST include BOTH original and - // compressed file size fields - if (sz < 16 || (off + sz) > len ) { - // Invalid zip64 extra fields, simply skip. Even it's - // rare, it's possible the entry size happens to be - // the magic value and it "accidnetly" has some bytes - // in extra match the id. - return e; - } - e.size = get64(bb, off); - e.csize = get64(bb, off + 8); - break; - } - off += (sz + 4); - } - } - } - return e; + impl.close(); } /** @@ -360,108 +167,28 @@ return new ZipEntry(name); } - /* - * Reads end of deflated entry as well as EXT descriptor if present. - */ - private void readEnd(ZipEntry e) throws IOException { - int n = inf.getRemaining(); - if (n > 0) { - ((PushbackInputStream)in).unread(buf, len - n, n); - } - if ((flag & 8) == 8) { - /* "Data Descriptor" present */ - if (inf.getBytesWritten() > ZIP64_MAGICVAL || - inf.getBytesRead() > ZIP64_MAGICVAL) { - // ZIP64 format - readFully(tmpbuf, 0, ZIP64_EXTHDR); - long sig = get32(tmpbuf, 0); - if (sig != EXTSIG) { // no EXTSIG present - e.crc = sig; - e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); - e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); - ((PushbackInputStream)in).unread( - tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC); - } else { - e.crc = get32(tmpbuf, ZIP64_EXTCRC); - e.csize = get64(tmpbuf, ZIP64_EXTSIZ); - e.size = get64(tmpbuf, ZIP64_EXTLEN); - } - } else { - readFully(tmpbuf, 0, EXTHDR); - long sig = get32(tmpbuf, 0); - if (sig != EXTSIG) { // no EXTSIG present - e.crc = sig; - e.csize = get32(tmpbuf, EXTSIZ - EXTCRC); - e.size = get32(tmpbuf, EXTLEN - EXTCRC); - ((PushbackInputStream)in).unread( - tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC); - } else { - e.crc = get32(tmpbuf, EXTCRC); - e.csize = get32(tmpbuf, EXTSIZ); - e.size = get32(tmpbuf, EXTLEN); - } - } - } - if (e.size != inf.getBytesWritten()) { - throw new ZipException( - "invalid entry size (expected " + e.size + - " but got " + inf.getBytesWritten() + " bytes)"); - } - if (e.csize != inf.getBytesRead()) { - throw new ZipException( - "invalid entry compressed size (expected " + e.csize + - " but got " + inf.getBytesRead() + " bytes)"); - } - if (e.crc != crc.getValue()) { - throw new ZipException( - "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) + - " but got 0x" + Long.toHexString(crc.getValue()) + ")"); - } + @Override + public int read() throws IOException { + return impl.read(); } - /* - * Reads bytes, blocking until all bytes are read. - */ - private void readFully(byte[] b, int off, int len) throws IOException { - while (len > 0) { - int n = in.read(b, off, len); - if (n == -1) { - throw new EOFException(); - } - off += n; - len -= n; - } + @Override + public boolean markSupported() { + return impl.markSupported(); } - /* - * Fetches unsigned 16-bit value from byte array at specified offset. - * The bytes are assumed to be in Intel (little-endian) byte order. - */ - private static final int get16(byte b[], int off) { - return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8); + @Override + public void mark(int readlimit) { + impl.mark(readlimit); } - /* - * Fetches unsigned 32-bit value from byte array at specified offset. - * The bytes are assumed to be in Intel (little-endian) byte order. - */ - private static final long get32(byte b[], int off) { - return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + @Override + public void reset() throws IOException { + impl.reset(); } - /* - * Fetches signed 64-bit value from byte array at specified offset. - * The bytes are assumed to be in Intel (little-endian) byte order. - */ - private static final long get64(byte b[], int off) { - return get32(b, off) | (get32(b, off+4) << 32); - } - - private static String toStringUTF8(byte[] arr, int len) { - return new String(arr, 0, len); - } - - private static String toString(byte[] b, int len) { - return new String(b, 0, len); + @Override + public int read(byte[] b) throws IOException { + return impl.read(b); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/ManifestInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/ManifestInputStream.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.apidesign.bck2brwsr.emul.lang; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/* + * A fast buffered input stream for parsing manifest files. + * + * Taken from java.util.jar.Manifest.FastInputStream and modified to be + * independent of other Manifest functionality. + */ +public abstract class ManifestInputStream extends FilterInputStream { + private byte[] buf; + private int count = 0; + private int pos = 0; + + protected ManifestInputStream(InputStream in) { + this(in, 8192); + } + + protected ManifestInputStream(InputStream in, int size) { + super(in); + buf = new byte[size]; + } + + public int read() throws IOException { + if (pos >= count) { + fill(); + if (pos >= count) { + return -1; + } + } + return buf[pos++] & 0xff; + } + + public int read(byte[] b, int off, int len) throws IOException { + int avail = count - pos; + if (avail <= 0) { + if (len >= buf.length) { + return in.read(b, off, len); + } + fill(); + avail = count - pos; + if (avail <= 0) { + return -1; + } + } + if (len > avail) { + len = avail; + } + System.arraycopy(buf, pos, b, off, len); + pos += len; + return len; + } + + /* + * Reads 'len' bytes from the input stream, or until an end-of-line + * is reached. Returns the number of bytes read. + */ + public int readLine(byte[] b, int off, int len) throws IOException { + byte[] tbuf = this.buf; + int total = 0; + while (total < len) { + int avail = count - pos; + if (avail <= 0) { + fill(); + avail = count - pos; + if (avail <= 0) { + return -1; + } + } + int n = len - total; + if (n > avail) { + n = avail; + } + int tpos = pos; + int maxpos = tpos + n; + while (tpos < maxpos && tbuf[tpos++] != '\n') { + ; + } + n = tpos - pos; + System.arraycopy(tbuf, pos, b, off, n); + off += n; + total += n; + pos = tpos; + if (tbuf[tpos - 1] == '\n') { + break; + } + } + return total; + } + + public byte peek() throws IOException { + if (pos == count) { + fill(); + } + if (pos == count) { + return -1; // nothing left in buffer + } + return buf[pos]; + } + + public int readLine(byte[] b) throws IOException { + return readLine(b, 0, b.length); + } + + public long skip(long n) throws IOException { + if (n <= 0) { + return 0; + } + long avail = count - pos; + if (avail <= 0) { + return in.skip(n); + } + if (n > avail) { + n = avail; + } + pos += n; + return n; + } + + public int available() throws IOException { + return (count - pos) + in.available(); + } + + public void close() throws IOException { + if (in != null) { + in.close(); + in = null; + buf = null; + } + } + + private void fill() throws IOException { + count = pos = 0; + int n = in.read(buf, 0, buf.length); + if (n > 0) { + count = n; + } + } + + protected abstract String putValue(String key, String value); + + public void readAttributes(byte[] lbuf) throws IOException { + ManifestInputStream is = this; + + String name = null; + String value = null; + byte[] lastline = null; + int len; + while ((len = is.readLine(lbuf)) != -1) { + boolean lineContinued = false; + if (lbuf[--len] != '\n') { + throw new IOException("line too long"); + } + if (len > 0 && lbuf[len - 1] == '\r') { + --len; + } + if (len == 0) { + break; + } + int i = 0; + if (lbuf[0] == ' ') { + if (name == null) { + throw new IOException("misplaced continuation line"); + } + lineContinued = true; + byte[] buf = new byte[lastline.length + len - 1]; + System.arraycopy(lastline, 0, buf, 0, lastline.length); + System.arraycopy(lbuf, 1, buf, lastline.length, len - 1); + if (is.peek() == ' ') { + lastline = buf; + continue; + } + value = new String(buf, 0, buf.length, "UTF8"); + lastline = null; + } else { + while (lbuf[i++] != ':') { + if (i >= len) { + throw new IOException("invalid header field"); + } + } + if (lbuf[i++] != ' ') { + throw new IOException("invalid header field"); + } + name = new String(lbuf, 0, 0, i - 2); + if (is.peek() == ' ') { + lastline = new byte[len - i]; + System.arraycopy(lbuf, i, lastline, 0, len - i); + continue; + } + value = new String(lbuf, i, len - i, "UTF8"); + } + try { + if ((putValue(name, value) != null) && (!lineContinued)) { + throw new IOException("Duplicate name in Manifest: " + name + ".\n" + "Ensure that the manifest does not " + "have duplicate entries, and\n" + "that blank lines separate " + "individual sections in both your\n" + "manifest and in the META-INF/MANIFEST.MF " + "entry in the jar file."); + } + } catch (IllegalArgumentException e) { + throw new IOException("invalid header field name: " + name); + } + } + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,6 +17,7 @@ */ package org.apidesign.bck2brwsr.emul.lang; +import java.lang.reflect.Method; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -38,7 +39,15 @@ " }\n" + "}" ) - public static native void arraycopy(Object value, int srcBegin, Object dst, int dstBegin, int count); + public static void arraycopy(Object src, int srcBegin, Object dst, int dstBegin, int count) { + try { + Class system = Class.forName("java.lang.System"); + Method m = system.getMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class); + m.invoke(null, src, srcBegin, dst, dstBegin, count); + } catch (Exception ex) { + throw new IllegalStateException(ex); + } + } @JavaScriptBody(args = { "arr", "expectedSize" }, body = "while (expectedSize-- > arr.length) { arr.push(0); }; return arr;" @@ -53,5 +62,4 @@ } @JavaScriptBody(args = { "obj" }, body="return vm.java_lang_Object(false).hashCode__I.call(obj);") public static native int identityHashCode(Object obj); - } diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java Fri Feb 15 21:16:05 2013 +0100 @@ -18,6 +18,8 @@ package org.apidesign.bck2brwsr.emul.reflect; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -25,39 +27,73 @@ * @author Jaroslav Tulach */ public final class AnnotationImpl implements Annotation { + private final Class type; + + public AnnotationImpl(Class type) { + this.type = type; + } + public Class annotationType() { - return getClass(); + return type; } - @JavaScriptBody(args = { "a", "n", "values" }, body = "" - + "function f(v, p) {\n" - + " var val = v;\n" - + " var prop = p;\n" + @JavaScriptBody(args = { "a", "n", "arr", "values" }, body = "" + + "function f(val, prop, clazz) {\n" + " return function() {\n" - + " return val[prop];\n" + + " if (clazz == null) return val[prop];\n" + + " if (clazz.isArray__Z()) {\n" + + " var valarr = val[prop];\n" + + " var cmp = clazz.getComponentType__Ljava_lang_Class_2();\n" + + " var retarr = vm.java_lang_reflect_Array(false).newInstance__Ljava_lang_Object_2Ljava_lang_Class_2I(cmp, valarr.length);\n" + + " for (var i = 0; i < valarr.length; i++) {\n" + + " retarr[i] = CLS.prototype.c__Ljava_lang_Object_2Ljava_lang_Class_2Ljava_lang_Object_2(cmp, valarr[i]);\n" + + " }\n" + + " return retarr;\n" + + " }\n" + + " return CLS.prototype.c__Ljava_lang_Object_2Ljava_lang_Class_2Ljava_lang_Object_2(clazz, val[prop]);\n" + " };\n" + "}\n" - + "var props = Object.getOwnPropertyNames(values);\n" - + "for (var i = 0; i < props.length; i++) {\n" - + " var p = props[i];\n" - + " a[p] = new f(values, p);\n" + + "for (var i = 0; i < arr.length; i += 3) {\n" + + " var m = arr[i];\n" + + " var p = arr[i + 1];\n" + + " var c = arr[i + 2];\n" + + " a[m] = f(values, p, c);\n" + "}\n" + "a['$instOf_' + n] = true;\n" + "return a;" ) - private static T create(AnnotationImpl a, String n, Object values) { - return null; + private static native T create( + AnnotationImpl a, String n, Object[] methodsAndProps, Object values + ); + + private static Object c(Class a, Object v) { + return create(a, v); } + public static T create(Class annoClass, Object values) { - return create(new AnnotationImpl(), annoClass.getName().replace('.', '_'), values); + return create(new AnnotationImpl(annoClass), + annoClass.getName().replace('.', '_'), + findProps(annoClass), values + ); } public static Annotation[] create(Object anno) { String[] names = findNames(anno); Annotation[] ret = new Annotation[names.length]; for (int i = 0; i < names.length; i++) { - String n = names[i].substring(1, names[i].length() - 1).replace('/', '_'); - ret[i] = create(new AnnotationImpl(), n, findData(anno, names[i])); + String annoNameSlash = names[i].substring(1, names[i].length() - 1); + Class annoClass; + try { + annoClass = (Class)Class.forName(annoNameSlash.replace('/', '.')); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException("Can't find annotation class " + annoNameSlash); + } + ret[i] = create( + new AnnotationImpl(annoClass), + annoNameSlash.replace('/', '_'), + findProps(annoClass), + findData(anno, names[i]) + ); } return ret; } @@ -70,12 +106,25 @@ + "}" + "return arr;" ) - private static String[] findNames(Object anno) { - throw new UnsupportedOperationException(); - } + private static native String[] findNames(Object anno); @JavaScriptBody(args={ "anno", "p"}, body="return anno[p];") - private static Object findData(Object anno, String p) { - throw new UnsupportedOperationException(); + private static native Object findData(Object anno, String p); + + private static Object[] findProps(Class annoClass) { + final Method[] marr = MethodImpl.findMethods(annoClass, Modifier.PUBLIC); + Object[] arr = new Object[marr.length * 3]; + int pos = 0; + for (Method m : marr) { + arr[pos++] = MethodImpl.toSignature(m); + arr[pos++] = m.getName(); + final Class rt = m.getReturnType(); + if (rt.isArray()) { + arr[pos++] = rt.getComponentType().isAnnotation() ? rt : null; + } else { + arr[pos++] = rt.isAnnotation() ? rt : null; + } + } + return arr; } } diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Fri Feb 15 11:54:45 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,6 +17,7 @@ */ package org.apidesign.bck2brwsr.emul.reflect; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.Enumeration; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -34,7 +35,7 @@ throw new IllegalStateException(ex); } } - + protected abstract Method create(Class declaringClass, String name, Object data, String sig); @@ -48,8 +49,10 @@ + "var arr = new Array();\n" + "for (m in c) {\n" + " if (m.indexOf(prefix) === 0) {\n" + + " if (!c[m].cls) continue;\n" + " arr.push(m);\n" + " arr.push(c[m]);\n" + + " arr.push(c[m].cls.$class);\n" + " }" + "}\n" + "return arr;") @@ -59,9 +62,10 @@ public static Method findMethod( Class clazz, String name, Class... parameterTypes) { Object[] data = findMethodData(clazz, name + "__"); - BIG: for (int i = 0; i < data.length; i += 2) { - String sig = ((String) data[0]).substring(name.length() + 2); - Method tmp = INSTANCE.create(clazz, name, data[1], sig); + BIG: for (int i = 0; i < data.length; i += 3) { + String sig = ((String) data[i]).substring(name.length() + 2); + Class cls = (Class) data[i + 2]; + Method tmp = INSTANCE.create(cls, name, data[i + 1], sig); Class[] tmpParms = tmp.getParameterTypes(); if (parameterTypes.length != tmpParms.length) { continue; @@ -79,7 +83,7 @@ public static Method[] findMethods(Class clazz, int mask) { Object[] namesAndData = findMethodData(clazz, ""); int cnt = 0; - for (int i = 0; i < namesAndData.length; i += 2) { + for (int i = 0; i < namesAndData.length; i += 3) { String sig = (String) namesAndData[i]; Object data = namesAndData[i + 1]; int middle = sig.indexOf("__"); @@ -88,7 +92,8 @@ } String name = sig.substring(0, middle); sig = sig.substring(middle + 2); - final Method m = INSTANCE.create(clazz, name, data, sig); + Class cls = (Class) namesAndData[i + 2]; + final Method m = INSTANCE.create(cls, name, data, sig); if ((m.getModifiers() & mask) == 0) { continue; } @@ -100,6 +105,62 @@ } return arr; } + static String toSignature(Method m) { + StringBuilder sb = new StringBuilder(); + sb.append(m.getName()).append("__"); + appendType(sb, m.getReturnType()); + Class[] arr = m.getParameterTypes(); + for (int i = 0; i < arr.length; i++) { + appendType(sb, arr[i]); + } + return sb.toString(); + } + + private static void appendType(StringBuilder sb, Class type) { + if (type == Integer.TYPE) { + sb.append('I'); + return; + } + if (type == Long.TYPE) { + sb.append('J'); + return; + } + if (type == Double.TYPE) { + sb.append('D'); + return; + } + if (type == Float.TYPE) { + sb.append('F'); + return; + } + if (type == Byte.TYPE) { + sb.append('B'); + return; + } + if (type == Boolean.TYPE) { + sb.append('Z'); + return; + } + if (type == Short.TYPE) { + sb.append('S'); + return; + } + if (type == Void.TYPE) { + sb.append('V'); + return; + } + if (type == Character.TYPE) { + sb.append('C'); + return; + } + if (type.isArray()) { + sb.append("_3"); + appendType(sb, type.getComponentType()); + return; + } + sb.append('L').append(type.getName().replace('.', '_')); + sb.append("_2"); + } public static int signatureElements(String sig) { Enumeration en = signatureParser(sig); @@ -141,13 +202,19 @@ return Character.TYPE; case 'L': try { - int up = sig.indexOf("_2"); - String type = sig.substring(1, up); + int up = sig.indexOf("_2", pos); + String type = sig.substring(pos, up); pos = up + 2; - return Class.forName(type); + return Class.forName(type.replace('_', '.')); } catch (ClassNotFoundException ex) { - // should not happen + throw new IllegalStateException(ex); } + case '_': { + char nch = sig.charAt(pos++); + assert nch == '3' : "Can't find '3' at " + sig.substring(pos - 1); + final Class compType = nextElement(); + return Array.newInstance(compType, 0).getClass(); + } } throw new UnsupportedOperationException(sig + " at " + pos); } diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Adler32.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Adler32.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,139 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +final class Adler32 implements Checksum { + + // largest prime smaller than 65536 + static final private int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + static final private int NMAX=5552; + + private long s1=1L; + private long s2=0L; + + public void reset(long init){ + s1=init&0xffff; + s2=(init>>16)&0xffff; + } + + public void reset(){ + s1=1L; + s2=0L; + } + + public long getValue(){ + return ((s2<<16)|s1); + } + + public void update(byte[] buf, int index, int len){ + + if(len==1){ + s1+=buf[index++]&0xff; s2+=s1; + s1%=BASE; + s2%=BASE; + return; + } + + int len1 = len/NMAX; + int len2 = len%NMAX; + while(len1-->0) { + int k=NMAX; + len-=k; + while(k-->0){ + s1+=buf[index++]&0xff; s2+=s1; + } + s1%=BASE; + s2%=BASE; + } + + int k=len2; + len-=k; + while(k-->0){ + s1+=buf[index++]&0xff; s2+=s1; + } + s1%=BASE; + s2%=BASE; + } + + public Adler32 copy(){ + Adler32 foo = new Adler32(); + foo.s1 = this.s1; + foo.s2 = this.s2; + return foo; + } + + // The following logic has come from zlib.1.2. + static long combine(long adler1, long adler2, long len2){ + long BASEL = (long)BASE; + long sum1; + long sum2; + long rem; // unsigned int + + rem = len2 % BASEL; + sum1 = adler1 & 0xffffL; + sum2 = rem * sum1; + sum2 %= BASEL; // MOD(sum2); + sum1 += (adler2 & 0xffffL) + BASEL - 1; + sum2 += ((adler1 >> 16) & 0xffffL) + ((adler2 >> 16) & 0xffffL) + BASEL - rem; + if (sum1 >= BASEL) sum1 -= BASEL; + if (sum1 >= BASEL) sum1 -= BASEL; + if (sum2 >= (BASEL << 1)) sum2 -= (BASEL << 1); + if (sum2 >= BASEL) sum2 -= BASEL; + return sum1 | (sum2 << 16); + } + +/* + private java.util.zip.Adler32 adler=new java.util.zip.Adler32(); + public void update(byte[] buf, int index, int len){ + if(buf==null) {adler.reset();} + else{adler.update(buf, index, len);} + } + public void reset(){ + adler.reset(); + } + public void reset(long init){ + if(init==1L){ + adler.reset(); + } + else{ + System.err.println("unsupported operation"); + } + } + public long getValue(){ + return adler.getValue(); + } +*/ +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/CRC32.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/CRC32.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,181 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class CRC32 implements Checksum { + + /* + * The following logic has come from RFC1952. + */ + private int v = 0; + private static int[] crc_table = null; + static { + crc_table = new int[256]; + for (int n = 0; n < 256; n++) { + int c = n; + for (int k = 8; --k >= 0; ) { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + } + + public void update (byte[] buf, int index, int len) { + int c = ~v; + while (--len >= 0) + c = crc_table[(c^buf[index++])&0xff]^(c >>> 8); + v = ~c; + } + + public void reset(){ + v = 0; + } + + public void reset(long vv){ + v = (int)(vv&0xffffffffL); + } + + public long getValue(){ + return (long)(v&0xffffffffL); + } + + // The following logic has come from zlib.1.2. + private static final int GF2_DIM = 32; + static long combine(long crc1, long crc2, long len2){ + long row; + long[] even = new long[GF2_DIM]; + long[] odd = new long[GF2_DIM]; + + // degenerate case (also disallow negative lengths) + if (len2 <= 0) + return crc1; + + // put operator for one zero bit in odd + odd[0] = 0xedb88320L; // CRC-32 polynomial + row = 1; + for (int n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + // put operator for two zero bits in even + gf2_matrix_square(even, odd); + + // put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + // apply len2 zeros to crc1 (first square will put the operator for one + // zero byte, eight zero bits, in even) + do { + // apply zeros operator for this bit of len2 + gf2_matrix_square(even, odd); + if ((len2 & 1)!=0) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + // if no more bits set, then done + if (len2 == 0) + break; + + // another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1)!=0) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + // if no more bits set, then done + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; + } + + private static long gf2_matrix_times(long[] mat, long vec){ + long sum = 0; + int index = 0; + while (vec!=0) { + if ((vec & 1)!=0) + sum ^= mat[index]; + vec >>= 1; + index++; + } + return sum; + } + + static final void gf2_matrix_square(long[] square, long[] mat) { + for (int n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); + } + + /* + private java.util.zip.CRC32 crc32 = new java.util.zip.CRC32(); + + public void update(byte[] buf, int index, int len){ + if(buf==null) {crc32.reset();} + else{crc32.update(buf, index, len);} + } + public void reset(){ + crc32.reset(); + } + public void reset(long init){ + if(init==0L){ + crc32.reset(); + } + else{ + System.err.println("unsupported operation"); + } + } + public long getValue(){ + return crc32.getValue(); + } +*/ + public CRC32 copy(){ + CRC32 foo = new CRC32(); + foo.v = this.v; + return foo; + } + + public static int[] getCRC32Table(){ + int[] tmp = new int[crc_table.length]; + System.arraycopy(crc_table, 0, tmp, 0, tmp.length); + return tmp; + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Checksum.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Checksum.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,43 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +interface Checksum { + void update(byte[] buf, int index, int len); + void reset(); + void reset(long init); + long getValue(); + Checksum copy(); +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,175 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ +package org.apidesign.bck2brwsr.emul.zip; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * + * @author Tomas Zezula + */ +public final class FastJar { + private final byte[] arr; + + public FastJar(byte[] arr) { + this.arr = arr; + } + + + private static final int GIVE_UP = 1<<16; + + public static final class Entry { + + public final String name; + final long offset; + private final long dosTime; + + Entry (String name, long offset, long time) { + assert name != null; + this.name = name; + this.offset = offset; + this.dosTime = time; + } +/* + public long getTime () { + Date d = new Date((int)(((dosTime >> 25) & 0x7f) + 80), + (int)(((dosTime >> 21) & 0x0f) - 1), + (int)((dosTime >> 16) & 0x1f), + (int)((dosTime >> 11) & 0x1f), + (int)((dosTime >> 5) & 0x3f), + (int)((dosTime << 1) & 0x3e)); + return d.getTime(); + } + */ + } + + public InputStream getInputStream (final Entry e) throws IOException { + return getInputStream(arr, e.offset); + } + + private static InputStream getInputStream (byte[] arr, final long offset) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream(arr); + is.skip(offset); + ZipInputStream in = new ZipInputStream (is); + ZipEntry e = in.getNextEntry(); + if (e != null && e.getCrc() == 0L && e.getMethod() == ZipEntry.STORED) { + int cp = arr.length - is.available(); + return new ByteArrayInputStream(arr, cp, (int)e.getSize()); + } + return in; + } + + public Entry[] list() throws IOException { + final int size = arr.length; + + int at = size - ZipInputStream.ENDHDR; + + byte[] data = new byte[ZipInputStream.ENDHDR]; + int giveup = 0; + + do { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, at, data, 0, data.length); + at--; + giveup++; + if (giveup > GIVE_UP) { + throw new IOException (); + } + } while (getsig(data) != ZipInputStream.ENDSIG); + + + final long censize = endsiz(data); + final long cenoff = endoff(data); + at = (int) cenoff; + + Entry[] result = new Entry[0]; + int cenread = 0; + data = new byte[ZipInputStream.CENHDR]; + while (cenread < censize) { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, at, data, 0, data.length); + at += data.length; + if (getsig(data) != ZipInputStream.CENSIG) { + throw new IOException("No central table"); //NOI18N + } + int cennam = cennam(data); + int cenext = cenext(data); + int cencom = cencom(data); + long lhoff = cenoff(data); + long centim = centim(data); + String name = new String(arr, at, cennam, "UTF-8"); + at += cennam; + int seekby = cenext+cencom; + int cendatalen = ZipInputStream.CENHDR + cennam + seekby; + cenread+=cendatalen; + result = addEntry(result, new Entry(name,lhoff, centim)); + at += seekby; + } + return result; + } + + private Entry[] addEntry(Entry[] result, Entry entry) { + Entry[] e = new Entry[result.length + 1]; + e[result.length] = entry; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(result, 0, e, 0, result.length); + return e; + } + + private static final long getsig(final byte[] b) throws IOException {return get32(b,0);} + private static final long endsiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.ENDSIZ);} + private static final long endoff(final byte[] b) throws IOException {return get32(b,ZipInputStream.ENDOFF);} + private static final long cenlen(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENLEN);} + private static final long censiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENSIZ);} + private static final long centim(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENTIM);} + private static final int cennam(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENNAM);} + private static final int cenext(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENEXT);} + private static final int cencom(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENCOM);} + private static final long cenoff (final byte[] b) throws IOException {return get32(b,ZipInputStream.CENOFF);} + private static final int lochow(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCHOW);} + private static final int locname(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCNAM);} + private static final int locext(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCEXT);} + private static final long locsiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.LOCSIZ);} + + private static final int get16(final byte[] b, int off) throws IOException { + final int b1 = b[off]; + final int b2 = b[off+1]; + return (b1 & 0xff) | ((b2 & 0xff) << 8); + } + + private static final long get32(final byte[] b, int off) throws IOException { + final int s1 = get16(b, off); + final int s2 = get16(b, off+2); + return s1 | ((long)s2 << 16); + } + +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/GZIPHeader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/GZIPHeader.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,215 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; +import java.io.UnsupportedEncodingException; + +/** + * @see "http://www.ietf.org/rfc/rfc1952.txt" + */ +final class GZIPHeader implements Cloneable { + + public static final byte OS_MSDOS = (byte) 0x00; + public static final byte OS_AMIGA = (byte) 0x01; + public static final byte OS_VMS = (byte) 0x02; + public static final byte OS_UNIX = (byte) 0x03; + public static final byte OS_ATARI = (byte) 0x05; + public static final byte OS_OS2 = (byte) 0x06; + public static final byte OS_MACOS = (byte) 0x07; + public static final byte OS_TOPS20 = (byte) 0x0a; + public static final byte OS_WIN32 = (byte) 0x0b; + public static final byte OS_VMCMS = (byte) 0x04; + public static final byte OS_ZSYSTEM = (byte) 0x08; + public static final byte OS_CPM = (byte) 0x09; + public static final byte OS_QDOS = (byte) 0x0c; + public static final byte OS_RISCOS = (byte) 0x0d; + public static final byte OS_UNKNOWN = (byte) 0xff; + + boolean text = false; + private boolean fhcrc = false; + long time; + int xflags; + int os = 255; + byte[] extra; + byte[] name; + byte[] comment; + int hcrc; + long crc; + boolean done = false; + long mtime = 0; + + public void setModifiedTime(long mtime) { + this.mtime = mtime; + } + + public long getModifiedTime() { + return mtime; + } + + public void setOS(int os) { + if((0<=os && os <=13) || os==255) + this.os=os; + else + throw new IllegalArgumentException("os: "+os); + } + + public int getOS(){ + return os; + } + + public void setName(String name) { + try{ + this.name=name.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e){ + throw new IllegalArgumentException("name must be in ISO-8859-1 "+name); + } + } + + public String getName(){ + if(name==null) return ""; + try { + return new String(name, "ISO-8859-1"); + } + catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.toString()); + } + } + + public void setComment(String comment) { + try{ + this.comment=comment.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e){ + throw new IllegalArgumentException("comment must be in ISO-8859-1 "+name); + } + } + + public String getComment(){ + if(comment==null) return ""; + try { + return new String(comment, "ISO-8859-1"); + } + catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.toString()); + } + } + + public void setCRC(long crc){ + this.crc = crc; + } + + public long getCRC(){ + return crc; + } +/* + void put(Deflate d){ + int flag = 0; + if(text){ + flag |= 1; // FTEXT + } + if(fhcrc){ + flag |= 2; // FHCRC + } + if(extra!=null){ + flag |= 4; // FEXTRA + } + if(name!=null){ + flag |= 8; // FNAME + } + if(comment!=null){ + flag |= 16; // FCOMMENT + } + int xfl = 0; + if(d.level == JZlib.Z_BEST_SPEED){ + xfl |= 4; + } + else if (d.level == JZlib.Z_BEST_COMPRESSION){ + xfl |= 2; + } + + d.put_short((short)0x8b1f); // ID1 ID2 + d.put_byte((byte)8); // CM(Compression Method) + d.put_byte((byte)flag); + d.put_byte((byte)mtime); + d.put_byte((byte)(mtime>>8)); + d.put_byte((byte)(mtime>>16)); + d.put_byte((byte)(mtime>>24)); + d.put_byte((byte)xfl); + d.put_byte((byte)os); + + if(extra!=null){ + d.put_byte((byte)extra.length); + d.put_byte((byte)(extra.length>>8)); + d.put_byte(extra, 0, extra.length); + } + + if(name!=null){ + d.put_byte(name, 0, name.length); + d.put_byte((byte)0); + } + + if(comment!=null){ + d.put_byte(comment, 0, comment.length); + d.put_byte((byte)0); + } + } +*/ + @Override + public Object clone() throws CloneNotSupportedException { + GZIPHeader gheader = (GZIPHeader)super.clone(); + byte[] tmp; + if(gheader.extra!=null){ + tmp=new byte[gheader.extra.length]; + System.arraycopy(gheader.extra, 0, tmp, 0, tmp.length); + gheader.extra = tmp; + } + + if(gheader.name!=null){ + tmp=new byte[gheader.name.length]; + System.arraycopy(gheader.name, 0, tmp, 0, tmp.length); + gheader.name = tmp; + } + + if(gheader.comment!=null){ + tmp=new byte[gheader.comment.length]; + System.arraycopy(gheader.comment, 0, tmp, 0, tmp.length); + gheader.comment = tmp; + } + + return gheader; + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfBlocks.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfBlocks.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,616 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class InfBlocks{ + static final private int MANY=1440; + + // And'ing with mask[n] masks the lower n bits + static final private int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static final int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + static final private int TYPE=0; // get type bits (3, including end bit) + static final private int LENS=1; // get lengths for stored + static final private int STORED=2;// processing stored block + static final private int TABLE=3; // get table lengths + static final private int BTREE=4; // get bit lengths tree for a dynamic block + static final private int DTREE=5; // get length, distance trees for a dynamic block + static final private int CODES=6; // processing fixed or dynamic block + static final private int DRY=7; // output remaining window bytes + static final private int DONE=8; // finished last block, done + static final private int BAD=9; // ot a data error--stuck here + + int mode; // current inflate_block mode + + int left; // if STORED, bytes left to copy + + int table; // table lengths (14 bits) + int index; // index into blens (or border) + int[] blens; // bit lengths of codes + int[] bb=new int[1]; // bit length tree depth + int[] tb=new int[1]; // bit length decoding tree + + int[] bl=new int[1]; + int[] bd=new int[1]; + + int[][] tl=new int[1][]; + int[][] td=new int[1][]; + int[] tli=new int[1]; // tl_index + int[] tdi=new int[1]; // td_index + + private final InfCodes codes; // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + int bitk; // bits in bit buffer + int bitb; // bit buffer + int[] hufts; // single malloc for tree space + byte[] window; // sliding window + int end; // one byte after sliding window + int read; // window read pointer + int write; // window write pointer + private boolean check; + + private final InfTree inftree=new InfTree(); + + private final ZStream z; + + InfBlocks(ZStream z, int w){ + this.z=z; + this.codes=new InfCodes(this.z, this); + hufts=new int[MANY*3]; + window=new byte[w]; + end=w; + this.check = (z.istate.wrap==0) ? false : true; + mode = TYPE; + reset(); + } + + void reset(){ + if(mode==BTREE || mode==DTREE){ + } + if(mode==CODES){ + codes.free(z); + } + mode=TYPE; + bitk=0; + bitb=0; + read=write=0; + if(check){ + z.adler.reset(); + } + } + + int proc(int r){ + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + {p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;} + {q=write;m=(int)(q>> 1){ + case 0: // stored + {b>>>=(3);k-=(3);} + t = k & 7; // go to byte boundary + + {b>>>=(t);k-=(t);} + mode = LENS; // get length of stored block + break; + case 1: // fixed + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); + + {b>>>=(3);k-=(3);} + + mode = CODES; + break; + case 2: // dynamic + + {b>>>=(3);k-=(3);} + + mode = TABLE; + break; + case 3: // illegal + + {b>>>=(3);k-=(3);} + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + break; + case LENS: + + while(k<(32)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>> 16) & 0xffff) != (b & 0xffff)){ + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); + break; + case STORED: + if (n == 0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + if(m==0){ + if(q==end&&read!=0){ + q=0; m=(int)(qn) t = n; + if(t>m) t = m; + System.arraycopy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last!=0 ? DRY : TYPE; + break; + case TABLE: + + while(k<(14)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)< 29 || ((t >> 5) & 0x1f) > 29) + { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if(blens==null || blens.length>>=(14);k-=(14);} + + index = 0; + mode = BTREE; + case BTREE: + while (index < 4 + (table >>> 10)){ + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(3);k-=(3);} + } + + while(index < 19){ + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK){ + r = t; + if (r == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + index = 0; + mode = DTREE; + case DTREE: + while (true){ + t = table; + if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ + break; + } + + int[] h; + int i, j, c; + + t = bb[0]; + + while(k<(t)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); + blens[index++] = c; + } + else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while(k<(t+i)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); + + j += (b & inflate_mask[i]); + + b>>>=(i);k-=(i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)){ + blens=null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + c = c == 16 ? blens[i-1] : 0; + do{ + blens[i++] = c; + } + while (--j!=0); + index = i; + } + } + + tb[0]=-1; + { + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tli, tdi, hufts, z); + + if (t != Z_OK){ + if (t == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + r = t; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + codes.init(bl[0], bd[0], hufts, tli[0], hufts, tdi[0]); + } + mode = CODES; + case CODES: + bitb=b; bitk=k; + z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + + if ((r = codes.proc(r)) != Z_STREAM_END){ + return inflate_flush(r); + } + r = Z_OK; + codes.free(z); + + p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; + q=write;m=(int)(q z.avail_out) n = z.avail_out; + if(n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(check && n>0){ + z.adler.update(window, q, n); + } + + // copy as far as end of window + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end){ + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(check && n>0){ + z.adler.update(window, q, n); + } + + // copy + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfCodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfCodes.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,612 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class InfCodes{ + + static final private int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + static final private int START=0; // x: set up for LEN + static final private int LEN=1; // i: get length/literal/eob next + static final private int LENEXT=2; // i: getting length extra (have base) + static final private int DIST=3; // i: get distance next + static final private int DISTEXT=4;// i: getting distance extra + static final private int COPY=5; // o: copying bytes in window, waiting for space + static final private int LIT=6; // o: got literal, waiting for output space + static final private int WASH=7; // o: got eob, possibly still output waiting + static final private int END=8; // x: got eob and all data flushed + static final private int BADCODE=9;// x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index=0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + private final ZStream z; + private final InfBlocks s; + InfCodes(ZStream z, InfBlocks s){ + this.z=z; + this.s=s; + } + + void init(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index){ + mode=START; + lbits=(byte)bl; + dbits=(byte)bd; + ltree=tl; + ltree_index=tl_index; + dtree = td; + dtree_index=td_index; + tree=null; + } + + int proc(int r){ + int j; // temporary storage + int[] t; // temporary pointer + int tindex; // temporary pointer + int e; // extra bits or operation + int b=0; // bit buffer + int k=0; // bits in bit buffer + int p=0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q= 258 && n >= 10){ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q>>=(tree[tindex+1]); + k-=(tree[tindex+1]); + + e=tree[tindex]; + + if(e == 0){ // literal + lit = tree[tindex+2]; + mode = LIT; + break; + } + if((e & 16)!=0 ){ // length + get = e & 15; + len = tree[tindex+2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3+tree[tindex+2]; + break; + } + if ((e & 32)!=0){ // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + need = dbits; + tree = dtree; + tree_index=dtree_index; + mode = DIST; + case DIST: // i: get distance next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=tree[tindex+1]; + k-=tree[tindex+1]; + + e = (tree[tindex]); + if((e & 16)!=0){ // distance + get = e & 15; + dist = tree[tindex+2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3 + tree[tindex+2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + + case DISTEXT: // i: getting distance extra + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while(f < 0){ // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len!=0){ + + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q 7){ // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write=q; r=s.inflate_flush(r); + q=s.write;m=q= 258 && n >= 10 + // get literal/length code + while(k<(20)){ // max bits for literal/length code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++] = (byte)tp[tp_index_t_3+2]; + m--; + continue; + } + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + e &= 15; + c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); + + b>>=e; k-=e; + + // decode distance base of block to copy + while(k<(15)){ // max bits for distance code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + // get extra bits to add to distance base + e &= 15; + while(k<(e)){ // get extra bits (up to 13) + n--; + b|=(z.next_in[p++]&0xff)<>=(e); k-=(e); + + // do the copy + m -= c; + if (q >= d){ // offset before dest + // just copy + r=q-d; + if(q-r>0 && 2>(q-r)){ + s.window[q++]=s.window[r++]; // minimum count is three, + s.window[q++]=s.window[r++]; // so unroll loop a little + c-=2; + } + else{ + System.arraycopy(s.window, r, s.window, q, 2); + q+=2; r+=2; c-=2; + } + } + else{ // else offset after destination + r=q-d; + do{ + r+=s.end; // force pointer in window + }while(r<0); // covers invalid distances + e=s.end-r; + if(c>e){ // if source crosses, + c-=e; // wrapped copy + if(q-r>0 && e>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--e!=0); + } + else{ + System.arraycopy(s.window, r, s.window, q, e); + q+=e; r+=e; e=0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if(q-r>0 && c>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--c!=0); + } + else{ + System.arraycopy(s.window, r, s.window, q, c); + q+=c; r+=c; c=0; + } + break; + } + else if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + e=tp[tp_index_t_3]; + } + else{ + z.msg = "invalid distance code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + break; + } + + if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + if((e=tp[tp_index_t_3])==0){ + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++]=(byte)tp[tp_index_t_3+2]; + m--; + break; + } + } + else if((e&32)!=0){ + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_STREAM_END; + } + else{ + z.msg="invalid literal/length code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + } + while(m>=258 && n>= 10); + + // not enough input or output--restore pointers and return + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_OK; + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfTree.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,520 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class InfTree{ + + static final private int MANY=1440; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + static final int fixed_bl = 9; + static final int fixed_bd = 5; + + static final int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static final int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static final int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static final int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static final int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static final int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + static final int BMAX=15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ){ + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do { + c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX + }while(i!=0); + + if(c[0] == n){ // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if(c[j]!=0) break; + k = j; // minimum code length + if(l < j){ + l = j; + } + for (i = BMAX; i!=0; i--){ + if(c[i]!=0) break; + } + g = i; // maximum code length + if(l > i){ + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1){ + if ((y -= c[j]) < 0){ + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0){ + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i!=0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do { + if ((j = b[bindex+p]) != 0){ + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++){ + a = c[k]; + while (a--!=0){ + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l){ + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if((f=1<<(j=k-w))>a+1){ // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if(j < z){ + while (++j < z){ // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if(h!=0){ + x[h]=i; // save pattern for backing up + r[0]=(byte)j; // bits in this table + r[1]=(byte)l; // bits to dump before this table + j=i>>>(w - l); + r[2] = (int)(q - u[h-1] - j); // offset to this table + System.arraycopy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table + } + else{ + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n){ + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s){ + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else{ + r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists + r[2]=d[v[p++] - s]; + } + + // fill code-like entries with r + f=1<<(k-w); + for (j=i>>>w;j>>= 1){ + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]){ + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + initWorkArea(19); + hn[0]=0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if(result == Z_BUF_ERROR || bb[0] == 0){ + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + + // build literal/length tree + initWorkArea(288); + hn[0]=0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0){ + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR){ + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)){ + if (result == Z_DATA_ERROR){ + z.msg = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR){ + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ){ + bl[0]=fixed_bl; + bd[0]=fixed_bd; + tl[0]=fixed_tl; + td[0]=fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize){ + if(hn==null){ + hn=new int[1]; + v=new int[vsize]; + c=new int[BMAX+1]; + r=new int[3]; + u=new int[BMAX]; + x=new int[BMAX+1]; + } + if(v.length> 4) + 1; + if(w < 48) + w &= 15; + } + + if(w<8 ||w>15){ + inflateEnd(); + return Z_STREAM_ERROR; + } + if(blocks != null && wbits != w){ + blocks.free(); + blocks=null; + } + + // set window size + wbits=w; + + this.blocks=new InfBlocks(z, 1<>8))&0xff; + + if((wrap&1)==0 || // check if zlib header allowed + (((this.method << 8)+b) % 31)!=0){ + this.mode = BAD; + z.msg = "incorrect header check"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + if((this.method&0xf)!=Z_DEFLATED){ + this.mode = BAD; + z.msg="unknown compression method"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + if((this.method>>4)+8>this.wbits){ + this.mode = BAD; + z.msg="invalid window size"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + z.adler=new Adler32(); + + if((b&PRESET_DICT)==0){ + this.mode = BLOCKS; + break; + } + this.mode = DICT4; + case DICT4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + this.mode=DICT3; + case DICT3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + this.mode=DICT2; + case DICT2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + this.mode=DICT1; + case DICT1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need += (z.next_in[z.next_in_index++]&0xffL); + z.adler.reset(this.need); + this.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + this.mode = BAD; + z.msg = "need dictionary"; + this.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + r = this.blocks.proc(r); + if(r == Z_DATA_ERROR){ + this.mode = BAD; + this.marker = 0; // can try inflateSync + break; + } + if(r == Z_OK){ + r = f; + } + if(r != Z_STREAM_END){ + return r; + } + r = f; + this.was=z.adler.getValue(); + this.blocks.reset(); + if(this.wrap==0){ + this.mode=DONE; + break; + } + this.mode=CHECK4; + case CHECK4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + this.mode=CHECK3; + case CHECK3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + this.mode = CHECK2; + case CHECK2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + this.mode = CHECK1; + case CHECK1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=(z.next_in[z.next_in_index++]&0xffL); + + if(flags!=0){ // gzip + this.need = ((this.need&0xff000000)>>24 | + (this.need&0x00ff0000)>>8 | + (this.need&0x0000ff00)<<8 | + (this.need&0x0000ffff)<<24)&0xffffffffL; + } + + if(((int)(this.was)) != ((int)(this.need))){ + z.msg = "incorrect data check"; + // chack is delayed + /* + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + */ + } + else if(flags!=0 && gheader!=null){ + gheader.crc = this.need; + } + + this.mode = LENGTH; + case LENGTH: + if (wrap!=0 && flags!=0) { + + try { r=readBytes(4, r, f); } + catch(Return e){ return e.r; } + + if(z.msg!=null && z.msg.equals("incorrect data check")){ + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + + if (this.need != (z.total_out & 0xffffffffL)) { + z.msg = "incorrect length check"; + this.mode = BAD; + break; + } + z.msg = null; + } + else { + if(z.msg!=null && z.msg.equals("incorrect data check")){ + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + } + + this.mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + + case FLAGS: + + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + + flags = ((int)this.need)&0xffff; + + if ((flags & 0xff) != Z_DEFLATED) { + z.msg = "unknown compression method"; + this.mode = BAD; + break; + } + if ((flags & 0xe000)!=0) { + z.msg = "unknown header flags set"; + this.mode = BAD; + break; + } + + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + + this.mode = TIME; + + case TIME: + try { r=readBytes(4, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null) + gheader.time = this.need; + if ((flags & 0x0200)!=0){ + checksum(4, this.need); + } + this.mode = OS; + case OS: + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.xflags = ((int)this.need)&0xff; + gheader.os = (((int)this.need)>>8)&0xff; + } + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + this.mode = EXLEN; + case EXLEN: + if ((flags & 0x0400)!=0) { + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.extra = new byte[((int)this.need)&0xffff]; + } + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + } + else if(gheader!=null){ + gheader.extra=null; + } + this.mode = EXTRA; + + case EXTRA: + if ((flags & 0x0400)!=0) { + try { + r=readBytes(r, f); + if(gheader!=null){ + byte[] foo = tmp_array; + tmp_array=null; + if(foo.length == gheader.extra.length){ + System.arraycopy(foo, 0, gheader.extra, 0, foo.length); + } + else{ + z.msg = "bad extra field length"; + this.mode = BAD; + break; + } + } + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.extra=null; + } + this.mode = NAME; + case NAME: + if ((flags & 0x0800)!=0) { + try { + r=readString(r, f); + if(gheader!=null){ + gheader.name=tmp_array; + } + tmp_array=null; + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.name=null; + } + this.mode = COMMENT; + case COMMENT: + if ((flags & 0x1000)!=0) { + try { + r=readString(r, f); + if(gheader!=null){ + gheader.comment=tmp_array; + } + tmp_array=null; + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.comment=null; + } + this.mode = HCRC; + case HCRC: + if ((flags & 0x0200)!=0) { + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.hcrc=(int)(this.need&0xffff); + } + if(this.need != (z.adler.getValue()&0xffffL)){ + this.mode = BAD; + z.msg = "header crc mismatch"; + this.marker = 5; // can't try inflateSync + break; + } + } + z.adler = new CRC32(); + + this.mode = BLOCKS; + break; + default: + return Z_STREAM_ERROR; + } + } + } + + int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(z==null || (this.mode != DICT0 && this.wrap != 0)){ + return Z_STREAM_ERROR; + } + + int index=0; + int length = dictLength; + + if(this.mode==DICT0){ + long adler_need=z.adler.getValue(); + z.adler.reset(); + z.adler.update(dictionary, 0, dictLength); + if(z.adler.getValue()!=adler_need){ + return Z_DATA_ERROR; + } + } + + z.adler.reset(); + + if(length >= (1<0){ + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + this.need = this.need | + ((z.next_in[z.next_in_index++]&0xff)<<((n-need_bytes)*8)); + need_bytes--; + } + if(n==2){ + this.need&=0xffffL; + } + else if(n==4) { + this.need&=0xffffffffL; + } + need_bytes=-1; + return r; + } + class Return extends Exception{ + int r; + Return(int r){this.r=r; } + } + + private byte[] tmp_array; + private int readString(int r, int f) throws Return{ + int b=0; + byte[] arr = new byte[4092]; + int at = 0; + do { + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + b = z.next_in[z.next_in_index]; + if(b!=0) arr = append(arr, z.next_in[z.next_in_index], at++); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + }while(b!=0); + + tmp_array = copy(arr, at); + + return r; + } + + private int readBytes(int r, int f) throws Return{ + int b=0; + byte[] arr = new byte[4092]; + int at = 0; + while(this.need>0){ + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + b = z.next_in[z.next_in_index]; + arr = append(arr, z.next_in[z.next_in_index], at++); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + this.need--; + } + + tmp_array = copy(arr, at); + + return r; + } + + private static byte[] copy(byte[] arr, int len) { + byte[] ret = new byte[len]; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, 0, ret, 0, len); + return ret; + } + private static byte[] append(byte[] arr, byte b, int index) { + arr[index] = b; + return arr; + } + + private void checksum(int n, long v){ + for(int i=0; i>=8; + } + z.adler.update(crcbuf, 0, n); + } + + public GZIPHeader getGZIPHeader(){ + return gheader; + } + + boolean inParsingHeader(){ + switch(mode){ + case HEAD: + case DICT4: + case DICT3: + case DICT2: + case DICT1: + case FLAGS: + case TIME: + case OS: + case EXLEN: + case EXTRA: + case NAME: + case COMMENT: + case HCRC: + return true; + default: + return false; + } + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Inflater.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Inflater.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import java.util.zip.*; +import java.io.IOException; + +/** + * This class provides support for general purpose decompression using the + * popular ZLIB compression library. The ZLIB compression library was + * initially developed as part of the PNG graphics standard and is not + * protected by patents. It is fully described in the specifications at + * the java.util.zip + * package description. + * + *

The following code fragment demonstrates a trivial compression + * and decompression of a string using Deflater and + * Inflater. + * + *

+ * try {
+ *     // Encode a String into bytes
+ *     String inputString = "blahblahblah\u20AC\u20AC";
+ *     byte[] input = inputString.getBytes("UTF-8");
+ *
+ *     // Compress the bytes
+ *     byte[] output = new byte[100];
+ *     Deflater compresser = new Deflater();
+ *     compresser.setInput(input);
+ *     compresser.finish();
+ *     int compressedDataLength = compresser.deflate(output);
+ *
+ *     // Decompress the bytes
+ *     Inflater decompresser = new Inflater();
+ *     decompresser.setInput(output, 0, compressedDataLength);
+ *     byte[] result = new byte[100];
+ *     int resultLength = decompresser.inflate(result);
+ *     decompresser.end();
+ *
+ *     // Decode the bytes into a String
+ *     String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch(java.io.UnsupportedEncodingException ex) {
+ *     // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ *     // handle
+ * }
+ * 
+ * + * @see Deflater + * @author David Connelly + * + */ +public +class Inflater extends java.util.zip.Inflater { + private final boolean nowrap; + private JzLibInflater impl; + + /** + * Creates a new decompressor. If the parameter 'nowrap' is true then + * the ZLIB header and checksum fields will not be used. This provides + * compatibility with the compression format used by both GZIP and PKZIP. + *

+ * Note: When using the 'nowrap' option it is also necessary to provide + * an extra "dummy" byte as input. This is required by the ZLIB native + * library in order to support certain optimizations. + * + * @param nowrap if true then support GZIP compatible compression + */ + public Inflater(boolean nowrap) { + this.nowrap = nowrap; + reset(); + } + + /** + * Creates a new decompressor. + */ + public Inflater() { + this(false); + } + + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @param off the start offset of the input data + * @param len the length of the input data + * @see Inflater#needsInput + */ + public void setInput(byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + impl.setInput(b, off, len, false); + } + + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @see Inflater#needsInput + */ + public void setInput(byte[] b) { + setInput(b, 0, b.length); + } + + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @param off the start offset of the data + * @param len the length of the data + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + byte[] arr; + if (off == 0) { + arr = b; + } else { + arr = new byte[len]; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(b, off, arr, 0, len); + } + impl.setDictionary(arr, len); + } + + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b) { + impl.setDictionary(b, b.length); + } + + /** + * Returns the total number of bytes remaining in the input buffer. + * This can be used to find out what bytes still remain in the input + * buffer after decompression has finished. + * @return the total number of bytes remaining in the input buffer + */ + public int getRemaining() { + return impl.getAvailIn(); + } + + /** + * Returns true if no data remains in the input buffer. This can + * be used to determine if #setInput should be called in order + * to provide more input. + * @return true if no data remains in the input buffer + */ + public boolean needsInput() { + return getRemaining() <= 0; + } + + /** + * Returns true if a preset dictionary is needed for decompression. + * @return true if a preset dictionary is needed for decompression + * @see Inflater#setDictionary + */ + public boolean needsDictionary() { + return impl.needDict(); + } + + /** + * Returns true if the end of the compressed data stream has been + * reached. + * @return true if the end of the compressed data stream has been + * reached + */ + public boolean finished() { + return impl.finished(); + } + + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @param off the start offset of the data + * @param len the maximum number of uncompressed bytes + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b, int off, int len) + throws DataFormatException + { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + impl.setOutput(b, off, len); + int err = impl.inflate(JzLibInflater.Z_NO_FLUSH); + return impl.next_out_index - off; + } + + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b) throws DataFormatException { + return inflate(b, 0, b.length); + } + + /** + * Returns the ADLER-32 value of the uncompressed data. + * @return the ADLER-32 value of the uncompressed data + */ + public int getAdler() { + return (int) impl.getAdler(); + } + + /** + * Returns the total number of compressed bytes input so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of compressed bytes input so far + */ + public int getTotalIn() { + return (int) getBytesRead(); + } + + /** + * Returns the total number of compressed bytes input so far.

+ * + * @return the total (non-negative) number of compressed bytes input so far + * @since 1.5 + */ + public long getBytesRead() { + return impl.total_in; + } + + /** + * Returns the total number of uncompressed bytes output so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of uncompressed bytes output so far + */ + public int getTotalOut() { + return (int) getBytesWritten(); + } + + /** + * Returns the total number of uncompressed bytes output so far.

+ * + * @return the total (non-negative) number of uncompressed bytes output so far + * @since 1.5 + */ + public long getBytesWritten() { + return impl.total_out; + } + + /** + * Resets inflater so that a new set of input data can be processed. + */ + public void reset() { + impl = new JzLibInflater(15, nowrap); + } + + /** + * Closes the decompressor and discards any unprocessed input. + * This method should be called when the decompressor is no longer + * being used, but will also be called automatically by the finalize() + * method. Once this method is called, the behavior of the Inflater + * object is undefined. + */ + public void end() { + impl.end(); + } + + /** + * Closes the decompressor when garbage is collected. + */ + protected void finalize() { + end(); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/JzLibInflater.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/JzLibInflater.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,137 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +final class JzLibInflater extends ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + public static final int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + public JzLibInflater() { + super(); + init(); + } + + public JzLibInflater(int w) { + this(w, false); + } + + public JzLibInflater(int w, boolean nowrap) { + super(); + int ret = init(w, nowrap); + if(ret!=Z_OK) + throw new IllegalStateException(ret+": "+msg); + } + + private boolean finished = false; + + public int init(){ + return init(DEF_WBITS); + } + + public int init(boolean nowrap){ + return init(DEF_WBITS, nowrap); + } + + public int init(int w){ + return init(w, false); + } + + public int init(int w, boolean nowrap){ + finished = false; + istate=new Inflate(this); + return istate.inflateInit(nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + int ret = istate.inflate(f); + if(ret == Z_STREAM_END) + finished = true; + return ret; + } + + public int end(){ + finished = true; + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(); +// istate = null; + return ret; + } + + public int sync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + + public int syncPoint(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + + public int setDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + + public boolean finished(){ + return istate.mode==12 /*DONE*/; + } + + public boolean needDict() { + return istate == null ? false : istate.mode == Inflate.DICT0; + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZStream.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,253 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * ZStream + * + * @deprecated Not for public use in the future. + */ +@Deprecated +class ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + static final private int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + public byte[] next_in; // next input byte + public int next_in_index; + public int avail_in; // number of bytes available at next_in + public long total_in; // total nb of input bytes read so far + + public byte[] next_out; // next output byte should be put there + public int next_out_index; + public int avail_out; // remaining free space at next_out + public long total_out; // total nb of bytes output so far + + public String msg; + + Inflate istate; + + int data_type; // best guess about the data type: ascii or binary + + Checksum adler; + + public ZStream(){ + this(new Adler32()); + } + + public ZStream(Checksum adler){ + this.adler=adler; + } + + public int inflateInit(){ + return inflateInit(DEF_WBITS); + } + public int inflateInit(boolean nowrap){ + return inflateInit(DEF_WBITS, nowrap); + } + public int inflateInit(int w){ + return inflateInit(w, false); + } + + public int inflateInit(int w, boolean nowrap){ + istate=new Inflate(this); + return istate.inflateInit(nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + return istate.inflate(f); + } + public int inflateEnd(){ + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(); +// istate = null; + return ret; + } + + public int inflateSync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + public int inflateSyncPoint(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + public int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + public boolean inflateFinished(){ + return istate.mode==12 /*DONE*/; + } + + + public long getAdler(){ + return adler.getValue(); + } + + public void free(){ + next_in=null; + next_out=null; + msg=null; + } + + public void setOutput(byte[] buf){ + setOutput(buf, 0, buf.length); + } + + public void setOutput(byte[] buf, int off, int len){ + next_out = buf; + next_out_index = off; + avail_out = len; + } + + public void setInput(byte[] buf){ + setInput(buf, 0, buf.length, false); + } + + public void setInput(byte[] buf, boolean append){ + setInput(buf, 0, buf.length, append); + } + + public void setInput(byte[] buf, int off, int len, boolean append){ + if(len<=0 && append && next_in!=null) return; + + if(avail_in>0 && append){ + byte[] tmp = new byte[avail_in+len]; + System.arraycopy(next_in, next_in_index, tmp, 0, avail_in); + System.arraycopy(buf, off, tmp, avail_in, len); + next_in=tmp; + next_in_index=0; + avail_in+=len; + } + else{ + next_in=buf; + next_in_index=off; + avail_in=len; + } + } + + public byte[] getNextIn(){ + return next_in; + } + + public void setNextIn(byte[] next_in){ + this.next_in = next_in; + } + + public int getNextInIndex(){ + return next_in_index; + } + + public void setNextInIndex(int next_in_index){ + this.next_in_index = next_in_index; + } + + public int getAvailIn(){ + return avail_in; + } + + public void setAvailIn(int avail_in){ + this.avail_in = avail_in; + } + + public byte[] getNextOut(){ + return next_out; + } + + public void setNextOut(byte[] next_out){ + this.next_out = next_out; + } + + public int getNextOutIndex(){ + return next_out_index; + } + + public void setNextOutIndex(int next_out_index){ + this.next_out_index = next_out_index; + } + + public int getAvailOut(){ + return avail_out; + + } + + public void setAvailOut(int avail_out){ + this.avail_out = avail_out; + } + + public long getTotalOut(){ + return total_out; + } + + public long getTotalIn(){ + return total_in; + } + + public String getMessage(){ + return msg; + } + + /** + * Those methods are expected to be override by Inflater and Deflater. + * In the future, they will become abstract methods. + */ + public int end(){ return Z_OK; } + public boolean finished(){ return false; } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipConstants64.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipConstants64.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1995, 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +/* + * This class defines the constants that are used by the classes + * which manipulate Zip64 files. + */ + +public class ZipConstants64 { + + /* + * ZIP64 constants + */ + static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006" + static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007" + static final int ZIP64_ENDHDR = 56; // ZIP64 end header size + static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size + static final int ZIP64_EXTHDR = 24; // EXT header size + static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID + + static final int ZIP64_MAGICCOUNT = 0xFFFF; + static final long ZIP64_MAGICVAL = 0xFFFFFFFFL; + + /* + * Zip64 End of central directory (END) header field offsets + */ + static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir + static final int ZIP64_ENDVEM = 12; // version made by + static final int ZIP64_ENDVER = 14; // version needed to extract + static final int ZIP64_ENDNMD = 16; // number of this disk + static final int ZIP64_ENDDSK = 20; // disk number of start + static final int ZIP64_ENDTOD = 24; // total number of entries on this disk + static final int ZIP64_ENDTOT = 32; // total number of entries + static final int ZIP64_ENDSIZ = 40; // central directory size in bytes + static final int ZIP64_ENDOFF = 48; // offset of first CEN header + static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector + + /* + * Zip64 End of central directory locator field offsets + */ + static final int ZIP64_LOCDSK = 4; // disk number start + static final int ZIP64_LOCOFF = 8; // offset of zip64 end + static final int ZIP64_LOCTOT = 16; // total number of disks + + /* + * Zip64 Extra local (EXT) header field offsets + */ + static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value + static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte + static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte + + /* + * Language encoding flag EFS + */ + static final int EFS = 0x800; // If this bit is set the filename and + // comment fields for this file must be + // encoded using UTF-8. + + private ZipConstants64() {} +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipInputStream.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,468 @@ +/* + * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import java.util.zip.*; +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; +import java.io.PushbackInputStream; +import static org.apidesign.bck2brwsr.emul.zip.ZipConstants64.*; +import static java.util.zip.ZipInputStream.*; + +/** + * This class implements an input stream filter for reading files in the + * ZIP file format. Includes support for both compressed and uncompressed + * entries. + * + * @author David Connelly + */ +public +class ZipInputStream extends InflaterInputStream { + private ZipEntry entry; + private int flag; + private CRC32 crc = new CRC32(); + private long remaining; + private byte[] tmpbuf = new byte[512]; + + private static final int STORED = ZipEntry.STORED; + private static final int DEFLATED = ZipEntry.DEFLATED; + + private boolean closed = false; + // this flag is set to true after EOF has reached for + // one entry + private boolean entryEOF = false; + + /** + * Check to make sure that this stream has not been closed + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + } + + /** + * Creates a new ZIP input stream. + * + *

The UTF-8 {@link java.nio.charset.Charset charset} is used to + * decode the entry names. + * + * @param in the actual input stream + */ + public ZipInputStream(InputStream in) { +// this(in, "UTF-8"); + super(new PushbackInputStream(in, 512), new Inflater(true), 512); + //usesDefaultInflater = true; + if(in == null) { + throw new NullPointerException("in is null"); + } + } + + /** + * Creates a new ZIP input stream. + * + * @param in the actual input stream + * + * @param charset + * The {@linkplain java.nio.charset.Charset charset} to be + * used to decode the ZIP entry name (ignored if the + * language + * encoding bit of the ZIP entry's general purpose bit + * flag is set). + * + * @since 1.7 + * + public ZipInputStream(InputStream in, Charset charset) { + super(new PushbackInputStream(in, 512), new Inflater(true), 512); + usesDefaultInflater = true; + if(in == null) { + throw new NullPointerException("in is null"); + } + if (charset == null) + throw new NullPointerException("charset is null"); + this.zc = ZipCoder.get(charset); + } + */ + + /** + * Reads the next ZIP file entry and positions the stream at the + * beginning of the entry data. + * @return the next ZIP file entry, or null if there are no more entries + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public ZipEntry getNextEntry() throws IOException { + ensureOpen(); + if (entry != null) { + closeEntry(); + } + crc.reset(); + inf.reset(); + if ((entry = readLOC()) == null) { + return null; + } + if (entry.getMethod() == STORED) { + remaining = entry.getSize(); + } + entryEOF = false; + return entry; + } + + /** + * Closes the current ZIP entry and positions the stream for reading the + * next entry. + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public void closeEntry() throws IOException { + ensureOpen(); + while (read(tmpbuf, 0, tmpbuf.length) != -1) ; + entryEOF = true; + } + + /** + * Returns 0 after EOF has reached for the current entry data, + * otherwise always return 1. + *

+ * Programs should not count on this method to return the actual number + * of bytes that could be read without blocking. + * + * @return 1 before EOF and 0 after EOF has reached for current entry. + * @exception IOException if an I/O error occurs. + * + */ + public int available() throws IOException { + ensureOpen(); + if (entryEOF) { + return 0; + } else { + return 1; + } + } + + /** + * Reads from the current ZIP entry into an array of bytes. + * If len is not zero, the method + * blocks until some input is available; otherwise, no + * bytes are read and 0 is returned. + * @param b the buffer into which the data is read + * @param off the start offset in the destination array b + * @param len the maximum number of bytes read + * @return the actual number of bytes read, or -1 if the end of the + * entry is reached + * @exception NullPointerException if b is null. + * @exception IndexOutOfBoundsException if off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public int read(byte[] b, int off, int len) throws IOException { + ensureOpen(); + if (off < 0 || len < 0 || off > b.length - len) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + if (entry == null) { + return -1; + } + switch (entry.getMethod()) { + case DEFLATED: + len = super.read(b, off, len); + if (len == -1) { + readEnd(entry); + entryEOF = true; + entry = null; + } else { + crc.update(b, off, len); + } + return len; + case STORED: + if (remaining <= 0) { + entryEOF = true; + entry = null; + return -1; + } + if (len > remaining) { + len = (int)remaining; + } + len = in.read(b, off, len); + if (len == -1) { + throw new ZipException("unexpected EOF"); + } + crc.update(b, off, len); + remaining -= len; + if (remaining == 0 && entry.getCrc() != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(entry.getCrc()) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } + return len; + default: + throw new ZipException("invalid compression method"); + } + } + + /** + * Skips specified number of bytes in the current ZIP entry. + * @param n the number of bytes to skip + * @return the actual number of bytes skipped + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + * @exception IllegalArgumentException if n < 0 + */ + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + ensureOpen(); + int max = (int)Math.min(n, Integer.MAX_VALUE); + int total = 0; + while (total < max) { + int len = max - total; + if (len > tmpbuf.length) { + len = tmpbuf.length; + } + len = read(tmpbuf, 0, len); + if (len == -1) { + entryEOF = true; + break; + } + total += len; + } + return total; + } + + /** + * Closes this input stream and releases any system resources associated + * with the stream. + * @exception IOException if an I/O error has occurred + */ + public void close() throws IOException { + if (!closed) { + super.close(); + closed = true; + } + } + + private byte[] b = new byte[256]; + + /* + * Reads local file (LOC) header for next entry. + */ + private ZipEntry readLOC() throws IOException { + try { + readFully(tmpbuf, 0, LOCHDR); + } catch (EOFException e) { + return null; + } + if (get32(tmpbuf, 0) != LOCSIG) { + return null; + } + // get flag first, we need check EFS. + flag = get16(tmpbuf, LOCFLG); + // get the entry name and create the ZipEntry first + int len = get16(tmpbuf, LOCNAM); + int blen = b.length; + if (len > blen) { + do + blen = blen * 2; + while (len > blen); + b = new byte[blen]; + } + readFully(b, 0, len); + // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8 + ZipEntry e = createZipEntry(((flag & EFS) != 0) + ? toStringUTF8(b, len) + : toString(b, len)); + // now get the remaining fields for the entry + if ((flag & 1) == 1) { + throw new ZipException("encrypted ZIP entry not supported"); + } + e.setMethod(get16(tmpbuf, LOCHOW)); + e.setTime(get32(tmpbuf, LOCTIM)); + if ((flag & 8) == 8) { + /* "Data Descriptor" present */ + if (e.getMethod() != DEFLATED) { + throw new ZipException( + "only DEFLATED entries can have EXT descriptor"); + } + } else { + e.setCrc(get32(tmpbuf, LOCCRC)); + e.setCompressedSize(get32(tmpbuf, LOCSIZ)); + e.setSize(get32(tmpbuf, LOCLEN)); + } + len = get16(tmpbuf, LOCEXT); + if (len > 0) { + byte[] bb = new byte[len]; + readFully(bb, 0, len); + e.setExtra(bb); + // extra fields are in "HeaderID(2)DataSize(2)Data... format + if (e.getCompressedSize() == ZIP64_MAGICVAL || e.getCompressedSize() == ZIP64_MAGICVAL) { + int off = 0; + while (off + 4 < len) { + int sz = get16(bb, off + 2); + if (get16(bb, off) == ZIP64_EXTID) { + off += 4; + // LOC extra zip64 entry MUST include BOTH original and + // compressed file size fields + if (sz < 16 || (off + sz) > len ) { + // Invalid zip64 extra fields, simply skip. Even it's + // rare, it's possible the entry size happens to be + // the magic value and it "accidnetly" has some bytes + // in extra match the id. + return e; + } + e.setSize(get64(bb, off)); + e.setCompressedSize(get64(bb, off + 8)); + break; + } + off += (sz + 4); + } + } + } + return e; + } + + /** + * Creates a new ZipEntry object for the specified + * entry name. + * + * @param name the ZIP file entry name + * @return the ZipEntry just created + */ + protected ZipEntry createZipEntry(String name) { + return new ZipEntry(name); + } + + /* + * Reads end of deflated entry as well as EXT descriptor if present. + */ + private void readEnd(ZipEntry e) throws IOException { + int n = inf.getRemaining(); + if (n > 0) { + ((PushbackInputStream)in).unread(buf, len - n, n); + } + if ((flag & 8) == 8) { + /* "Data Descriptor" present */ + if (inf.getBytesWritten() > ZIP64_MAGICVAL || + inf.getBytesRead() > ZIP64_MAGICVAL) { + // ZIP64 format + readFully(tmpbuf, 0, ZIP64_EXTHDR); + long sig = get32(tmpbuf, 0); + if (sig != EXTSIG) { // no EXTSIG present + e.setCrc(sig); + e.setCompressedSize(get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC)); + e.setSize(get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC)); + ((PushbackInputStream)in).unread( + tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC); + } else { + e.setCrc(get32(tmpbuf, ZIP64_EXTCRC)); + e.setCompressedSize(get64(tmpbuf, ZIP64_EXTSIZ)); + e.setSize(get64(tmpbuf, ZIP64_EXTLEN)); + } + } else { + readFully(tmpbuf, 0, EXTHDR); + long sig = get32(tmpbuf, 0); + if (sig != EXTSIG) { // no EXTSIG present + e.setCrc(sig); + e.setCompressedSize(get32(tmpbuf, EXTSIZ - EXTCRC)); + e.setSize(get32(tmpbuf, EXTLEN - EXTCRC)); + ((PushbackInputStream)in).unread( + tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC); + } else { + e.setCrc(get32(tmpbuf, EXTCRC)); + e.setCompressedSize(get32(tmpbuf, EXTSIZ)); + e.setSize(get32(tmpbuf, EXTLEN)); + } + } + } + if (e.getSize() != inf.getBytesWritten()) { + throw new ZipException( + "invalid entry size (expected " + e.getSize() + + " but got " + inf.getBytesWritten() + " bytes)"); + } + if (e.getCompressedSize() != inf.getBytesRead()) { + throw new ZipException( + "invalid entry compressed size (expected " + e.getCompressedSize() + + " but got " + inf.getBytesRead() + " bytes)"); + } + if (e.getCrc() != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(e.getCrc()) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } + } + + /* + * Reads bytes, blocking until all bytes are read. + */ + private void readFully(byte[] b, int off, int len) throws IOException { + while (len > 0) { + int n = in.read(b, off, len); + if (n == -1) { + throw new EOFException(); + } + off += n; + len -= n; + } + } + + /* + * Fetches unsigned 16-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final int get16(byte b[], int off) { + return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8); + } + + /* + * Fetches unsigned 32-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final long get32(byte b[], int off) { + return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + } + + /* + * Fetches signed 64-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final long get64(byte b[], int off) { + return get32(b, off) | (get32(b, off+4) << 32); + } + + private static String toStringUTF8(byte[] arr, int len) { + return new String(arr, 0, len); + } + + private static String toString(byte[] b, int len) { + return new String(b, 0, len); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 emul/mini/src/test/java/org/apidesign/bck2brwsr/emul/reflect/MethodImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/test/java/org/apidesign/bck2brwsr/emul/reflect/MethodImplTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,49 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.emul.reflect; + +import java.lang.reflect.Method; +import java.util.Enumeration; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class MethodImplTest { + + public MethodImplTest() { + } + + public static String[] arr(String... arr) { + return arr; + } + + @Test + public void testSignatureForMethodWithAnArray() throws NoSuchMethodException { + Method m = MethodImplTest.class.getMethod("arr", String[].class); + String sig = MethodImpl.toSignature(m); + int sep = sig.indexOf("__"); + assert sep > 0 : "Separator found " + sig; + + Enumeration en = MethodImpl.signatureParser(sig.substring(sep + 2)); + + assert en.nextElement() == m.getReturnType() : "Return type is the same"; + assert en.nextElement() == m.getParameterTypes()[0] : "1st param type is the same"; + } +} \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 javap/src/main/java/org/apidesign/javap/AnnotationParser.java --- a/javap/src/main/java/org/apidesign/javap/AnnotationParser.java Fri Feb 15 11:54:45 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/AnnotationParser.java Fri Feb 15 21:16:05 2013 +0100 @@ -35,22 +35,37 @@ */ public class AnnotationParser { private final boolean textual; + private final boolean iterateArray; - protected AnnotationParser(boolean textual) { + protected AnnotationParser(boolean textual, boolean iterateArray) { this.textual = textual; + this.iterateArray = iterateArray; } - protected void visitAnnotationStart(String type) throws IOException { + protected void visitAnnotationStart(String type, boolean top) throws IOException { } - protected void visitAnnotationEnd(String type) throws IOException { + protected void visitAnnotationEnd(String type, boolean top) throws IOException { } + + protected void visitValueStart(String attrName, char type) throws IOException { + } + + protected void visitValueEnd(String attrName, char type) throws IOException { + } + protected void visitAttr( String annoType, String attr, String attrType, String value ) throws IOException { } + protected void visitEnumAttr( + String annoType, String attr, String attrType, String value + ) throws IOException { + visitAttr(annoType, attr, attrType, value); + } + /** Initialize the parsing with constant pool from cd. * * @param attr the attribute defining annotations @@ -70,30 +85,32 @@ private void read(DataInputStream dis, ClassData cd) throws IOException { int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { - readAnno(dis, cd); + readAnno(dis, cd, true); } } - private void readAnno(DataInputStream dis, ClassData cd) throws IOException { + private void readAnno(DataInputStream dis, ClassData cd, boolean top) throws IOException { int type = dis.readUnsignedShort(); String typeName = cd.StringValue(type); - visitAnnotationStart(typeName); + visitAnnotationStart(typeName, top); int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { String attrName = cd.StringValue(dis.readUnsignedShort()); readValue(dis, cd, typeName, attrName); } - visitAnnotationEnd(typeName); + visitAnnotationEnd(typeName, top); if (cnt == 0) { visitAttr(typeName, null, null, null); } } - private void readValue(DataInputStream dis, ClassData cd, String typeName, String attrName) - throws IOException { + private void readValue( + DataInputStream dis, ClassData cd, String typeName, String attrName + ) throws IOException { char type = (char)dis.readByte(); + visitValueStart(attrName, type); if (type == '@') { - readAnno(dis, cd); + readAnno(dis, cd, false); } else if ("CFJZsSIDB".indexOf(type) >= 0) { // NOI18N int primitive = dis.readUnsignedShort(); String val = cd.stringValue(primitive, textual); @@ -112,13 +129,17 @@ } else if (type == '[') { int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { - readValue(dis, cd, typeName, attrName); + readValue(dis, cd, typeName, iterateArray ? attrName : null); } } else if (type == 'e') { int enumT = dis.readUnsignedShort(); + String attrType = cd.stringValue(enumT, textual); int enumN = dis.readUnsignedShort(); + String val = cd.stringValue(enumN, textual); + visitEnumAttr(typeName, attrName, attrType, val); } else { throw new IOException("Unknown type " + type); } + visitValueEnd(attrName, type); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/api/pom.xml --- a/javaquery/api/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/api/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -47,21 +47,16 @@ org.apidesign.bck2brwsr - core - 0.3-SNAPSHOT + emul + ${project.version} + rt jar - - - org.apidesign.bck2brwsr - emul.mini - 0.3-SNAPSHOT - jar - runtime + compile org.apidesign.bck2brwsr vm4brwsr - 0.3-SNAPSHOT + ${project.version} jar test diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -128,6 +128,8 @@ sb = new StringBuilder(); } Bck2Brwsr.generate(sb, ProcessPageTest.class.getClassLoader(), names); + sb.append("var vm = this.bck2brwsr();\n"); + ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js"); try { diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator-dynamic/pom.xml --- a/javaquery/demo-calculator-dynamic/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/demo-calculator-dynamic/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -40,22 +40,54 @@ 1.7 - + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + org.apache.maven.plugins maven-deploy-plugin 2.7 true - + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + org.apidesign.bck2brwsr - emul.mini + emul 0.3-SNAPSHOT + rt org.apidesign.bck2brwsr @@ -68,5 +100,13 @@ 6.5.2 test + + org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.3-SNAPSHOT + provided + diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator-dynamic/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/src/main/assembly/bck2brwsr.xml Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,62 @@ + + + + + bck2brwsr + + zip + dir + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml + / + index.xhtml + + + + \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Fri Feb 15 21:16:05 2013 +0100 @@ -78,9 +78,9 @@

- - + diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator/nbactions.xml --- a/javaquery/demo-calculator/nbactions.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/demo-calculator/nbactions.xml Fri Feb 15 21:16:05 2013 +0100 @@ -22,8 +22,11 @@ run - process-classes + package org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr + + true + - + diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -23,7 +23,6 @@ - j2js brwsr @@ -43,6 +42,19 @@ + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + maven-assembly-plugin 2.4 @@ -74,13 +86,22 @@ org.apidesign.bck2brwsr - emul.mini + emul 0.3-SNAPSHOT + rt org.apidesign.bck2brwsr javaquery.api 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.3-SNAPSHOT + provided + diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml --- a/javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml Fri Feb 15 21:16:05 2013 +0100 @@ -32,6 +32,19 @@ false runtime lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / @@ -44,11 +57,6 @@ / index.xhtml - - ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/staticcompilation/bootjava.js - / - bck2brwsr.js - \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Fri Feb 15 11:54:45 2013 +0100 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Fri Feb 15 21:16:05 2013 +0100 @@ -78,5 +78,9 @@
diff -r cb5ac4ada10d -r 3883d08b8cb3 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Fri Feb 15 11:54:45 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Fri Feb 15 21:16:05 2013 +0100 @@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; import org.apidesign.vm4brwsr.Bck2Brwsr; import org.glassfish.grizzly.PortRange; import org.glassfish.grizzly.http.server.HttpHandler; @@ -47,6 +48,7 @@ import org.glassfish.grizzly.http.server.Request; import org.glassfish.grizzly.http.server.Response; import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.http.util.HttpStatus; /** * Lightweight server to launch Bck2Brwsr applications and tests. @@ -94,9 +96,12 @@ startpage = "/" + startpage; } HttpServer s = initServer(".", true); + int last = startpage.lastIndexOf('/'); + String simpleName = startpage.substring(last); + s.getServerConfiguration().addHttpHandler(new Page(resources, startpage), simpleName); s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/"); try { - launchServerAndBrwsr(s, startpage); + launchServerAndBrwsr(s, simpleName); } catch (URISyntaxException | InterruptedException ex) { throw new IOException(ex); } @@ -138,7 +143,7 @@ final ServerConfiguration conf = s.getServerConfiguration(); if (addClasses) { - conf.addHttpHandler(new VM(resources), "/vm.js"); + conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); conf.addHttpHandler(new Classes(resources), "/classes/"); } return s; @@ -152,11 +157,13 @@ class DynamicResourceHandler extends HttpHandler { private final InvocationContext ic; public DynamicResourceHandler(InvocationContext ic) { - if (ic == null || ic.httpPath == null) { + if (ic == null || ic.resources.isEmpty()) { throw new NullPointerException(); } this.ic = ic; - conf.addHttpHandler(this, ic.httpPath); + for (Resource r : ic.resources) { + conf.addHttpHandler(this, r.httpPath); + } } public void close() { @@ -165,10 +172,12 @@ @Override public void service(Request request, Response response) throws Exception { - if (ic.httpPath.equals(request.getRequestURI())) { - LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); - response.setContentType(ic.httpType); - copyStream(ic.httpContent, response.getOutputStream(), null); + for (Resource r : ic.resources) { + if (r.httpPath.equals(request.getRequestURI())) { + LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); + response.setContentType(r.httpType); + copyStream(r.httpContent, response.getOutputStream(), null); + } } } } @@ -186,10 +195,26 @@ String id = request.getParameter("request"); String value = request.getParameter("result"); + + InvocationContext mi = null; + int caseNmbr = -1; + if (id != null && value != null) { LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); value = decodeURL(value); - cases.get(Integer.parseInt(id)).result(value, null); + int indx = Integer.parseInt(id); + cases.get(indx).result(value, null); + if (++indx < cases.size()) { + mi = cases.get(indx); + LOG.log(Level.INFO, "Re-executing case {0}", indx); + caseNmbr = indx; + } + } else { + if (!cases.isEmpty()) { + LOG.info("Re-executing test cases"); + mi = cases.get(0); + caseNmbr = 0; + } } if (prev != null) { @@ -197,7 +222,10 @@ prev = null; } - InvocationContext mi = methods.take(); + if (mi == null) { + mi = methods.take(); + caseNmbr = cnt++; + } if (mi == END) { response.getWriter().write(""); wait.countDown(); @@ -206,18 +234,18 @@ return; } - if (mi.httpPath != null) { + if (!mi.resources.isEmpty()) { prev = new DynamicResourceHandler(mi); } cases.add(mi); final String cn = mi.clazz.getName(); final String mn = mi.methodName; - LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{cnt, cn, mn}); + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); response.getWriter().write("{" + "className: '" + cn + "', " + "methodName: '" + mn + "', " - + "request: " + cnt + + "request: " + caseNmbr ); if (mi.html != null) { response.getWriter().write(", html: '"); @@ -225,7 +253,6 @@ response.getWriter().write("'"); } response.getWriter().write("}"); - cnt++; } }, "/data"); @@ -427,9 +454,9 @@ String r = resource; if (r == null) { r = request.getHttpHandlerPath(); - if (r.startsWith("/")) { - r = r.substring(1); - } + } + if (r.startsWith("/")) { + r = r.substring(1); } String[] replace = {}; if (r.endsWith(".html")) { @@ -460,14 +487,22 @@ StringBuilder sb = new StringBuilder(); Bck2Brwsr.generate(sb, loader); sb.append( - "function ldCls(res) {\n" - + " var request = new XMLHttpRequest();\n" - + " request.open('GET', '/classes/' + res, false);\n" - + " request.send();\n" - + " var arr = eval('(' + request.responseText + ')');\n" - + " return arr;\n" - + "}\n" - + "var vm = new bck2brwsr(ldCls);\n" + "(function WrapperVM(global) {" + + " function ldCls(res) {\n" + + " var request = new XMLHttpRequest();\n" + + " request.open('GET', '/classes/' + res, false);\n" + + " request.send();\n" + + " if (request.status !== 200) return null;\n" + + " var arr = eval('(' + request.responseText + ')');\n" + + " return arr;\n" + + " }\n" + + " var prevvm = global.bck2brwsr;\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " args.unshift(ldCls);\n" + + " return prevvm.apply(null, args);\n" + + " };\n" + + "})(this);\n" ); this.bck2brwsr = sb.toString(); } @@ -515,6 +550,7 @@ } w.append("\n]"); } catch (IOException ex) { + response.setStatus(HttpStatus.NOT_FOUND_404); response.setError(); response.setDetailMessage(ex.getMessage()); } diff -r cb5ac4ada10d -r 3883d08b8cb3 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Fri Feb 15 11:54:45 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Fri Feb 15 21:16:05 2013 +0100 @@ -19,6 +19,8 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -34,9 +36,7 @@ private String result; private Throwable exception; String html; - InputStream httpContent; - String httpType; - String httpPath; + final List resources = new ArrayList<>(); InvocationContext(Launcher launcher, Class clazz, String methodName) { this.launcher = launcher; @@ -55,13 +55,11 @@ /** HTTP resource to be available during execution. An invocation may * perform an HTTP query and obtain a resource relative to the page. */ - public void setHttpResource(String relativePath, String mimeType, InputStream content) { + public void addHttpResource(String relativePath, String mimeType, InputStream content) { if (relativePath == null || mimeType == null || content == null) { throw new NullPointerException(); } - this.httpPath = relativePath; - this.httpType = mimeType; - this.httpContent = content; + resources.add(new Resource(content, mimeType, relativePath)); } /** Invokes the associated method. @@ -97,5 +95,16 @@ wait.countDown(); } - + + static final class Resource { + final InputStream httpContent; + final String httpType; + final String httpPath; + + Resource(InputStream httpContent, String httpType, String httpPath) { + this.httpContent = httpContent; + this.httpType = httpType; + this.httpPath = httpPath; + } + } } diff -r cb5ac4ada10d -r 3883d08b8cb3 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Fri Feb 15 11:54:45 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Fri Feb 15 21:16:05 2013 +0100 @@ -125,7 +125,7 @@ loadText(u, this, arr); } catch (Exception ex) { - log(ex.getMessage()); + log(ex.getClass().getName() + ":" + ex.getMessage()); } } } diff -r cb5ac4ada10d -r 3883d08b8cb3 launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- a/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Fri Feb 15 11:54:45 2013 +0100 +++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Fri Feb 15 21:16:05 2013 +0100 @@ -24,7 +24,10 @@ Bck2Brwsr Harness - + +

Bck2Brwsr Execution Harness

diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java Fri Feb 15 11:54:45 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.mojo; - -import org.apache.maven.plugin.AbstractMojo; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apidesign.vm4brwsr.Bck2Brwsr; - -/** Compiles classes into JavaScript. */ -@Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) -public class Bck2BrswrMojo extends AbstractMojo { - public Bck2BrswrMojo() { - } - /** Root of the class files */ - @Parameter(defaultValue="${project.build.directory}/classes") - private File classes; - /** File to generate. Defaults bootjava.js in the first non-empty - package under the classes directory */ - @Parameter - private File javascript; - - @Parameter(defaultValue="${project}") - private MavenProject prj; - - - - @Override - public void execute() throws MojoExecutionException { - if (!classes.isDirectory()) { - throw new MojoExecutionException("Can't find " + classes); - } - - if (javascript == null) { - javascript = new File(findNonEmptyFolder(classes), "bootjava.js"); - } - - List arr = new ArrayList(); - long newest = collectAllClasses("", classes, arr); - - if (javascript.lastModified() > newest) { - return; - } - - try { - URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); - FileWriter w = new FileWriter(javascript); - Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); - w.close(); - } catch (IOException ex) { - throw new MojoExecutionException("Can't compile", ex); - } - } - - private static File findNonEmptyFolder(File dir) throws MojoExecutionException { - if (!dir.isDirectory()) { - throw new MojoExecutionException("Not a directory " + dir); - } - File[] arr = dir.listFiles(); - if (arr.length == 1 && arr[0].isDirectory()) { - return findNonEmptyFolder(arr[0]); - } - return dir; - } - - private static long collectAllClasses(String prefix, File toCheck, List arr) { - File[] files = toCheck.listFiles(); - if (files != null) { - long newest = 0L; - for (File f : files) { - long lastModified = collectAllClasses(prefix + f.getName() + "/", f, arr); - if (newest < lastModified) { - newest = lastModified; - } - } - return newest; - } else if (toCheck.getName().endsWith(".class")) { - arr.add(prefix.substring(0, prefix.length() - 7)); - return toCheck.lastModified(); - } else { - return 0L; - } - } - - private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { - List arr = new ArrayList(); - arr.add(root.toURI().toURL()); - for (Artifact a : deps) { - arr.add(a.getFile().toURI().toURL()); - } - return new URLClassLoader(arr.toArray(new URL[0]), Bck2BrswrMojo.class.getClassLoader()); - } -} diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,122 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.mojo; + +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** Compiles classes into JavaScript. */ +@Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) +public class Java2JavaScript extends AbstractMojo { + public Java2JavaScript() { + } + /** Root of the class files */ + @Parameter(defaultValue="${project.build.directory}/classes") + private File classes; + /** File to generate. Defaults bootjava.js in the first non-empty + package under the classes directory */ + @Parameter + private File javascript; + + @Parameter(defaultValue="${project}") + private MavenProject prj; + + + + @Override + public void execute() throws MojoExecutionException { + if (!classes.isDirectory()) { + throw new MojoExecutionException("Can't find " + classes); + } + + if (javascript == null) { + javascript = new File(findNonEmptyFolder(classes), "bootjava.js"); + } + + List arr = new ArrayList(); + long newest = collectAllClasses("", classes, arr); + + if (javascript.lastModified() > newest) { + return; + } + + try { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + FileWriter w = new FileWriter(javascript); + Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); + w.close(); + } catch (IOException ex) { + throw new MojoExecutionException("Can't compile", ex); + } + } + + private static File findNonEmptyFolder(File dir) throws MojoExecutionException { + if (!dir.isDirectory()) { + throw new MojoExecutionException("Not a directory " + dir); + } + File[] arr = dir.listFiles(); + if (arr.length == 1 && arr[0].isDirectory()) { + return findNonEmptyFolder(arr[0]); + } + return dir; + } + + private static long collectAllClasses(String prefix, File toCheck, List arr) { + File[] files = toCheck.listFiles(); + if (files != null) { + long newest = 0L; + for (File f : files) { + long lastModified = collectAllClasses(prefix + f.getName() + "/", f, arr); + if (newest < lastModified) { + newest = lastModified; + } + } + return newest; + } else if (toCheck.getName().endsWith(".class")) { + arr.add(prefix.substring(0, prefix.length() - 7)); + return toCheck.lastModified(); + } else { + return 0L; + } + } + + private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { + List arr = new ArrayList(); + arr.add(root.toURI().toURL()); + for (Artifact a : deps) { + arr.add(a.getFile().toURI().toURL()); + } + return new URLClassLoader(arr.toArray(new URL[0]), Java2JavaScript.class.getClassLoader()); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/resources/META-INF/maven/archetype-metadata.xml --- a/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml Fri Feb 15 21:16:05 2013 +0100 @@ -30,6 +30,7 @@ src/main/resources **/*.xhtml + **/*.html @@ -44,5 +45,11 @@ nbactions.xml + + + + bck2brwsr-assembly.xml + + \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,61 @@ + + + + + bck2brwsr + + zip + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/${package.replace('.','/')}/index.html + / + index.html + + + + \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/resources/archetype-resources/pom.xml --- a/mojo/src/main/resources/archetype-resources/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/mojo/src/main/resources/archetype-resources/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -53,7 +53,7 @@
- ${package.replace('.','/')}/index.xhtml + ${package.replace('.','/')}/index.html
@@ -65,14 +65,46 @@ 1.7 + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + bck2brwsr-assembly.xml + + + + +
org.apidesign.bck2brwsr - emul.mini + emul 0.3-SNAPSHOT + rt org.apidesign.bck2brwsr @@ -87,6 +119,14 @@ org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.3-SNAPSHOT + provided + + + org.apidesign.bck2brwsr vmtest 0.3-SNAPSHOT test diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/resources/archetype-resources/src/main/java/App.java --- a/mojo/src/main/resources/archetype-resources/src/main/java/App.java Fri Feb 15 11:54:45 2013 +0100 +++ b/mojo/src/main/resources/archetype-resources/src/main/java/App.java Fri Feb 15 21:16:05 2013 +0100 @@ -9,7 +9,7 @@ /** Edit the index.xhtml file. Use 'id' to name certain HTML elements. * Use this class to define behavior of the elements. */ -@Page(xhtml="index.xhtml", className="Index", properties={ +@Page(xhtml="index.html", className="Index", properties={ @Property(name="name", type=String.class) }) public class App { diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/resources/archetype-resources/src/main/resources/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/src/main/resources/index.html Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,22 @@ + + + + + Bck2Brwsr's Hello World + + +

Loading Bck2Brwsr's Hello World...

+ Your name: + +

+ + +

+ + + + + diff -r cb5ac4ada10d -r 3883d08b8cb3 mojo/src/main/resources/archetype-resources/src/main/resources/index.xhtml --- a/mojo/src/main/resources/archetype-resources/src/main/resources/index.xhtml Fri Feb 15 11:54:45 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ - - - - - Bck2Brwsr's Hello World - - -

Loading Bck2Brwsr's Hello World...

- Your name: - -

- - -

- - - - - - diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/pom.xml --- a/vm/pom.xml Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/pom.xml Fri Feb 15 21:16:05 2013 +0100 @@ -12,7 +12,7 @@ 0.3-SNAPSHOT jar - Java VM for Browser + Virtual Machine for Browser http://bck2brwsr.apidesign.org @@ -68,6 +68,45 @@ 1.7 + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + generate-js + process-classes + + java + + + + + org.apidesign.vm4brwsr.Main + + ${project.build.directory}/bck2brwsr.js + org/apidesign/vm4brwsr/Bck2Brwsr + + + + + maven-assembly-plugin + 2.4 + + + js + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + @@ -85,19 +124,20 @@ ${project.groupId} core - 0.3-SNAPSHOT + ${project.version} jar ${project.groupId} emul.mini - 0.3-SNAPSHOT - test + ${project.version} + compile ${project.groupId} javap - 0.3-SNAPSHOT + ${project.version} + compile diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/assembly/bck2brwsr.xml Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,36 @@ + + + + + js + + zip + + / + + + ${project.build.directory}/bck2brwsr.js + / + + + + \ No newline at end of file diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Feb 15 21:16:05 2013 +0100 @@ -40,6 +40,16 @@ * In this scenario, when a request for an unknown class is made, the loader * function is asked for its byte code and the system dynamically transforms * it to JavaScript. + *

+ * Instead of a loader function, one can also provide a URL to a JAR file. + * The bck2brwsr system will do its best to download the file + * and provide loader function for it automatically. + *

+ * One can provide as many loader functions and JAR URL references as necessary. + * Then the initialization code would look like:

+ * var vm = bck2brwsr(url1, url2, fnctn1, url3, functn2);
+ * 
+ * The provided URLs and loader functions will be consulted one by one. * * @author Jaroslav Tulach */ diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Feb 15 21:16:05 2013 +0100 @@ -113,7 +113,7 @@ final String className = className(jc); out.append("\n\n").append(assignClass(className)); out.append("function CLS() {"); - out.append("\n if (!CLS.prototype.$instOf_").append(className).append(") {"); + out.append("\n if (!CLS.$class) {"); if (proto == null) { String sc = jc.getSuperClassName(); // with _ out.append("\n var pp = "). @@ -175,12 +175,14 @@ out.append("\n };"); } out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";"); + out.append(prefix).append(mn).append(".cls = CLS;"); } out.append("\n c.constructor = CLS;"); out.append("\n c.$instOf_").append(className).append(" = true;"); for (String superInterface : jc.getSuperInterfaces()) { out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;"); } + out.append("\n CLS.$class = 'temp';"); out.append("\n CLS.$class = "); out.append(accessClass("java_lang_Class(true);")); out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';"); @@ -1619,7 +1621,7 @@ final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;"; class P extends AnnotationParser { public P() { - super(false); + super(false, true); } int cnt; @@ -1675,7 +1677,7 @@ final String[] values = new String[attrNames.length]; final boolean[] found = { false }; final String jvmType = "L" + className.replace('.', '/') + ";"; - AnnotationParser ap = new AnnotationParser(false) { + AnnotationParser ap = new AnnotationParser(false, true) { @Override protected void visitAttr(String type, String attr, String at, String value) { if (type.equals(jvmType)) { @@ -1712,35 +1714,71 @@ return " = null;"; } - private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException { - AnnotationParser ap = new AnnotationParser(true) { - int anno; - int cnt; + private void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException { + AnnotationParser ap = new AnnotationParser(true, false) { + int[] cnt = new int[32]; + int depth; @Override - protected void visitAnnotationStart(String type) throws IOException { - if (anno++ > 0) { + protected void visitAnnotationStart(String attrType, boolean top) throws IOException { + final String slashType = attrType.substring(1, attrType.length() - 1); + requireReference(slashType); + + if (cnt[depth]++ > 0) { out.append(","); } - out.append('"').append(type).append("\" : {\n"); - cnt = 0; + if (top) { + out.append('"').append(attrType).append("\" : "); + } + out.append("{\n"); + cnt[++depth] = 0; } @Override - protected void visitAnnotationEnd(String type) throws IOException { + protected void visitAnnotationEnd(String type, boolean top) throws IOException { out.append("\n}\n"); + depth--; + } + + @Override + protected void visitValueStart(String attrName, char type) throws IOException { + if (cnt[depth]++ > 0) { + out.append(",\n"); + } + cnt[++depth] = 0; + if (attrName != null) { + out.append(attrName).append(" : "); + } + if (type == '[') { + out.append("["); + } + } + + @Override + protected void visitValueEnd(String attrName, char type) throws IOException { + if (type == '[') { + out.append("]"); + } + depth--; } @Override protected void visitAttr(String type, String attr, String attrType, String value) throws IOException { - if (attr == null) { + if (attr == null && value == null) { return; } - if (cnt++ > 0) { - out.append(",\n"); - } - out.append(attr).append("__").append(attrType).append(" : ").append(value); + out.append(value); + } + + @Override + protected void visitEnumAttr(String type, String attr, String attrType, String value) + throws IOException { + final String slashType = attrType.substring(1, attrType.length() - 1); + requireReference(slashType); + + out.append(accessClass(slashType.replace('/', '_'))) + .append("(false).constructor.").append(value); } }; ap.parse(data, cd); diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/java/org/apidesign/vm4brwsr/ParseMan.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ParseMan.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,57 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.emul.lang.ManifestInputStream; + +/** + * + * @author Jaroslav Tulach + */ +final class ParseMan extends ManifestInputStream { + private String cp; + private String mc; + + public ParseMan(InputStream is) throws IOException { + super(is); + readAttributes(new byte[512]); + } + + @Override + protected String putValue(String key, String value) { + if ("Class-Path".equals(key)) { + cp = value; + } + if ("Main-Class".equals(key)) { + mc = value; + } + return null; + } + + String getMainClass() { + return mc; + } + + @Override + public String toString() { + return cp; + } + +} diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Feb 15 21:16:05 2013 +0100 @@ -31,8 +31,12 @@ static { // uses VMLazy to load dynamic classes - VMLazy.init(); - Zips.init(); + boolean assertsOn = false; + assert assertsOn = true; + if (assertsOn) { + VMLazy.init(); + Zips.init(); + } } @Override @@ -44,8 +48,7 @@ new VM(out).doCompile(l, names); } protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { - out.append("(function VM(global) {"); - out.append("\n var vm = {};"); + out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); StringArray processed = new StringArray(); StringArray initCode = new StringArray(); for (String baseClass : names.toArray()) { @@ -113,31 +116,31 @@ } } out.append( - " global.bck2brwsr = function() {\n" - + " var args = arguments;\n" + " return vm;\n" + + " };\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " var vm = fillInVMSkeleton({});\n" + " var loader = {};\n" + " loader.vm = vm;\n" - + " if (args.length == 1 && typeof args[0] !== 'function') {;\n" - + " var classpath = args[0];\n" - + " args[0] = function(name) {\n" - + " return vm.org_apidesign_vm4brwsr_Zips(false).loadFromCp___3B_3Ljava_lang_Object_2Ljava_lang_String_2(classpath, name);\n" - + " };\n" - + " };\n" + " loader.loadClass = function(name) {\n" + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" + " var fn = vm[attr];\n" + " if (fn) return fn(false);\n" - + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + " }\n" - + " if (args[0]) {\n" - + " vm.loadClass = loader.loadClass;\n" - + " vm.loadBytes = function(name) {\n" - + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" - + " return args[0](name);\n" - + " }\n" + + " if (vm.loadClass) {\n" + + " throw 'Cannot initialize the bck2brwsr VM twice!';\n" + " }\n" + + " vm.loadClass = loader.loadClass;\n" + + " vm.loadBytes = function(name) {\n" + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " }\n" + + " vm.java_lang_reflect_Array(false);\n" + + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, null, args);\n" + " return loader;\n" + " };\n"); out.append("}(this));"); diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Feb 15 21:16:05 2013 +0100 @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Array; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -38,23 +39,19 @@ static void init() { } - @JavaScriptBody(args={"l", "res", "args" }, body = "" - + "\ntry {" - + "\n return args[0](res.toString());" - + "\n} catch (x) {" - + "\n throw Object.getOwnPropertyNames(l.vm).toString() + x.toString();" - + "\n}") - private static native byte[] read(Object l, String res, Object[] args); - static Object load(Object loader, String name, Object[] arguments) throws IOException, ClassNotFoundException { return new VMLazy(loader, arguments).load(name, false); } + static byte[] loadBytes(Object loader, String name, Object[] arguments) throws Exception { + return Zips.loadFromCp(arguments, name); + } + private Object load(String name, boolean instance) throws IOException, ClassNotFoundException { String res = name.replace('.', '/') + ".class"; - byte[] arr = read(loader, res, args); + byte[] arr = Zips.loadFromCp(args, res); if (arr == null) { throw new ClassNotFoundException(name); } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/main/java/org/apidesign/vm4brwsr/Zips.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,81 +17,171 @@ */ package org.apidesign.vm4brwsr; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.emul.zip.FastJar; /** Conversion from classpath to load function. * * @author Jaroslav Tulach */ final class Zips { - private Zips() { + private final FastJar fj; + + private Zips(String path, byte[] zipData) throws IOException { + long bef = timeNow(); + fj = new FastJar(zipData); + for (FastJar.Entry e : fj.list()) { + putRes(e.name, e); + } + log("Iterating thru " + path + " took " + (timeNow() - bef) + "ms"); } public static void init() { } + @JavaScriptBody(args = { "arr" }, body = "return arr.length;") + private static native int length(Object arr); + @JavaScriptBody(args = { "arr", "index" }, body = "return arr[index];") + private static native Object at(Object arr, int index); + @JavaScriptBody(args = { "arr", "index", "value" }, body = "arr[index] = value; return value;") + private static native Object set(Object arr, int index, Object value); - public static byte[] loadFromCp(Object[] classpath, String res) { - for (int i = 0; i < classpath.length; i++) { - Object c = classpath[i]; + public static byte[] loadFromCp(Object classpath, String res) + throws IOException, ClassNotFoundException { + for (int i = 0; i < length(classpath); i++) { + Object c = at(classpath, i); if (c instanceof String) { try { - c = classpath[i] = toZip((String)c); + String url = (String)c; + final Zips z = toZip(url); + c = set(classpath, i, z); + final byte[] man = z.findRes("META-INF/MANIFEST.MF"); + if (man != null) { + String mainClass = processClassPathAttr(man, url, classpath); +// if (mainClass != null) { +// Class.forName(mainClass); +// } + } } catch (IOException ex) { - classpath[i] = ex; + set(classpath, i, ex); + log("Cannot load " + c + " - " + ex.getClass().getName() + ":" + ex.getMessage()); } } - if (c instanceof Zips) { - Object checkRes = ((Zips)c).findRes(res); - if (checkRes instanceof byte[]) { - return (byte[])checkRes; + if (res != null) { + byte[] checkRes; + if (c instanceof Zips) { + checkRes = ((Zips)c).findRes(res); + } else { + checkRes = callFunction(c, res); + } + if (checkRes != null) { + return checkRes; } } } return null; } + + @JavaScriptBody(args = { "fn", "res" }, body = + "if (typeof fn === 'function') return fn(res);\n" + + "return null;" + ) + private static native byte[] callFunction(Object fn, String res); + + @JavaScriptBody(args = { "msg" }, body = "console.log(msg.toString());") + private static native void log(String msg); + + private byte[] findRes(String res) throws IOException { + Object arr = findResImpl(res); + if (arr instanceof FastJar.Entry) { + long bef = timeNow(); + InputStream zip = fj.getInputStream((FastJar.Entry)arr); + arr = readFully(new byte[512], zip); + putRes(res, arr); + log("Reading " + res + " took " + (timeNow() - bef) + "ms"); + } + return (byte[]) arr; + } @JavaScriptBody(args = { "res" }, body = "var r = this[res]; return r ? r : null;") - private native byte[] findRes(String res); + private native Object findResImpl(String res); @JavaScriptBody(args = { "res", "arr" }, body = "this[res] = arr;") - private native void putRes(String res, byte[] arr); + private native void putRes(String res, Object arr); private static Zips toZip(String path) throws IOException { URL u = new URL(path); - ZipInputStream zip = new ZipInputStream(u.openStream()); - Zips z = new Zips(); - for (;;) { - ZipEntry entry = zip.getNextEntry(); - if (entry == null) { - break; - } - byte[] arr = new byte[4096]; - int offset = 0; - for (;;) { - int len = zip.read(arr, offset, arr.length - offset); - if (len == -1) { - break; - } - offset += len; - if (offset == arr.length) { - enlargeArray(arr, arr.length + 4096); + byte[] zipData = (byte[]) u.getContent(new Class[] { byte[].class }); + return new Zips(path, zipData); + } + + private static String processClassPathAttr(final byte[] man, String url, Object classpath) throws IOException { + try (ParseMan is = new ParseMan(new ByteArrayInputStream(man))) { + String cp = is.toString(); + if (cp != null) { + cp = cp.trim(); + for (int p = 0; p < cp.length();) { + int n = cp.indexOf(' ', p); + if (n == -1) { + n = cp.length(); + } + String el = cp.substring(p, n); + URL u = new URL(new URL(url), el); + classpath = addToArray(classpath, u.toString()); + p = n + 1; } } - sliceArray(arr, offset); - z.putRes(entry.getName(), arr); + return is.getMainClass(); } - return z; } + private static Object addToArray(Object arr, String value) { + final int last = length(arr); + Object ret = enlargeArray(arr, last + 1); + set(ret, last, value); + return ret; + } + + @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(null); return arr;") + private static native Object enlargeArray(Object arr, int len); @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(0);") - private static native void enlargeArray(byte[] arr, int len); + private static native void enlargeBytes(byte[] arr, int len); @JavaScriptBody(args = { "arr", "len" }, body = "arr.splice(len, arr.length - len);") private static native void sliceArray(byte[] arr, int len); + + private static Object readFully(byte[] arr, InputStream zip) throws IOException { + int offset = 0; + for (;;) { + int len = zip.read(arr, offset, arr.length - offset); + if (len == -1) { + break; + } + offset += len; + if (offset == arr.length) { + enlargeBytes(arr, arr.length + 4096); + } + } + sliceArray(arr, offset); + return arr; + } + + private static long timeNow() { + double time = m(); + if (time >= 0) { + return (long)time; + } + return org.apidesign.bck2brwsr.emul.lang.System.currentTimeMillis(); + } + @JavaScriptBody(args = {}, body = + "if (typeof window.performance === 'undefined') return -1;\n" + + "if (typeof window.performance.now === 'undefined') return -1;\n" + + "return window.performance.now();" + ) + private static native double m(); } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -76,18 +75,13 @@ assertExec("Returns 'false'", Array.class, "instanceOfArray__ZLjava_lang_Object_2", Double.valueOf(0), "non-array"); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/Array" - ); - codeSeq = sb; + code = TestVM.compileClass("org/apidesign/vm4brwsr/Array"); } private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.Test; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; @@ -87,12 +86,27 @@ @Test public void jsAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getMarker__I", Double.valueOf(10)); } + @Test public void jsArrayAnnotation() throws Exception { + assertExec("Check array annotation", Classes.class, "getMarkerNicknames__Ljava_lang_String_2", Classes.getMarkerNicknames()); + } + @Test public void jsEnumAnnotation() throws Exception { + assertExec("Check enum annotation", Classes.class, "getMarkerE__Ljava_lang_String_2", Classes.getMarkerE()); + } + @Test public void jsRetentionAnnotation() throws Exception { + assertExec("Check enum annotation", Classes.class, "getRetention__Ljava_lang_String_2", Classes.getRetention()); + } @Test public void jsStringAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getNamer__Ljava_lang_String_2Z", "my text", true); } @Test public void jsStringAnnotationFromArray() throws Exception { assertExec("Check class annotation", Classes.class, "getNamer__Ljava_lang_String_2Z", "my text", false); } + @Test public void jsInnerAnnotation() throws Exception { + assertExec("Check inner annotation", Classes.class, "getInnerNamer__I", Double.valueOf(Classes.getInnerNamer())); + } + @Test public void jsInnerAnnotationFromArray() throws Exception { + assertExec("Check inner annotation", Classes.class, "getInnerNamers__I", Double.valueOf(Classes.getInnerNamers())); + } @Test public void javaInvokeMethod() throws Exception { assertEquals(Classes.reflectiveMethodCall(true, "name"), "java.io.IOException", "Calls the name() method via reflection"); } @@ -102,6 +116,14 @@ "java.io.IOException", true, "name" ); } + + @Test public void jsMethodDeclaredInObject() throws Exception { + assertExec("Defined in Object", Classes.class, + "objectName__Ljava_lang_String_2", + "java.lang.Object" + ); + } + @Test public void jsInvokeParamMethod() throws Exception { assertExec("sums two numbers via reflection", Classes.class, "reflectiveSum__III", Double.valueOf(5), 2, 3 @@ -149,14 +171,12 @@ 0.0, "java.lang.String" ); } - /* @Test public void isInterface() throws Exception { assertExec("Calls Class.isInterface", Classes.class, "isInterface__ZLjava_lang_String_2", 1.0, "java.lang.Runnable" ); } - */ @Test public void integerType() throws Exception { assertExec("Computes the type", Classes.class, "intType__Ljava_lang_String_2", @@ -164,22 +184,17 @@ ); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - if (codeSeq == null) { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, "org/apidesign/vm4brwsr/Classes"); - codeSeq = sb; - } + code = TestVM.compileClass("org/apidesign/vm4brwsr/Classes"); } private void assertExec( String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } @Test public void isClassAssignable() throws Exception { assertExec("isAssignable works on subclasses", Classes.class, diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Fri Feb 15 21:16:05 2013 +0100 @@ -19,6 +19,8 @@ import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.net.MalformedURLException; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -27,8 +29,11 @@ * * @author Jaroslav Tulach */ -@ClassesMarker(number = 10) -@ClassesNamer(name = "my text") +@ClassesMarker(number = 10, nicknames = { "Ten", "Deset" }, count = ClassesMarker.E.TWO, subs = { + @ClassesMarker.Anno(Integer.SIZE), + @ClassesMarker.Anno(Integer.MIN_VALUE) +}) +@ClassesNamer(name = "my text", anno = @ClassesMarker.Anno(333)) public class Classes { public static String nameOfIO() { return nameFor(IOException.class); @@ -38,6 +43,8 @@ return c.getName(); } + private static final Class PRELOAD = Runnable.class; + public static boolean isInterface(String s) throws ClassNotFoundException { return Class.forName(s).isInterface(); } @@ -55,7 +62,7 @@ return new IOException().getClass().getName().toString(); } - @ClassesMarker(number = 1) + @ClassesMarker(number = 1, nicknames = { "One", "Jedna" } ) public static String name() { return IOException.class.getName().toString(); } @@ -65,6 +72,11 @@ public static String canonicalName() { return IOException.class.getCanonicalName(); } + + public static String objectName() throws NoSuchMethodException { + return IOException.class.getMethod("wait").getDeclaringClass().getName(); + } + public static boolean newInstance() throws Exception { IOException ioe = IOException.class.newInstance(); if (ioe instanceof IOException) { @@ -85,8 +97,45 @@ return -2; } ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + assert cm instanceof Object : "Is object " + cm; + assert cm instanceof Annotation : "Is annotation " + cm; + assert !((Object)cm instanceof Class) : "Is not Class " + cm; return cm == null ? -1 : cm.number(); } + public static String getMarkerNicknames() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + if (cm == null) { + return null; + } + + final Object[] arr = cm.nicknames(); + assert arr instanceof Object[] : "Instance of Object array: " + arr; + assert arr instanceof String[] : "Instance of String array: " + arr; + assert !(arr instanceof Integer[]) : "Not instance of Integer array: " + arr; + + StringBuilder sb = new StringBuilder(); + for (String s : cm.nicknames()) { + sb.append(s).append("\n"); + } + return sb.toString().toString(); + } + @Retention(RetentionPolicy.CLASS) + @interface Ann { + } + + public static String getRetention() throws Exception { + Retention r = Ann.class.getAnnotation(Retention.class); + assert r != null : "Annotation is present"; + assert r.value() == RetentionPolicy.CLASS : "Policy value is OK: " + r.value(); + return r.annotationType().getName(); + } + public static String getMarkerE() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + if (cm == null) { + return null; + } + return cm.count().name(); + } public static String getNamer(boolean direct) { if (direct) { ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class); @@ -99,6 +148,20 @@ } return null; } + public static int getInnerNamer() { + ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class); + assert cm != null : "ClassesNamer is present"; + return cm.anno().value(); + } + public static int getInnerNamers() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + assert cm != null : "ClassesNamer is present"; + int sum = 0; + for (ClassesMarker.Anno anno : cm.subs()) { + sum += anno.value(); + } + return sum; + } public static String intType() { return Integer.TYPE.getName(); diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java Fri Feb 15 21:16:05 2013 +0100 @@ -27,4 +27,16 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ClassesMarker { int number(); + String[] nicknames(); + E count() default E.ONE; + + enum E { + ONE, TWO; + } + + Anno[] subs() default {}; + + public @interface Anno { + int value(); + } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/ClassesNamer.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassesNamer.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassesNamer.java Fri Feb 15 21:16:05 2013 +0100 @@ -27,4 +27,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ClassesNamer { String name(); + ClassesMarker.Anno anno(); } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; @@ -81,8 +80,7 @@ } @Test public void testThreeCalls() throws Exception { - Object vm = code.invokeFunction("bck2brwsr"); - Object clazz = code.invokeMethod(vm, "loadClass", Exceptions.class.getName()); + Object clazz = code.loadClass("loadClass", Exceptions.class.getName()); String method = "readCounter__ILjava_lang_String_2"; @@ -104,18 +102,13 @@ } } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/Exceptions" - ); - codeSeq = sb; + code = TestVM.compileClass("org/apidesign/vm4brwsr/Exceptions"); } private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.Test; import org.testng.annotations.BeforeClass; @@ -151,22 +150,17 @@ return "org/apidesign/vm4brwsr/Instance"; } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - if (codeSeq == null) { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, startCompilationWith()); - codeSeq = sb; - } + code = TestVM.compileClass(startCompilationWith()); } private void assertExec( String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,8 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; -import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -144,22 +142,17 @@ ); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - if (codeSeq == null) { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, "org/apidesign/vm4brwsr/Numbers"); - codeSeq = sb; - } + code = TestVM.compileClass("org/apidesign/vm4brwsr/Numbers"); } private static void assertExec( String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - Object ret = TestUtils.execCode(code, codeSeq, msg, clazz, method, expRes, args); + Object ret = code.execCode(msg, clazz, method, expRes, args); if (ret == null) { return; } @@ -167,10 +160,10 @@ double expD = ((Double)expRes).doubleValue(); double retD = ((Double)ret).doubleValue(); assertEquals(retD, expD, 0.000004, msg + " " - + StaticMethodTest.dumpJS(codeSeq)); + + code.toString()); return; } - assertEquals(ret, expRes, msg + " " + StaticMethodTest.dumpJS(codeSeq)); + assertEquals(ret, expRes, msg + " " + code.toString()); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,16 +17,6 @@ */ package org.apidesign.vm4brwsr; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -331,88 +321,18 @@ ); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { StringBuilder sb = new StringBuilder(); - code = compileClass(sb, "org/apidesign/vm4brwsr/StaticMethod"); - codeSeq = sb; - } - - - private static void assertExec( - String msg, Class clazz, String method, - Object expRes, Object... args - ) throws Exception { - assertExec(code, codeSeq, msg, clazz, method, expRes, args); - } - static void assertExec( - Invocable toRun, CharSequence theCode, - String msg, Class clazz, String method, - Object expRes, Object... args - ) throws Exception { - Object ret = TestUtils.execCode(toRun, theCode, msg, clazz, method, expRes, args); - if (ret == null) { - return; - } - if (expRes != null && expRes.equals(ret)) { - return; - } - assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(theCode)); - + code = TestVM.compileClass(sb, "org/apidesign/vm4brwsr/StaticMethod"); } - static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { - return compileClass(sb, null, names); - } - static Invocable compileClass( - StringBuilder sb, ScriptEngine[] eng, String... names - ) throws ScriptException, IOException { - if (sb == null) { - sb = new StringBuilder(); - } - Bck2Brwsr.generate(sb, new EmulationResources(), names); - ScriptEngineManager sem = new ScriptEngineManager(); - ScriptEngine js = sem.getEngineByExtension("js"); - if (eng != null) { - eng[0] = js; - } - try { - Object res = js.eval(sb.toString()); - assertTrue(js instanceof Invocable, "It is invocable object: " + res); - return (Invocable)js; - } catch (Exception ex) { - if (sb.length() > 2000) { - sb = dumpJS(sb); - } - fail("Could not evaluate:\n" + sb, ex); - return null; - } - } - static StringBuilder dumpJS(CharSequence sb) throws IOException { - File f = File.createTempFile("execution", ".js"); - FileWriter w = new FileWriter(f); - w.append(sb); - w.close(); - return new StringBuilder(f.getPath()); - } - private static class EmulationResources implements Bck2Brwsr.Resources { - @Override - public InputStream get(String name) throws IOException { - Enumeration en = StaticMethodTest.class.getClassLoader().getResources(name); - URL u = null; - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - if (u.toExternalForm().contains("rt.jar!")) { - throw new IOException("No emulation for " + u); - } - return u.openStream(); - } + private void assertExec( + String msg, Class clazz, String method, + Object ret, Object... args + ) throws Exception { + code.assertExec(msg, clazz, method, ret, args); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.Test; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; @@ -194,23 +193,20 @@ } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, + code = TestVM.compileClass( "org/apidesign/vm4brwsr/StringSample", "java/lang/String" ); - codeSeq = sb; } private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/SystemTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/SystemTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/SystemTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,10 +17,8 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.BeforeClass; import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** @@ -28,10 +26,12 @@ * @author Jaroslav Tulach */ public class SystemTest { + private static TestVM code; + @Test public void verifyJSTime() throws Exception { long now = System.currentTimeMillis(); - Object js = TestUtils.execCode(code, codeSeq, "Get js time", + Object js = code.execCode("Get js time", org.apidesign.bck2brwsr.emul.lang.System.class, "currentTimeMillis__J", null ); @@ -45,14 +45,11 @@ assertTrue(time <= later, "Upper bound is OK: " + time + " <= " + later); } - private static CharSequence codeSeq; - private static Invocable code; @BeforeClass - public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, "org/apidesign/bck2brwsr/emul/lang/System"); - codeSeq = sb; + public static void compileTheCode() throws Exception { + code = TestVM.compileClass( + "org/apidesign/bck2brwsr/emul/lang/System"); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/TestUtils.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/TestUtils.java Fri Feb 15 11:54:45 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr; - -import javax.script.Invocable; -import javax.script.ScriptException; -import static org.testng.Assert.*; - -class TestUtils { - - static Object execCode(Invocable code, CharSequence codeSeq, - String msg, Class clazz, String method, Object expRes, Object... args) - throws Exception - { - Object ret = null; - try { - ret = code.invokeFunction("bck2brwsr"); - ret = code.invokeMethod(ret, "loadClass", clazz.getName()); - ret = code.invokeMethod(ret, method, args); - } catch (ScriptException ex) { - fail("Execution failed in " + StaticMethodTest.dumpJS(codeSeq), ex); - } catch (NoSuchMethodException ex) { - fail("Cannot find method in " + StaticMethodTest.dumpJS(codeSeq), ex); - } - if (ret == null && expRes == null) { - return null; - } - if (expRes != null && expRes.equals(ret)) { - return null; - } - if (expRes instanceof Number) { - // in case of Long it is necessary convert it to number - // since the Long is represented by two numbers in JavaScript - try { - ret = code.invokeMethod(ret, "toFP"); - ret = code.invokeFunction("Number", ret); - } catch (ScriptException ex) { - fail("Conversion to number failed in " + StaticMethodTest.dumpJS(codeSeq), ex); - } catch (NoSuchMethodException ex) { - fail("Cannot find global Number(x) function in " + StaticMethodTest.dumpJS(codeSeq), ex); - } - } - return ret; - } -} diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,170 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import static org.testng.Assert.*; + +final class TestVM { + private final Invocable code; + private final CharSequence codeSeq; + private final Object bck2brwsr; + + + private TestVM(Invocable code, CharSequence codeSeq) throws ScriptException, NoSuchMethodException { + this.code = code; + this.codeSeq = codeSeq; + this.bck2brwsr = code.invokeFunction("bck2brwsr"); + } + + + public Object execCode( + String msg, Class clazz, String method, + Object expRes, Object... args + ) throws Exception { + Object ret = null; + try { + ret = code.invokeMethod(bck2brwsr, "loadClass", clazz.getName()); + ret = code.invokeMethod(ret, method, args); + } catch (ScriptException ex) { + fail("Execution failed in " + dumpJS(codeSeq), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find method in " + dumpJS(codeSeq), ex); + } + if (ret == null && expRes == null) { + return null; + } + if (expRes != null && expRes.equals(ret)) { + return null; + } + if (expRes instanceof Number) { + // in case of Long it is necessary convert it to number + // since the Long is represented by two numbers in JavaScript + try { + ret = code.invokeMethod(ret, "toFP"); + ret = code.invokeFunction("Number", ret); + } catch (ScriptException ex) { + fail("Conversion to number failed in " + dumpJS(codeSeq), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find global Number(x) function in " + dumpJS(codeSeq), ex); + } + } + return ret; + } + + void assertExec( + String msg, Class clazz, String method, Object expRes, Object... args + ) throws Exception { + Object ret = execCode(msg, clazz, method, expRes, args); + if (ret == null) { + return; + } + if (expRes != null && expRes.equals(ret)) { + return; + } + assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(codeSeq)); + } + + static TestVM compileClass(String... names) throws ScriptException, IOException { + return compileClass(null, names); + } + + static TestVM compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { + return compileClass(sb, null, names); + } + + static TestVM compileClass(StringBuilder sb, ScriptEngine[] eng, String... names) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + Bck2Brwsr.generate(sb, new EmulationResources(), names); + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + if (eng != null) { + eng[0] = js; + } + try { + Object res = js.eval(sb.toString()); + assertTrue(js instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) js, sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:\n" + sb, ex); + return null; + } + } + + Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException { + return code.invokeMethod(bck2brwsr, "loadClass", Exceptions.class.getName()); + } + + Object invokeMethod(Object obj, String method, Object... params) throws ScriptException, NoSuchMethodException { + return code.invokeMethod(obj, method, params); + } + + Object invokeFunction(String methodName, Object... args) throws ScriptException, NoSuchMethodException { + return code.invokeFunction(methodName, args); + } + + static StringBuilder dumpJS(CharSequence sb) throws IOException { + File f = File.createTempFile("execution", ".js"); + FileWriter w = new FileWriter(f); + w.append(sb); + w.close(); + return new StringBuilder(f.getPath()); + } + + @Override + public String toString() { + try { + return dumpJS(codeSeq).toString(); + } catch (IOException ex) { + return ex.toString(); + } + } + + + private static class EmulationResources implements Bck2Brwsr.Resources { + @Override + public InputStream get(String name) throws IOException { + Enumeration en = StaticMethodTest.class.getClassLoader().getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + if (u.toExternalForm().contains("rt.jar!")) { + throw new IOException("No emulation for " + u); + } + return u.openStream(); + } + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptException; @@ -30,10 +29,7 @@ * @author Jaroslav Tulach */ public class VMLazyTest { - - - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { @@ -50,11 +46,10 @@ sb.append("\n}"); ScriptEngine[] arr = { null }; - code = StaticMethodTest.compileClass(sb, arr, + code = TestVM.compileClass(sb, arr, new String[]{"org/apidesign/vm4brwsr/VM", "org/apidesign/vm4brwsr/StaticMethod"} ); arr[0].getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE); - codeSeq = sb; } @Test public void invokeStaticMethod() throws Exception { @@ -83,9 +78,9 @@ try { ret = code.invokeFunction(methodName, args); } catch (ScriptException ex) { - fail("Execution failed in\n" + StaticMethodTest.dumpJS(codeSeq), ex); + fail("Execution failed in\n" + code.toString(), ex); } catch (NoSuchMethodException ex) { - fail("Cannot find method in\n" + StaticMethodTest.dumpJS(codeSeq), ex); + fail("Cannot find method in\n" + code.toString(), ex); } if (ret == null && expRes == null) { return; @@ -93,6 +88,6 @@ if (expRes.equals(ret)) { return; } - assertEquals(ret, expRes, msg + "was: " + ret + "\n" + StaticMethodTest.dumpJS(codeSeq)); + assertEquals(ret, expRes, msg + "was: " + ret + "\n" + code.toString()); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -21,7 +21,6 @@ import java.io.FileWriter; import java.io.IOException; import static org.testng.Assert.*; -import javax.script.Invocable; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -30,9 +29,7 @@ * @author Jaroslav Tulach */ public class VMinVMTest { - - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @Test public void compareGeneratedCodeForArrayClass() throws Exception { compareCode("org/apidesign/vm4brwsr/Array.class"); @@ -44,11 +41,7 @@ @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/VMinVM" - ); - codeSeq = sb; + code = TestVM.compileClass("org/apidesign/vm4brwsr/VMinVM"); } private void compareCode(final String nm) throws Exception, IOException { @@ -73,7 +66,7 @@ } } w.append("\n];\n"); - w.append(codeSeq); + w.append(code.toString()); w.close(); throw new Exception(ex.getMessage() + " file: " + f, ex); } @@ -83,11 +76,11 @@ if (!ret1.toString().equals(ret)) { StringBuilder msg = new StringBuilder("Difference found between "); - msg.append(StaticMethodTest.dumpJS(ret1)); + msg.append(TestVM.dumpJS(ret1)); msg.append(" "); - msg.append(StaticMethodTest.dumpJS((CharSequence) ret)); + msg.append(TestVM.dumpJS((CharSequence) ret)); msg.append(" compiled by "); - msg.append(StaticMethodTest.dumpJS(codeSeq)); + msg.append(code.toString()); fail(msg.toString()); } } diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,56 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Exposes HTTP page or pages to the running {@link BrwsrTest}, so it can access under + * the relative path. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface Http { + /** Set of pages to make available */ + public Resource[] value(); + + /** Exposes an HTTP page to the running {@link BrwsrTest}, so it can access + * under the relative path. + * + * @author Jaroslav Tulach + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({}) + public @interface Resource { + /** path on the server that the test can use to access the exposed resource */ + String path(); + /** the content of the HttpResource */ + String content(); + /** resource relative to the class that should be used instead of content. + * Leave content equal to empty string. + */ + String resource() default ""; + /** mime type of the resource */ + String mimeType(); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/HttpResource.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/HttpResource.java Fri Feb 15 11:54:45 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.vmtest; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Exposes an HTTP page to the running {@link BrwsrTest}, so it can access - * under the relative path. - * - * @author Jaroslav Tulach - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.TYPE}) -public @interface HttpResource { - /** path on the server that the test can use to access the exposed resource */ - String path(); - /** the content of the HttpResource */ - String content(); - /** resource relative to the class that should be used instead of content. - * Leave content equal to empty string. - */ - String resource() default ""; - /** mime type of the resource */ - String mimeType(); -} diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Fri Feb 15 21:16:05 2013 +0100 @@ -28,7 +28,7 @@ import org.apidesign.bck2brwsr.launcher.Launcher; import org.apidesign.bck2brwsr.launcher.InvocationContext; import org.apidesign.bck2brwsr.vmtest.HtmlFragment; -import org.apidesign.bck2brwsr.vmtest.HttpResource; +import org.apidesign.bck2brwsr.vmtest.Http; import org.testng.ITest; import org.testng.annotations.Test; @@ -42,10 +42,10 @@ private final String type; private final boolean fail; private final HtmlFragment html; - private final HttpResource http; + private final Http.Resource[] http; Object value; - Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, HtmlFragment html, HttpResource http) { + Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, HtmlFragment html, Http.Resource[] http) { this.l = l; this.m = m; this.type = type; @@ -62,12 +62,14 @@ c.setHtmlFragment(html.value()); } if (http != null) { - if (!http.content().isEmpty()) { - InputStream is = new ByteArrayInputStream(http.content().getBytes("UTF-8")); - c.setHttpResource(http.path(), http.mimeType(), is); - } else { - InputStream is = m.getDeclaringClass().getResourceAsStream(http.resource()); - c.setHttpResource(http.path(), http.mimeType(), is); + for (Http.Resource r : http) { + if (!r.content().isEmpty()) { + InputStream is = new ByteArrayInputStream(r.content().getBytes("UTF-8")); + c.addHttpResource(r.path(), r.mimeType(), is); + } else { + InputStream is = m.getDeclaringClass().getResourceAsStream(r.resource()); + c.addHttpResource(r.path(), r.mimeType(), is); + } } } String res = c.invoke(); diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Fri Feb 15 21:16:05 2013 +0100 @@ -135,9 +135,15 @@ if (f == null) { f = m.getDeclaringClass().getAnnotation(HtmlFragment.class); } - HttpResource r = m.getAnnotation(HttpResource.class); - if (r == null) { - r = m.getDeclaringClass().getAnnotation(HttpResource.class); + Http.Resource[] r = {}; + Http h = m.getAnnotation(Http.class); + if (h == null) { + h = m.getDeclaringClass().getAnnotation(Http.class); + if (h != null) { + r = h.value(); + } + } else { + r = h.value(); } if (brwsr.length == 0) { final Launcher s = l.brwsr(null); diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java Fri Feb 15 21:16:05 2013 +0100 @@ -25,6 +25,7 @@ import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; @@ -73,7 +74,8 @@ } private void generateJar(PackageElement pe, GenerateZip gz, Element e) throws IOException { - FileObject res = processingEnv.getFiler().createResource( + final Filer filer = processingEnv.getFiler(); + FileObject res = filer.createResource( StandardLocation.CLASS_OUTPUT, pe.getQualifiedName().toString(), gz.name(), e @@ -93,6 +95,7 @@ jar.write(arr[i + 1].getBytes("UTF-8")); jar.closeEntry(); } + jar.close(); } - + } diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -47,6 +47,19 @@ return "Ahoj".equals(null); } + @Compare public int highByteLenght() { + byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; + return new String(arr, 0).length(); + } + + @Compare public String highByte() { + byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; + StringBuilder sb = new StringBuilder(); + sb.append("pref:"); + sb.append(new String(arr, 0)); + return sb.toString(); + } + @Compare public static Object compareURLs() throws MalformedURLException { return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); } diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -21,7 +21,7 @@ import java.net.URL; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.vmtest.BrwsrTest; -import org.apidesign.bck2brwsr.vmtest.HttpResource; +import org.apidesign.bck2brwsr.vmtest.Http; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -31,15 +31,21 @@ */ public class HttpResourceTest { - @HttpResource(path = "/xhr", content = "Hello Brwsr!", mimeType = "text/plain") + @Http({ + @Http.Resource(path = "/xhr", content = "Hello Brwsr!", mimeType = "text/plain") + }) @BrwsrTest + + public String testReadContentViaXHR() throws Exception { String msg = read("/xhr"); assert "Hello Brwsr!".equals(msg) : "The message was " + msg; return msg; } - @HttpResource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + @Http({ + @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + }) @BrwsrTest public String testReadContentViaURL() throws Exception { URL url = new URL("http:/url"); @@ -47,7 +53,9 @@ assert "Hello via URL!".equals(msg) : "The message was " + msg; return msg; } - @HttpResource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + @Http({ + @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + }) @BrwsrTest public String testReadContentViaURLWithStringParam() throws Exception { URL url = new URL("http:/url"); @@ -56,7 +64,9 @@ return msg; } - @HttpResource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + @Http({ + @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + }) @BrwsrTest public void testReadByte() throws Exception { URL url = new URL("http:/bytes"); @@ -67,7 +77,9 @@ assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); } - @HttpResource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + @Http({ + @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + }) @BrwsrTest public void testReadByteViaInputStream() throws Exception { URL url = new URL("http:/bytes"); diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -17,6 +17,8 @@ */ package org.apidesign.bck2brwsr.tck; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; @@ -47,6 +49,14 @@ return long.class.toString(); } + @Compare public boolean isRunnableInterface() { + return Runnable.class.isInterface(); + } + + @Compare public String isRunnableHasRunMethod() throws NoSuchMethodException { + return Runnable.class.getMethod("run").getName(); + } + @Compare public String namesOfMethods() { StringBuilder sb = new StringBuilder(); String[] arr = new String[20]; @@ -59,6 +69,19 @@ } return sb.toString(); } + + @Compare public String namesOfDeclaringClassesOfMethods() { + StringBuilder sb = new StringBuilder(); + String[] arr = new String[20]; + int i = 0; + for (Method m : StaticUse.class.getMethods()) { + arr[i++] = m.getName() + "@" + m.getDeclaringClass().getName(); + } + for (String s : sort(arr, i)) { + sb.append(s).append("\n"); + } + return sb.toString(); + } @Compare public String cannotCallNonStaticMethodWithNull() throws Exception { StaticUse.class.getMethod("instanceMethod").invoke(null); @@ -69,6 +92,26 @@ return StaticUse.class.getMethod("instanceMethod").getReturnType(); } + @Retention(RetentionPolicy.RUNTIME) + @interface Ann { + } + + @Compare public String annoClass() throws Exception { + Retention r = Ann.class.getAnnotation(Retention.class); + assert r != null : "Annotation is present"; + assert r.value() == RetentionPolicy.RUNTIME : "Policy value is OK: " + r.value(); + return r.annotationType().getName(); + } + + @Compare public boolean isAnnotation() { + return Ann.class.isAnnotation(); + } + @Compare public boolean isNotAnnotation() { + return String.class.isAnnotation(); + } + @Compare public boolean isNotAnnotationEnum() { + return E.class.isAnnotation(); + } enum E { A, B }; @Compare public boolean isEnum() { return E.A.getClass().isEnum(); diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -0,0 +1,67 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.emul.zip.FastJar; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "five.zip", contents = { + "1.txt", "one", + "2.txt", "duo", + "3.txt", "three", + "4.txt", "four", + "5.txt", "five" +}) +public class ZipEntryTest { + @Test + public void readEntriesEffectively() throws IOException { + InputStream is = ZipEntryTest.class.getResourceAsStream("five.zip"); + byte[] arr = new byte[is.available()]; + int len = is.read(arr); + assertEquals(len, arr.length, "Read fully"); + + FastJar fj = new FastJar(arr); + FastJar.Entry[] entrs = fj.list(); + + assertEquals(5, entrs.length, "Five entries"); + + for (int i = 1; i <= 5; i++) { + FastJar.Entry en = entrs[i - 1]; + assertEquals(en.name, i + ".txt"); +// assertEquals(cis.cnt, 0, "Content of the file should be skipped, not read"); + } + + assertContent("three", fj.getInputStream(entrs[3 - 1]), "read OK"); + assertContent("five", fj.getInputStream(entrs[5 - 1]), "read OK"); + } + + private static void assertContent(String exp, InputStream is, String msg) throws IOException { + byte[] arr = new byte[512]; + int len = is.read(arr); + String s = new String(arr, 0, len); + assertEquals(exp, s, msg); + } +} diff -r cb5ac4ada10d -r 3883d08b8cb3 vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Fri Feb 15 11:54:45 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Fri Feb 15 21:16:05 2013 +0100 @@ -25,7 +25,7 @@ import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.vmtest.BrwsrTest; import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.HttpResource; +import org.apidesign.bck2brwsr.vmtest.Http; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -54,13 +54,15 @@ } @JavaScriptBody(args = { "res", "path" }, body = - "var myvm = new bck2brwsr(path);\n" + "var myvm = bck2brwsr.apply(null, path);\n" + "var cls = myvm.loadClass('java.lang.String');\n" + "return cls.getClass__Ljava_lang_Class_2().getResourceAsStream__Ljava_io_InputStream_2Ljava_lang_String_2(res);\n" ) private static native Object loadVMResource(String res, String...path); - @HttpResource(path = "/readAnEntry.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip") + @Http({ + @Http.Resource(path = "/readAnEntry.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip") + }) @BrwsrTest public void canVmLoadResourceFromZip() throws IOException { Object res = loadVMResource("/my/main/file.txt", "/readAnEntry.jar"); assert res instanceof InputStream : "Got array of bytes: " + res; @@ -74,6 +76,28 @@ assertEquals(ret, "Hello World!", "Can read the bytes"); } + @GenerateZip(name = "cpattr.zip", contents = { + "META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" + + "Created-By: hand\n" + + "Class-Path: realJar.jar\n\n\n" + }) + @Http({ + @Http.Resource(path = "/readComplexEntry.jar", mimeType = "x-application/zip", content = "", resource="cpattr.zip"), + @Http.Resource(path = "/realJar.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip"), + }) + @BrwsrTest public void understandsClassPathAttr() throws IOException { + Object res = loadVMResource("/my/main/file.txt", "/readComplexEntry.jar"); + assert res instanceof InputStream : "Got array of bytes: " + res; + InputStream is = (InputStream)res; + + byte[] arr = new byte[4096]; + int len = is.read(arr); + + final String ret = new String(arr, 0, len, "UTF-8"); + + assertEquals(ret, "Hello World!", "Can read the bytes from secondary JAR"); + } + private static void assertEquals(Object real, Object exp, String msg) { assert Objects.equals(exp, real) : msg + " exp: " + exp + " real: " + real; }