diff options
| author | Vlad Dumitrescu <[email protected]> | 2014-02-11 14:17:07 +0100 | 
|---|---|---|
| committer | Vlad Dumitrescu <[email protected]> | 2014-02-11 19:48:53 +0100 | 
| commit | 2b4582d2154b157092fc05c387655e5426ed7d8e (patch) | |
| tree | 3225373219ff8ffe7450d4e3ad39290482eafe77 /lib/jinterface/java_src | |
| parent | d7d240bc3d03cf708f6feaf20efac1addcd76242 (diff) | |
| download | otp-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')
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 | 
