aboutsummaryrefslogtreecommitdiffstats
path: root/lib/jinterface/java_src/com
diff options
context:
space:
mode:
authorVlad Dumitrescu <[email protected]>2014-02-11 14:17:07 +0100
committerVlad Dumitrescu <[email protected]>2014-02-11 19:48:53 +0100
commit2b4582d2154b157092fc05c387655e5426ed7d8e (patch)
tree3225373219ff8ffe7450d4e3ad39290482eafe77 /lib/jinterface/java_src/com
parentd7d240bc3d03cf708f6feaf20efac1addcd76242 (diff)
downloadotp-2b4582d2154b157092fc05c387655e5426ed7d8e.tar.gz
otp-2b4582d2154b157092fc05c387655e5426ed7d8e.tar.bz2
otp-2b4582d2154b157092fc05c387655e5426ed7d8e.zip
jinterface: implement support for maps
The API and implementation are simplistic, like for lists and tuples, using arrays and without any connection to java.util.Map.
Diffstat (limited to 'lib/jinterface/java_src/com')
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java293
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java3
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java20
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/java_files1
5 files changed, 322 insertions, 0 deletions
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
new file mode 100644
index 0000000000..7c1cf84e98
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -0,0 +1,293 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+package com.ericsson.otp.erlang;
+
+import java.io.Serializable;
+
+/**
+ * Provides a Java representation of Erlang maps. Maps are created from one or
+ * more arbitrary Erlang terms.
+ *
+ * <p>
+ * The arity of the map is the number of elements it contains. The keys and
+ * values can be retrieved as arrays and the value for a key can be queried.
+ *
+ */
+public class OtpErlangMap extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ private static final long serialVersionUID = -6410770117696198497L;
+
+ private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
+
+ private OtpErlangObject[] keys = NO_ELEMENTS;
+ private OtpErlangObject[] values = NO_ELEMENTS;
+
+ /**
+ * Create a map from an array of keys and an array of values.
+ *
+ * @param keys
+ * the array of terms to create the map keys from.
+ * @param values
+ * the array of terms to create the map values from.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if any array is empty (null) or contains null elements.
+ */
+ public OtpErlangMap(final OtpErlangObject[] keys,
+ final OtpErlangObject[] values) {
+ this(keys, 0, keys.length, values, 0, values.length);
+ }
+
+ /**
+ * Create a map from an array of terms.
+ *
+ * @param elems
+ * the array of terms to create the map from.
+ * @param start
+ * the offset of the first term to insert.
+ * @param vcount
+ * the number of terms to insert.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if any array is empty (null) or contains null elements.
+ */
+ public OtpErlangMap(final OtpErlangObject[] keys, final int kstart,
+ final int kcount, final OtpErlangObject[] values, final int vstart,
+ final int vcount) {
+ if (keys == null || values == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Map content can't be null");
+ } else if (kcount != vcount) {
+ throw new java.lang.IllegalArgumentException(
+ "Map keys and values must have same arity");
+ } else if (vcount < 1) {
+ this.keys = NO_ELEMENTS;
+ this.values = NO_ELEMENTS;
+ } else {
+ this.keys = new OtpErlangObject[vcount];
+ for (int i = 0; i < vcount; i++) {
+ if (keys[kstart + i] != null) {
+ this.keys[i] = keys[kstart + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Map key cannot be null (element" + (kstart + i)
+ + ")");
+ }
+ }
+ this.values = new OtpErlangObject[vcount];
+ for (int i = 0; i < vcount; i++) {
+ if (values[vstart + i] != null) {
+ this.values[i] = values[vstart + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Map value cannot be null (element" + (vstart + i)
+ + ")");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a map from a stream containing a map encoded in Erlang external
+ * format.
+ *
+ * @param buf
+ * the stream containing the encoded map.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang map.
+ */
+ public OtpErlangMap(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final int arity = buf.read_map_head();
+
+ if (arity > 0) {
+ keys = new OtpErlangObject[arity];
+ values = new OtpErlangObject[arity];
+
+ for (int i = 0; i < arity; i++) {
+ keys[i] = buf.read_any();
+ }
+ for (int i = 0; i < arity; i++) {
+ values[i] = buf.read_any();
+ }
+ } else {
+ keys = NO_ELEMENTS;
+ values = NO_ELEMENTS;
+ }
+ }
+
+ /**
+ * Get the arity of the map.
+ *
+ * @return the number of elements contained in the map.
+ */
+ public int arity() {
+ return keys.length;
+ }
+
+ /**
+ * Get the specified value from the map.
+ *
+ * @param key
+ * the key of the requested value.
+ *
+ * @return the requested value, of null if key is not a valid key.
+ */
+ public OtpErlangObject get(final OtpErlangObject key) {
+ if (key == null) {
+ return null;
+ }
+ for (int i = 0; i < keys.length; i++) {
+ if (key.equals(keys[i])) {
+ return values[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the keys from the map as an array.
+ *
+ * @return an array containing all of the map's keys.
+ */
+ public OtpErlangObject[] keys() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(keys, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get all the values from the map as an array.
+ *
+ * @return an array containing all of the map's values.
+ */
+ public OtpErlangObject[] values() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(values, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get the string representation of the map.
+ *
+ * @return the string representation of the map.
+ */
+ @Override
+ public String toString() {
+ int i;
+ final StringBuffer s = new StringBuffer();
+ final int arity = values.length;
+
+ s.append("#{");
+
+ for (i = 0; i < arity; i++) {
+ if (i > 0) {
+ s.append(",");
+ }
+ s.append(keys[i].toString());
+ s.append(" => ");
+ s.append(values[i].toString());
+ }
+
+ s.append("}");
+
+ return s.toString();
+ }
+
+ /**
+ * Convert this map to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded map should be written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ final int arity = values.length;
+
+ buf.write_map_head(arity);
+
+ for (int i = 0; i < arity; i++) {
+ buf.write_any(keys[i]);
+ }
+ for (int i = 0; i < arity; i++) {
+ buf.write_any(values[i]);
+ }
+ }
+
+ /**
+ * Determine if two maps are equal. Maps are equal if they have the same
+ * arity and all of the elements are equal.
+ *
+ * @param o
+ * the map to compare to.
+ *
+ * @return true if the maps have the same arity and all the elements are
+ * equal.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangMap)) {
+ return false;
+ }
+
+ final OtpErlangMap t = (OtpErlangMap) o;
+ final int a = arity();
+
+ if (a != t.arity()) {
+ return false;
+ }
+
+ for (int i = 0; i < a; i++) {
+ if (!keys[i].equals(t.keys[i])) {
+ return false; // early exit
+ }
+ }
+ for (int i = 0; i < a; i++) {
+ if (!values[i].equals(t.values[i])) {
+ return false; // early exit
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ protected int doHashCode() {
+ final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
+ final int a = arity();
+ hash.combine(a);
+ for (int i = 0; i < a; i++) {
+ hash.combine(keys[i].hashCode());
+ }
+ for (int i = 0; i < a; i++) {
+ hash.combine(values[i].hashCode());
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangMap newMap = (OtpErlangMap) super.clone();
+ newMap.values = values.clone();
+ return newMap;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
index 45a82d6c94..fa0fe18e95 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
@@ -85,6 +85,9 @@ public class OtpExternal {
/** The tag used for new style references */
public static final int newRefTag = 114;
+ /** The tag used for maps */
+ public static final int mapTag = 116;
+
/** The tag used for old Funs */
public static final int funTag = 117;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index 9dc1728346..0d1342d796 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -1202,6 +1202,9 @@ public class OtpInputStream extends ByteArrayInputStream {
case OtpExternal.newRefTag:
return new OtpErlangRef(this);
+ case OtpExternal.mapTag:
+ return new OtpErlangMap(this);
+
case OtpExternal.portTag:
return new OtpErlangPort(this);
@@ -1244,4 +1247,21 @@ public class OtpInputStream extends ByteArrayInputStream {
throw new OtpErlangDecodeException("Uknown data type: " + tag);
}
}
+
+ public int read_map_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ // decode the map header and get arity
+ switch (tag) {
+ case OtpExternal.mapTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid map tag: " + tag);
+ }
+
+ return arity;
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index 78f47aa32f..a78423db44 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -974,4 +974,9 @@ public class OtpOutputStream extends ByteArrayOutputStream {
write_atom(function);
write_long(arity);
}
+
+ public void write_map_head(final int arity) {
+ write1(OtpExternal.mapTag);
+ write4BE(arity);
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
index 1390542194..62fa7f990e 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
@@ -74,6 +74,7 @@ ERL = \
OtpErlangShort\
OtpErlangString\
OtpErlangTuple \
+ OtpErlangMap \
OtpErlangUInt \
OtpErlangUShort