org.tellervo.desktop.io.TridasDoc.java Source code

Java tutorial

Introduction

Here is the source code for org.tellervo.desktop.io.TridasDoc.java

Source

/*******************************************************************************
 * Copyright (C) 2011 Peter Brewer.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Peter Brewer
 ******************************************************************************/
package org.tellervo.desktop.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.math.util.MultidimensionalCounter.Iterator;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tellervo.desktop.Range;
import org.tellervo.desktop.Year;
import org.tellervo.desktop.sample.BaseSample;
import org.tellervo.desktop.sample.CachedElement;
import org.tellervo.desktop.sample.ElementList;
import org.tellervo.desktop.sample.Sample;
import org.tellervo.desktop.sample.SampleType;
import org.tellervo.desktop.tridasv2.LabCode;
import org.tellervo.desktop.tridasv2.LabCodeFormatter;
import org.tellervo.desktop.tridasv2.SeriesLinkUtil;
import org.tellervo.desktop.tridasv2.TridasIdentifierMap;
import org.tellervo.desktop.ui.I18n;
import org.tellervo.desktop.wsi.tellervo.TridasGenericFieldMap;
import org.tridas.interfaces.ITridasDerivedSeries;
import org.tridas.interfaces.ITridasSeries;
import org.tridas.io.util.TridasUtils;
import org.tridas.schema.NormalTridasVariable;
import org.tridas.schema.TridasElement;
import org.tridas.schema.TridasGenericField;
import org.tridas.schema.TridasIdentifier;
import org.tridas.schema.TridasObject;
import org.tridas.schema.TridasProject;
import org.tridas.schema.TridasRadius;
import org.tridas.schema.TridasSample;
import org.tridas.schema.TridasValues;
import org.tridas.schema.TridasVariable;

public class TridasDoc implements Filetype {
    private final static Logger log = LoggerFactory.getLogger(TridasDoc.class);

    @Override
    public String toString() {
        return I18n.getText("format.tridas") + " (*" + getDefaultExtension() + ")";
    }

    public String getDefaultExtension() {
        return ".xml";
    }

    private void breakUpTridasLinks(TridasProject p) {
        // first, disassociate any children of child objects
        if (p.isSetObjects()) {

            for (TridasObject o : p.getObjects()) {
                breakUpTridasLinks(o);
            }
        }
    }

    private void breakUpTridasLinks(TridasObject obj) {
        // first, disassociate any children of child objects
        if (obj.isSetObjects()) {
            for (TridasObject childObj : obj.getObjects())
                breakUpTridasLinks(childObj);

            obj.unsetObjects();
        }

        // now, disassociate everything else
        for (TridasElement element : obj.getElements()) {
            for (TridasSample sample : element.getSamples()) {
                for (TridasRadius radius : sample.getRadiuses()) {
                    //for(TridasMeasurementSeries series : radius.getMeasurementSeries()) {
                    // do we need to do anything in here?
                    //}
                    radius.unsetMeasurementSeries();
                }
                sample.unsetRadiuses();
            }
            element.unsetSamples();
        }
        obj.unsetElements();
    }

    private void loadProjectMeasurementsIntoList(TridasProject p, List<BaseSample> samples,
            TridasIdentifierMap<BaseSample> references, List<TridasObject> objectHierarchy) throws IOException {

        for (TridasObject o : p.getObjects()) {
            loadObjectMeasurementsIntoList(o, samples, references, objectHierarchy);
        }

    }

    /**
     * Load an object tree!
     * 
     * @param obj
     *       the base object from which to start loading
     * @param samples
     *       a list of basesamples we've loaded
     * @param references
     *       a map from identifier->sample for samples we've already loaded
     * @param objectHierarchy
     *       the hierarchy of objects at this depth (starts as an empty list)
     * @throws IOException
     */
    private void loadObjectMeasurementsIntoList(TridasObject obj, List<BaseSample> samples,
            TridasIdentifierMap<BaseSample> references, List<TridasObject> objectHierarchy) throws IOException {

        // add myself to the hierarchy
        objectHierarchy.add(obj);

        // create an array of the hierarchy so far (not modified by next recursive call)
        TridasObject[] objArray = objectHierarchy.toArray(new TridasObject[0]);

        // do child objects first
        for (TridasObject child : obj.getObjects())
            loadObjectMeasurementsIntoList(child, samples, references, objectHierarchy);

        // ok, now load some samples from this tree!
        for (TridasElement element : obj.getElements()) {
            for (TridasSample sample : element.getSamples()) {
                for (TridasRadius radius : sample.getRadiuses()) {
                    for (ITridasSeries series : radius.getMeasurementSeries()) {
                        BaseSample s = loadFromBaseSeries(series, references);

                        s.setMeta(Metadata.OBJECT, obj);
                        s.setMeta(Metadata.OBJECT_ARRAY, objArray);
                        s.setMeta(Metadata.ELEMENT, element);
                        s.setMeta(Metadata.SAMPLE, sample);
                        s.setMeta(Metadata.RADIUS, radius);
                        // series is set in loadFromBaseSeries()

                        // (re-)populate lab code
                        LabCode labcode = (LabCode) s.getMeta(Metadata.LABCODE);
                        if (labcode == null)
                            labcode = new LabCode();

                        // object codes are more obnoxious
                        labcode.clearSites();
                        for (TridasObject object : objArray) {
                            for (TridasGenericField f : object.getGenericFields()) {
                                if (TridasUtils.GENERIC_FIELD_STRING_OBJECTCODE.equals(f.getName())) {
                                    labcode.appendSiteCode(f.getValue());
                                }
                            }

                            labcode.appendSiteTitle(object.getTitle());
                        }

                        // this stuff is easier
                        labcode.setElementCode(element.getTitle());
                        labcode.setSampleCode(sample.getTitle());
                        labcode.setRadiusCode(radius.getTitle());
                        labcode.setSeriesCode(series.getTitle());

                        // populate new metadata
                        s.setMeta(Metadata.LABCODE, labcode);
                        s.setMeta(Metadata.TITLE, LabCodeFormatter.getDefaultFormatter().format(labcode));

                        // add it to our list
                        samples.add(s);
                    }
                }
            }
        }
    }

    /**
     * Returns an array of BaseSamples derived from this object
     * 
     * @param obj The base tridas object
     * @param appendSamples the list to append new samples onto (must not be null)
     * @param references a map from identifier->sample for each already loaded object
     * @param disassociate if true, takes the object list given by 'obj' and breaks all
     *       parent->child links (to prevent one sample from holding on to another sample 
     *       in garbage collection, for instance)
     * @return
     */
    public List<BaseSample> loadFromObject(TridasObject obj, List<BaseSample> appendSamples,
            TridasIdentifierMap<BaseSample> references, boolean disassociate) throws IOException {

        loadObjectMeasurementsIntoList(obj, appendSamples, references, new ArrayList<TridasObject>());

        if (disassociate)
            breakUpTridasLinks(obj);

        return appendSamples;
    }

    public List<BaseSample> loadFromProject(TridasProject p, List<BaseSample> appendSamples,
            TridasIdentifierMap<BaseSample> references, boolean disassociate) throws IOException {

        loadProjectMeasurementsIntoList(p, appendSamples, references, new ArrayList<TridasObject>());

        if (disassociate)
            breakUpTridasLinks(p);

        return appendSamples;
    }

    /**
     * @see loadFromObject(TridasObject, List, boolean)
     * @param obj
     * @param disassociate
     * @return
     */
    public List<BaseSample> loadFromObject(TridasObject obj, boolean disassociate) throws IOException {
        return loadFromObject(obj, new ArrayList<BaseSample>(), new TridasIdentifierMap<BaseSample>(),
                disassociate);
    }

    /**
     * "Finishes" a derived sample
     * - Loads elements into a sample for a derived series
     * - Creates labcodes where applicable
     * @param s
     * @param series
     * @param references
     */
    public void finishDerivedSample(Sample s, TridasIdentifierMap<BaseSample> references) {

        if (!(s.getSeries() instanceof ITridasDerivedSeries))
            throw new IllegalArgumentException("loadReferences requires derived series!");

        // only works with a derived series
        ITridasDerivedSeries series = (ITridasDerivedSeries) s.getSeries();

        // the list of elements
        ElementList elements = new ElementList();

        // go through the linkseries and use the identifiers
        List<TridasIdentifier> identifiers = SeriesLinkUtil.getIdentifiers(series.getLinkSeries());

        for (TridasIdentifier identifier : identifiers) {
            BaseSample ref = references.get(identifier);

            if (ref != null) {
                // easy enough, found the reference
                elements.add(new CachedElement(ref));
            } else {
                log.error("Sample " + s + " references unknown element: [" + identifier.getDomain() + "] "
                        + identifier.getValue());
                log.debug("References list has " + references.size() + " items in it");
                for (TridasIdentifier key : references.keySet()) {
                    log.debug("   - [" + key.getDomain() + "] " + key.getValue());
                }
            }
        }

        s.setElements(elements);

        LabCode labcode = null;

        // if it's derived from one thing, try to get its lab code and change it for our purposes
        if (elements.size() == 1) {
            try {
                BaseSample ref = elements.get(0).loadBasic();
                if (ref.hasMeta(Metadata.LABCODE)) {
                    labcode = new LabCode(ref.getMeta(Metadata.LABCODE, LabCode.class));
                    labcode.setSeriesCode(series.getTitle());
                }
            } catch (IOException e) {
                // oh well
            }
        }

        // no lab code? make a sad one
        if (labcode == null) {
            labcode = new LabCode();
            labcode.setSeriesCode(series.getTitle());
        }

        s.setMeta(Metadata.LABCODE, labcode);
        s.setMeta(Metadata.TITLE, LabCodeFormatter.getDefaultFormatter().format(labcode));
    }

    /**
     * Given a derivedSeries or measurementSeries, load into a BaseSample 
     * (or a sample, if the series has values)
     * 
     * @param series The series to generate this BaseSample/Sample from
     * @param references A list of samples appearing before this in xml which this sample can reference in loading (can be null)
     * @return a BaseSample or Sample encapsulating this series
     * @throws IOException
     */
    public BaseSample loadFromBaseSeries(ITridasSeries series, TridasIdentifierMap<BaseSample> references)
            throws IOException {
        BaseSample s;

        // if it has values, it's a sample. Otherwise, it's a basesample.
        if (series.isSetValues())
            s = new Sample(series);
        else
            s = new BaseSample(series);

        // add to references
        references.put(s);

        // Start with a basic title
        s.setMeta(Metadata.TITLE, series.isSetTitle() ? series.getTitle() : series.getIdentifier().toString());

        // set up SampleType
        if (series instanceof ITridasDerivedSeries) {
            ITridasDerivedSeries derived = (ITridasDerivedSeries) series;
            s.setSampleType(SampleType.fromString(derived.getType().getValue()));
        } else
            s.setSampleType(SampleType.DIRECT);

        // prep generic fields
        TridasGenericFieldMap genericFields = new TridasGenericFieldMap(series.getGenericFields());

        // easy metadata bits
        s.setMeta(Metadata.TRIDAS_IDENTIFIER, series.getIdentifier());
        s.setMeta(Metadata.CREATED_TIMESTAMP,
                series.getCreatedTimestamp().getValue().toGregorianCalendar().getTime());
        s.setMeta(Metadata.MODIFIED_TIMESTAMP,
                series.getLastModifiedTimestamp().getValue().toGregorianCalendar().getTime());

        // count of direct children
        s.setMeta(Metadata.CHILD_COUNT, genericFields.getInteger("tellervo.directChildCount", 0));

        // reconciled only works on Direct VMs
        if (s.getSampleType() == SampleType.DIRECT) {
            // set it to the value of reconciled, or false if it's not present
            if (genericFields.containsKey("tellervo.isReconciled"))
                s.setMeta(Metadata.RECONCILED, genericFields.getBoolean("tellervo.isReconciled"));
            else
                s.setMeta(Metadata.RECONCILED, Boolean.FALSE);
        }

        // translate start year
        Year firstYear;
        if (series.isSetInterpretation() && series.getInterpretation().isSetFirstYear()) {
            org.tridas.schema.Year tridasFirstYear = series.getInterpretation().getFirstYear();

            int y = tridasFirstYear.getValue().intValue();

            switch (tridasFirstYear.getSuffix()) {
            case AD:
                firstYear = new Year(y);
                break;
            case BC:
                firstYear = new Year(-y);
                break;
            case BP: // years before 1950
                firstYear = new Year(1950 - y);
                break;
            default:
                throw new InvalidDataException("Invalid year data: Suffix of unknown type.");
            }
        } else
            firstYear = new Year(); // default to standard year

        // this has values: it's a standard/comprehensive VM
        if (series.isSetValues()) {
            for (TridasValues valuesElement : series.getValues()) {
                TridasVariable variable = valuesElement.getVariable();

                // seek out tridas normal "ring width," and use the size of that to create our range
                if (variable.isSetNormalTridas()) {
                    if (variable.getNormalTridas() == NormalTridasVariable.RING_WIDTH) {
                        // compute our range!
                        s.setRange(new Range(firstYear, valuesElement.getValues().size()));
                        s.setMeta(Metadata.UNITS, valuesElement.getUnit());
                    }
                }
            }
        }
        // no values: summary VM
        else {
            // make up our range...
            s.setRange(new Range(firstYear, genericFields.getInteger("tellervo.readingCount", 0)));

            // ok, build lab code...
            LabCode labcode = new LabCode();

            for (int objectIdx = 1;; objectIdx++) {
                String objectCode = genericFields.getString("tellervo.objectCode." + objectIdx);
                String objectTitle = genericFields.getString("tellervo.objectTitle." + objectIdx);

                // we're done here!
                if (objectCode == null || objectTitle == null)
                    break;

                labcode.appendSiteCode(objectCode);
                labcode.appendSiteTitle(objectTitle);
            }

            // ok if these are null
            labcode.setElementCode(genericFields.getString("tellervo.elementTitle"));
            labcode.setRadiusCode(genericFields.getString("tellervo.radiusTitle"));
            labcode.setSampleCode(genericFields.getString("tellervo.sampleTitle"));
            labcode.setSeriesCode(series.getTitle());

            s.setMeta(Metadata.LABCODE, labcode);
            s.setMeta(Metadata.TITLE, LabCodeFormatter.getDefaultFormatter().format(labcode));

            // set up summary metadata
            if (genericFields.containsKey("tellervo.seriesCount") && s.getSampleType() == SampleType.SUM)
                s.setMeta(Metadata.SUMMARY_SUM_CONSTITUENT_COUNT, genericFields.getInteger("tellervo.seriesCount"));
            if (genericFields.containsKey("tellervo.summaryTaxonName"))
                s.setMeta(Metadata.SUMMARY_MUTUAL_TAXON, genericFields.getString("tellervo.summaryTaxonName"));
            if (genericFields.containsKey("tellervo.summaryTaxonCount"))
                s.setMeta(Metadata.SUMMARY_MUTUAL_TAXON_COUNT,
                        genericFields.getInteger("tellervo.summaryTaxonCount"));

            // Version
            if (series instanceof ITridasDerivedSeries) {
                s.setMeta(Metadata.VERSION, ((ITridasDerivedSeries) series).getVersion());
            }
        }

        return s;
    }

    public void loadSeries(Sample s, Element root) throws IOException {

    }

    public Sample load(BufferedReader r) throws IOException, WrongFiletypeException {
        // quickly check: am I an XML document?
        quickVerify(r);

        Sample s = new Sample();
        Document doc;

        try {
            doc = new SAXBuilder().build(r);
        } catch (JDOMException jdome) {
            log.error(jdome.getLocalizedMessage());
            throw new WrongFiletypeException();
        }

        Element root = doc.getRootElement();

        // no root element??
        if (root == null)
            throw new WrongFiletypeException();

        if (root.getName().equals("measurementSeries") || root.getName().equals("derivedSeries")) {
            loadSeries(s, root);
        } else
            throw new WrongFiletypeException();

        return s;
    }

    public void save(Sample s, BufferedWriter w) throws IOException {

    }

    /**
     * Quickly check to see if it's an XML document
     * @param r
     * @throws IOException
     */
    private void quickVerify(BufferedReader r) throws IOException {
        r.mark(4096);

        String firstLine = r.readLine();
        if (firstLine == null || !firstLine.startsWith("<?xml"))
            throw new WrongFiletypeException();

        r.reset();
    }

    public Boolean isPackedFileCapable() {
        return true;
    }

    public String getDeficiencyDescription() {
        return null;
    }

    public Boolean isLossless() {
        return true;
    }

}