ec.tss.TsCollection.java Source code

Java tutorial

Introduction

Here is the source code for ec.tss.TsCollection.java

Source

/*
 * Copyright 2013 National Bank of Belgium
 *
 * Licensed under the EUPL, Version 1.1 or  as soon they will be approved 
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://ec.europa.eu/idabc/eupl
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and 
 * limitations under the Licence.
 */
package ec.tss;

import com.google.common.base.Strings;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import ec.tss.tsproviders.utils.MultiLineNameUtil;
import java.util.Iterator;
import java.util.List;

import ec.tstoolkit.IDocumented;
import ec.tstoolkit.MetaData;
import ec.tstoolkit.design.Development;
import ec.tstoolkit.timeseries.simplets.TsData;
import ec.tstoolkit.timeseries.simplets.TsDataTable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 *
 * @author Jean Palate
 */
@Development(status = Development.Status.Alpha)
public final class TsCollection implements ITsIdentified, IDocumented, Iterable<Ts> {

    private final TsMoniker m_moniker;
    private final String m_name;
    private MetaData m_metadata;
    private final List<Ts> m_ts;
    private volatile TsInformationType m_info;
    private volatile Set<TsMoniker> m_set;
    private String m_invalidDataCause;

    TsCollection(@Nullable String name) {
        m_name = Strings.nullToEmpty(name);
        m_moniker = new TsMoniker();
        m_metadata = null;
        m_ts = new ArrayList<>();
        m_info = TsInformationType.UserDefined;
        m_set = null;
        m_invalidDataCause = null;
    }

    TsCollection(@Nullable String name, @Nonnull TsMoniker moniker) {
        m_name = Strings.nullToEmpty(name);
        m_moniker = moniker;
        m_metadata = null;
        m_ts = new ArrayList<>();
        m_info = m_moniker.getSource() == null ? TsInformationType.UserDefined : TsInformationType.None;
        m_set = null;
        m_invalidDataCause = null;
    }

    TsCollection(@Nullable String name, @Nonnull TsMoniker moniker, @Nullable MetaData md,
            @Nullable Iterable<Ts> ts) {
        m_name = Strings.nullToEmpty(name);
        m_moniker = moniker;
        m_metadata = md;
        m_ts = ts != null ? Lists.newArrayList(ts) : new ArrayList<>();
        m_info = TsInformationType.UserDefined;
        m_set = null;
        m_invalidDataCause = null;
    }

    @Override
    public String getName() {
        return m_name;
    }

    public String getInvalidDataCause() {
        return m_invalidDataCause;
    }

    public void setInvalidDataCause(String message) {
        m_invalidDataCause = message;
    }

    /**
     *
     * @param ts
     * @return
     */
    public boolean add(Ts ts) {
        if (quietAdd(ts)) {
            TsFactory.instance.notify(this, TsInformationType.Definition, this);
            return true;
        } else {
            return false;
        }
    }

    public boolean quietAdd(Ts ts) {
        if (isLocked()) {
            return false;
        }
        synchronized (m_moniker) {
            if (contains(ts)) {
                return false;
            }
            m_ts.add(ts);
            m_set = null;
        }
        return true;
    }

    /**
     *
     * @param list
     * @return
     */
    public int append(Iterable<? extends Ts> list) {
        int n = quietAppend(list);
        if (n > 0) {
            TsFactory.instance.notify(this, TsInformationType.Definition, this);
        }
        return n;
    }

    public int quietAppend(Iterable<? extends Ts> list) {
        if (isLocked()) {
            return 0;
        }
        synchronized (m_moniker) {
            List<Ts> tmp = new ArrayList<>();
            for (Ts s : list) {
                if (!contains(s) && !tmp.contains(s)) {
                    tmp.add(s);
                }
            }
            if (!tmp.isEmpty()) {
                m_ts.addAll(tmp);
                m_set = null;
                return tmp.size();
            } else {
                return 0;
            }
        }
    }

    /**
     *
     * @param c
     * @return
     */
    public int append(TsCollection c) {
        return append(c.m_ts);
    }

    public int quietAppend(TsCollection c) {
        return quietAppend(c.m_ts);
    }

    private void buildSet() {
        m_set = m_ts.stream().map(Ts::getMoniker).collect(Collectors.toSet());
    }

    /**
     *
     * @param empty
     * @return
     */
    public TsCollection clean(boolean empty) {
        synchronized (m_moniker) {
            List<Ts> list = new ArrayList(m_ts.size());
            for (Ts s : m_ts) {
                if (s.hasData() == TsStatus.Valid) {
                    list.add(s);
                } else if (s.hasData() == TsStatus.Undefined) {
                    s.load(TsInformationType.Data);
                    if (s.hasData() == TsStatus.Valid) {
                        list.add(s);
                    }
                }
            }
            if (list.isEmpty() && !empty) {
                return null;
            }
            return TsFactory.instance.createTsCollection(m_name, new TsMoniker(), null, list);
        }
    }

    /**
     *
     * @param cntVal
     * @param empty
     * @return
     */
    public TsCollection clean(double cntVal, boolean empty) {
        synchronized (m_moniker) {
            List<Ts> list = new ArrayList(m_ts.size());
            for (Ts s : m_ts) {
                if (s.hasData() == TsStatus.Undefined) {
                    s.load(TsInformationType.Data);
                }
                if (s.hasData() == TsStatus.Valid) {
                    TsData data = s.getTsData();
                    for (int i = 0; i < data.getLength(); ++i) {
                        double d = data.get(i);
                        if (!Double.isNaN(d) && d != cntVal) {
                            list.add(s);
                            break;
                        }
                    }
                }
            }
            if (list.isEmpty() && !empty) {
                return null;
            }
            return TsFactory.instance.createTsCollection(m_name, new TsMoniker(), null, list);
        }
    }

    /**
     *
     */
    public void clear() {
        if (isLocked()) {
            return;
        }
        synchronized (m_moniker) {
            if (m_ts.isEmpty()) {
                return;
            }
            m_ts.clear();
            m_set = null;
        }
        TsFactory.instance.notify(this, TsInformationType.Definition, this);
    }

    /**
     *
     * @param s
     * @return
     */
    public boolean contains(Ts s) {
        synchronized (m_moniker) {
            if (m_set == null) {
                buildSet();
            }
            return m_set.contains(s.getMoniker());
        }
    }

    public Ts[] retain(Iterable<Ts> all) {
        synchronized (m_moniker) {
            if (all == null) {
                return new Ts[0];
            } else {
                if (m_set == null) {
                    buildSet();
                }
                return Streams.stream(all).filter(o -> m_set.contains(o.getMoniker())).toArray(Ts[]::new);
            }
        }
    }

    /**
     *
     * @return
     */
    public Ts[] toArray() {
        synchronized (m_moniker) {
            return m_ts.toArray(new Ts[m_ts.size()]);
        }
    }

    public int indexOf(Ts ts) {
        synchronized (m_moniker) {
            for (int i = 0; i < m_ts.size(); i++) {
                if (ts.equals(m_ts.get(i))) {
                    return i;
                }
            }
            return -1;
        }
    }

    /**
     *
     * @param idx
     * @return
     */
    public Ts get(int idx) {
        synchronized (m_moniker) {
            return m_ts.get(idx);
        }
    }

    /**
     * Gets all the available data in the collection.
     *
     * @return A list containing the data. Missing series are not included in
     * the list, so that the number of items in the list could differ from the
     * number of items in the collection.
     */
    public List<TsData> getAllData() {
        Ts[] all = this.toArray();
        ArrayList<TsData> data = new ArrayList<>();
        for (int i = 0; i < all.length; ++i) {
            if (all[i].hasData() == TsStatus.Undefined) {
                all[i].load(TsInformationType.Data);
            }
            if (all[i].hasData() == TsStatus.Valid) {
                data.add(all[i].getTsData());
            }
        }
        return data;
    }

    /**
     *
     * @return
     */
    public int getCount() {
        synchronized (m_moniker) {
            return m_ts.size();
        }
    }

    public boolean isEmpty() {
        synchronized (m_moniker) {
            return m_ts.isEmpty();
        }
    }

    /**
     *
     * @return
     */
    public TsInformationType getInformationType() {
        synchronized (m_moniker) {
            return m_info;
        }
    }

    @Override
    public MetaData getMetaData() {
        synchronized (m_moniker) {
            return m_metadata;
        }
    }

    @Override
    public TsMoniker getMoniker() {
        return m_moniker;
    }

    /**
     *
     * @return
     */
    public TsStatus hasMetaData() {
        if (m_info == TsInformationType.All || m_info == TsInformationType.MetaData
                || m_info == TsInformationType.UserDefined) {
            return m_metadata == null ? TsStatus.Invalid : TsStatus.Valid;
        } else {
            return TsStatus.Undefined;
        }
    }

    /**
     *
     * @param pos
     * @param ts
     * @return
     */
    public boolean insert(int pos, Ts ts) {
        if (isLocked()) {
            return false;
        }
        synchronized (m_moniker) {
            if (contains(ts)) {
                return false;
            }
            m_ts.add(pos, ts);
            m_set = null;
        }
        TsFactory.instance.notify(this, TsInformationType.Definition, this);
        return true;
    }

    /**
     *
     * @return
     */
    public boolean isLocked() {
        return m_moniker.getSource() != null;
    }

    @Override
    public Iterator<Ts> iterator() {
        return Iterators.forArray(toArray());
    }

    @Nonnull
    public Stream<Ts> stream() {
        return Stream.of(toArray());
    }

    /**
     *
     * @param type
     * @return
     */
    public boolean load(TsInformationType type) {
        // check if the information is available...
        if (m_info.encompass(type)) {
            return true;
        }
        if (m_moniker.isAnonymous()) {
            return m_ts.stream().map(o -> o.load(type)).reduce(Boolean.TRUE, (l, r) -> r && l);
        }
        return TsFactory.instance.load(this, type);
    }

    /**
     *
     * @return
     */
    public TsCollection makeCopy() {
        TsCollection coll = TsFactory.instance.createTsCollection(m_name);
        if (m_metadata != null) {
            coll.m_metadata = m_metadata.clone();
        }
        coll.m_ts.addAll(m_ts);
        return coll;
    }

    /**
     *
     * @param type
     * @return
     */
    public boolean query(TsInformationType type) {
        // check if the information is available...
        if (m_info.encompass(type)) {
            return true;
        }
        return TsFactory.instance.query(this, type);
    }

    /**
     *
     * @param type
     * @return
     */
    public boolean reload(TsInformationType type) {
        return TsFactory.instance.load(this, type);
    }

    /**
     *
     * @param c
     * @return
     */
    public int remove(Iterable<Ts> c) {
        if (isLocked()) {
            return 0;
        }
        int rslt = 0;
        synchronized (m_moniker) {
            List<Ts> tmp = new ArrayList<>();
            for (Ts s : c) {
                if (contains(s) && !tmp.contains(s)) {
                    tmp.add(s);
                }
            }
            if (!tmp.isEmpty()) {
                m_ts.removeAll(tmp);
                m_set = null;
                rslt = tmp.size();
            }
        }
        if (rslt > 0) {
            TsFactory.instance.notify(this, TsInformationType.Definition, this);
        }
        return rslt;
    }

    /**
     *
     * @param s
     * @return
     */
    public boolean remove(Ts s) {
        if (isLocked()) {
            return false;
        }
        synchronized (m_moniker) {
            if (!contains(s)) {
                return false;
            }
            m_ts.remove(s);
            m_set = null;
        }
        TsFactory.instance.notify(this, TsInformationType.Definition, this);
        return true;
    }

    /**
     *
     * @param pos
     */
    public void removeAt(int pos) {
        if (isLocked()) {
            return;
        }
        synchronized (m_moniker) {
            m_ts.remove(pos);
            m_set = null;
        }
        TsFactory.instance.notify(this, TsInformationType.Definition, this);
    }

    /**
     *
     * @param start
     * @param n
     */
    public void removeRange(int start, int n) {
        if (isLocked()) {
            return;
        }
        synchronized (m_moniker) {
            for (int pos = start + n - 1; pos >= start; --pos) {
                m_ts.remove(pos);
            }
            m_set = null;
        }
        TsFactory.instance.notify(this, TsInformationType.Definition, this);
    }

    /**
     *
     * @param c
     * @return
     */
    public boolean replace(Iterable<Ts> c) {
        if (quietReplace(c)) {
            TsFactory.instance.notify(this, TsInformationType.Definition, this);
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @param c
     * @return
     */
    public boolean quietReplace(Iterable<Ts> c) {
        if (isLocked()) {
            return false;
        }
        synchronized (m_moniker) {
            m_ts.clear();
            for (Ts s : c) {
                m_ts.add(s);
            }
            m_set = null;
        }
        return true;
    }

    /**
     *
     * @param s
     * @return
     */
    public boolean replace(Ts s) {
        if (quietReplace(s)) {
            TsFactory.instance.notify(this, TsInformationType.Definition, this);
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @param s
     * @return
     */
    public boolean replace(Ts... s) {
        if (quietReplace(s)) {
            TsFactory.instance.notify(this, TsInformationType.Definition, this);
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @param c
     * @return
     */
    public boolean quietReplace(Ts... c) {
        if (isLocked()) {
            return false;
        }
        synchronized (m_moniker) {
            m_ts.clear();
            if (c != null) {
                m_ts.addAll(Arrays.asList(c));
            }
            m_set = null;
        }
        return true;
    }

    public boolean quietReplace(Ts s) {
        if (isLocked()) {
            return false;
        }
        synchronized (m_moniker) {
            if (m_ts.size() == 1 && m_ts.get(0).getMoniker().equals(s.getMoniker())) {
                return true;
            }
            m_ts.clear();
            m_ts.add(s);
            m_set = null;
        }
        return true;
    }

    /**
     * Search a series by its name
     *
     * @param name The name of the series
     * @return The first series with the given name is returned
     */
    public Ts search(String name) {
        synchronized (m_moniker) {
            for (Ts s : m_ts) {
                if (s.getName().equals(name)) {
                    return s;
                }
            }
            return null;
        }
    }

    /**
     *
     * @param moniker
     * @return
     */
    public Ts search(TsMoniker moniker) {
        synchronized (m_moniker) {
            for (Ts s : m_ts) {
                if (s.getMoniker().equals(moniker)) {
                    return s;
                }
            }
            return null;
        }
    }

    public boolean rename(Ts s, String newname) {
        synchronized (m_moniker) {
            int pos = m_ts.indexOf(s);
            if (pos < 0) {
                return false;
            }
            m_ts.set(pos, m_ts.get(pos).rename(newname));
            return true;
        }
    }

    /**
     *
     * @param md
     * @return
     */
    public boolean set(MetaData md) {
        if (m_info != TsInformationType.UserDefined) {
            return false;
        }
        synchronized (m_moniker) {
            m_metadata = md;
        }
        TsFactory.instance.notify(this, TsInformationType.MetaData, this);
        return true;
    }

    List<Ts> update(TsCollectionInformation info) {
        synchronized (m_moniker) {
            List<Ts> updated;
            if (info.hasMetaData()) {
                m_metadata = info.metaData;
            }
            if (info.hasDefinition()) {
                // load the series...
                m_ts.clear();
                if (!info.items.isEmpty()) {
                    updated = new ArrayList(info.items.size());
                    for (TsInformation sinfo : info.items) {
                        Ts s = TsFactory.instance.getTs(sinfo.moniker);
                        if (s != null) {
                            s.getMaster().update(sinfo);
                            updated.add(s);
                        } else {
                            s = TsFactory.instance.createTs(sinfo);
                        }
                        m_ts.add(s);
                    }
                } else {
                    updated = Collections.emptyList();
                }
            } else {
                updated = Collections.emptyList();
            }
            m_info = m_info.union(info.type);
            m_invalidDataCause = info.invalidDataCause;
            return updated;
        }
    }

    @Override
    public String toString() {
        synchronized (m_moniker) {
            String[] headers = new String[m_ts.size()];
            List<TsData> all = getAllData();
            for (int i = 0; i < headers.length; ++i) {
                headers[i] = MultiLineNameUtil.last(m_ts.get(i).getName());
            }
            TsDataTable table = new TsDataTable();
            table.add(all);
            return table.toString(headers);
        }
    }

    @Override
    public boolean equals(Object obj) {
        return this == obj || (obj instanceof TsCollection && equals((TsCollection) obj));
    }

    @Override
    public int hashCode() {
        return getMoniker().hashCode();
    }

    private boolean equals(TsCollection other) {
        return other == null ? false : getMoniker().equals(other.getMoniker());
    }
}