package org.openlca.app.navigation;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
import org.openlca.app.cloud.CloudUtil;
import org.openlca.app.db.Database;
import org.openlca.app.db.DatabaseDir;
import org.openlca.cloud.model.data.Dataset;
import org.openlca.core.database.BaseDao;
import org.openlca.core.database.CategorizedEntityDao;
import org.openlca.core.database.CategoryDao;
import org.openlca.core.model.CategorizedEntity;
import org.openlca.core.model.Category;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.descriptors.CategorizedDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
public class CopyPaste {
private enum Action {
NONE, COPY, CUT;
}
private static INavigationElement<?>[] cache = null;
private static Action currentAction = Action.NONE;
public static void copy(Collection<INavigationElement<?>> elements) {
copy(elements.toArray(new INavigationElement<?>[elements.size()]));
}
public static void copy(INavigationElement<?>[] elements) {
if (!isSupported(elements))
return;
initialize(Action.COPY, elements);
}
public static void cut(Collection<INavigationElement<?>> elements) {
cut(elements.toArray(new INavigationElement<?>[elements.size()]));
}
private static void cut(INavigationElement<?>[] elements) {
if (!isSupported(elements))
return;
initialize(Action.CUT, elements);
for (INavigationElement<?> element : cache) {
element.getParent().getChildren().remove(element);
Navigator.getInstance().getCommonViewer()
.refresh(element.getParent());
}
}
public static boolean isSupported(INavigationElement<?> element) {
return element instanceof ModelElement
|| element instanceof CategoryElement;
}
public static boolean isSupported(Collection<INavigationElement<?>> elements) {
if (elements == null)
return false;
return isSupported(elements.toArray(new INavigationElement[elements
.size()]));
}
public static boolean isSupported(INavigationElement<?>[] elements) {
if (elements == null || elements.length == 0)
return false;
ModelType modelType = null;
for (INavigationElement<?> element : elements) {
if (!isSupported(element))
return false;
ModelType currentModelType = getModelType(element);
if (modelType == null)
modelType = currentModelType;
else if (currentModelType != modelType)
return false;
}
return true;
}
private static ModelType getModelType(INavigationElement<?> element) {
if (element instanceof ModelElement)
return ((ModelElement) element).getContent().getModelType();
if (element instanceof CategoryElement)
return ((CategoryElement) element).getContent().getModelType();
if (element instanceof ModelTypeElement)
return ((ModelTypeElement) element).getContent();
return ModelType.UNKNOWN;
}
private static void initialize(Action action,
INavigationElement<?>[] elements) {
if (action == Action.CUT && currentAction == Action.CUT) {
extendCache(elements);
return;
}
if (currentAction == Action.CUT)
restore();
cache = elements;
currentAction = action;
}
private static void extendCache(INavigationElement<?>[] elements) {
if (elements == null || elements.length == 0)
return;
if (cacheIsEmpty()) {
cache = elements;
return;
}
INavigationElement<?>[] newCache = new INavigationElement<?>[cache.length
+ elements.length];
System.arraycopy(cache, 0, newCache, 0, cache.length);
System.arraycopy(elements, 0, newCache, cache.length, elements.length);
cache = newCache;
}
private static void restore() {
if (cacheIsEmpty())
return;
for (INavigationElement<?> element : cache) {
paste(element, element.getParent());
INavigationElement<?> root = Navigator
.findElement(getModelType(element));
Navigator.refresh(root);
}
}
public static void pasteTo(INavigationElement<?> categoryElement) {
if (cacheIsEmpty())
return;
if (!canPasteTo(categoryElement))
return;
boolean started = false;
try {
Database.getIndexUpdater().beginTransaction();
started = true;
for (INavigationElement<?> element : cache)
paste(element, categoryElement);
} finally {
if (started)
Database.getIndexUpdater().endTransaction();
clearCache();
}
}
public static void clearCache() {
cache = null;
currentAction = Action.NONE;
INavigationElement<?> root = Navigator.findElement(Database
.getActiveConfiguration());
Navigator.refresh(root);
}
public static boolean canMove(Collection<INavigationElement<?>> elements,
INavigationElement<?> target) {
return canMove(
elements.toArray(new INavigationElement[elements.size()]),
target);
}
private static boolean canMove(INavigationElement<?>[] elements,
INavigationElement<?> target) {
if (!isSupported(elements))
return false;
if (!(target instanceof CategoryElement || target instanceof ModelTypeElement))
return false;
return getModelType(target) == getModelType(elements[0]);
}
public static boolean canPasteTo(INavigationElement<?> element) {
if (cacheIsEmpty())
return false;
if (!(element instanceof CategoryElement || element instanceof ModelTypeElement))
return false;
return getModelType(element) == getModelType(cache[0]);
}
private static void paste(INavigationElement<?> element,
INavigationElement<?> category) {
if (currentAction == Action.CUT) {
if (element instanceof CategoryElement)
move((CategoryElement) element, category);
else if (element instanceof ModelElement)
move((ModelElement) element, category);
} else if (currentAction == Action.COPY) {
if (element instanceof CategoryElement)
copy((CategoryElement) element, category);
else if (element instanceof ModelElement)
copy((ModelElement) element, category);
}
}
private static void copy(ModelElement element,
INavigationElement<?> categoryElement) {
CategorizedEntity copy = copy(element);
if (copy == null)
return;
Category category = getCategory(categoryElement);
copy.setCategory(category);
copy = insert(copy);
}
private static Category getCategory(INavigationElement<?> element) {
return element instanceof CategoryElement ? ((CategoryElement) element)
.getContent() : null;
}
private static void move(CategoryElement element,
INavigationElement<?> categoryElement) {
Category newParent = getCategory(categoryElement);
Category oldParent = getCategory(element.getParent());
Category category = element.getContent();
if (Objects.equals(category, newParent))
return;
if (isChild(newParent, category))
return; // do not create category cycles
if (oldParent != null)
oldParent.getChildCategories().remove(category);
if (newParent != null)
newParent.getChildCategories().add(category);
category.setCategory(newParent);
CategoryDao dao = new CategoryDao(Database.get());
if (oldParent != null)
oldParent = dao.update(oldParent);
if (newParent != null)
newParent = dao.update(newParent);
category = dao.update(category);
}
private static boolean isChild(Category category, Category parent) {
if (category == null || parent == null)
return false;
Category p = category.getCategory();
while (p != null) {
if (Objects.equals(p, parent))
return true;
p = p.getCategory();
}
return false;
}
private static void move(ModelElement element,
INavigationElement<?> categoryElement) {
CategorizedDescriptor entity = element.getContent();
Category category = getCategory(categoryElement);
Optional<Category> parent = Optional.fromNullable(category);
Database.createCategorizedDao(entity.getModelType()).updateCategory(entity,
parent);
// need to notifiy index updater manually here
Dataset dataset = CloudUtil.toDataset(entity, category);
Database.getIndexUpdater().update(dataset);
}
private static void copy(CategoryElement element,
INavigationElement<?> category) {
Category parent = getCategory(category);
Queue<CategoryElement> elements = new LinkedList<>();
elements.add(element);
while (!elements.isEmpty()) {
CategoryElement current = elements.poll();
Category copy = current.getContent().clone();
copy.getChildCategories().clear();
copy.setCategory(parent);
if (parent == null)
copy = new CategoryDao(Database.get()).insert(copy);
else {
parent.getChildCategories().add(copy);
copy = new CategoryDao(Database.get()).update(parent);
}
for (INavigationElement<?> child : current.getChildren())
if (child instanceof CategoryElement)
elements.add((CategoryElement) child);
else {
CategorizedEntity modelCopy = copy((ModelElement) child);
modelCopy.setCategory(copy);
modelCopy = insert(modelCopy);
}
parent = copy;
}
}
private static CategorizedEntity copy(ModelElement element) {
CategorizedDescriptor descriptor = element.getContent();
CategorizedEntityDao<?, ?> dao = Database.createCategorizedDao(descriptor
.getModelType());
CategorizedEntity entity = dao.getForId(descriptor.getId());
CategorizedEntity copy = cloneIt(entity);
if (copy != null)
copy.setName(copy.getName() + " (copy)");
return copy;
}
private static CategorizedEntity cloneIt(CategorizedEntity entity) {
try {
CategorizedEntity clone = (CategorizedEntity) entity.clone();
DatabaseDir.copyDir(entity, clone);
return clone;
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(CopyPaste.class);
log.error("failed to clone " + entity, e);
return null;
}
}
@SuppressWarnings("unchecked")
private static <T extends CategorizedEntity> T insert(T entity) {
BaseDao<T> dao = (BaseDao<T>) Database.get().createDao(
entity.getClass());
return dao.insert(entity);
}
public static boolean cacheIsEmpty() {
return cache == null || cache.length == 0;
}
}