/* AWE - Amanzi Wireless Explorer
* http://awe.amanzi.org
* (C) 2008-2009, AmanziTel AB
*
* This library is provided under the terms of the Eclipse Public License
* as described at http://www.eclipse.org/legal/epl-v10.html. Any use,
* reproduction or distribution of the library constitutes recipient's
* acceptance of this agreement.
*
* This library is distributed WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.amanzi.neo.services.impl.statistics;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Stack;
import org.amanzi.neo.nodeproperties.IGeneralNodeProperties;
import org.amanzi.neo.nodetypes.INodeType;
import org.amanzi.neo.nodetypes.NodeTypeManager;
import org.amanzi.neo.nodetypes.NodeTypeNotExistsException;
import org.amanzi.neo.services.INodeService;
import org.amanzi.neo.services.exceptions.ServiceException;
import org.amanzi.neo.services.impl.NodeService.NodeServiceRelationshipType;
import org.amanzi.neo.services.impl.internal.AbstractService;
import org.amanzi.neo.services.impl.statistics.internal.NodeTypeVault;
import org.amanzi.neo.services.impl.statistics.internal.PropertyVault;
import org.amanzi.neo.services.impl.statistics.internal.StatisticsVault;
import org.amanzi.neo.services.statistics.IPropertyStatisticsNodeProperties;
import org.amanzi.neo.services.statistics.IPropertyStatisticsService;
import org.apache.commons.lang3.math.NumberUtils;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
/**
* TODO Purpose of
* <p>
* </p>
*
* @author Nikolay Lagutko (nikolay.lagutko@amanzitel.com)
* @since 1.0.0
*/
public class PropertyStatisticsService extends AbstractService implements IPropertyStatisticsService {
public enum PropertyStatisticsRelationshipType implements RelationshipType {
PROPERTY_STATISTICS;
}
private final INodeService nodeService;
private final IPropertyStatisticsNodeProperties statisticsNodeProperties;
/**
* @param graphDb
* @param generalNodeProperties
*/
public PropertyStatisticsService(final GraphDatabaseService graphDb, final IGeneralNodeProperties generalNodeProperties,
final INodeService nodeService, final IPropertyStatisticsNodeProperties statisticsNodeProperties) {
super(graphDb, generalNodeProperties);
this.nodeService = nodeService;
this.statisticsNodeProperties = statisticsNodeProperties;
}
private Node createChildVaultNode(final Node parentVaultNode, final String name) throws ServiceException {
return nodeService.createNode(parentVaultNode, PropertyStatisticsNodeType.STATISTICS_VAULT,
NodeServiceRelationshipType.CHILD, name);
}
private Iterator<Node> getChildren(final Node node) throws ServiceException {
return nodeService.getChildren(node, PropertyStatisticsNodeType.STATISTICS_VAULT);
}
private Node getChildVaultNode(final Node parentVaultNode, final String name) throws ServiceException {
return nodeService.getChildByName(parentVaultNode, name, PropertyStatisticsNodeType.STATISTICS_VAULT);
}
private int getCount(final Node node, final boolean shouldExist) throws ServiceException {
return nodeService.getNodeProperty(node, statisticsNodeProperties.getCountProperty(), shouldExist ? null : 0, shouldExist);
}
protected Node getStatisticsNode(final Node datasetNode) throws ServiceException {
Node result = nodeService.getSingleChild(datasetNode, PropertyStatisticsNodeType.PROPERTY_STATISTICS,
PropertyStatisticsRelationshipType.PROPERTY_STATISTICS);
if (result == null) {
result = nodeService.createNode(datasetNode, PropertyStatisticsNodeType.PROPERTY_STATISTICS,
PropertyStatisticsRelationshipType.PROPERTY_STATISTICS);
}
return result;
}
protected NodeTypeVault loadNodeTypeVault(final Node nodeTypeVaultNode) throws ServiceException, NodeTypeNotExistsException {
String nodeTypeId = nodeService.getNodeName(nodeTypeVaultNode);
INodeType nodeType = NodeTypeManager.getInstance().getType(nodeTypeId);
NodeTypeVault vault = new NodeTypeVault(nodeType);
vault.setCount(getCount(nodeTypeVaultNode, true));
Iterator<Node> propertyVaultNodeIterator = getChildren(nodeTypeVaultNode);
while (propertyVaultNodeIterator.hasNext()) {
vault.addPropertyVault(loadPropertyVault(propertyVaultNodeIterator.next()));
}
return vault;
}
protected PropertyVault loadPropertyVault(final Node propertyVaultNode) throws ServiceException {
String propertyName = nodeService.getNodeName(propertyVaultNode);
PropertyVault vault = new PropertyVault(propertyName);
vault.setClass((String)nodeService.getNodeProperty(propertyVaultNode, statisticsNodeProperties.getClassProperty(), null,
true));
int size = nodeService.getNodeProperty(propertyVaultNode, getGeneralNodeProperties().getSizeProperty(), null, true);
for (int i = 0; i < size; i++) {
Object value = nodeService
.getNodeProperty(propertyVaultNode, statisticsNodeProperties.getValuePrefix() + i, null, true);
int count = nodeService.getNodeProperty(propertyVaultNode, statisticsNodeProperties.getCountPrefix() + i, null, true);
vault.addValue(value, count);
}
vault.setDefaultValue(nodeService.getNodeProperty(propertyVaultNode, statisticsNodeProperties.getDefaultValueProperty(),
null, false));
return vault;
}
@Override
public synchronized StatisticsVault loadStatistics(final Node rootNode) throws ServiceException, NodeTypeNotExistsException {
assert rootNode != null;
Node statisticsNode = getStatisticsNode(rootNode);
return loadStatisticsVault(statisticsNode);
}
protected StatisticsVault loadStatisticsVault(final Node node) throws ServiceException, NodeTypeNotExistsException {
StatisticsVault vault = new StatisticsVault();
vault.setCount(getCount(node, false));
Iterator<Node> nodeTypeVaultNodesIterator = getChildren(node);
while (nodeTypeVaultNodesIterator.hasNext()) {
vault.addNodeTypeVault(loadNodeTypeVault(nodeTypeVaultNodesIterator.next()));
}
return vault;
}
@Override
public void renameProperty(final Node rootNode, final INodeType nodeType, final String propertyName, final Object oldValue,
final Object newValue) throws ServiceException {
Node statisticsNode = getStatisticsNode(rootNode);
Node nodeTypeVault = getChildVaultNode(statisticsNode, nodeType.getId());
Node propertyVault = getChildVaultNode(nodeTypeVault, propertyName);
for (String property : propertyVault.getPropertyKeys()) {
if (propertyVault.getProperty(property).equals(oldValue)) {
nodeService.updateProperty(propertyVault, property, newValue);
break;
}
}
}
protected void saveNodeTypeVault(final Node statisticsNode, final NodeTypeVault vault) throws ServiceException {
Node nodeTypeVault = updateNodeTypeVault(statisticsNode, vault);
for (PropertyVault propertyVault : vault.getAllPropertyVaults()) {
if (propertyVault.isChanged()) {
savePropertyStatistics(nodeTypeVault, propertyVault);
}
}
}
protected void savePropertyStatistics(final Node nodeTypeVault, final PropertyVault vault) throws ServiceException {
Node propertyVault = getChildVaultNode(nodeTypeVault, vault.getPropertyName());
if (propertyVault == null) {
propertyVault = createChildVaultNode(nodeTypeVault, vault.getPropertyName());
}
updatePropertyVault(propertyVault, vault);
}
@Override
public synchronized void saveStatistics(final Node node, final StatisticsVault vault) throws ServiceException {
assert node != null;
assert vault != null;
Node statisticsNode = getStatisticsNode(node);
saveStatisticsVault(statisticsNode, vault);
}
protected void saveStatisticsVault(final Node node, final StatisticsVault vault) throws ServiceException {
if (vault.isChanged()) {
updateStatisticsInfo(node, vault);
for (NodeTypeVault nodeTypeVault : vault.getAllNodeTypeVaults()) {
if (nodeTypeVault.isChanged()) {
saveNodeTypeVault(node, nodeTypeVault);
}
}
}
}
protected Node updateNodeTypeVault(final Node statisticsNode, final NodeTypeVault vault) throws ServiceException {
Node vaultNode = getChildVaultNode(statisticsNode, vault.getNodeType().getId());
if (vaultNode == null) {
vaultNode = createChildVaultNode(statisticsNode, vault.getNodeType().getId());
}
nodeService.updateProperty(vaultNode, statisticsNodeProperties.getCountProperty(), vault.getCount());
return vaultNode;
}
protected void updatePropertyVault(final Node propertyVault, final PropertyVault vault) throws ServiceException {
nodeService.updateProperty(propertyVault, statisticsNodeProperties.getClassProperty(), vault.getClassName());
int size = nodeService.getNodeProperty(propertyVault, getGeneralNodeProperties().getSizeProperty(),
NumberUtils.INTEGER_ZERO, false);
Map<Object, Integer> values = new HashMap<Object, Integer>(vault.getValuesMap());
Queue<Integer> removedIndexes = new LinkedList<Integer>();
Stack<Integer> processedIndex = new Stack<Integer>();
if (size > 0) {
for (int i = 0; i < size; i++) {
Object property = nodeService.getNodeProperty(propertyVault, statisticsNodeProperties.getValuePrefix() + i, null,
true);
Integer newCount = values.remove(property);
if (newCount != null) {
nodeService.updateProperty(propertyVault, statisticsNodeProperties.getCountPrefix() + i, newCount);
} else {
removedIndexes.add(i);
}
processedIndex.add(i);
}
}
// remove old values
for (Integer index : removedIndexes) {
nodeService.removeNodeProperty(propertyVault, statisticsNodeProperties.getValuePrefix() + index, false);
nodeService.removeNodeProperty(propertyVault, statisticsNodeProperties.getCountPrefix() + index, false);
}
int counter = size;
for (Entry<Object, Integer> statEntry : values.entrySet()) {
counter = removedIndexes.isEmpty() ? counter : removedIndexes.remove();
nodeService.updateProperty(propertyVault, statisticsNodeProperties.getValuePrefix() + counter, statEntry.getKey());
nodeService.updateProperty(propertyVault, statisticsNodeProperties.getCountPrefix() + counter, statEntry.getValue());
counter++;
}
for (Integer newIndex : removedIndexes) {
int oldIndex = processedIndex.pop();
nodeService.renameNodeProperty(propertyVault, statisticsNodeProperties.getValuePrefix() + oldIndex,
statisticsNodeProperties.getValuePrefix() + newIndex, false);
nodeService.renameNodeProperty(propertyVault, statisticsNodeProperties.getCountPrefix() + oldIndex,
statisticsNodeProperties.getCountPrefix() + newIndex, false);
}
nodeService.updateProperty(propertyVault, getGeneralNodeProperties().getSizeProperty(), values.size());
nodeService.updateProperty(propertyVault, statisticsNodeProperties.getDefaultValueProperty(), vault.getDefaultValue());
}
@Override
public void updateStatistics(final Node rootNode, final StatisticsVault vault, final NodeTypeVault nodeType)
throws ServiceException {
Node statisticsNode = getStatisticsNode(rootNode);
Node nodeTypeVault = getChildVaultNode(statisticsNode, nodeType.getNodeType().getId());
updateNodeTypeVault(statisticsNode, nodeType);
updateStatisticsInfo(statisticsNode, vault);
for (PropertyVault propertyVault : nodeType.getAllPropertyVaults()) {
savePropertyStatistics(nodeTypeVault, propertyVault);
}
}
protected void updateStatisticsInfo(final Node node, final StatisticsVault vault) throws ServiceException {
nodeService.updateProperty(node, statisticsNodeProperties.getCountProperty(), vault.getCount());
}
}