/*
* Copyright (C) 2014 Jan Pokorsky
*
* 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/>.
*/
package cz.cas.lib.proarc.common.object;
import com.yourmediashelf.fedora.client.FedoraClientException;
import cz.cas.lib.proarc.common.fedora.DigitalObjectException;
import cz.cas.lib.proarc.common.fedora.DigitalObjectNotFoundException;
import cz.cas.lib.proarc.common.fedora.FedoraObject;
import cz.cas.lib.proarc.common.fedora.SearchView;
import cz.cas.lib.proarc.common.fedora.SearchView.Item;
import cz.cas.lib.proarc.common.object.DigitalObjectElement.Factory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* It helps to crawl hierarchies of digital objects and provides access to the index and the storage.
* It caches visited objects.
*
* @author Jan Pokorsky
*/
public class DigitalObjectCrawler {
private final DigitalObjectManager dom;
private final SearchView search;
/** Maps PIDs to their parent elements. */
private final Map<String, DigitalObjectElement> parents;
/** Maps PIDs to elements. */
private final Map<String, DigitalObjectElement> cache;
private final DigitalObjectElement.Factory elmFactory;
public DigitalObjectCrawler(DigitalObjectManager dom, SearchView search) {
this(dom, search, null);
}
public DigitalObjectCrawler(DigitalObjectManager dom, SearchView search, DigitalObjectElement.Factory elmFactory) {
this.dom = dom;
this.search = search;
this.parents = new HashMap<String, DigitalObjectElement>();
this.cache = new HashMap<String, DigitalObjectElement>();
this.elmFactory = elmFactory != null ? elmFactory : new Factory();
}
public DigitalObjectElement getEntry(Item item) throws DigitalObjectException {
String pid = item.getPid();
return getEntry(pid);
}
public DigitalObjectElement getEntry(String pid) throws DigitalObjectException {
DigitalObjectElement entry = cache.get(pid);
if (entry == null) {
Item item = searchItem(pid);
if (item != null) {
entry = createEntry(item);
cache.put(pid, entry);
} else {
throw new DigitalObjectNotFoundException(pid);
}
}
return entry;
}
private DigitalObjectHandler createHandler(Item item) throws DigitalObjectNotFoundException {
FedoraObject fo = dom.find(item.getPid(), null);
DigitalObjectHandler doHandler = dom.createHandler(fo);
return doHandler;
}
private DigitalObjectElement createEntry(Item item) throws DigitalObjectNotFoundException {
DigitalObjectElement entry = elmFactory.create(item, createHandler(item));
return entry;
}
/**
* Gets parent.
* @param pid child ID
* @return the parent element or {@link DigitalObjectElement#NULL} in case of root.
* @throws DigitalObjectNotFoundException failure
*/
public DigitalObjectElement getParent(String pid) throws DigitalObjectNotFoundException {
DigitalObjectElement parentEntry = parents.get(pid);
if (parentEntry == null) {
Item parentItem = searchParentItem(pid);
if (parentItem == null) {
parentEntry = DigitalObjectElement.NULL;
} else {
parentEntry = cache.get(parentItem.getPid());
if (parentEntry == null) {
parentEntry = createEntry(parentItem);
cache.put(parentItem.getPid(), parentEntry);
}
}
parents.put(pid, parentEntry);
}
return parentEntry;
}
public List<DigitalObjectElement> getChildren(String pid) throws DigitalObjectException {
List<Item> children = searchChildren(pid);
ArrayList<DigitalObjectElement> result = new ArrayList<DigitalObjectElement>();
for (Item item : children) {
DigitalObjectElement childElement = cache.get(item.getPid());
if (childElement == null) {
childElement = createEntry(item);
cache.put(item.getPid(), childElement);
}
DigitalObjectElement parentElm = cache.get(pid);
if (parentElm != null) {
parents.put(item.getPid(), parentElm);
}
result.add(childElement);
}
return result;
}
public List<DigitalObjectElement> getReversePath(String pid) throws DigitalObjectNotFoundException {
List<DigitalObjectElement> path = getPath(pid);
Collections.reverse(path);
return path;
}
/**
* Gets list of parents up to the root.
* @param pid object ID to search parents
* @return the list
*/
public List<DigitalObjectElement> getPath(String pid) throws DigitalObjectNotFoundException {
ArrayList<DigitalObjectElement> path = new ArrayList<DigitalObjectElement>();
for (DigitalObjectElement entry = getParent(pid); entry != DigitalObjectElement.NULL; entry = getParent(pid)) {
pid = entry.getItem().getPid();
path.add(entry);
}
return path;
}
Item searchParentItem(String pid) {
List<Item> issueParents;
try {
issueParents = search.findReferrers(pid);
} catch (Exception ex) {
throw new IllegalStateException(pid, ex);
}
if (issueParents.isEmpty()) {
return null;
}
return issueParents.get(0);
}
Item searchItem(String pid) throws DigitalObjectException {
try {
List<Item> items = search.find(pid);
if (items.isEmpty()) {
return null;
}
return items.get(0);
} catch (FedoraClientException ex) {
throw new DigitalObjectException(pid, ex);
} catch (IOException ex) {
throw new DigitalObjectException(pid, ex);
}
}
List<Item> searchChildren(String pid) throws DigitalObjectException {
try {
List<Item> children = search.findSortedChildren(pid);
return children;
} catch (FedoraClientException ex) {
throw new DigitalObjectException(pid, ex);
} catch (IOException ex) {
throw new DigitalObjectException(pid, ex);
}
}
}