From 60800e155612bea797eed93c67046a23d26054cc Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 24 Nov 2014 12:54:30 -0800 Subject: Break out into separate repo. --- .../whispersystems/libaxolotl/util/ByteUtil.java | 248 ++++++++++++ .../org/whispersystems/libaxolotl/util/Hex.java | 77 ++++ .../whispersystems/libaxolotl/util/KeyHelper.java | 143 +++++++ .../org/whispersystems/libaxolotl/util/Medium.java | 5 + .../org/whispersystems/libaxolotl/util/Pair.java | 51 +++ .../libaxolotl/util/guava/Absent.java | 87 ++++ .../libaxolotl/util/guava/Function.java | 61 +++ .../libaxolotl/util/guava/Optional.java | 232 +++++++++++ .../libaxolotl/util/guava/Preconditions.java | 447 +++++++++++++++++++++ .../libaxolotl/util/guava/Present.java | 88 ++++ .../libaxolotl/util/guava/Supplier.java | 36 ++ 11 files changed, 1475 insertions(+) create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/Hex.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/Medium.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/Pair.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java (limited to 'src/main/java/org/whispersystems/libaxolotl/util') diff --git a/src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java b/src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java new file mode 100644 index 00000000..c213ba0b --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java @@ -0,0 +1,248 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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. If not, see . + */ +package org.whispersystems.libaxolotl.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.text.ParseException; + +public class ByteUtil { + + public static byte[] combine(byte[]... elements) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + for (byte[] element : elements) { + baos.write(element); + } + + return baos.toByteArray(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength) { + byte[][] parts = new byte[2][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + return parts; + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength, int thirdLength) + throws ParseException + { + if (input == null || firstLength < 0 || secondLength < 0 || thirdLength < 0 || + input.length < firstLength + secondLength + thirdLength) + { + throw new ParseException("Input too small: " + (input == null ? null : Hex.toString(input)), 0); + } + + byte[][] parts = new byte[3][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + parts[2] = new byte[thirdLength]; + System.arraycopy(input, firstLength + secondLength, parts[2], 0, thirdLength); + + return parts; + } + + public static byte[] trim(byte[] input, int length) { + byte[] result = new byte[length]; + System.arraycopy(input, 0, result, 0, result.length); + + return result; + } + + public static byte[] copyFrom(byte[] input) { + byte[] output = new byte[input.length]; + System.arraycopy(input, 0, output, 0, output.length); + + return output; + } + + public static byte intsToByteHighAndLow(int highValue, int lowValue) { + return (byte)((highValue << 4 | lowValue) & 0xFF); + } + + public static int highBitsToInt(byte value) { + return (value & 0xFF) >> 4; + } + + public static int lowBitsToInt(byte value) { + return (value & 0xF); + } + + public static int highBitsToMedium(int value) { + return (value >> 12); + } + + public static int lowBitsToMedium(int value) { + return (value & 0xFFF); + } + + public static byte[] shortToByteArray(int value) { + byte[] bytes = new byte[2]; + shortToByteArray(bytes, 0, value); + return bytes; + } + + public static int shortToByteArray(byte[] bytes, int offset, int value) { + bytes[offset+1] = (byte)value; + bytes[offset] = (byte)(value >> 8); + return 2; + } + + public static int shortToLittleEndianByteArray(byte[] bytes, int offset, int value) { + bytes[offset] = (byte)value; + bytes[offset+1] = (byte)(value >> 8); + return 2; + } + + public static byte[] mediumToByteArray(int value) { + byte[] bytes = new byte[3]; + mediumToByteArray(bytes, 0, value); + return bytes; + } + + public static int mediumToByteArray(byte[] bytes, int offset, int value) { + bytes[offset + 2] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + bytes[offset] = (byte)(value >> 16); + return 3; + } + + public static byte[] intToByteArray(int value) { + byte[] bytes = new byte[4]; + intToByteArray(bytes, 0, value); + return bytes; + } + + public static int intToByteArray(byte[] bytes, int offset, int value) { + bytes[offset + 3] = (byte)value; + bytes[offset + 2] = (byte)(value >> 8); + bytes[offset + 1] = (byte)(value >> 16); + bytes[offset] = (byte)(value >> 24); + return 4; + } + + public static int intToLittleEndianByteArray(byte[] bytes, int offset, int value) { + bytes[offset] = (byte)value; + bytes[offset+1] = (byte)(value >> 8); + bytes[offset+2] = (byte)(value >> 16); + bytes[offset+3] = (byte)(value >> 24); + return 4; + } + + public static byte[] longToByteArray(long l) { + byte[] bytes = new byte[8]; + longToByteArray(bytes, 0, l); + return bytes; + } + + public static int longToByteArray(byte[] bytes, int offset, long value) { + bytes[offset + 7] = (byte)value; + bytes[offset + 6] = (byte)(value >> 8); + bytes[offset + 5] = (byte)(value >> 16); + bytes[offset + 4] = (byte)(value >> 24); + bytes[offset + 3] = (byte)(value >> 32); + bytes[offset + 2] = (byte)(value >> 40); + bytes[offset + 1] = (byte)(value >> 48); + bytes[offset] = (byte)(value >> 56); + return 8; + } + + public static int longTo4ByteArray(byte[] bytes, int offset, long value) { + bytes[offset + 3] = (byte)value; + bytes[offset + 2] = (byte)(value >> 8); + bytes[offset + 1] = (byte)(value >> 16); + bytes[offset + 0] = (byte)(value >> 24); + return 4; + } + + public static int byteArrayToShort(byte[] bytes) { + return byteArrayToShort(bytes, 0); + } + + public static int byteArrayToShort(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 8 | (bytes[offset + 1] & 0xff); + } + + // The SSL patented 3-byte Value. + public static int byteArrayToMedium(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 16 | + (bytes[offset + 1] & 0xff) << 8 | + (bytes[offset + 2] & 0xff); + } + + public static int byteArrayToInt(byte[] bytes) { + return byteArrayToInt(bytes, 0); + } + + public static int byteArrayToInt(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 24 | + (bytes[offset + 1] & 0xff) << 16 | + (bytes[offset + 2] & 0xff) << 8 | + (bytes[offset + 3] & 0xff); + } + + public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) { + return + (bytes[offset + 3] & 0xff) << 24 | + (bytes[offset + 2] & 0xff) << 16 | + (bytes[offset + 1] & 0xff) << 8 | + (bytes[offset] & 0xff); + } + + public static long byteArrayToLong(byte[] bytes) { + return byteArrayToLong(bytes, 0); + } + + public static long byteArray4ToLong(byte[] bytes, int offset) { + return + ((bytes[offset + 0] & 0xffL) << 24) | + ((bytes[offset + 1] & 0xffL) << 16) | + ((bytes[offset + 2] & 0xffL) << 8) | + ((bytes[offset + 3] & 0xffL)); + } + + public static long byteArrayToLong(byte[] bytes, int offset) { + return + ((bytes[offset] & 0xffL) << 56) | + ((bytes[offset + 1] & 0xffL) << 48) | + ((bytes[offset + 2] & 0xffL) << 40) | + ((bytes[offset + 3] & 0xffL) << 32) | + ((bytes[offset + 4] & 0xffL) << 24) | + ((bytes[offset + 5] & 0xffL) << 16) | + ((bytes[offset + 6] & 0xffL) << 8) | + ((bytes[offset + 7] & 0xffL)); + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/Hex.java b/src/main/java/org/whispersystems/libaxolotl/util/Hex.java new file mode 100644 index 00000000..19285464 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/Hex.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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. If not, see . + */ +package org.whispersystems.libaxolotl.util; + +import java.io.IOException; + +/** + * Utility for generating hex dumps. + */ +public class Hex { + + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static String toString(byte[] bytes) { + return toString(bytes, 0, bytes.length); + } + + public static String toString(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < length; i++) { + appendHexChar(buf, bytes[offset + i]); + buf.append(" "); + } + return buf.toString(); + } + + public static String toStringCondensed(byte[] bytes) { + StringBuffer buf = new StringBuffer(); + for (int i=0;i> 1]; + + for (int i = 0, j = 0; j < len; i++) { + int f = Character.digit(data[j], 16) << 4; + j++; + f = f | Character.digit(data[j], 16); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + private static void appendHexChar(StringBuffer buf, int b) { + buf.append(HEX_DIGITS[(b >> 4) & 0xf]); + buf.append(HEX_DIGITS[b & 0xf]); + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java b/src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java new file mode 100644 index 00000000..96ee6563 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java @@ -0,0 +1,143 @@ +package org.whispersystems.libaxolotl.util; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.LinkedList; +import java.util.List; + +/** + * Helper class for generating keys of different types. + * + * @author Moxie Marlinspike + */ +public class KeyHelper { + + private KeyHelper() {} + + /** + * Generate an identity key pair. Clients should only do this once, + * at install time. + * + * @return the generated IdentityKeyPair. + */ + public static IdentityKeyPair generateIdentityKeyPair() { + ECKeyPair keyPair = Curve.generateKeyPair(); + IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); + return new IdentityKeyPair(publicKey, keyPair.getPrivateKey()); + } + + /** + * Generate a registration ID. Clients should only do this once, + * at install time. + * + * @param extendedRange By default (false), the generated registration + * ID is sized to require the minimal possible protobuf + * encoding overhead. Specify true if the caller needs + * the full range of MAX_INT at the cost of slightly + * higher encoding overhead. + * @return the generated registration ID. + */ + public static int generateRegistrationId(boolean extendedRange) { + try { + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + if (extendedRange) return secureRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + else return secureRandom.nextInt(16380) + 1; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public static int getRandomSequence(int max) { + try { + return SecureRandom.getInstance("SHA1PRNG").nextInt(max); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + /** + * Generate a list of PreKeys. Clients should do this at install time, and + * subsequently any time the list of PreKeys stored on the server runs low. + *

+ * PreKey IDs are shorts, so they will eventually be repeated. Clients should + * store PreKeys in a circular buffer, so that they are repeated as infrequently + * as possible. + * + * @param start The starting PreKey ID, inclusive. + * @param count The number of PreKeys to generate. + * @return the list of generated PreKeyRecords. + */ + public static List generatePreKeys(int start, int count) { + List results = new LinkedList<>(); + + start--; + + for (int i=0;i. + */ +package org.whispersystems.libaxolotl.util; + +public class Pair { + private final T1 v1; + private final T2 v2; + + public Pair(T1 v1, T2 v2) { + this.v1 = v1; + this.v2 = v2; + } + + public T1 first(){ + return v1; + } + + public T2 second(){ + return v2; + } + + public boolean equals(Object o) { + return o instanceof Pair && + equal(((Pair) o).first(), first()) && + equal(((Pair) o).second(), second()); + } + + public int hashCode() { + return first().hashCode() ^ second().hashCode(); + } + + private boolean equal(Object first, Object second) { + if (first == null && second == null) return true; + if (first == null || second == null) return false; + return first.equals(second); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java new file mode 100644 index 00000000..bd06ded9 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + +import static org.whispersystems.libaxolotl.util.guava.Preconditions.checkNotNull; + + + +import java.util.Collections; +import java.util.Set; + + +/** + * Implementation of an {@link Optional} not containing a reference. + */ + +final class Absent extends Optional { + static final Absent INSTANCE = new Absent(); + + @Override public boolean isPresent() { + return false; + } + + @Override public Object get() { + throw new IllegalStateException("value is absent"); + } + + @Override public Object or(Object defaultValue) { + return checkNotNull(defaultValue, "use orNull() instead of or(null)"); + } + + @SuppressWarnings("unchecked") // safe covariant cast + @Override public Optional or(Optional secondChoice) { + return (Optional) checkNotNull(secondChoice); + } + + @Override public Object or(Supplier supplier) { + return checkNotNull(supplier.get(), + "use orNull() instead of a Supplier that returns null"); + } + + @Override public Object orNull() { + return null; + } + + @Override public Set asSet() { + return Collections.emptySet(); + } + + @Override + public Optional transform(Function function) { + checkNotNull(function); + return Optional.absent(); + } + + @Override public boolean equals(Object object) { + return object == this; + } + + @Override public int hashCode() { + return 0x598df91c; + } + + @Override public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java new file mode 100644 index 00000000..1ad516c5 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + + + +/** + * Determines an output value based on an input value. + * + *

See the Guava User Guide article on the use of {@code + * Function}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public interface Function { + /** + * Returns the result of applying this function to {@code input}. This method is generally + * expected, but not absolutely required, to have the following properties: + * + *

    + *
  • Its execution does not cause any observable side effects. + *
  • The computation is consistent with equals; that is, {@link Objects#equal + * Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a), + * function.apply(b))}. + *
+ * + * @throws NullPointerException if {@code input} is null and this function does not accept null + * arguments + */ + T apply(F input); + + /** + * Indicates whether another object is equal to this function. + * + *

Most implementations will have no reason to override the behavior of {@link Object#equals}. + * However, an implementation may also choose to return {@code true} whenever {@code object} is a + * {@link Function} that it considers interchangeable with this one. "Interchangeable" + * typically means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all + * {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply + * that the functions are known not to be interchangeable. + */ + @Override + boolean equals(Object object); +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java new file mode 100644 index 00000000..4f2de832 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + +import static org.whispersystems.libaxolotl.util.guava.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; + + +/** + * An immutable object that may contain a non-null reference to another object. Each + * instance of this type either contains a non-null reference, or contains nothing (in + * which case we say that the reference is "absent"); it is never said to "contain {@code + * null}". + * + *

A non-null {@code Optional} reference can be used as a replacement for a nullable + * {@code T} reference. It allows you to represent "a {@code T} that must be present" and + * a "a {@code T} that might be absent" as two distinct types in your program, which can + * aid clarity. + * + *

Some uses of this class include + * + *

    + *
  • As a method return type, as an alternative to returning {@code null} to indicate + * that no value was available + *
  • To distinguish between "unknown" (for example, not present in a map) and "known to + * have no value" (present in the map, with value {@code Optional.absent()}) + *
  • To wrap nullable references for storage in a collection that does not support + * {@code null} (though there are + * + * several other approaches to this that should be considered first) + *
+ * + *

A common alternative to using this class is to find or create a suitable + * null object for the + * type in question. + * + *

This class is not intended as a direct analogue of any existing "option" or "maybe" + * construct from other programming environments, though it may bear some similarities. + * + *

See the Guava User Guide article on + * using {@code Optional}. + * + * @param the type of instance that can be contained. {@code Optional} is naturally + * covariant on this type, so it is safe to cast an {@code Optional} to {@code + * Optional} for any supertype {@code S} of {@code T}. + * @author Kurt Alfred Kluever + * @author Kevin Bourrillion + * @since 10.0 + */ +public abstract class Optional implements Serializable { + /** + * Returns an {@code Optional} instance with no contained reference. + */ + @SuppressWarnings("unchecked") + public static Optional absent() { + return (Optional) Absent.INSTANCE; + } + + /** + * Returns an {@code Optional} instance containing the given non-null reference. + */ + public static Optional of(T reference) { + return new Present(checkNotNull(reference)); + } + + /** + * If {@code nullableReference} is non-null, returns an {@code Optional} instance containing that + * reference; otherwise returns {@link Optional#absent}. + */ + public static Optional fromNullable(T nullableReference) { + return (nullableReference == null) + ? Optional.absent() + : new Present(nullableReference); + } + + Optional() {} + + /** + * Returns {@code true} if this holder contains a (non-null) instance. + */ + public abstract boolean isPresent(); + + /** + * Returns the contained instance, which must be present. If the instance might be + * absent, use {@link #or(Object)} or {@link #orNull} instead. + * + * @throws IllegalStateException if the instance is absent ({@link #isPresent} returns + * {@code false}) + */ + public abstract T get(); + + /** + * Returns the contained instance if it is present; {@code defaultValue} otherwise. If + * no default value should be required because the instance is known to be present, use + * {@link #get()} instead. For a default value of {@code null}, use {@link #orNull}. + * + *

Note about generics: The signature {@code public T or(T defaultValue)} is overly + * restrictive. However, the ideal signature, {@code public S or(S)}, is not legal + * Java. As a result, some sensible operations involving subtypes are compile errors: + *

   {@code
+   *
+   *   Optional optionalInt = getSomeOptionalInt();
+   *   Number value = optionalInt.or(0.5); // error
+   *
+   *   FluentIterable numbers = getSomeNumbers();
+   *   Optional first = numbers.first();
+   *   Number value = first.or(0.5); // error}
+ * + * As a workaround, it is always safe to cast an {@code Optional} to {@code + * Optional}. Casting either of the above example {@code Optional} instances to {@code + * Optional} (where {@code Number} is the desired output type) solves the problem: + *
   {@code
+   *
+   *   Optional optionalInt = (Optional) getSomeOptionalInt();
+   *   Number value = optionalInt.or(0.5); // fine
+   *
+   *   FluentIterable numbers = getSomeNumbers();
+   *   Optional first = (Optional) numbers.first();
+   *   Number value = first.or(0.5); // fine}
+ */ + public abstract T or(T defaultValue); + + /** + * Returns this {@code Optional} if it has a value present; {@code secondChoice} + * otherwise. + */ + public abstract Optional or(Optional secondChoice); + + /** + * Returns the contained instance if it is present; {@code supplier.get()} otherwise. If the + * supplier returns {@code null}, a {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the supplier returns {@code null} + */ + public abstract T or(Supplier supplier); + + /** + * Returns the contained instance if it is present; {@code null} otherwise. If the + * instance is known to be present, use {@link #get()} instead. + */ + public abstract T orNull(); + + /** + * Returns an immutable singleton {@link Set} whose only element is the contained instance + * if it is present; an empty immutable {@link Set} otherwise. + * + * @since 11.0 + */ + public abstract Set asSet(); + + /** + * If the instance is present, it is transformed with the given {@link Function}; otherwise, + * {@link Optional#absent} is returned. If the function returns {@code null}, a + * {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the function returns {@code null} + * + * @since 12.0 + */ + + public abstract Optional transform(Function function); + + /** + * Returns {@code true} if {@code object} is an {@code Optional} instance, and either + * the contained references are {@linkplain Object#equals equal} to each other or both + * are absent. Note that {@code Optional} instances of differing parameterized types can + * be equal. + */ + @Override public abstract boolean equals(Object object); + + /** + * Returns a hash code for this instance. + */ + @Override public abstract int hashCode(); + + /** + * Returns a string representation for this instance. The form of this string + * representation is unspecified. + */ + @Override public abstract String toString(); + + /** + * Returns the value of each present instance from the supplied {@code optionals}, in order, + * skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are + * evaluated lazily. + * + * @since 11.0 (generics widened in 13.0) + */ + +// public static Iterable presentInstances( +// final Iterable> optionals) { +// checkNotNull(optionals); +// return new Iterable() { +// @Override public Iterator iterator() { +// return new AbstractIterator() { +// private final Iterator> iterator = +// checkNotNull(optionals.iterator()); +// +// @Override protected T computeNext() { +// while (iterator.hasNext()) { +// Optional optional = iterator.next(); +// if (optional.isPresent()) { +// return optional.get(); +// } +// } +// return endOfData(); +// } +// }; +// }; +// }; +// } + + private static final long serialVersionUID = 0; +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java new file mode 100644 index 00000000..ce253c65 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + + +import java.util.NoSuchElementException; + + + +/** + * Simple static methods to be called at the start of your own methods to verify + * correct arguments and state. This allows constructs such as + *
+ *     if (count <= 0) {
+ *       throw new IllegalArgumentException("must be positive: " + count);
+ *     }
+ * + * to be replaced with the more compact + *
+ *     checkArgument(count > 0, "must be positive: %s", count);
+ * + * Note that the sense of the expression is inverted; with {@code Preconditions} + * you declare what you expect to be true, just as you do with an + * + * {@code assert} or a JUnit {@code assertTrue} call. + * + *

Warning: only the {@code "%s"} specifier is recognized as a + * placeholder in these messages, not the full range of {@link + * String#format(String, Object[])} specifiers. + * + *

Take care not to confuse precondition checking with other similar types + * of checks! Precondition exceptions -- including those provided here, but also + * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link + * UnsupportedOperationException} and others -- are used to signal that the + * calling method has made an error. This tells the caller that it should + * not have invoked the method when it did, with the arguments it did, or + * perhaps ever. Postcondition or other invariant failures should not throw + * these types of exceptions. + * + *

See the Guava User Guide on + * using {@code Preconditions}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Preconditions { + private Preconditions() {} + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument( + boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkArgument(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState( + boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkState(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (reference == null) { + // If either of these parameters is null, the right thing happens anyway + throw new NullPointerException( + format(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate + * String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning + * methods are much slower. This is a big deal - we're talking factors of + * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer + * bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. + * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, + * depending on the args, so it appears that this pattern is not directly + * applicable. But we can use the ridiculous, devious trick of throwing an + * exception in the middle of the construction of another exception. + * Hotspot is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid element in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid element in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return format("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid position in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return format("%s (%s) must not be greater than size (%s)", + desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify a valid positions + * in an array, list or string of size {@code size}, and are in order. A + * position index may range from zero to {@code size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an + * array, list or string + * @param end a user-supplied index identifying a ending position in an array, + * list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is + * greater than {@code size}, or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start < 0 || start > size) { + return badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return format("end index (%s) must not be less than start index (%s)", + end, start); + } + + /** + * Substitutes each {@code %s} in {@code template} with an argument. These + * are matched by position - the first {@code %s} gets {@code args[0]}, etc. + * If there are more arguments than placeholders, the unmatched arguments will + * be appended to the end of the formatted message in square braces. + * + * @param template a non-null string containing 0 or more {@code %s} + * placeholders. + * @param args the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. Arguments can be null. + */ + static String format(String template, + Object... args) { + template = String.valueOf(template); // null -> "null" + + // start substituting the arguments into the '%s' placeholders + StringBuilder builder = new StringBuilder( + template.length() + 16 * args.length); + int templateStart = 0; + int i = 0; + while (i < args.length) { + int placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + builder.append(template.substring(templateStart, placeholderStart)); + builder.append(args[i++]); + templateStart = placeholderStart + 2; + } + builder.append(template.substring(templateStart)); + + // if we run out of placeholders, append the extra args in square braces + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + while (i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java new file mode 100644 index 00000000..630570ff --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + +import static org.whispersystems.libaxolotl.util.guava.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Set; + +/** + * Implementation of an {@link Optional} containing a reference. + */ + +final class Present extends Optional { + private final T reference; + + Present(T reference) { + this.reference = reference; + } + + @Override public boolean isPresent() { + return true; + } + + @Override public T get() { + return reference; + } + + @Override public T or(T defaultValue) { + checkNotNull(defaultValue, "use orNull() instead of or(null)"); + return reference; + } + + @Override public Optional or(Optional secondChoice) { + checkNotNull(secondChoice); + return this; + } + + @Override public T or(Supplier supplier) { + checkNotNull(supplier); + return reference; + } + + @Override public T orNull() { + return reference; + } + + @Override public Set asSet() { + return Collections.singleton(reference); + } + + @Override public Optional transform(Function function) { + return new Present(checkNotNull(function.apply(reference), + "Transformation function cannot return null.")); + } + + @Override public boolean equals(Object object) { + if (object instanceof Present) { + Present other = (Present) object; + return reference.equals(other.reference); + } + return false; + } + + @Override public int hashCode() { + return 0x598df91c + reference.hashCode(); + } + + @Override public String toString() { + return "Optional.of(" + reference + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java new file mode 100644 index 00000000..ba880707 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + + +/** + * A class that can supply objects of a single type. Semantically, this could + * be a factory, generator, builder, closure, or something else entirely. No + * guarantees are implied by this interface. + * + * @author Harry Heymann + * @since 2.0 (imported from Google Collections Library) + */ +public interface Supplier { + /** + * Retrieves an instance of the appropriate type. The returned object may or + * may not be a new instance, depending on the implementation. + * + * @return an instance of the appropriate type + */ + T get(); +} -- cgit v1.2.3