/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.core.tables.memory; import com.opendoorlogistics.api.tables.ODLColumnType; import com.opendoorlogistics.api.tables.ODLDatastore; import com.opendoorlogistics.api.tables.ODLTableAlterable; import com.opendoorlogistics.api.tables.ODLTableDefinition; import com.opendoorlogistics.api.tables.ODLTableDefinitionAlterable; import com.opendoorlogistics.api.tables.ODLTableReadOnly; import com.opendoorlogistics.api.tables.TableQuery; import com.opendoorlogistics.core.tables.ColumnValueProcessor; import com.opendoorlogistics.core.tables.ODLTableFactory; import com.opendoorlogistics.core.tables.utils.TableUtils; import com.opendoorlogistics.core.utils.IntIDGenerator; import com.opendoorlogistics.core.utils.IntIDGenerator.IsExistingId; import com.opendoorlogistics.core.utils.MapList; final public class ODLTableImpl extends ODLTableDefinitionImpl implements ODLTableAlterable{ /** * */ private static final long serialVersionUID = 3487573687352027587L; private final MapList<ODLRowImpl> list = new MapList<>(); private IntIDGenerator rowIdGenerator = new IntIDGenerator(new IsExistingId() { @Override public boolean isExistingId(int id) { return list.containsID(id); } }); /** * Deep copy the input table * * @param copyThis */ public ODLTableImpl(ODLTableImpl copyThis) { super(copyThis); // copy all rows; column indexes will create themselves later if needed for (ODLRowImpl row : copyThis.list) { int n = row.getColumnCount(); ODLRowImpl copy = new ODLRowImpl(row.getTableInternalId(), n); copy.setFlags(row.getFlags()); for (int i = 0; i < n; i++) { // values in rows should be treated as immutable, copying ref should be safe copy.add(row.get(i)); } list.add(copy.getTableInternalId(), copy); } // ensure the next ids match as well ... needed when we merge modified tables rowIdGenerator.setNextId(copyThis.rowIdGenerator.getNextId()); } @Override public synchronized ODLTableDefinition deepCopyWithShallowValueCopy() { return new ODLTableImpl(this); } public ODLTableImpl(int id, String name) { super(id, name); // rows = new TreeList<ODLRowImpl>(); } // protected ODLRow getRowByIndx(int index) { // return rows.get(index).getValue(); // } @Override public synchronized int addColumn(int id, String name, ODLColumnType type, long flags) { int index = super.addColumn(id, name, type, flags); if (index!=-1) { for (ODLRowImpl node : list) { node.add(null); } return index; } return index; } @Override public synchronized int getRowCount() { return list.size(); } @Override public synchronized Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex >= columns.size() || rowIndex >= list.size()) { return null; } return list.getAt(rowIndex).get(columnIndex); } @Override public synchronized void setValueAt(Object aValue, int rowIndex, int columnIndex) { if (columnIndex >= columns.size() || rowIndex >= list.size()) { return; } // input value may not be of the expected type .. we should do a conversion to ensure it is aValue = toValidated(aValue, columnIndex); // update index getIndex(columnIndex).set(getRowId(rowIndex), getValueAt(rowIndex, columnIndex), aValue, this, columnIndex); // set the value list.getAt(rowIndex).set(columnIndex, aValue); } /** * Input value may not be of the expected type .. we should do a conversion to ensure it is * * @param val * @param col * @return */ private Object toValidated(Object val, int col) { val = ColumnValueProcessor.convertToMe(getColumnType(col),val); return val; } @Override public final synchronized int createEmptyRow(long rowId) { int row = list.size(); insertEmptyRow(row, rowId); return row; } @Override public final synchronized void insertEmptyRow(int insertAtRowNb, long rowId) { // get internal id int localId = -1; if (rowId == -1) { localId = rowIdGenerator.generateId(); } else { // we only use the local part of the rowid as may be copying from another table.. localId = TableUtils.getLocalRowId(rowId); } // generate new id if this one already used if (list.containsID(localId)) { localId = rowIdGenerator.generateId(); } // allocate row object int n = getColumnCount(); ODLRowImpl newRow = new ODLRowImpl(localId, getColumnCount()); for (int i = 0; i < n; i++) { newRow.add(null); } // set default values if we have them int nc = getColumnCount(); for (int col = 0; col < nc; col++) { Object val = getColumnDefaultValue(col); if (val != null) { val = toValidated(val, col); newRow.set(col, val); } } // save row list.insertAt(insertAtRowNb, newRow.getTableInternalId(), newRow); // update indices long rowid = getRowId(insertAtRowNb); for (int col = 0; col < nc; col++) { getIndex(col).insert(rowid, newRow.get(col), this, col); } } @Override public synchronized void deleteRow(int rowNumber) { if (rowNumber < list.size()) { // remove values from column indexes int nc = getColumnCount(); long rowid = getRowId(rowNumber); for (int col = 0; col < nc; col++) { Object value = getValueAt(rowNumber, col); getIndex(col).remove(rowid, value, this, col); } // remove row list.removeAt(rowNumber); } } @Override public synchronized void deleteColumn(int col) { if (col >= getColumnCount()) { return; } super.deleteColumn(col); for (ODLRowImpl row : list) { row.remove(col); } } @Override public synchronized boolean insertColumn(int id, int col, String name, ODLColumnType type, long flags, boolean allowDuplicateNames) { if (col > getColumnCount()) { col = getColumnCount(); } if (super.insertColumn(id, col, name, type, flags, allowDuplicateNames)) { for (ODLRowImpl row : list) { if (col < row.getColumnCount()) { row.add(col, null); } else { row.add(null); } } return true; } return false; } private static ODLTableImpl createTable(ODLDatastore<? extends ODLTableDefinition> ds, String name, int id) { if (id == -1) { throw new RuntimeException(); } if (ds.getTableByImmutableId(id) != null) { return null; } return new ODLTableImpl(id, name); } public final static ODLTableFactory<ODLTableAlterable> ODLTableAlterableFactory = new ODLTableFactory<ODLTableAlterable>() { @Override public ODLTableAlterable create(ODLDatastore<? extends ODLTableDefinition> ds, String name, int id) { return createTable(ds, name, id); } }; public final static ODLTableFactory<ODLTableDefinitionAlterable> ODLTableDefinitionAlterableFactory = new ODLTableFactory<ODLTableDefinitionAlterable>() { @Override public ODLTableDefinitionAlterable create(ODLDatastore<? extends ODLTableDefinition> ds, String name, int id) { return createTable(ds, name, id); } }; @Override public synchronized String toString() { return TableUtils.convertToString(this); } @Override public synchronized long getRowId(int rowIndex) { if (rowIndex >= list.size()) { return -1; } ODLRowImpl row = list.getAt(rowIndex); if (row.getTableInternalId() != list.getIDAt(rowIndex)) { throw new RuntimeException(); } int localId = row.getTableInternalId(); return TableUtils.getGlobalId(getImmutableId(), localId); } @Override public synchronized Object getValueById(long rowId, int columnIndex) { if (TableUtils.getTableId(rowId) != getImmutableId()) { return null; } ODLRowImpl row = list.getByID(TableUtils.getLocalRowId(rowId)); if (row != null) { return row.get(columnIndex); } return null; } @Override public synchronized void setValueById(Object aValue, long rowid, int columnIndex) { if (TableUtils.getTableId(rowid) == getImmutableId()) { // convert to correct type aValue = toValidated(aValue, columnIndex); ODLRowImpl row = list.getByID(TableUtils.getLocalRowId(rowid)); if (row != null) { // update index getIndex(columnIndex).set(rowid, row.get(columnIndex), aValue, this, columnIndex); // set the value row.set(columnIndex, aValue); } } } private ColumnIndex getIndex(int columnIndex) { return ((ODLIndexableColumn) columns.get(columnIndex)).index; } @Override public synchronized boolean containsRowId(long rowId) { return TableUtils.getTableId(rowId) == getImmutableId() && list.containsID(TableUtils.getLocalRowId(rowId)); } @Override protected ODLColumnDefinition createColObj(int id, String name, ODLColumnType type, long flags) { id = validateNewColumnId(id); return new ODLIndexableColumn(id, name, type, flags); } @Override public long[] find(int col, Object value) { return getIndex(col).find(this, col, value); } @Override public long getRowFlags(long rowId) { if (TableUtils.getTableId(rowId) != getImmutableId()) { return 0; } ODLRowImpl row = list.getByID(TableUtils.getLocalRowId(rowId)); if (row != null) { return row.getFlags(); } return 0; } @Override public void setRowFlags(long flags, long rowId) { if (TableUtils.getTableId(rowId) != getImmutableId()) { return ; } ODLRowImpl row = list.getByID(TableUtils.getLocalRowId(rowId)); if (row != null) { row.setFlags(flags); } } @Override public long getRowLastModifiedTimeMillsecs(long rowId) { ODLRowImpl row = list.getByID(TableUtils.getLocalRowId(rowId)); if (row != null) { return row.getLastModifiedMillisecs(); } return 0; } @Override public ODLTableReadOnly query(TableQuery query) { // to do ... implement queries throw new UnsupportedOperationException(); } }