Package org.geoserver.gss

Source Code of org.geoserver.gss.MergingFeatureDiffReader

/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.gss;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;

import org.geotools.data.FeatureDiff;
import org.geotools.data.FeatureDiffReader;
import org.geotools.data.postgis.FeatureDiffImpl;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

/**
* Merges the diffs of the various delegates into one. Delegates will be examined in order to build
* a global diff. All changes to feaures under conflict will be ignored. This class is used to get a
* history of all the local changes that still haven't been communicated to Central, skipping
* Central own changes.
*
* @author Andrea Aime - OpenGeo
*
*/
class MergingFeatureDiffReader implements FeatureDiffReader {

    /**
     * The readers that we need to merge. They are supposed to be sorted
     */
    FeatureDiffReader[] delegates;

    /**
     * The lowest fromVersion
     */
    String fromVersion;

    /**
     * The highest toVersion
     */
    String toVersion;

    /**
     * The schema shared by all readers
     */
    SimpleFeatureType schema;

    /**
     * The set of fids that we have "live" FeatureDiff for. At even next() we just want to grab the
     * lowest and proces it.
     */
    TreeSet<String> sortedFids = new TreeSet<String>();

    /**
     * A map from the ids to the set of FeatureDiff gathered from the readers. Usually we'll just
     * have one of them, but we might have one per readers. The various diffs are placed in the same
     * order as the readers, there might be holes.
     */
    Map<String, FeatureDiff[]> diffHistory = new HashMap<String, FeatureDiff[]>();

    /**
     * Flag marking if we have an outstanding FeatureDiff to be processed for the corresponding
     * reader. Since the readers return diffs in feature id order we just need to keep one
     * FeatureDiff per reader at any time.
     */
    boolean[] featureRead;

    /**
     * The next difference we're going to return
     */
    FeatureDiff nextDifference;
   
    /**
     * Builds a new merging reader. Mind, the order of the delegates is important, as changes are
     * built up in order
     *
     * @param delegates
     */
    public MergingFeatureDiffReader(FeatureDiffReader... delegates) throws IOException {
        this.delegates = new FeatureDiffReader[delegates.length];
        System.arraycopy(delegates, 0, this.delegates, 0, delegates.length);
        this.featureRead = new boolean[delegates.length];
        this.fromVersion = delegates[0].getFromVersion();
        this.toVersion = delegates[delegates.length - 1].getToVersion();
        this.schema = delegates[0].getSchema();
    }

    public String getFromVersion() {
        return fromVersion;
    }

    public SimpleFeatureType getSchema() {
        return schema;
    }

    public String getToVersion() {
        return toVersion;
    }

    public boolean hasNext() throws IOException {
        if (nextDifference != null) {
            return true;
        }

        advance();
        while (sortedFids.size() > 0) {
            nextDifference = buildNextDiff();
            if (nextDifference != null && (nextDifference.getState() != FeatureDiff.UPDATED
                    || nextDifference.getChangedAttributes().size() > 0)) {
                return true;
            } else {
                // we grabbed the result of a revert over modifications -> two modification changes
                // that amount to no global change. Let's move to the next set of changes.
                nextDifference = null;
                advance();
            }
        }

        // if we got here it means the set of fids to process dried up
        return false;
    }

    public FeatureDiff next() throws IOException, NoSuchElementException {
        if (!hasNext()) {
            throw new NoSuchElementException("No more feature diffs in this reader");
        }

        FeatureDiff result = nextDifference;
        nextDifference = null;

        return result;
    }

    /**
     * Builds the next difference object using the stored fids and diffs (assumes you called
     * advance() before calling it)
     *
     * @return
     */
    FeatureDiff buildNextDiff() {
        String fid = sortedFids.first();
        sortedFids.remove(fid);
        FeatureDiff[] history = diffHistory.remove(fid);
        // cases we need to handle
        // - we have only updates -> build a cumulative update
        // - we have insert and then updates -> build a new insert with the final values
        // - we have a remove at the end -> forget the rest, it is a remove
        // - we have some diff, then a remove and then an insert -> the remove has been rolled back
        // but we don't really know if the roll back included also the roll backs of
        // updates -> the set of changes has to be rebuilt
        SimpleFeature from = null;
        SimpleFeature to = null;
        boolean removed = false;
        for (int i = 0; i < history.length; i++) {
            FeatureDiff diff = history[i];
            if (diff != null) {
                if (diff.getState() == FeatureDiff.INSERTED) {
                    // is this the rollback of a removal?
                    if(removed == true) {
                        from = null;
                        to = null;
                    } else {
                        from = null;
                        to = diff.getFeature();
                    }
                    removed = false;
                } else if (diff.getState() == FeatureDiff.DELETED) {
                    if (from == null) {
                        from = diff.getOldFeature();
                    }
                    to = null;
                    removed = true;
                } else {
                    // might we have a removed flag before this update? Maybe... if Central
                    // for some reason reinserted the feature (remember we're skipping
                    // changes coming from Central. But in that case we start over fresh
                    if (removed) {
                        removed = false;
                        from = diff.getOldFeature();
                    }

                    // is this the first diff or we have a history?
                    if (from == null) {
                        from = diff.getOldFeature();
                    }
                    to = diff.getFeature();
                }
            }
        }
       
        if(from == null && to == null) {
            return null;
        }

        return new FeatureDiffImpl(from, to);
    }

    /**
     * Reads a new FeatureDiff from all readers that still don't have a pending one and that are
     * still open
     */
    void advance() throws IOException {
        for (int i = 0; i < delegates.length; i++) {
            if (delegates[i] != null && !featureRead[i]) {
                // read the next diff that is not linked to a conflicting feature
                while (delegates[i].hasNext()) {
                    // grab a new feature diff
                    FeatureDiff fd = delegates[i].next();
                    String fid = fd.getID();
                   
                    // is it about a feature id we already know about?
                    FeatureDiff[] history;
                    if (sortedFids.contains(fid)) {
                        history = diffHistory.get(fid);
                    } else {
                        history = new FeatureDiff[delegates.length];
                        diffHistory.put(fid, history);
                        sortedFids.add(fid);
                    }
                    history[i] = fd;
                    // ok, we're done
                    break;
                }
               
                // did we exit the loop because the reader ended?
                if(!delegates[i].hasNext()) {
                    // close the delegate and get rid of it
                    delegates[i].close();
                    delegates[i] = null;
                }
            }
        }
    }

    public void close() throws IOException {
        for (int i = 0; i < delegates.length; i++) {
            if (delegates[i] != null) {
                delegates[i].close();
                delegates[i] = null;
            }
        }
    }

}
TOP

Related Classes of org.geoserver.gss.MergingFeatureDiffReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.