package com.revolsys.record.code;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PreDestroy;
import javax.swing.JComponent;
import com.revolsys.identifier.Identifier;
import com.revolsys.identifier.SingleIdentifier;
import com.revolsys.io.BaseCloseable;
import com.revolsys.properties.BaseObjectWithPropertiesAndChange;
import com.revolsys.util.CaseConverter;
import com.revolsys.util.number.Numbers;
public abstract class AbstractCodeTable extends BaseObjectWithPropertiesAndChange
implements BaseCloseable, CodeTable, Cloneable {
private boolean capitalizeWords = false;
private boolean caseSensitive = false;
private List<Identifier> identifiers = new ArrayList<>();
private Map<Identifier, Identifier> idIdCache = new LinkedHashMap<>();
private Map<Identifier, List<Object>> idValueCache = new LinkedHashMap<>();
private long maxId;
private String name;
private final Map<String, Identifier> stringIdMap = new HashMap<>();
private JComponent swingEditor;
private Map<List<Object>, Identifier> valueIdCache = new LinkedHashMap<>();
public AbstractCodeTable() {
}
public AbstractCodeTable(final boolean capitalizeWords) {
this.capitalizeWords = capitalizeWords;
}
protected synchronized void addValue(final Identifier id, final List<Object> values) {
if (id instanceof Number) {
final Number number = (Number)id;
final long longValue = number.longValue();
if (longValue > this.maxId) {
this.maxId = longValue;
}
}
this.identifiers.add(id);
this.idValueCache.put(id, values);
this.idIdCache.put(id, id);
addValueId(id, values);
String lowerId = id.toString();
if (!this.caseSensitive) {
lowerId = lowerId.toLowerCase();
}
this.stringIdMap.put(lowerId, id);
}
protected void addValue(final Identifier id, final Object... values) {
final List<Object> valueList = Arrays.asList(values);
addValue(id, valueList);
}
protected void addValueId(final Identifier id, final List<Object> values) {
this.valueIdCache.put(values, id);
this.valueIdCache.put(getNormalizedValues(values), id);
}
protected synchronized void addValues(final Map<Identifier, List<Object>> valueMap) {
for (final Entry<Identifier, List<Object>> entry : valueMap.entrySet()) {
final Identifier id = entry.getKey();
final List<Object> values = entry.getValue();
addValue(id, values);
}
}
@Override
public AbstractCodeTable clone() {
final AbstractCodeTable clone = (AbstractCodeTable)super.clone();
clone.identifiers = new ArrayList<>(this.identifiers);
clone.idValueCache = new LinkedHashMap<>(this.idValueCache);
clone.idIdCache = new LinkedHashMap<>(this.idIdCache);
clone.valueIdCache = new LinkedHashMap<>(this.valueIdCache);
return clone;
}
@Override
@PreDestroy
public void close() {
this.identifiers.clear();
this.idValueCache.clear();
this.idIdCache.clear();
this.stringIdMap.clear();
this.valueIdCache.clear();
this.swingEditor = null;
}
@Override
public Map<Identifier, List<Object>> getCodes() {
return Collections.unmodifiableMap(this.idValueCache);
}
protected Identifier getIdByValue(final List<Object> valueList) {
processValues(valueList);
Identifier id = this.valueIdCache.get(valueList);
if (id == null) {
final List<Object> normalizedValues = getNormalizedValues(valueList);
id = this.valueIdCache.get(normalizedValues);
}
return id;
}
@Override
public Identifier getIdentifier(final List<Object> values, final boolean loadMissing) {
if (values.size() == 1) {
final Object id = values.get(0);
if (id == null) {
return null;
} else {
final Identifier cachedId = this.idIdCache.get(id);
if (cachedId != null) {
return cachedId;
} else {
String lowerId = id.toString();
if (!this.caseSensitive) {
lowerId = lowerId.toLowerCase();
}
if (this.stringIdMap.containsKey(lowerId)) {
return this.stringIdMap.get(lowerId);
}
}
}
}
processValues(values);
Identifier id = getIdByValue(values);
if (id == null && loadMissing) {
synchronized (this) {
id = loadId(values, true);
if (id != null && !this.idValueCache.containsKey(id)) {
addValue(id, values);
}
}
}
return id;
}
@Override
public List<Identifier> getIdentifiers() {
return Collections.unmodifiableList(this.identifiers);
}
@Override
public Identifier getIdExact(final List<Object> values, final boolean loadValues) {
Identifier id = this.valueIdCache.get(values);
if (id == null && loadValues) {
synchronized (this) {
id = loadId(values, false);
return this.valueIdCache.get(values);
}
}
return id;
}
@Override
public String getName() {
return this.name;
}
protected synchronized long getNextId() {
return ++this.maxId;
}
private List<Object> getNormalizedValues(final List<Object> values) {
final List<Object> normalizedValues = new ArrayList<>();
for (final Object value : values) {
if (value == null) {
normalizedValues.add(null);
} else if (value instanceof Number) {
final Number number = (Number)value;
normalizedValues.add(Numbers.toString(number));
} else {
normalizedValues.add(value.toString().toLowerCase());
}
}
return normalizedValues;
}
@Override
public JComponent getSwingEditor() {
return this.swingEditor;
}
protected List<Object> getValueById(Object id) {
if (this.valueIdCache.containsKey(Collections.singletonList(id))) {
if (id instanceof SingleIdentifier) {
final SingleIdentifier identifier = (SingleIdentifier)id;
return Collections.singletonList(identifier.getValue(0));
} else {
return Collections.singletonList(id);
}
} else {
List<Object> values = this.idValueCache.get(id);
if (values == null) {
String lowerId = id.toString();
if (!this.caseSensitive) {
lowerId = lowerId.toLowerCase();
}
if (this.stringIdMap.containsKey(lowerId)) {
id = this.stringIdMap.get(lowerId);
values = this.idValueCache.get(id);
}
}
return values;
}
}
@Override
public List<Object> getValues(final Identifier id) {
if (id != null) {
List<Object> values = getValueById(id);
if (values == null) {
synchronized (this) {
values = loadValues(id);
if (values != null) {
addValue(id, values);
}
}
}
if (values != null) {
return Collections.unmodifiableList(values);
}
}
return null;
}
public boolean isCapitalizeWords() {
return this.capitalizeWords;
}
public boolean isCaseSensitive() {
return this.caseSensitive;
}
@Override
public boolean isEmpty() {
return this.idIdCache.isEmpty();
}
protected Identifier loadId(final List<Object> values, final boolean createId) {
return null;
}
protected List<Object> loadValues(final Object id) {
return null;
}
private void processValues(final List<Object> valueList) {
if (isCapitalizeWords()) {
for (int i = 0; i < valueList.size(); i++) {
final Object value = valueList.get(i);
if (value != null) {
final String newValue = CaseConverter.toCapitalizedWords(value.toString());
valueList.set(i, newValue);
}
}
}
}
@Override
public synchronized void refresh() {
this.identifiers.clear();
this.idValueCache.clear();
this.stringIdMap.clear();
this.valueIdCache.clear();
}
public void setCapitalizeWords(final boolean capitalizedWords) {
this.capitalizeWords = capitalizedWords;
}
public void setCaseSensitive(final boolean caseSensitive) {
this.caseSensitive = caseSensitive;
}
public void setName(final String name) {
this.name = name;
}
public void setSwingEditor(final JComponent swingEditor) {
this.swingEditor = swingEditor;
}
}