/*
* #%L
* =====================================================
* _____ _ ____ _ _ _ _
* |_ _|_ __ _ _ ___| |_ / __ \| | | | ___ | | | |
* | | | '__| | | / __| __|/ / _` | |_| |/ __|| |_| |
* | | | | | |_| \__ \ |_| | (_| | _ |\__ \| _ |
* |_| |_| \__,_|___/\__|\ \__,_|_| |_||___/|_| |_|
* \____/
*
* =====================================================
*
* Hochschule Hannover
* (University of Applied Sciences and Arts, Hannover)
* Faculty IV, Dept. of Computer Science
* Ricklinger Stadtweg 118, 30459 Hannover, Germany
*
* Email: trust@f4-i.fh-hannover.de
* Website: http://trust.f4.hs-hannover.de/
*
* This file is part of visitmeta-dataservice, version 0.6.0,
* implemented by the Trust@HsH research group at the Hochschule Hannover.
* %%
* Copyright (C) 2012 - 2016 Trust@HsH
* %%
* Licensed 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.
* #L%
*/
package de.hshannover.f4.trust.visitmeta.persistence;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.apache.log4j.Logger;
import de.hshannover.f4.trust.visitmeta.dataservice.graphservice.NamespaceHolder;
import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalIdentifier;
import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalIdentifierGraph;
import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalIdentifierPair;
import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalLink;
import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalMetadata;
import de.hshannover.f4.trust.visitmeta.persistence.inmemory.InMemoryIdentifierGraph;
public abstract class AbstractReader implements Reader {
private static final Logger log = Logger.getLogger(AbstractReader.class);
protected Repository mRepo;
protected String mConnectionName;
protected void savePrexif(InternalMetadata m) {
String prefix = null, value = null;
for (String key : m.getProperties()) {
if (key.contains("@xmlns")) {
prefix = key.substring(key.indexOf("@xmlns") + 7,
key.length() - 1);
value = m.valueFor(key);
}
}
if (prefix != null && value != null) {
NamespaceHolder.addPrefix(mConnectionName, prefix, value);
}
}
@Override
public List<InternalIdentifierGraph> getCurrentState() {
log.debug("reading current graph state from repository ...");
return getGraphAt(Long.MAX_VALUE);
}
/**
* @param available
* @param start
* @param timestamp
* @return
*/
private InternalIdentifierGraph buildGraph(
List<InternalIdentifier> available,
InternalIdentifier start,
long timestamp
) {
InMemoryIdentifierGraph graph = new InMemoryIdentifierGraph(timestamp);
HashSet<InternalIdentifier> seen = new HashSet<>();
detachIdentifierSingleGraph(start, seen, graph, timestamp);
available.removeAll(seen);
return graph;
}
/**
* Detach the identifier <tt>current</tt> and recursively all its neighbors
* from the neo4j database graph. The link between two identifiers is created
* by a backtracking-like approach:
* <ul>
* <li>first detach the current identifier and store it into the graph</li>
* <li>iterate over all links of the current identifier</li>
* <ul>
* <li>if the other identifier on a link is already in the graph, detach/create
* the link between the current and the other identifier</li>
* <li>if the other identifier is not in the graph, start the recursion for the
* other identifier</li>
* </ul>
* </ul>
*
* @param current
* @param seen
* @param graph
* @param timestamp
*/
private void detachIdentifierSingleGraph(
InternalIdentifier current,
HashSet<InternalIdentifier> seen,
InMemoryIdentifierGraph graph,
long timestamp
) {
InternalIdentifier detachedCurrent = null;
if (!seen.contains(current)) {
if (current.isValidAt(timestamp)) {
detachedCurrent = graph.insert(current);
detachIdentifierMetadata(current, detachedCurrent, graph, timestamp);
seen.add(current);
for (InternalLink l : current.getLinks()) {
if (l.isValidAt(timestamp)) {
InternalIdentifierPair pair = l.getIdentifiers();
InternalIdentifier other = (pair.getFirst().equals(current)) ? pair.getSecond() : pair.getFirst();
if (seen.contains(other)) {
if (other.isValidAt(timestamp)) {
InternalIdentifier detachedOther = graph.findIdentifier(other);
InternalLink link = graph.connect(detachedCurrent, detachedOther);
detachLinkMetadata(l, link, graph, timestamp);
}
} else {
detachIdentifierSingleGraph(other, seen, graph, timestamp);
}
}
}
}
}
}
/**
* Detaches the Metadata of Identifier <tt>from</tt> by copying it to Identifier <tt>to</tt>.
* @param from
* @param to
* @param graph
* @param timestamp
*/
private void detachIdentifierMetadata(
InternalIdentifier from,
InternalIdentifier to,
InMemoryIdentifierGraph graph,
long timestamp
) {
for (InternalMetadata m : from.getMetadata()) {
this.savePrexif(m);
if (m.isValidAt(timestamp)) {
InternalMetadata loadedMeta = graph.insert(m);
graph.connectMeta(to, loadedMeta);
}
}
}
/**
* Detaches the Metadata of Link <tt>from</tt> by copying it to Link <tt>to</tt>
* @param from
* @param to
* @param graph
* @param timestamp
*/
private void detachLinkMetadata(
InternalLink from,
InternalLink to,
InMemoryIdentifierGraph graph,
long timestamp
) {
for (InternalMetadata m : from.getMetadata()) {
this.savePrexif(m);
if (m.isValidAt(timestamp)) {
InternalMetadata loadedMeta = graph.insert(m);
graph.connectMeta(to, loadedMeta);
}
}
}
@Override
public List<InternalIdentifierGraph> getGraphAt(long timestamp) {
log.debug("Get the graph at "+timestamp);
ArrayList<InternalIdentifier> validIds = new ArrayList<>();
ArrayList<InternalIdentifierGraph> graph = new ArrayList<>();
for (InternalIdentifier current : mRepo.getAllIdentifier()) {
if(current.isValidAt(timestamp)){
validIds.add(current);
}
}
if (validIds.isEmpty()) {
graph.add(new InMemoryIdentifierGraph(timestamp));
return graph;
}
while (!validIds.isEmpty()) {
InternalIdentifier start = validIds.get(0);
graph.add(buildGraph(validIds, start, timestamp));
}
return graph;
}
@Override
public List<InternalIdentifierGraph> getNotifiesAt(long timestamp) {
log.debug("Get the notifies at "+timestamp);
ArrayList<InternalIdentifierGraph> notifies = new ArrayList<>();
for(InternalIdentifier current : mRepo.getAllIdentifier()) {
for(InternalMetadata meta : current.getMetadata()) {
if(meta.isNotify() && meta.getPublishTimestamp() == timestamp) {
InMemoryIdentifierGraph temp = new InMemoryIdentifierGraph(timestamp);
InternalIdentifier first = temp.insert(current);
InternalMetadata newMeta = temp.insert(meta);
temp.connectMeta(first, newMeta);
notifies.add(temp);
}
}
for(InternalLink link : current.getLinks()) {
if(checkIfListContainsLink(notifies, link)) {
continue;
}
for(InternalMetadata meta : link.getMetadata()) {
if(meta.isNotify() && meta.getPublishTimestamp() == timestamp) {
InMemoryIdentifierGraph temp = new InMemoryIdentifierGraph(timestamp);
InternalIdentifierPair ids = link.getIdentifiers();
InternalIdentifier first = temp.insert(ids.getFirst());
InternalIdentifier second = temp.insert(ids.getSecond());
InternalMetadata newMeta = temp.insert(meta);
InternalLink newLink = temp.connect(first, second);
temp.connectMeta(newLink, newMeta);
notifies.add(temp);
}
}
}
}
return notifies;
}
private boolean checkIfListContainsLink(List<InternalIdentifierGraph> list, InternalLink link) {
for(InternalIdentifierGraph graph : list) {
for(InternalIdentifier current : graph.getIdentifiers()) {
for(InternalLink tmpLink : current.getLinks())
if(link.equals(tmpLink)) {
return true;
}
}
}
return false;
}
@Override
public abstract long getTimeOfLastUpdate();
}