/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.portal.mop.importer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.exoplatform.portal.config.model.I18NString;
import org.exoplatform.portal.config.model.PageNode;
import org.exoplatform.portal.config.model.PageNodeContainer;
import org.exoplatform.portal.mop.Described;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.description.DescriptionService;
import org.exoplatform.portal.mop.navigation.GenericScope;
import org.exoplatform.portal.mop.navigation.NavigationContext;
import org.exoplatform.portal.mop.navigation.NavigationService;
import org.exoplatform.portal.mop.navigation.NodeContext;
import org.exoplatform.portal.mop.navigation.NodeModel;
import org.exoplatform.portal.mop.navigation.NodeState;
import org.exoplatform.portal.mop.navigation.Scope;
import org.exoplatform.portal.tree.diff.Adapters;
import org.exoplatform.portal.tree.diff.ListAdapter;
import org.exoplatform.portal.tree.diff.ListChangeIterator;
import org.exoplatform.portal.tree.diff.ListChangeType;
import org.exoplatform.portal.tree.diff.ListDiff;
/**
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public class NavigationFragmentImporter {
private static final ListAdapter<PageNodeContainer, String> PAGE_NODE_CONTAINER_ADAPTER = new ListAdapter<PageNodeContainer, String>() {
public int size(PageNodeContainer list) {
List<PageNode> nodes = list.getNodes();
if (nodes == null) {
return 0;
} else {
return nodes.size();
}
}
public Iterator<String> iterator(PageNodeContainer list, boolean reverse) {
List<PageNode> nodes = list.getNodes();
if (nodes == null) {
return Collections.<String> emptyList().iterator();
} else {
String[] names = new String[nodes.size()];
int index = 0;
for (PageNode child : nodes) {
names[index++] = child.getName();
}
return Adapters.<String> list().iterator(names, reverse);
}
}
};
private static final ListAdapter<NodeContext<?>, String> NODE_ADAPTER = new ListAdapter<NodeContext<?>, String>() {
public int size(NodeContext<?> list) {
return list.getNodeCount();
}
public Iterator<String> iterator(NodeContext<?> list, boolean reverse) {
int size = list.getNodeCount();
String[] names = new String[size];
int index = 0;
for (NodeContext<?> child = list.getFirst(); child != null; child = child.getNext()) {
names[index++] = child.getName();
}
return Adapters.<String> list().iterator(names, reverse);
}
};
/** . */
private final String[] path;
/** . */
private final NavigationService navigationService;
/** . */
private final SiteKey navigationKey;
/** . */
private final Locale portalLocale;
/** . */
private final DescriptionService descriptionService;
/** . */
private final PageNodeContainer src;
/** . */
private final ImportConfig config;
public NavigationFragmentImporter(String[] path, NavigationService navigationService, SiteKey navigationKey,
Locale portalLocale, DescriptionService descriptionService, PageNodeContainer src, ImportConfig config) {
this.path = path;
this.navigationService = navigationService;
this.navigationKey = navigationKey;
this.portalLocale = portalLocale;
this.descriptionService = descriptionService;
this.src = src;
this.config = config;
}
public ImportConfig getConfig() {
return config;
}
public NodeContext<?> perform() {
NavigationContext navigationCtx = navigationService.loadNavigation(navigationKey);
//
if (navigationCtx != null) {
NodeContext root = navigationService.loadNode(NodeModel.SELF_MODEL, navigationCtx, GenericScope.branchShape(path),
null);
//
NodeContext from = root;
for (String name : path) {
NodeContext a = from.get(name);
if (a != null) {
from = a;
} else {
from = from.add(null, name);
}
}
// Collect labels
Map<NodeContext<?>, Map<Locale, Described.State>> labelMap = new HashMap<NodeContext<?>, Map<Locale, Described.State>>();
// Perform save
perform(src, from, labelMap);
// Save the node
navigationService.saveNode(root, null);
//
for (Map.Entry<NodeContext<?>, Map<Locale, Described.State>> entry : labelMap.entrySet()) {
String id = entry.getKey().getId();
descriptionService.setDescriptions(id, entry.getValue());
}
//
return from;
} else {
return null;
}
}
private void perform(PageNodeContainer src, final NodeContext<?> dst,
final Map<NodeContext<?>, Map<Locale, Described.State>> labelMap) {
navigationService.rebaseNode(dst, Scope.CHILDREN, null);
//
ListDiff<PageNodeContainer, NodeContext<?>, String> diff = new ListDiff<PageNodeContainer, NodeContext<?>, String>(
PAGE_NODE_CONTAINER_ADAPTER, NODE_ADAPTER);
//
List<PageNode> srcChildren = src.getNodes();
ListChangeIterator<PageNodeContainer, NodeContext<?>, String> it = diff.iterator(src, dst);
class Change {
final ListChangeType type;
final String name;
final int index1;
final int index2;
Change(ListChangeType type, String name, int index1, int index2) {
this.type = type;
this.name = name;
this.index1 = index1;
this.index2 = index2;
}
}
// Buffer the changes in a list
LinkedList<Change> foo = new LinkedList<Change>();
while (it.hasNext()) {
ListChangeType type = it.next();
foo.add(new Change(type, it.getElement(), it.getIndex1(), it.getIndex2()));
}
// The last encountered child
NodeContext<?> previousChild = null;
// Replay the changes and apply them
for (Change change : foo) {
PageNode srcChild = src.getNode(change.name);
NodeContext<?> dstChild = dst.get(change.name);
//
switch (change.type) {
case SAME:
// Perform recursively
perform(srcChild, dstChild, labelMap);
//
if (config.updatedSame) {
update(srcChild, dstChild, labelMap);
}
//
previousChild = dstChild;
break;
case REMOVE:
if (dst.getNode(change.name) != null) {
} else {
if (config.createMissing) {
previousChild = add(srcChild, previousChild, dst, labelMap);
}
}
break;
case ADD:
if (src.getNode(change.name) != null) {
if (config.updatedSame) {
update(srcChild, dstChild, labelMap);
}
previousChild = dstChild;
} else {
if (config.destroyOrphan) {
dstChild.removeNode();
} else {
previousChild = dstChild;
}
}
break;
}
}
}
private NodeContext<?> add(PageNode target, NodeContext<?> previous, NodeContext<?> parent,
Map<NodeContext<?>, Map<Locale, Described.State>> labelMap) {
I18NString labels = target.getLabels();
//
Map<Locale, Described.State> description;
if (labels.isSimple()) {
description = null;
} else if (labels.isEmpty()) {
description = null;
} else {
description = new HashMap<Locale, Described.State>();
for (Map.Entry<Locale, String> entry : labels.getExtended(portalLocale).entrySet()) {
description.put(entry.getKey(), new Described.State(entry.getValue(), null));
}
}
//
String name = target.getName();
int index;
if (previous != null) {
index = parent.get((previous).getName()).getIndex() + 1;
} else {
index = 0;
}
NodeContext<?> child = parent.add(index, name);
NodeState state = target.getState();
child.setState(state);
//
if (description != null) {
labelMap.put(child, description);
}
// We recurse to create the descendants
List<PageNode> targetChildren = target.getNodes();
if (targetChildren != null) {
NodeContext<?> targetPrevious = null;
for (PageNode targetChild : targetChildren) {
targetPrevious = add(targetChild, targetPrevious, child, labelMap);
}
}
//
return child;
}
private void update(PageNode src, NodeContext<?> target, Map<NodeContext<?>, Map<Locale, Described.State>> labelMap) {
target.setState(src.getState());
// Update extended labels if necessary
I18NString labels = src.getLabels();
Map<Locale, Described.State> description;
if (labels.isSimple()) {
description = null;
} else if (labels.isEmpty()) {
description = null;
} else {
description = new HashMap<Locale, Described.State>();
for (Map.Entry<Locale, String> entry : labels.getExtended(portalLocale).entrySet()) {
description.put(entry.getKey(), new Described.State(entry.getValue(), null));
}
}
if (description != null) {
labelMap.put(target, description);
} else {
labelMap.put(target, Collections.<Locale, Described.State> emptyMap());
}
}
}