package org.cytoscape.rest.internal.datamapper; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.ws.rs.NotFoundException; import org.cytoscape.model.CyColumn; import org.cytoscape.model.CyIdentifiable; import org.cytoscape.model.CyRow; import org.cytoscape.model.CyTable; import org.cytoscape.rest.internal.resource.JsonTags; import com.fasterxml.jackson.databind.JsonNode; /** * Create & Update table objects. * * */ public class TableMapper { public void updateColumnName(final JsonNode rootNode, final CyTable table) { final JsonNode currentNameTag = rootNode.get(JsonTags.COLUMN_NAME_OLD); if(currentNameTag == null) { throw new IllegalArgumentException("Original column name is missing."); } final JsonNode newNameTag = rootNode.get(JsonTags.COLUMN_NAME_NEW); if(newNameTag == null) { throw new IllegalArgumentException("New column name is missing."); } final String currentName = currentNameTag.asText(); if(currentName == null || currentName.isEmpty()) { throw new IllegalArgumentException("Original column name is missing."); } final String newName = newNameTag.asText(); if(newName == null || newName.isEmpty()) { throw new IllegalArgumentException("New column name is missing."); } final CyColumn column = table.getColumn(currentName); if (column == null) { throw new NotFoundException("Column does not exist."); } column.setName(newName); } public void createNewColumn(final JsonNode rootNode, final CyTable table, final CyTable localTable) { // Extract required fields final String columnName = rootNode.get(JsonTags.COLUMN_NAME).textValue(); final Class<?> type = MapperUtil.getColumnClass(rootNode.get(JsonTags.COLUMN_TYPE).textValue()); if(table.getColumn(columnName) !=null) { throw new IllegalArgumentException("Column already exists: " + columnName); } // Optional: fields boolean isImmutable = false; boolean isList = false; boolean isLocal = false; final JsonNode immutable = rootNode.get(JsonTags.COLUMN_IMMUTABLE); final JsonNode list = rootNode.get(JsonTags.COLUMN_IS_LIST); final JsonNode local = rootNode.get(JsonTags.COLUMN_IS_LOCAL); if(list != null) { isList = list.asBoolean(); } if(immutable != null) { isImmutable = immutable.asBoolean(); } if(local != null) { isLocal = local.asBoolean(); } if(isList) { if(isLocal) { localTable.createListColumn(columnName, type, isImmutable); } else { table.createListColumn(columnName, type, isImmutable); } } else { if(isLocal) { localTable.createColumn(columnName, type, isImmutable); } else { table.createColumn(columnName, type, isImmutable); } } } public void updateColumnValues(final JsonNode rootNode, final CyTable table, final String columnName) { // This should be an array of objects for(final JsonNode entry:rootNode) { final Long primaryKey = entry.get(CyIdentifiable.SUID).asLong(); final CyRow row = table.getRow(primaryKey); if(row == null) { continue; } JsonNode value = entry.get("value"); setValue(table.getColumn(columnName).getType(), value, row, columnName); } } public void updateAllColumnValues(final String defaultValue, final CyTable table, final String columnName) { // This should be an array of objects final Class<?> dataType = table.getColumn(columnName).getType(); for(final CyRow row : table.getAllRows()) { assignValue(defaultValue, dataType, row, columnName); } } /** * This is for PUT method for default tables. * * @param rootNode a JSON array. * @param table CyTable to be updated. * */ private static final String KEY = "key"; private static final String DATA_KEY = "dataKey"; private static final String DATA = "data"; public void updateTableValues(final JsonNode rootNode, final CyTable table) { // Validate body final JsonNode data = rootNode.get(DATA); if(data == null) { throw new NotFoundException("Data array is missing."); } if(!data.isArray()) { throw new IllegalArgumentException("Data should be an array."); } final JsonNode keyCol = rootNode.get(KEY); final String keyColName; if(keyCol == null) { // If not specified, simply use SUID. keyColName = CyIdentifiable.SUID; } else { keyColName = keyCol.asText(); } // Check such column exists or not. final CyColumn col = table.getColumn(keyColName); if(col == null) { throw new NotFoundException("No such column in the table: " + keyColName); } final JsonNode dataKeyCol = rootNode.get(DATA_KEY); final String dataKeyColName; if(dataKeyCol == null) { dataKeyColName = CyIdentifiable.SUID; } else { dataKeyColName = dataKeyCol.asText(); } // This should be an array of objects for(final JsonNode entry:data) { final JsonNode keyValue = entry.get(dataKeyColName); if(keyValue == null) { // Skip the entry if there is no mapping key value. continue; } final Object key = MapperUtil.getValue(keyValue, col.getType()); if(key == null) { // Key is invalid. continue; } final Collection<CyRow> machingRows = table.getMatchingRows(keyColName, key); if(machingRows.isEmpty()) { continue; } for (final CyRow row : machingRows) { final Iterator<String> fields = entry.fieldNames(); while (fields.hasNext()) { final String field = fields.next(); final JsonNode value = entry.get(field); if(value == null) { continue; } CyColumn column = table.getColumn(field); if (column == null) { // Need to create new column. final Class<?> type = getValueType(value); if(type == List.class) { // List is not supported. continue; } table.createColumn(field, type, false); column = table.getColumn(field); } try { setValue(column.getType(), value, row, field); } catch (Exception e) { // Simply ignore invalid value e.printStackTrace(); continue; } } } } } private final void setValue(final Class<?> type, final JsonNode value, final CyRow row, final String columnName) { if (type == String.class) { row.set(columnName, value.asText()); } else if (type == Boolean.class) { row.set(columnName, value.asBoolean()); } else if (type == Double.class) { row.set(columnName, value.asDouble()); } else if (type == Integer.class) { row.set(columnName, value.asInt()); } else if (type == Long.class) { row.set(columnName, value.asLong()); } else if (type == Float.class) { row.set(columnName, value.asDouble()); } } private final void assignValue(final String value, final Class<?> type, final CyRow row, final String columnName) { if (type == String.class) { row.set(columnName, value.toString()); } else if (type == Boolean.class) { row.set(columnName, Boolean.parseBoolean(value)); } else if (type == Double.class) { row.set(columnName, Double.parseDouble(value)); } else if (type == Integer.class) { row.set(columnName, Integer.parseInt(value)); } else if (type == Long.class) { row.set(columnName, Long.parseLong(value)); } else if (type == Float.class) { row.set(columnName, Double.parseDouble(value)); } } /** * Check data type. All numbers will be set to Double. * * @param value * @return * */ private final Class<?> getValueType(final JsonNode value) { if (value.isArray()) { return List.class; } else if (value.isBoolean()) { return Boolean.class; } else if (value.isNumber()) { return Double.class; } else { return String.class; } } }