001/**
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 *
006 */
007package org.fcrepo.migration.handlers;
008
009import java.util.ArrayList;
010import java.util.Comparator;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Map;
014import java.util.Collections;
015
016import org.fcrepo.migration.DatastreamVersion;
017import org.fcrepo.migration.ObjectInfo;
018import org.fcrepo.migration.ObjectProperties;
019import org.fcrepo.migration.ObjectReference;
020import org.fcrepo.migration.ObjectVersionReference;
021import org.fcrepo.migration.FedoraObjectVersionHandler;
022import org.fcrepo.migration.StreamingFedoraObjectHandler;
023
024/**
025 * A StreamingFedoraObjectHandler implementation that caches all the references to
026 * the Fedora 3 object and provides them to a FedoraObjectHandler implementation
027 * which in turn can process the object as a whole in a random-access fashion rather
028 * than as a stream.
029 * @author mdurbin
030 */
031public class ObjectAbstractionStreamingFedoraObjectHandler implements StreamingFedoraObjectHandler {
032
033    private FedoraObjectVersionHandler versionHandler;
034
035    private ObjectInfo objectInfo;
036
037    private ObjectProperties objectProperties;
038
039    private List<String> dsIds;
040
041    private Map<String, List<DatastreamVersion>> dsIdToVersionListMap;
042
043    /**
044     * the object abstraction streaming fedora object handler.
045     * @param versionHandler the fedora object version handler
046     */
047    public ObjectAbstractionStreamingFedoraObjectHandler(final FedoraObjectVersionHandler versionHandler) {
048        this.versionHandler = versionHandler;
049        this.dsIds = new ArrayList<String>();
050        this.dsIdToVersionListMap = new HashMap<String, List<DatastreamVersion>>();
051    }
052
053    @Override
054    public void beginObject(final ObjectInfo object) {
055        this.objectInfo = object;
056    }
057
058    @Override
059    public void processObjectProperties(final ObjectProperties properties) {
060        this.objectProperties = properties;
061    }
062
063    @Override
064    public void processDatastreamVersion(final DatastreamVersion dsVersion) {
065        List<DatastreamVersion> versions = dsIdToVersionListMap.get(dsVersion.getDatastreamInfo().getDatastreamId());
066        if (versions == null) {
067            dsIds.add(dsVersion.getDatastreamInfo().getDatastreamId());
068            versions = new ArrayList<DatastreamVersion>();
069            dsIdToVersionListMap.put(dsVersion.getDatastreamInfo().getDatastreamId(), versions);
070        }
071        versions.add(dsVersion);
072    }
073
074    @Override
075    public void completeObject(final ObjectInfo object) {
076        final var objectReference = getObjectReference();
077        try {
078            final Map<String, List<DatastreamVersion>> versionMap = buildVersionMap();
079            final List<String> versionDates = new ArrayList<String>(versionMap.keySet());
080            Collections.sort(versionDates);
081            final List<ObjectVersionReference> versions = new ArrayList<ObjectVersionReference>();
082            for (final String versionDate : versionDates) {
083                versions.add(getObjectVersionReference(versionDate, versionDates, objectReference, versionMap));
084            }
085
086            // Need to sort the datastream versions because they may not appear in order in FOXML
087            dsIdToVersionListMap.forEach((dsId, dsVersions) -> {
088                dsVersions.sort(Comparator.comparing(DatastreamVersion::getCreatedInstant));
089            });
090
091            versionHandler.processObjectVersions(versions, object);
092        } finally {
093            cleanForReuse();
094        }
095    }
096
097    @Override
098    public void abortObject(final ObjectInfo object) {
099        cleanForReuse();
100    }
101
102    /**
103     * Removes any state that's specific to a Fedora 3 object that was processed
104     * so that this Handler may be reused for a different object.
105     */
106    private void cleanForReuse() {
107        this.dsIds.clear();
108        this.dsIdToVersionListMap.clear();
109    }
110
111    private Map<String, List<DatastreamVersion>> buildVersionMap() {
112        final Map<String, List<DatastreamVersion>> versionMap = new HashMap<String, List<DatastreamVersion>>();
113        for (final String dsId : dsIds) {
114            for (final DatastreamVersion v : dsIdToVersionListMap.get(dsId)) {
115                final String date = v.getCreated();
116                List<DatastreamVersion> versionsForDate = versionMap.get(date);
117                if (versionsForDate == null) {
118                    versionsForDate = new ArrayList<DatastreamVersion>();
119                    versionMap.put(date, versionsForDate);
120                }
121                versionsForDate.add(v);
122            }
123        }
124        return versionMap;
125    }
126
127    private ObjectReference getObjectReference() {
128        return new ObjectReference() {
129            @Override
130            public ObjectInfo getObjectInfo() {
131                return objectInfo;
132            }
133
134            @Override
135            public ObjectProperties getObjectProperties() {
136                return objectProperties;
137            }
138
139            @Override
140            public List<String> listDatastreamIds() {
141                return dsIds;
142            }
143
144            @Override
145            public List<DatastreamVersion> getDatastreamVersions(final String datastreamId) {
146                return dsIdToVersionListMap.get(datastreamId);
147            }
148        };
149    }
150
151    private ObjectVersionReference getObjectVersionReference(final String versionDate, final List<String> versionDates,
152                                                             final ObjectReference objectReference,
153                                                             final Map<String, List<DatastreamVersion>> versionMap) {
154        return new ObjectVersionReference() {
155            @Override
156            public ObjectReference getObject() {
157                return objectReference;
158            }
159
160            @Override
161            public ObjectProperties getObjectProperties() {
162                return objectProperties;
163            }
164
165            @Override
166            public String getVersionDate() {
167                return versionDate;
168            }
169
170            @Override
171            public List<DatastreamVersion> listChangedDatastreams() {
172                return versionMap.get(versionDate);
173            }
174
175            @Override
176            public boolean isLastVersion() {
177                return versionDates.get(versionDates.size() - 1).equals(versionDate);
178            }
179
180            @Override
181            public boolean isFirstVersion() {
182                return versionDates.get(0).equals(versionDate);
183            }
184
185            @Override
186            public int getVersionIndex() {
187                return versionDates.indexOf(versionDate);
188            }
189
190            @Override
191            public boolean wasDatastreamChanged(final String dsId) {
192                for (final DatastreamVersion v : listChangedDatastreams()) {
193                    if (v.getDatastreamInfo().getDatastreamId().equals(dsId)) {
194                        return true;
195                    }
196                }
197                return false;
198            }
199        };
200    }
201}