/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you 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.olap4j.driver.xmla;

import org.olap4j.OlapException;
import org.olap4j.impl.Named;
import org.olap4j.impl.NamedListImpl;
import org.olap4j.metadata.NamedList;

import java.util.AbstractList;
import java.util.Map;
import java.util.concurrent.Semaphore;

/**
 * Named list which instantiates itself on first use.
 *
 * <p><code>DeferredNamedListImpl</code> is useful way to load an object model
 * representing a hierarchical schema. If a catalog contains schemas, which
 * contain cubes, which contain dimensions, and so forth, and if all
 * collections loaded immediately, loading the catalog would immediately load
 * all sub-objects into memory, taking a lot of memory and time.
 *
 * <p>This class is not gc-friendly at present. Once populated,
 * <code>DeferredNamedListImpl</code> holds hard references
 * to the objects it contains, so they are not available to be
 * garbage-collected. Support for weak references might be a future enhancement
 * to this class.</p>
 *
 * @author jhyde
 * @since Dec 4, 2007
 */
class DeferredNamedListImpl<T extends Named>
    extends AbstractList<T>
    implements NamedList<T>
{
    private final NamedList<T> list = new NamedListImpl<T>();
    private State state = State.NEW;

    protected final XmlaOlap4jConnection.MetadataRequest metadataRequest;
    protected final XmlaOlap4jConnection.Context context;
    protected final XmlaOlap4jConnection.Handler<T> handler;
    protected final Object[] restrictions;
    protected final Semaphore semaphore = new Semaphore(1);

    DeferredNamedListImpl(
        XmlaOlap4jConnection.MetadataRequest metadataRequest,
        XmlaOlap4jConnection.Context context,
        XmlaOlap4jConnection.Handler<T> handler,
        Object[] restrictions)
    {
        this.metadataRequest = metadataRequest;
        this.context = context;
        this.handler = handler;
        this.restrictions = (restrictions == null)
            ? new Object[0] : restrictions;
    }

    /**
     * Flushes the contents of the list. Next access will re-populate.
     */
    void reset() {
        state = State.NEW;
        list.clear();
    }

    private NamedList<T> getList() {
        try {
            semaphore.acquire();
            switch (state) {
            case POPULATING:
                throw new RuntimeException("recursive population");
            case NEW:
                try {
                    state = State.POPULATING;
                    populateList(list);
                    state = State.POPULATED;
                } catch (Exception e) {
                    state = State.NEW;
                    // TODO: fetch metadata on getCollection() method, so we
                    // can't get an exception while traversing the list
                    throw new RuntimeException(e);
                }
                // fall through
            case POPULATED:
            default:
                return list;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        } finally {
            semaphore.release();
        }
    }

    public T get(int index) {
        return getList().get(index);
    }

    public int size() {
        return getList().size();
    }

    public T get(String name) {
        return getList().get(name);
    }

    public int indexOfName(String name) {
        return getList().indexOfName(name);
    }

    public String getName(Object element) {
        return getList().getName(element);
    }

    public Map<String, T> asMap() {
        return getList().asMap();
    }

    protected void populateList(NamedList<T> list) throws OlapException {
        context.olap4jConnection.populateList(
            list, context, metadataRequest, handler, restrictions);
    }

    private enum State {
        NEW,
        POPULATING,
        POPULATED
    }
}

// End DeferredNamedListImpl.java
