// ============================================================================
//
// Copyright (C) 2006-2012 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.designer.components.lookup.memory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.commons.collections.list.GrowthList;
import org.talend.commons.utils.data.map.MultiLazyValuesMap;
/**
* DOC amaumont class global comment. Detailled comment
*
* @param value
*/
public class AdvancedMemoryLookup implements IMemoryLookup, Cloneable {
private MultiLazyValuesMap mapOfCol;
private Map uniqueHash;
private boolean countValuesForEachKey;
private Map counterHash;
private List list = new ArrayList();
private Object[] arrayValues;
private boolean arrayIsDirty = true;
private List listResult;
private V objectResult;
private boolean keepAllValues;
private MATCHING_MODE matchingMode;
private static final int ZERO = 0;
private static final int ONE = 1;
int currentIndex = 0;
private int sizeResultList;
private boolean hasResult;
/**
*
* AdvancedLookup
can be configured to store values in different modes.
*
* @param useHashKeys use equals()
and hashCode()
methods by storing objects in hash maps
* @param matchingMode to optimize storing and searching, and to specify which matching mode should used
* @param uniqueMatch keep in the lookup only the last put object, but store the current number of same values for
* each key
* @param keepAllValues keep all identical values (with same key values) in each list of each key
* @param countValuesForEachKey force internal count of values
*/
public AdvancedMemoryLookup(MATCHING_MODE matchingMode, boolean keepAllValues, boolean countValuesForEachKey) {
super();
this.keepAllValues = keepAllValues;
this.matchingMode = matchingMode == null ? MATCHING_MODE.UNIQUE_MATCH : matchingMode;
this.countValuesForEachKey = countValuesForEachKey; // || this.matchingMode == MATCHING_MODE.UNIQUE_MATCH;
if (matchingMode != MATCHING_MODE.ALL_ROWS) {
if (matchingMode == MATCHING_MODE.UNIQUE_MATCH && !keepAllValues) {
uniqueHash = new HashMap();
}
if (this.countValuesForEachKey) {
counterHash = new HashMap();
}
mapOfCol = new MultiLazyValuesMap(new HashMap()) {
@Override
public Collection instanciateNewCollection() {
return new GrowthList(3);
}
};
}
}
public AdvancedMemoryLookup() {
}
public static AdvancedMemoryLookup getLookup(MATCHING_MODE matchingMode) {
return new AdvancedMemoryLookup(matchingMode, false, false);
}
public Object[] getResultArray() {
if (matchingMode == MATCHING_MODE.ALL_ROWS) {
if (listResult == null) {
listResult = list;
}
if (arrayIsDirty) {
arrayValues = listResult.toArray();
arrayIsDirty = false;
}
return arrayValues;
} else {
return listResult.toArray();
}
}
public List getResultList() {
return listResult;
}
public V getResultObject() {
return objectResult;
}
public boolean resultIsObject() {
return objectResult != null;
}
public boolean resultIsList() {
return listResult != null;
}
public void initPut() {
}
public V put(V value) {
if (value != null) {
if (matchingMode == MATCHING_MODE.UNIQUE_MATCH && !keepAllValues) {
V previousValue = uniqueHash.put(value, value);
incrementCountValues(value, previousValue);
return previousValue;
} else {
if (matchingMode == MATCHING_MODE.ALL_ROWS) {
list.add(value);
return null;
} else {
arrayIsDirty = true;
V previousValue = (V) mapOfCol.put(value, value);
incrementCountValues(value, previousValue);
return previousValue;
}
}
}
return null;
}
public void endPut() {
}
public void initGet() {
}
public void lookup(V key) {
if (matchingMode == MATCHING_MODE.UNIQUE_MATCH) {
listResult = null;
objectResult = uniqueHash.get(key);
} else {
if (matchingMode != MATCHING_MODE.ALL_ROWS && key != null) {
Object v = mapOfCol.get(key);
if (v instanceof List) {
List localList = (List) v;
if (matchingMode == MATCHING_MODE.ALL_MATCHES) {
listResult = localList;
currentIndex = 0;
sizeResultList = localList.size();
objectResult = null;
} else if (matchingMode == MATCHING_MODE.FIRST_MATCH) {
objectResult = localList.get(ZERO);
} else if (matchingMode == MATCHING_MODE.LAST_MATCH) {
hasResult = false;
listResult = null;
objectResult = localList.get(localList.size() - ONE);
}
} else {
hasResult = false;
objectResult = (V) v;
listResult = null;
}
} else {
hasResult = false;
listResult = list;
currentIndex = 0;
sizeResultList = list.size();
objectResult = null;
}
}
}
public boolean hasNext() {
if (objectResult != null) {
return true;
} else if (listResult != null && currentIndex != sizeResultList) {
return true;
}
return false;
}
public V next() {
if (objectResult != null) {
hasResult = true;
V object = objectResult;
objectResult = null;
return object;
} else if (listResult != null) {
hasResult = true;
return listResult.get(currentIndex++);
}
throw new NoSuchElementException();
}
public void endGet() {
clear();
}
/**
* DOC amaumont Comment method "incrementCountValues".
*
* @param value
* @param previousValue
*/
private void incrementCountValues(V value, V previousValue) {
if (countValuesForEachKey) {
Integer count;
if (previousValue == null) {
count = ONE;
} else {
count = counterHash.get(value);
count++;
}
counterHash.put(value, count);
}
}
public void clear() {
if (mapOfCol != null) {
mapOfCol.clear();
}
if (uniqueHash != null) {
uniqueHash.clear();
}
if (counterHash != null) {
counterHash.clear();
}
arrayValues = null;
if (list != null) {
list.clear();
}
listResult = null;
}
/**
* DOC amaumont Comment method "hasResult".
*
* @return
*/
public boolean hasResult() {
return hasResult;
}
public boolean isEmpty() {
if (matchingMode == MATCHING_MODE.UNIQUE_MATCH && !keepAllValues) {
return uniqueHash.isEmpty();
} else if (matchingMode == MATCHING_MODE.ALL_ROWS) {
return list.isEmpty();
} else {
return mapOfCol.isEmpty();
}
}
/**
* Getter for hasHashKeys.
*
* @return the hasHashKeys
*/
public boolean isUseHashKeys() {
return matchingMode != MATCHING_MODE.ALL_ROWS;
}
/**
* Getter for countValuesForEachKey.
*
* @return the countValuesForEachKey
*/
public boolean isCountValuesForEachKey() {
return this.countValuesForEachKey;
}
/**
* Getter for keepAllValues.
*
* @return the keepAllValues
*/
public boolean isKeepAllValues() {
return this.keepAllValues;
}
/**
* Getter for uniqueMatch.
*
* @return the uniqueMatch
*/
public boolean isUniqueMatch() {
return matchingMode == MATCHING_MODE.UNIQUE_MATCH;
}
/**
* Getter for uniqueMatch.
*
* @return the uniqueMatch
*/
public boolean isOnlyOneMatchResult() {
return matchingMode == MATCHING_MODE.UNIQUE_MATCH || matchingMode == MATCHING_MODE.FIRST_MATCH
|| matchingMode == MATCHING_MODE.LAST_MATCH;
}
public int getCount(V key) {
if (countValuesForEachKey) {
Integer count = counterHash.get(key);
if (count == null) {
return ZERO;
} else {
return count;
}
} else if (matchingMode == MATCHING_MODE.UNIQUE_MATCH && !keepAllValues) {
if (uniqueHash.get(key) != null) {
return ONE;
} else {
return ZERO;
}
} else if (matchingMode != MATCHING_MODE.ALL_ROWS) {
Object v = mapOfCol.get(key);
if (v instanceof List) {
List localList = (List) v;
return localList.size();
} else {
if (v != null) {
return ONE;
} else {
return ZERO;
}
}
} else {
if (list.contains(key)) {
return 1;
} else {
return ZERO;
}
}
}
/**
* Getter for matchingMode.
*
* @return the matchingMode
*/
public MATCHING_MODE getMatchingMode() {
return matchingMode;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* Getter for id_Document lookup(for tXMLMap) Purpose : Get all value data storing in the lookup Object Use case :
* When no basic lookup(not Document lookup) exists,but Document lookup exists for ALL,First Matching(When no basic
* lookup,not override the hashCode(),equals() method,so no List value,only V value)
*/
public void lookup() {
List localList = new ArrayList();
if (matchingMode == MATCHING_MODE.UNIQUE_MATCH) {
for (V value : uniqueHash.values()) {
localList.add(value);
}
} else {
for (Object value : mapOfCol.values()) {
localList.add((V) value);
}
}
listResult = localList;
sizeResultList = localList.size();
objectResult = null;
currentIndex = 0;
}
}