/* * Copyright (c) 2008, 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 com.codename1.io; import com.codename1.components.InfiniteProgress; import com.codename1.impl.CodenameOneImplementation; import com.codename1.io.Externalizable; import com.codename1.properties.PropertyBusinessObject; import com.codename1.ui.Dialog; import com.codename1.ui.Display; import com.codename1.ui.EncodedImage; import com.codename1.ui.Image; import com.codename1.ui.events.ActionListener; import com.codename1.util.Base64; import com.codename1.util.CallbackAdapter; import com.codename1.util.FailureCallback; import com.codename1.util.SuccessCallback; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Vector; /** * Various utility methods used for HTTP/IO operations * * @author Shai Almog */ public class Util { private static CodenameOneImplementation implInstance; private static Hashtable externalizables = new Hashtable(); private static boolean charArrayBugTested; private static boolean charArrayBug; static { register("EncodedImage", EncodedImage.class); } /** * Fix for RFE 427: http://java.net/jira/browse/LWUIT-427 * Allows determining chars that should not be encoded */ private static String ignoreCharsWhenEncoding = ""; /** * These chars will not be encoded by the encoding method in this class * as requested in RFE 427 http://java.net/jira/browse/LWUIT-427 * @param s set of characters to skip when encoding */ public static void setIgnorCharsWhileEncoding(String s) { ignoreCharsWhenEncoding = s; } /** * These chars will not be encoded by the encoding method in this class * as requested in RFE 427 http://java.net/jira/browse/LWUIT-427 * @return chars skipped */ public static String getIgnorCharsWhileEncoding() { return ignoreCharsWhenEncoding; } /** * Copy the input stream into the output stream, closes both streams when finishing or in * a case of an exception * * @param i source * @param o destination */ public static void copy(InputStream i, OutputStream o) throws IOException { copy(i, o, 8192); } /** * Copy the input stream into the output stream, without closing the streams when done * * @param i source * @param o destination * @param bufferSize the size of the buffer, which should be a power of 2 large enough */ public static void copyNoClose(InputStream i, OutputStream o, int bufferSize) throws IOException { byte[] buffer = new byte[bufferSize]; int size = i.read(buffer); while(size > -1) { o.write(buffer, 0, size); size = i.read(buffer); } } /** * Copy the input stream into the output stream, closes both streams when finishing or in * a case of an exception * * @param i source * @param o destination * @param bufferSize the size of the buffer, which should be a power of 2 large enough */ public static void copy(InputStream i, OutputStream o, int bufferSize) throws IOException { try { copyNoClose(i, o, bufferSize); } finally { Util.getImplementation().cleanup(o); Util.getImplementation().cleanup(i); } } /** * Closes the object (connection, stream etc.) without throwing any exception, even if the * object is null * * @param o Connection, Stream or other closeable object */ public static void cleanup(Object o) { Util.getImplementation().cleanup(o); } /** * Reads an input stream to a string * * @param i the input stream * @return a UTF-8 string * @throws IOException thrown by the stream */ public static String readToString(InputStream i) throws IOException { return readToString(i, "UTF-8"); } /** * Reads an input stream to a string * * @param i the input stream * @param encoding the encoding of the stream * @return a string * @throws IOException thrown by the stream */ public static String readToString(InputStream i, String encoding) throws IOException { byte[] b = readInputStream(i); return new String(b, 0, b.length, encoding); } /** * Converts a small input stream to a byte array * * @param i the stream to convert * @return byte array of the content of the stream */ public static byte[] readInputStream(InputStream i) throws IOException { ByteArrayOutputStream b = new ByteArrayOutputStream(); copy(i, b); return b.toByteArray(); } /** * <p>Registers this externalizable so readObject will be able to load such objects.</p> * <p> * The sample below demonstrates the usage and registration of the {@link com.codename1.io.Externalizable} interface: * </p> * <script src="https://gist.github.com/codenameone/858d8634e3cf1a82a1eb.js"></script> * * * @param e the externalizable instance */ public static void register(Externalizable e) { externalizables.put(e.getObjectId(), e.getClass()); } /** * <p>Registers this externalizable so readObject will be able to load such objects.</p> * * <p> * The sample below demonstrates the usage and registration of the {@link com.codename1.io.Externalizable} interface: * </p> * <script src="https://gist.github.com/codenameone/858d8634e3cf1a82a1eb.js"></script> * * @param id id of the externalizable * @param c the class for the externalizable */ public static void register(String id, Class c) { externalizables.put(id, c); } /** * <p>Writes an object to the given output stream, notice that it should be externalizable or one of * the supported types.</p> * * <p> * The sample below demonstrates the usage and registration of the {@link com.codename1.io.Externalizable} interface: * </p> * <script src="https://gist.github.com/codenameone/858d8634e3cf1a82a1eb.js"></script> * * @param o the object to write which can be null * @param out the destination output stream * @throws IOException thrown by the stream */ public static void writeObject(Object o, DataOutputStream out) throws IOException { if(o == null) { out.writeBoolean(false); return; } out.writeBoolean(true); if(o instanceof Externalizable) { Externalizable e = (Externalizable)o; out.writeUTF(e.getObjectId()); out.writeInt(e.getVersion()); e.externalize(out); return; } if(o instanceof PropertyBusinessObject) { Externalizable e = ((PropertyBusinessObject)o).getPropertyIndex().asExternalizable(); out.writeUTF(e.getObjectId()); out.writeInt(e.getVersion()); e.externalize(out); return; } if(o instanceof Vector) { Vector v = (Vector)o; out.writeUTF("java.util.Vector"); int size = v.size(); out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { writeObject(v.elementAt(iter), out); } return; } if(o instanceof Collection) { Collection v = (Collection)o; out.writeUTF("java.util.Collection"); int size = v.size(); out.writeInt(size); for(Object cur : v) { writeObject(cur, out); } return; } if(o instanceof Hashtable) { Hashtable v = (Hashtable)o; out.writeUTF("java.util.Hashtable"); out.writeInt(v.size()); Enumeration k = v.keys(); while(k.hasMoreElements()) { Object key = k.nextElement(); writeObject(key, out); writeObject(v.get(key), out); } return; } if(o instanceof Map) { Map v = (Map)o; out.writeUTF("java.util.Map"); out.writeInt(v.size()); for(Object key : v.keySet()) { writeObject(key, out); writeObject(v.get(key), out); } return; } if(o instanceof String) { String v = (String)o; out.writeUTF("String"); out.writeUTF(v); return; } if(o instanceof Date) { Date v = (Date)o; out.writeUTF("Date"); out.writeLong(v.getTime()); return; } if(o instanceof Integer) { Integer v = (Integer)o; out.writeUTF("int"); out.writeInt(v.intValue()); return; } if(o instanceof Long) { Long v = (Long)o; out.writeUTF("long"); out.writeLong(v.longValue()); return; } if(o instanceof Byte) { Byte v = (Byte)o; out.writeUTF("byte"); out.writeByte(v.byteValue()); return; } if(o instanceof Short) { Short v = (Short)o; out.writeUTF("short"); out.writeShort(v.shortValue()); return; } if(o instanceof Float) { Float v = (Float)o; out.writeUTF("float"); out.writeFloat(v.floatValue()); return; } if(o instanceof Double) { Double v = (Double)o; out.writeUTF("double"); out.writeDouble(v.doubleValue()); return; } if(o instanceof Boolean) { Boolean v = (Boolean)o; out.writeUTF("bool"); out.writeBoolean(v.booleanValue()); return; } if (o instanceof EncodedImage) { out.writeUTF("EncodedImage"); EncodedImage e = (EncodedImage)o; out.writeInt(e.getWidth()); out.writeInt(e.getHeight()); out.writeBoolean(e.isOpaque()); byte[] b = e.getImageData(); out.writeInt(b.length); out.write(b); return; } if(instanceofObjArray(o)) { Object[] v = (Object[])o; out.writeUTF("ObjectArray"); int size = v.length; out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { writeObject(v[iter], out); } return; } if(instanceofByteArray(o)) { byte[] v = (byte[])o; out.writeUTF("ByteArray"); int size = v.length; out.writeInt(size); out.write(v); return; } if(instanceofShortArray(o)) { short[] v = (short[])o; out.writeUTF("ShortArray"); int size = v.length; out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { out.writeShort(v[iter]); } return; } if(instanceofDoubleArray(o)) { double[] v = (double[])o; out.writeUTF("DoubleArray"); int size = v.length; out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { out.writeDouble(v[iter]); } return; } if(instanceofFloatArray(o)) { float[] v = (float[])o; out.writeUTF("FloatArray"); int size = v.length; out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { out.writeFloat(v[iter]); } return; } if(instanceofIntArray(o)) { int[] v = (int[])o; out.writeUTF("IntArray"); int size = v.length; out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { out.writeInt(v[iter]); } return; } if(instanceofLongArray(o)) { long[] v = (long[])o; out.writeUTF("LongArray"); int size = v.length; out.writeInt(size); for(int iter = 0 ; iter < size ; iter++) { out.writeLong(v[iter]); } return; } throw new IOException("Object type not supported: " + o.getClass().getName() + " value: " + o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofObjArray(Object o) { return getImplementation().instanceofObjArray(o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofByteArray(Object o) { return getImplementation().instanceofByteArray(o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofShortArray(Object o) { return getImplementation().instanceofShortArray(o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofLongArray(Object o) { return getImplementation().instanceofLongArray(o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofIntArray(Object o) { return getImplementation().instanceofIntArray(o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofFloatArray(Object o) { return getImplementation().instanceofFloatArray(o); } /** * This method allows working around <a href="http://code.google.com/p/codenameone/issues/detail?id=58">issue 58</a> * * @param o object to test * @return true if it matches the state * @deprecated this method serves as a temporary workaround for an XMLVM bug and will be removed * once the bug is fixed */ public static boolean instanceofDoubleArray(Object o) { return getImplementation().instanceofDoubleArray(o); } /** * <p>Reads an object from the stream, notice that this is the inverse of the * {@link #writeObject(java.lang.Object, java.io.DataOutputStream)}.</p> * * <p> * The sample below demonstrates the usage and registration of the {@link com.codename1.io.Externalizable} interface: * </p> * <script src="https://gist.github.com/codenameone/858d8634e3cf1a82a1eb.js"></script> * * @param input the source input stream * @throws IOException thrown by the stream */ public static Object readObject(DataInputStream input) throws IOException { try { if (!input.readBoolean()) { return null; } String type = input.readUTF(); if ("int".equals(type)) { return new Integer(input.readInt()); } if ("byte".equals(type)) { return new Byte(input.readByte()); } if ("short".equals(type)) { return new Short(input.readShort()); } if ("long".equals(type)) { return new Long(input.readLong()); } if ("float".equals(type)) { return new Float(input.readFloat()); } if ("double".equals(type)) { return new Double(input.readDouble()); } if ("bool".equals(type)) { return new Boolean(input.readBoolean()); } if ("String".equals(type)) { return input.readUTF(); } if ("Date".equals(type)) { return new Date(input.readLong()); } if ("ObjectArray".equals(type)) { Object[] v = new Object[input.readInt()]; int vlen = v.length; for (int iter = 0; iter < vlen; iter++) { v[iter] = readObject(input); } return v; } if ("ByteArray".equals(type)) { byte[] v = new byte[input.readInt()]; input.readFully(v); return v; } if ("LongArray".equals(type)) { long[] v = new long[input.readInt()]; int vlen = v.length; for (int iter = 0; iter < vlen; iter++) { v[iter] = input.readLong(); } return v; } if ("ShortArray".equals(type)) { short[] v = new short[input.readInt()]; int vlen = v.length; for (int iter = 0; iter < vlen; iter++) { v[iter] = input.readShort(); } return v; } if ("DoubleArray".equals(type)) { double[] v = new double[input.readInt()]; int vlen = v.length; for (int iter = 0; iter < vlen; iter++) { v[iter] = input.readDouble(); } return v; } if ("FloatArray".equals(type)) { float[] v = new float[input.readInt()]; int vlen = v.length; for (int iter = 0; iter < vlen; iter++) { v[iter] = input.readFloat(); } return v; } if ("IntArray".equals(type)) { int[] v = new int[input.readInt()]; int vlen = v.length; for (int iter = 0; iter < vlen; iter++) { v[iter] = input.readInt(); } return v; } if ("java.util.Vector".equals(type)) { Vector v = new Vector(); int size = input.readInt(); for (int iter = 0; iter < size; iter++) { v.addElement(readObject(input)); } return v; } if ("java.util.Hashtable".equals(type)) { Hashtable v = new Hashtable(); int size = input.readInt(); for(int iter = 0 ; iter < size ; iter++) { v.put(readObject(input), readObject(input)); } return v; } if ("java.util.Collection".equals(type)) { Collection v = new ArrayList(); int size = input.readInt(); for (int iter = 0; iter < size; iter++) { v.add(readObject(input)); } return v; } if ("java.util.Map".equals(type)) { Map v = new HashMap(); int size = input.readInt(); for(int iter = 0 ; iter < size ; iter++) { v.put(readObject(input), readObject(input)); } return v; } if ("EncodedImage".equals(type)) { int width = input.readInt(); int height = input.readInt(); boolean op = input.readBoolean(); byte[] data = new byte[input.readInt()]; input.readFully(data); return EncodedImage.create(data, width, height, op); } Class cls = (Class) externalizables.get(type); if (cls != null) { Object o = cls.newInstance(); if(o instanceof Externalizable) { Externalizable ex = (Externalizable)o; ex.internalize(input.readInt(), input); return ex; } else { PropertyBusinessObject pb = (PropertyBusinessObject)o; pb.getPropertyIndex().asExternalizable().internalize(input.readInt(), input); return pb; } } throw new IOException("Object type not supported: " + type); } catch (InstantiationException ex1) { Log.e(ex1); throw new IOException(ex1.getClass().getName() + ": " + ex1.getMessage()); } catch (IllegalAccessException ex1) { Log.e(ex1); throw new IOException(ex1.getClass().getName() + ": " + ex1.getMessage()); } } /** * Encode a string for HTML requests * * @param str none encoded string * @return encoded string */ public static String encodeUrl(final String str) { return encode(str, "%20"); } /** * toCharArray should return a new array always, however some devices might * suffer a bug that allows mutating a String (serious security hole in the JVM) * hence this method simulates the proper behavior * @param s a string * @return the contents of the string as a char array guaranteed to be a copy of the current array */ public static char[] toCharArray(String s) { // toCharArray should return a new array always, however some devices might // suffer a bug that allows mutating a String (serious security hole in the JVM) // hence this method simulates the proper behavior if(!charArrayBugTested) { charArrayBugTested = true; if(s.toCharArray() == s.toCharArray()) { charArrayBug = true; } } if(charArrayBug) { char[] c = new char[s.length()]; System.arraycopy(s.toCharArray(), 0, c, 0, c.length); return c; } return s.toCharArray(); } private static String encode(String str, String spaceChar) { if (str == null) { return null; } return encode(toCharArray(str), spaceChar); } /** * Decodes a String URL encoded URL * * @param s the string * @param enc the encoding (defaults to UTF-8 if null) * @param plusToSpace true if plus signs be converted to spaces * @return a decoded string */ public static String decode(String s, String enc, boolean plusToSpace) { boolean modified = false; if(enc == null || enc.length() == 0) { enc = "UTF-8"; } int numChars = s.length(); StringBuilder sb = new StringBuilder(numChars > 500 ? numChars / 2 : numChars); int i = 0; char c; byte[] bytes = null; while (i < numChars) { c = s.charAt(i); switch (c) { case '+': if(plusToSpace) { sb.append(' '); } else { sb.append('+'); } i++; modified = true; break; case '%': try { if (bytes == null) { bytes = new byte[(numChars - i) / 3]; } int pos = 0; while (((i + 2) < numChars) && (c == '%')) { bytes[pos++] = (byte) Integer.parseInt(s.substring(i + 1, i + 3), 16); i += 3; if (i < numChars) { c = s.charAt(i); } } if ((i < numChars) && (c == '%')) { throw new IllegalArgumentException("Illegal URL % character: " + s); } try { sb.append(new String(bytes, 0, pos, enc)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } } catch (NumberFormatException e) { throw new IllegalArgumentException("Illegal URL encoding: " + s); } modified = true; break; default: sb.append(c); i++; break; } } if(modified) { return sb.toString(); } return s; } private static String encode(char[] buf, String spaceChar) { final StringBuilder sbuf = new StringBuilder(buf.length * 3); int blen = buf.length; for (int i = 0; i < blen; i++) { final char ch = buf[i]; if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '-' || ch == '_' || ch == '.' || ch == '~' || ch == '!' || ch == '*' || ch == '\'' || ch == '(' || ch == ')' || ignoreCharsWhenEncoding.indexOf(ch) > -1)) { sbuf.append(ch); } else if (ch == ' ') { sbuf.append(spaceChar); } else { appendHex(sbuf, ch); } } return sbuf.toString(); } /** * Encode a string for HTML post requests matching the style used in application/x-www-form-urlencoded * * @param str none encoded string * @return encoded string */ public static String encodeBody(final String str) { return encode(str, "+"); } /** * Encode a string for HTML requests * * @param buf none encoded string * @return encoded string * @deprecated use encodeUrl(char[]) instead */ public static String encodeUrl(final byte[] buf) { char[] b = new char[buf.length]; for(int iter = 0 ; iter < buf.length ; iter++) { b[iter] = (char)buf[iter]; } return encode(b, "%20"); } /** * Encode a string for HTML requests * * @param buf none encoded string * @return encoded string */ public static String encodeUrl(final char[] buf) { return encode(buf, "%20"); } /** * Encode a string for HTML post requests matching the style used in application/x-www-form-urlencoded * * @param buf none encoded string * @return encoded string */ public static String encodeBody(final char[] buf) { return encode(buf, "+"); } /** * Encode a string for HTML post requests matching the style used in application/x-www-form-urlencoded * * @param buf none encoded string * @return encoded string * @deprecated use encodeUrl(char[]) instead */ public static String encodeBody(final byte[] buf) { char[] b = new char[buf.length]; int blen = buf.length; for(int iter = 0 ; iter < blen ; iter++) { b[iter] = (char)buf[iter]; } return encode(b, "+"); } private static void appendHex(StringBuilder sbuf, char ch) { int firstLiteral = ch / 256; int secLiteral = ch % 256; if(firstLiteral == 0 && secLiteral < 127) { sbuf.append("%"); String s = Integer.toHexString(secLiteral).toUpperCase(); if(s.length() == 1) { sbuf.append("0"); } sbuf.append(s); return; } if (ch <= 0x07ff) { // 2 literals unicode firstLiteral = 192 + (firstLiteral << 2) +(secLiteral >> 6); secLiteral=128+(secLiteral & 63); sbuf.append("%"); sbuf.append(Integer.toHexString(firstLiteral).toUpperCase()); sbuf.append("%"); sbuf.append(Integer.toHexString(secLiteral).toUpperCase()); } else { // 3 literals unicode int thirdLiteral = 128 + (secLiteral & 63); secLiteral = 128 + ((firstLiteral % 16) << 2) + (secLiteral >> 6); firstLiteral=224+(firstLiteral>>4); sbuf.append("%"); sbuf.append(Integer.toHexString(firstLiteral).toUpperCase()); sbuf.append("%"); sbuf.append(Integer.toHexString(secLiteral).toUpperCase()); sbuf.append("%"); sbuf.append(Integer.toHexString(thirdLiteral).toUpperCase()); } } /** * Converts a relative url e.g.: /myfile.html to an absolute url * * @param baseURL a source URL whose properties should be used to construct the actual URL * @param relativeURL relative address * @return an absolute URL */ public static String relativeToAbsolute(String baseURL, String relativeURL) { if(relativeURL.startsWith("/")) { return getURLProtocol(baseURL) + "://" + getURLHost(baseURL) + relativeURL; } else { return getURLProtocol(baseURL) + "://" + getURLHost(baseURL) + getURLBasePath(baseURL) + relativeURL; } } /** * Returns the protocol of an absolute URL e.g. http, https etc. * * @param url absolute URL * @return protocol */ public static String getURLProtocol(String url) { int index = url.indexOf("://"); if (index != -1) { return url.substring(0, index); } return null; } /** * Returns the URL's host portion * * @param url absolute URL * @return the domain of the URL */ public static String getURLHost(String url) { int start = url.indexOf("://"); int end = url.indexOf('/', start + 3); if (end != -1) { return url.substring(start + 3, end); } else { return url.substring(start + 3); } } /** * Returns the URL's path * * @param url absolute URL * @return the path within the host */ public static String getURLPath(String url) { int start = url.indexOf('/', url.indexOf("://") + 3); if (start != -1) { return url.substring(start + 1); } return "/"; } /** * Returns the URL's base path, which is the same as the path only without an ending file e.g.: * http://domain.com/f/f.html would return as: /f/ * * @param url absolute URL * @return the path within the host */ public static String getURLBasePath(String url) { int start = url.indexOf('/', url.indexOf("://") + 3); int end = url.lastIndexOf('/'); if (start != -1 && end > start) { return url.substring(start, end + 1); } return "/"; } /** * Writes a string with a null flag, this allows a String which may be null * * @param s the string to write * @param d the destination output stream * @throws java.io.IOException */ public static void writeUTF(String s, DataOutputStream d) throws IOException { if(s == null) { d.writeBoolean(false); return; } d.writeBoolean(true); d.writeUTF(s); } /** * Reads a UTF string that may be null previously written by writeUTF * * @param d the stream * @return a string or null * @throws java.io.IOException */ public static String readUTF(DataInputStream d) throws IOException { if(d.readBoolean()) { return d.readUTF(); } return null; } /** * The read fully method from data input stream is very useful for all types of * streams... * * @param b the buffer into which the data is read. * @exception IOException the stream has been closed and the contained * input stream does not support reading after close, or * another I/O error occurs. */ public static void readFully(InputStream i, byte b[]) throws IOException { readFully(i, b, 0, b.length); } /** * The read fully method from data input stream is very useful for all types of * streams... * * @param b the buffer into which the data is read. * @param off the start offset of the data. * @param len the number of bytes to read. * @exception IOException the stream has been closed and the contained * input stream does not support reading after close, or * another I/O error occurs. */ public static final void readFully(InputStream i, byte b[], int off, int len) throws IOException { if (len < 0) { throw new IndexOutOfBoundsException(); } int n = 0; while (n < len) { int count = i.read(b, off + n, len - n); if (count < 0) { throw new EOFException(); } n += count; } } /** * Reads until the array is full or until the stream ends * * @param b the buffer into which the data is read. * @return the amount read * @exception IOException the stream has been closed and the contained * input stream does not support reading after close, or * another I/O error occurs. */ public static int readAll(InputStream i, byte b[]) throws IOException { int len = b.length; int n = 0; while (n < len) { int count = i.read(b, n, len - n); if (count < 0) { return n; } n += count; } return n; } /** * Provides a utility method breaks a given String to array of String according * to the given separator * @param original the String to break * @param separator the pattern to look in the original String * @return array of Strings from the original String */ public static String[] split(String original, String separator) { Vector nodes = new Vector(); int index = original.indexOf(separator); while (index >= 0) { nodes.addElement(original.substring(0, index)); original = original.substring(index + separator.length()); index = original.indexOf(separator); } nodes.addElement(original); String [] ret = new String[nodes.size()]; for (int i = 0; i < nodes.size(); i++) { ret[i] = (String) nodes.elementAt(i); } return ret; } /** * Invoked internally from Display, this method is for internal use only * * @param impl implementation instance */ public static void setImplementation(CodenameOneImplementation impl) { implInstance = impl; } static CodenameOneImplementation getImplementation() { return implInstance; } /** * Merges arrays into one larger array */ public static void mergeArrays(Object[] arr1, Object[] arr2, Object[] destinationArray) { System.arraycopy(arr1, 0, destinationArray, 0, arr1.length); System.arraycopy(arr2, 0, destinationArray, arr1.length, arr2.length); } /** * Removes the object at the source array offset and copies all other objects to the destination array * @param sourceArray the source array * @param destinationArray the resulting array which should be of the length sourceArray.length - 1 * @param o the object to remove from the array */ public static void removeObjectAtOffset(Object[] sourceArray, Object[] destinationArray, Object o) { int off = indexOf(sourceArray, o); removeObjectAtOffset(sourceArray, destinationArray, off); } /** * Removes the object at the source array offset and copies all other objects to the destination array * @param sourceArray the source array * @param destinationArray the resulting array which should be of the length sourceArray.length - 1 * @param offset the offset of the array */ public static void removeObjectAtOffset(Object[] sourceArray, Object[] destinationArray, int offset) { System.arraycopy(sourceArray, 0, destinationArray, 0, offset); System.arraycopy(sourceArray, offset + 1, destinationArray, offset, sourceArray.length - offset - 1); } /** * Inserts the object at the destination array offset * @param sourceArray the source array * @param destinationArray the resulting array which should be of the length sourceArray.length + 1 * @param offset the offset of the array * @param o the object */ public static void insertObjectAtOffset(Object[] sourceArray, Object[] destinationArray, int offset, Object o) { if(offset == 0) { destinationArray[0] = o; System.arraycopy(sourceArray, 0, destinationArray, 1, sourceArray.length); } else { if(offset == sourceArray.length) { System.arraycopy(sourceArray, 0, destinationArray, 0, sourceArray.length); destinationArray[sourceArray.length] = o; } else { System.arraycopy(sourceArray, 0, destinationArray, 0, offset); destinationArray[offset] = o; System.arraycopy(sourceArray, offset, destinationArray, offset + 1, sourceArray.length - offset); } } } /** * Finds the object at the given offset while using the == operator and not the equals method call, it doesn't * rely on the ordering of the elements like the Arrays method. * @param arr the array * @param value the value to search * @return the offset or -1 */ public static int indexOf(Object[] arr, Object value) { int l = arr.length; for(int iter = 0 ; iter < l ; iter++) { if(arr[iter] == value) { return iter; } } return -1; } /** * Blocking method that will download the given URL to storage and return when the * operation completes * @param url the URL * @param fileName the storage file name * @param showProgress whether to block the UI until download completes/fails * @return true on success false on error */ public static boolean downloadUrlToStorage(String url, String fileName, boolean showProgress) { return downloadUrlTo(url, fileName, showProgress, false, true, null); } /** * Blocking method that will download the given URL to the file system storage and return when the * operation completes * @param url the URL * @param fileName the file name * @param showProgress whether to block the UI until download completes/fails * @return true on success false on error */ public static boolean downloadUrlToFile(String url, String fileName, boolean showProgress) { return downloadUrlTo(url, fileName, showProgress, false, false, null); } /** * <p>Non-blocking method that will download the given URL to storage in the background and return * immediately. This method can be used to fetch data dynamically and asynchronously e.g. in this code it is used * to fetch book covers for the {@link com.codename1.components.ImageViewer}:</p> * * <script src="https://gist.github.com/codenameone/305c3f5426b0e2e80833.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/components-imageviewer-dynamic.png" alt="Image viewer with dynamic URL fetching model" /> * @param url the URL * @param fileName the storage file name */ public static void downloadUrlToStorageInBackground(String url, String fileName) { downloadUrlTo(url, fileName, false, true, true, null); } /** * Non-blocking method that will download the given URL to file system storage in the background and return immediately * @param url the URL * @param fileName the file name */ public static void downloadUrlToFileSystemInBackground(String url, String fileName) { downloadUrlTo(url, fileName, false, true, false, null); } /** * Non-blocking method that will download the given URL to storage in the background and return immediately * @param url the URL * @param fileName the storage file name * @param onCompletion invoked when download completes */ public static void downloadUrlToStorageInBackground(String url, String fileName, ActionListener onCompletion) { downloadUrlTo(url, fileName, false, true, true, onCompletion); } /** * Non-blocking method that will download the given URL to file system storage in the background and return immediately * @param url the URL * @param fileName the file name * @param onCompletion invoked when download completes */ public static void downloadUrlToFileSystemInBackground(String url, String fileName, ActionListener onCompletion) { downloadUrlTo(url, fileName, false, true, false, onCompletion); } /** * Downloads an image to the file system asynchronously. If the image is already downloaded it will just load it directly from * the file system. * @param url The URL to download the image from. * @param fileName The the path to the file where the image should be downloaded. If this file already exists, it will simply load this file and skip the * network request altogether. * @param onSuccess Callback called on success. * @param onFail Callback called if we fail to load the image. * @since 3.4 * @see ConnectionRequest#downloadImageToFileSystem(java.lang.String, com.codename1.util.SuccessCallback, com.codename1.util.FailureCallback) */ public static void downloadImageToFileSystem(String url, String fileName, SuccessCallback<Image> onSuccess, FailureCallback<Image> onFail) { implInstance.downloadImageToFileSystem(url, fileName, onSuccess, onFail); } /** * Downloads an image to the file system asynchronously. If the image is already downloaded it will just load it directly from * the file system. * @param url The URL to download the image from. * @param fileName The the path to the file where the image should be downloaded. If this file already exists, it will simply load this file and skip the * network request altogether. * @param onSuccess Callback called on success. * @since 3.4 * @see ConnectionRequest#downloadImageToFileSystem(java.lang.String, com.codename1.util.SuccessCallback) */ public static void downloadImageToFileSystem(String url, String fileName, SuccessCallback<Image> onSuccess) { downloadImageToFileSystem(url, fileName, onSuccess, new CallbackAdapter<Image>()); } /** * Downloads an image to storage asynchronously. If the image is already downloaded it will just load it directly from * storage. * @param url The URL to download the image from. * @param fileName The the storage file to save the image to. If this file already exists, it will simply load this file and skip the * network request altogether. * @param onSuccess Callback called on success. * @param onFail Callback called if we fail to load the image. * @since 3.4 * @see ConnectionRequest#downloadImageToStorage(java.lang.String, com.codename1.util.SuccessCallback, com.codename1.util.FailureCallback) */ public static void downloadImageToStorage(String url, String fileName, SuccessCallback<Image> onSuccess, FailureCallback<Image> onFail) { implInstance.downloadImageToStorage(url, fileName, onSuccess, onFail); } public static void downloadImageToCache(String url, SuccessCallback<Image> onSuccess, FailureCallback<Image> onFail) { implInstance.downloadImageToCache(url, onSuccess, onFail); } /** * Downloads an image to storage asynchronously. If the image is already downloaded it will just load it directly from * storage. * @param url The URL to download the image from. * @param fileName The the storage file to save the image to. If this file already exists, it will simply load this file and skip the * network request altogether. * @param onSuccess Callback called on success. * @since 3.4 * @see ConnectionRequest#downloadImageToStorage(java.lang.String, com.codename1.util.SuccessCallback) */ public static void downloadImageToStorage(String url, String fileName, SuccessCallback<Image> onSuccess) { downloadImageToStorage(url, fileName, onSuccess, new CallbackAdapter<Image>()); } private static boolean downloadUrlTo(String url, String fileName, boolean showProgress, boolean background, boolean storage, ActionListener callback) { ConnectionRequest cr = new ConnectionRequest(); cr.setPost(false); cr.setFailSilently(true); cr.setDuplicateSupported(true); cr.setUrl(url); if(callback != null) { cr.addResponseListener(callback); } if(storage) { cr.setDestinationStorage(fileName); } else { cr.setDestinationFile(fileName); } if(background) { NetworkManager.getInstance().addToQueue(cr); return true; } if(showProgress) { InfiniteProgress ip = new InfiniteProgress(); Dialog d = ip.showInifiniteBlocking(); NetworkManager.getInstance().addToQueueAndWait(cr); d.dispose(); } else { NetworkManager.getInstance().addToQueueAndWait(cr); } int rc = cr.getResponseCode(); return rc == 200 || rc == 201; } /** * Shorthand method for Thread sleep that doesn't throw the stupid interrupted checked exception * @param t the time */ public static void sleep(int t) { try { Thread.sleep(t); } catch(InterruptedException e) { } } /** * Shorthand method wait method that doesn't throw the stupid interrupted checked exception, it also * includes the synchronized block to further reduce code clutter * @param o the object to wait on * @param t the time */ public static void wait(Object o, int t) { synchronized(o) { try { o.wait(t); } catch(InterruptedException e) { } } } /** * Shorthand method wait method that doesn't throw the stupid interrupted checked exception, it also * includes the synchronized block to further reduce code clutter * @param o the object to wait on */ public static void wait(Object o) { synchronized(o) { try { o.wait(); } catch(InterruptedException e) { } } } /** * Returns the number object as an int * @param number this can be a String or any number type * @return an int value or an exception */ public static int toIntValue(Object number) { // we should convert this to use Number if(number instanceof Integer) { return ((Integer)number).intValue(); } if(number instanceof String) { return Integer.parseInt((String)number); } if(number instanceof Double) { return ((Double)number).intValue(); } if(number instanceof Float) { return ((Float)number).intValue(); } if(number instanceof Long) { return ((Long)number).intValue(); } /*if(number instanceof Short) { return ((Short)number).intValue(); } if(number instanceof Byte) { return ((Byte)number).intValue(); }*/ if(number instanceof Boolean) { Boolean b = (Boolean)number; if(b.booleanValue()) { return 1; } return 0; } throw new IllegalArgumentException("Not a number: " + number); } /** * Returns the number object as a long * @param number this can be a String or any number type * @return a long value or an exception */ public static long toLongValue(Object number) { // we should convert this to use Number if(number instanceof Long) { return ((Long)number).longValue(); } if(number instanceof Integer) { return ((Integer)number).longValue(); } if(number instanceof String) { return Long.parseLong((String)number); } if(number instanceof Double) { return ((Double)number).longValue(); } if(number instanceof Float) { return ((Float)number).longValue(); } if(number instanceof Date) { return ((Date)number).getTime(); } /*if(number instanceof Short) { return ((Short)number).longValue(); } if(number instanceof Byte) { return ((Byte)number).longValue(); }*/ if(number instanceof Boolean) { Boolean b = (Boolean)number; if(b.booleanValue()) { return 1; } return 0; } throw new IllegalArgumentException("Not a number: " + number); } /** * Returns the number object as a float * @param number this can be a String or any number type * @return a float value or an exception */ public static float toFloatValue(Object number) { // we should convert this to use Number if(number instanceof Float) { return ((Float)number).floatValue(); } if(number instanceof Long) { return ((Long)number).floatValue(); } if(number instanceof Integer) { return ((Integer)number).floatValue(); } if(number instanceof String) { return Float.parseFloat((String)number); } if(number instanceof Double) { return ((Double)number).floatValue(); } /*if(number instanceof Short) { return ((Short)number).floatValue(); } if(number instanceof Byte) { return ((Byte)number).floatValue(); }*/ if(number instanceof Boolean) { Boolean b = (Boolean)number; if(b.booleanValue()) { return 1; } return 0; } throw new IllegalArgumentException("Not a number: " + number); } /** * Returns the number object as a double * @param number this can be a String or any number type * @return a double value or an exception */ public static double toDoubleValue(Object number) { // we should convert this to use Number if(number instanceof Double) { return ((Double)number).doubleValue(); } if(number instanceof Float) { return ((Float)number).doubleValue(); } if(number instanceof Long) { return ((Long)number).doubleValue(); } if(number instanceof Integer) { return ((Integer)number).doubleValue(); } if(number instanceof String) { return Double.parseDouble((String)number); } /*if(number instanceof Short) { return ((Short)number).doubleValue(); } if(number instanceof Byte) { return ((Byte)number).doubleValue(); }*/ if(number instanceof Boolean) { Boolean b = (Boolean)number; if(b.booleanValue()) { return 1; } return 0; } throw new IllegalArgumentException("Not a number: " + number); } /** * Encodes a string in a way that makes it harder to read it "as is" this makes it possible for Strings to be * "encoded" within the app and thus harder to discover by a casual search. * * @param s the string to decode * @return the decoded string */ public static String xorDecode(String s) { try { byte[] dat = Base64.decode(s.getBytes("UTF-8")); for(int iter = 0 ; iter < dat.length ; iter++) { dat[iter] = (byte)(dat[iter] ^ (iter % 254 + 1)); } return new String(dat, "UTF-8"); } catch(UnsupportedEncodingException err) { // will never happen damn stupid exception err.printStackTrace(); return null; } } /** * The inverse method of xorDecode, this is normally unnecessary and is here mostly for completeness * * @param s a regular string * @return a String that can be used in the xorDecode method */ public static String xorEncode(String s) { try { byte[] dat = s.getBytes("UTF-8"); for(int iter = 0 ; iter < dat.length ; iter++) { dat[iter] = (byte)(dat[iter] ^ (iter % 254 + 1)); } return Base64.encodeNoNewline(dat); } catch(UnsupportedEncodingException err) { // will never happen damn stupid exception err.printStackTrace(); return null; } } }