001/*
002 * This file is part of OCaml-Java runtime.
003 * Copyright (C) 2007-2013 Xavier Clerc.
004 *
005 * OCaml-Java runtime is free software; you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation; either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * OCaml-Java runtime is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.ocamljava.runtime.wrappers;
020
021import java.util.Iterator;
022import java.util.NoSuchElementException;
023
024import org.ocamljava.runtime.values.Value;
025
026/**
027 * The {@code OCamlArray} class is the wrapper class for OCaml values of
028 * type {@code 'a array}.
029 *
030 * @author <a href="mailto:xclerc@ocamljava.org">Xavier Clerc</a>
031 * @version 2.0
032 * @since 2.0
033 */
034public final class OCamlArray<T extends OCamlValue> extends OCamlValue implements Iterable<T> {
035
036    /** Wrapper for elements. */
037    private final Wrapper<T> wrapper;
038
039    /**
040     * Constructs a new instance wrapping the passed value.
041     * @param w wrapper for elements - should not be {@code null}
042     * @param v value to wrap - should not be {@code null}
043     */
044    private OCamlArray(final Wrapper<T> w, final Value v) {
045        super(v);
046        assert w != null : "null w";
047        this.wrapper = w;
048    } // end constructor(Wrapper<T>, Value)
049
050    /**
051     * {@inheritDoc}
052     */
053    @Override
054    public Wrapper<? extends OCamlArray<T>> getWrapper() {
055        return OCamlArray.wrapper(this.wrapper);
056    } // end method 'getWrapper()'
057
058    /**
059     * {@inheritDoc}
060     */
061    @Override
062    public Wrapper<? extends OCamlValue> getWrapper(final int idx) {
063        switch (idx) {
064        case 0: return this.wrapper;
065        default: return OCamlUnit.WRAPPER;
066        } // end switch
067    } // end method 'getWrapper(int)'
068
069    /**
070     * Returns the value at the specified index.
071     * @param idx index of value
072     * @return the value at the specified index
073     */
074    public T get(final int idx) {
075        return this.wrapper.wrap(this.value.get(idx));
076    } // end method 'get(int)'
077
078    /**
079     * Changes the value at the specified index.
080     * @param idx index of value to modify
081     * @param v new value
082     */
083    public void set(final int idx, final T v) {
084        this.value.set(idx, v.value());
085    } // end method 'set(int, T)'
086
087    /**
088     * Returns the array length.
089     * @return the array length
090     */
091    public long length() {
092        return this.value.sizeValues();
093    } // end method 'length()'
094
095    /**
096     * {@inheritDoc}
097     */
098    @Override
099    public Iterator<T> iterator() {
100        return new Iterator<T>() {
101            /** Index of next element to return. */
102            private long next = 0L;
103
104            /**
105             * {@inheritDoc}
106             */
107            @Override
108            public boolean hasNext() {
109                return this.next < OCamlArray.this.value.sizeValues();
110            } // end method 'hasNext()'
111
112            /**
113             * {@inheritDoc}
114             */
115            @Override
116            public T next() {
117                if (hasNext()) {
118                    final Value res = OCamlArray.this.value.get(this.next++);
119                    return OCamlArray.this.wrapper.wrap(res);
120                } else {
121                    throw new NoSuchElementException();
122                } // end if/else
123            } // end method 'next()'
124
125            /**
126             * {@inheritDoc}
127             */
128            @Override
129            public void remove() {
130                throw new UnsupportedOperationException();
131            } // end method 'remove()'
132        }; // end anonymous inner-class
133    } // end method 'iterator()'
134
135    /**
136     * {@inheritDoc}
137     */
138    @Override
139    public int hashCode() {
140        return this.value.hashCode();
141    } // end method 'hashCode()'
142
143    /**
144     * {@inheritDoc}
145     */
146    @Override
147    public boolean equals(final Object obj) {
148        if (obj instanceof OCamlArray) {
149            final OCamlArray<?> that = (OCamlArray) obj;
150            return this == that;
151        } else {
152            return false;
153        } // end if/else
154    } // end method 'equals(Object)'
155
156    /**
157     * {@inheritDoc}
158     */
159    @Override
160    public String toString() {
161        final StringBuilder sb = new StringBuilder();
162        sb.append("OCamlArray(");
163        int i = 0;
164        final int sz = (int) this.value.sizeValues();
165        while ((i < sz) && (sb.length() < 256)) {
166            if (i > 0) {
167                sb.append(", ");
168            } // end if
169            final Value v = this.value.get(i++);
170            final T w = this.wrapper.wrap(v);
171            sb.append(w.toString());
172        } // end while
173        if (i < sz) {
174            sb.append("...");
175        } // end if
176        sb.append(")");
177        return sb.toString();
178    } // end method 'toString()'
179
180    /**
181     * Constructs a new {@code 'a array} value, and wraps it.
182     * @param sz array size - should not be negative
183     * @param v initial value for each array element - should not be {@code null}
184     * @return a new {@code OCamlArray} instance wrapping the passed value
185     */
186    @SuppressWarnings("unchecked")
187    public static <T extends OCamlValue> OCamlArray<T> create(final int sz,
188                                                              final T v) {
189        assert sz >= 0 : "negative sz";
190        assert v != null : "null v";
191        if (v instanceof OCamlFloat) {
192            final double d = ((OCamlFloat) v).doubleValue();
193            final Value wrapped = Value.createDoubleArray(sz); // XXX definir Value.createDoubleArray(tag, sz, initvalue)
194            for (int i = 0; i < sz; i++) {
195                wrapped.setDouble(i, d);
196            } // end for
197            return new OCamlArray<T>((Wrapper<T>) v.getWrapper(), wrapped);
198        } else {
199            final Value wrapped = Value.createBlock(0, sz); // XXX definir Value.createBlock(tag, sz, initvalue)
200            for (int i = 0; i < sz; i++) {
201                wrapped.set(i, v.value());
202            } // end for
203            return new OCamlArray<T>((Wrapper<T>) v.getWrapper(), wrapped);
204        } // end if/else
205    } // end method 'create(int, T)'
206
207    /**
208     * Constructs a new {@code 'a array} value, and wraps it. <br/>
209     * The passed array is copied and hence its modification are not
210     * reflected by the wrapped value.
211     * @param w wrapper for elements - should not be {@code null}
212     * @param v array to wrap - should not be {@code null}
213     * @return a new {@code OCamlArray} instance wrapping the passed value
214     */
215    @SuppressWarnings("unchecked")
216    public static <T extends OCamlValue> OCamlArray<T> create(final Wrapper<T> w,
217                                                              final T[] v) {
218        assert w != null : "null w";
219        assert v != null : "null v";
220        final int sz = v.length;
221        if ((sz > 0) && (v[0] instanceof OCamlFloat)) {
222            final Value wrapped = Value.createDoubleArray(sz);
223            for (int i = 0; i < sz; i++) {
224                wrapped.setDouble(i, ((OCamlFloat) v[i]).doubleValue());
225            } // end for
226            return new OCamlArray<T>(w, wrapped);
227        } else {
228            final Value wrapped = Value.createBlock(0, sz);
229            for (int i = 0; i < sz; i++) {
230                wrapped.set(i, v[i].value());
231            } // end for
232            return new OCamlArray<T>(w, wrapped);
233        } // end if/else
234    } // end method 'create(T[])'
235
236    /**
237     * Wraps the passed value.
238     * @param w wrapper for elements - should not be {@code null}
239     * @param v value to wrap - should not be {@code null}
240     * @return a new {@code OCamlArray} instance wrapping the passed value
241     */
242    public static <T extends OCamlValue> OCamlArray<T> wrap(final Wrapper<T> w,
243                                                            final Value v) {
244        assert v != null : "null v";
245        return new OCamlArray<T>(w, v);
246    } // end method 'wrap(Wrapper<T>, Value)'
247
248    /**
249     * Returns a wrapper for {@code OCamlArray} values.
250     * @param w wrapper for elements - should not be {@code null}
251     * @return a wrapper for {@code OCamlArray} values
252     */
253    @SuppressWarnings("unchecked")
254    public static <T extends OCamlValue> Wrapper<? extends OCamlArray<T>> wrapper(final Wrapper<T> w) {
255        return new ComposedWrapper<OCamlArray<T>>(w) {
256            /**
257             * {@inheritDoc}
258             */
259            @Override
260                public OCamlArray<T> wrap(final Value v) {
261                return new OCamlArray<T>(w, v);
262            } // end method 'wrap(Value)'
263        }; // end anonymous inner-class
264    } // end method 'wrapper()'
265
266} // end class 'OCamlArray'