/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florent Guillaume
*/
package org.eclipse.ecr.core.storage.sql;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* The data of a single row in a table (keys/values form a map), or of multiple
* rows with the same id (values is an array of Serializable).
* <p>
* The id of the row is distinguished internally from other columns. For
* fragments corresponding to created data, the initial id is a temporary one,
* and it will be changed after database insert.
*/
public final class Row extends RowId implements Serializable {
private static final long serialVersionUID = 1L;
private static final int DEFAULT = 5;
/**
* The row keys, for single row.
*/
protected String[] keys;
/**
* The row values.
*/
public Serializable[] values;
/**
* The size of the allocated part of {@link #values}, for single rows.
*/
protected int size;
/** Copy constructor. */
private Row(Row row) {
super(row);
keys = row.keys == null ? null : row.keys.clone();
values = row.values == null ? null : row.values.clone();
size = row.size;
}
@Override
public Row clone() {
return new Row(this);
}
/**
* Constructs an empty {@link Row} for the given table with the given id
* (may be {@code null}).
*/
public Row(String tableName, Serializable id) {
super(tableName, id);
keys = new String[DEFAULT];
values = new Serializable[DEFAULT];
// size = 0;
}
/**
* Constructs a new {@link Row} from a map.
*
* @param map the initial data to use
*/
public Row(String tableName, Map<String, Serializable> map) {
super(tableName, null); // id set through map
keys = new String[map.size()];
values = new Serializable[map.size()];
// size = 0;
for (Entry<String, Serializable> entry : map.entrySet()) {
putNew(entry.getKey(), entry.getValue());
}
}
/**
* Constructs a new {@link Row} from an array of values.
*
* @param array the initial data to use
*/
public Row(String tableName, Serializable id, Serializable[] array) {
super(tableName, id);
values = array.clone();
keys = null;
size = -1;
}
public boolean isCollection() {
return size == -1;
}
private void ensureCapacity(int minCapacity) {
if (minCapacity > values.length) {
Serializable[] k = keys;
Serializable[] d = values;
int newCapacity = (values.length * 3) / 2 + 1;
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
keys = new String[newCapacity];
values = new Serializable[newCapacity];
System.arraycopy(d, 0, values, 0, size);
System.arraycopy(k, 0, keys, 0, size);
}
}
/**
* Puts a key/value.
*
* @param key the key
* @param value the value
*/
public void put(String key, Serializable value) {
if (key.equals(Model.MAIN_KEY)) {
id = value;
return;
}
// linear search but the array is small
for (int i = 0; i < size; i++) {
if (key.equals(keys[i])) {
values[i] = value;
return;
}
}
ensureCapacity(size + 1);
keys[size] = key;
values[size++] = value;
}
/**
* Puts a key/value, assuming the key is not already there.
*
* @param key the key
* @param value the value
*/
public void putNew(String key, Serializable value) {
if (key.equals(Model.MAIN_KEY)) {
id = value;
return;
}
ensureCapacity(size + 1);
keys[size] = key;
values[size++] = value;
}
/**
* Gets a value from a key.
*
* @param key the key
* @return the value
*/
public Serializable get(String key) {
if (key.equals(Model.MAIN_KEY)) {
return id;
}
// linear search but the array is small
for (int i = 0; i < size; i++) {
if (key.equals(keys[i])) {
return values[i];
}
}
return null;
}
/**
* Gets the list of keys. The id is not included.
*/
public List<String> getKeys() {
List<String> list = new ArrayList<String>(size);
for (int i = 0; i < size; i++) {
list.add(keys[i]);
}
return list;
}
/**
* Gets the list of values. The id is not included.
*/
public List<Serializable> getValues() {
List<Serializable> list = new ArrayList<Serializable>(size);
for (int i = 0; i < size; i++) {
list.add(values[i]);
}
return list;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append('(');
buf.append(tableName);
buf.append(", ");
buf.append(id);
if (size != -1) {
// single row
buf.append(", {");
for (int i = 0; i < size; i++) {
if (i > 0) {
buf.append(", ");
}
buf.append(keys[i]);
buf.append('=');
printValue(values[i], buf);
}
buf.append('}');
} else {
// multiple rows
buf.append(", [");
for (int i = 0; i < values.length; i++) {
if (i > 0) {
buf.append(", ");
}
printValue(values[i], buf);
}
buf.append(']');
}
buf.append(')');
return buf.toString();
}
public static final int MAX_STRING = 100;
public static final int MAX_ARRAY = 10;
@SuppressWarnings("boxing")
public static void printValue(Serializable value, StringBuilder buf) {
if (value == null) {
buf.append("NULL");
} else if (value instanceof String) {
String v = (String) value;
if (v.length() > MAX_STRING) {
v = v.substring(0, MAX_STRING) + "...(" + v.length()
+ " chars)...";
}
buf.append('"');
buf.append(v);
buf.append('"');
} else if (value instanceof Calendar) {
Calendar cal = (Calendar) value;
char sign;
int offset = cal.getTimeZone().getOffset(cal.getTimeInMillis()) / 60000;
if (offset < 0) {
offset = -offset;
sign = '-';
} else {
sign = '+';
}
buf.append(String.format(
"Calendar(%04d-%02d-%02dT%02d:%02d:%02d.%03d%c%02d:%02d)",
cal.get(Calendar.YEAR), //
cal.get(Calendar.MONTH) + 1, //
cal.get(Calendar.DAY_OF_MONTH), //
cal.get(Calendar.HOUR_OF_DAY), //
cal.get(Calendar.MINUTE), //
cal.get(Calendar.SECOND), //
cal.get(Calendar.MILLISECOND), //
sign, offset / 60, offset % 60));
} else if (value instanceof Binary) {
buf.append("Binary(");
buf.append(((Binary) value).getDigest());
buf.append(')');
} else if (value.getClass().isArray()) {
Serializable[] v = (Serializable[]) value;
buf.append('[');
for (int i = 0; i < v.length; i++) {
if (i > 0) {
buf.append(',');
if (i > MAX_ARRAY) {
buf.append("...(");
buf.append(v.length);
buf.append(" items)...");
break;
}
}
printValue(v[i], buf);
}
buf.append(']');
} else {
buf.append(value.toString());
}
}
}