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 org.ocamljava.runtime.kernel.NativeApply;
022import org.ocamljava.runtime.values.BlockValue;
023import org.ocamljava.runtime.values.Value;
024
025/**
026 * The {@code OCamlLazy} class is the wrapper class for OCaml values of
027 * type {@code 'a lazy_t}.
028 *
029 * @author <a href="mailto:xclerc@ocamljava.org">Xavier Clerc</a>
030 * @version 2.0
031 * @since 2.0
032 */
033public final class OCamlLazy<T extends OCamlValue> extends OCamlValue {
034
035    /** Wrapper for nested value. */
036    private final Wrapper<T> wrapper;
037
038    /**
039     * Constructs a new instance wrapping the passed value.
040     * @param w wrapper for nested value - should not be {@code null}
041     * @param v value to wrap - should not be {@code null}
042     */
043    private OCamlLazy(final Wrapper<T> w, final Value v) {
044        super(v);
045        assert w != null : "null w";
046        this.wrapper = w;
047    } // end constructor(Wrapper<T>, Value)
048
049    /**
050     * {@inheritDoc}
051     */
052    @Override
053    public Wrapper<? extends OCamlLazy<T>> getWrapper() {
054        return OCamlLazy.wrapper(this.wrapper);
055    } // end method 'getWrapper()'
056
057    /**
058     * {@inheritDoc}
059     */
060    @Override
061    public Wrapper<? extends OCamlValue> getWrapper(final int idx) {
062        switch (idx) {
063        case 0: return this.wrapper;
064        default: return OCamlUnit.WRAPPER;
065        } // end switch
066    } // end method 'getWrapper(int)'
067
068    /**
069     * Returns the wrapped value.
070     * @return the wrapped value if already forced, or {@code null} otherwise
071     */
072    public T get() {
073        if (this.value.isLong()) {
074            return this.wrapper.wrap(this.value);
075        } else {
076            switch (this.value.getTag()) { // XXX quid lazy imbriques ?
077            case BlockValue.LAZY_TAG: return null;
078            case BlockValue.FORWARD_TAG: return this.wrapper.wrap(this.value.get0());
079            default: return this.wrapper.wrap(this.value);
080            } // end switch
081        } // end if/else
082    } // end method 'get()'
083
084    /**
085     * Returns the wrapped value, by forcing it if necessary.
086     * @return the wrapped value
087     */
088    public T force() {
089        if (this.value.isLong()) {
090            return this.wrapper.wrap(this.value);
091        } else {
092            switch (this.value.getTag()) { // XXX quid lazy imbriques ?
093            case BlockValue.LAZY_TAG:
094                try {
095                    // XXX what is the state of a lazy value when (first) evaluation has raised an exception?
096                    final Value res = NativeApply.apply(this.value.get0(), Value.UNIT);
097                    this.value.setTag(BlockValue.FORWARD_TAG);
098                    this.value.set0(res);
099                    return this.wrapper.wrap(res);
100                } catch (final Throwable t) {
101                    assert false : "exception during lazy evaluation";
102                    return null; // XXX
103                } // end try/catch
104            case BlockValue.FORWARD_TAG: return this.wrapper.wrap(this.value.get0());
105            default: return this.wrapper.wrap(this.value);
106            } // end switch
107        } // end if/else
108    } // end method 'force()'
109
110    /**
111     * {@inheritDoc}
112     */
113    @Override
114    public int hashCode() {
115        return this.value.hashCode();
116    } // end method 'hashCode()'
117
118    /**
119     * {@inheritDoc}
120     */
121    @Override
122    public boolean equals(final Object obj) {
123        if (obj instanceof OCamlLazy) {
124            final OCamlLazy<?> that = (OCamlLazy) obj;
125            return this == that;
126        } else {
127            return false;
128        } // end if/else
129    } // end method 'equals(Object)'
130
131    /**
132     * {@inheritDoc}
133     */
134    @Override
135    public String toString() {
136        final StringBuilder sb = new StringBuilder();
137        sb.append("OCamlLazy(");
138        final T v = get();
139        if (v == null) {
140            sb.append("...");
141        } else {
142            sb.append(this.wrapper.wrap(v.value()).toString());
143        } // end if/else
144        sb.append(")");
145        return sb.toString();
146    } // end method 'toString()'
147
148    /**
149     * Constructs a new {@code 'a lazy_t} value, and wraps it.
150     * @param v value to wrap
151     * @return a new {@code OCamlLazy} instance wrapping the passed value
152     */
153    @SuppressWarnings("unchecked")
154    public static <T extends OCamlValue> OCamlLazy<T> create(final T v) {
155        return new OCamlLazy<T>((Wrapper<T>) v.getWrapper(),
156                                Value.createBlock(BlockValue.FORWARD_TAG, v.value()));
157    } // end method 'create(T)'
158
159    /**
160     * Constructs a new {@code 'a lazy_t} value, and wraps it.
161     * @param w wrapper for nested value - should not be {@code null}
162     * @param c closure computing the value
163     * @return a new {@code OCamlLazy} instance wrapping the passed value
164     */
165    public static <T extends OCamlValue> OCamlLazy<T> create(final Wrapper<T> w,
166                                                             final OCamlFunction<OCamlUnit, T> c) {
167        final Value clos = c.getClosure(OCamlUnit.WRAPPER);
168        return new OCamlLazy<T>(w, Value.createBlock(BlockValue.LAZY_TAG, clos));
169    } // end method 'create(Wrapper<T>, OCamlFunction<OCamlUnit, T>)'
170
171    /**
172     * Wraps the passed value.
173     * @param w wrapper for nested value - should not be {@code null}
174     * @param v value to wrap - should not be {@code null}
175     * @return a new {@code OCamlLazy} instance wrapping the passed value
176     */
177    public static <T extends OCamlValue> OCamlLazy<T> wrap(final Wrapper<T> w,
178                                                           final Value v) {
179        assert v != null : "null v";
180        return new OCamlLazy<T>(w, v);
181    } // end method 'wrap(Wrapper<T>, Value)'
182
183    /**
184     * Returns a wrapper for {@code OCamlLazy} values.
185     * @param w wrapper for nested value - should not be {@code null}
186     * @return a wrapper for {@code OCamlLazy} values
187     */
188    @SuppressWarnings("unchecked")
189    public static <T extends OCamlValue> Wrapper<? extends OCamlLazy<T>> wrapper(final Wrapper<T> w) {
190        return new ComposedWrapper<OCamlLazy<T>>(w) {
191            /**
192             * {@inheritDoc}
193             */
194            @Override
195                public OCamlLazy<T> wrap(final Value v) {
196                return new OCamlLazy<T>(w, v);
197            } // end method 'wrap(Value)'
198        }; // end anonymous inner-class
199    } // end method 'wrapper()'
200
201} // end class 'OCamlLazy'