From 88cb239aef46da29ae151330834dc52ddfa46e49 Mon Sep 17 00:00:00 2001 From: Marshall Hampson Date: Thu, 15 Nov 2018 12:14:41 -0800 Subject: [PATCH] WIP: add record storage --- .../mrhampson/database/ColumnDefinition.java | 25 +++--- src/com/mrhampson/database/ColumnValue.java | 4 +- src/com/mrhampson/database/Record.java | 77 +++++++++++++++++++ .../{DataType.java => StorageDataType.java} | 23 ++++-- .../database/TableStorageManager.java | 5 ++ src/com/mrhampson/database/TestMain.java | 4 +- .../database/VarCharColumnValue.java | 11 ++- 7 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 src/com/mrhampson/database/Record.java rename src/com/mrhampson/database/{DataType.java => StorageDataType.java} (53%) diff --git a/src/com/mrhampson/database/ColumnDefinition.java b/src/com/mrhampson/database/ColumnDefinition.java index df2c787..4c0eb3b 100644 --- a/src/com/mrhampson/database/ColumnDefinition.java +++ b/src/com/mrhampson/database/ColumnDefinition.java @@ -21,27 +21,26 @@ */ public class ColumnDefinition { static final int MAX_NAME_LENGTH = 256; - // Number of bytes in a column def on disk name + fieldLength + dataType + // Number of bytes in a column def on disk name + fieldLength + storageType static final int NUM_BYTES = 256 + 4 + 1; - private final DataType dataType; + private final StorageDataType storageType; private final int fieldLength; private final String columnName; - public ColumnDefinition(DataType dataType, int fieldLength, String columnName) { - Objects.requireNonNull(dataType); + public ColumnDefinition(StorageDataType storageType, int fieldLength, String columnName) { + Objects.requireNonNull(storageType); Objects.requireNonNull(columnName); if (columnName.length() > MAX_NAME_LENGTH) { throw new IllegalArgumentException("Name too long"); } - - this.dataType = dataType; + this.storageType = storageType; this.fieldLength = fieldLength; this.columnName = columnName; } - - public DataType getDataType() { - return dataType; + + public StorageDataType getStorageType() { + return storageType; } public int getFieldLength() { @@ -55,7 +54,7 @@ public String getColumnName() { public byte[] toBytes() { ByteBuffer output = ByteBuffer.allocate(NUM_BYTES); // Data type - int dataTypeOrdinal = dataType.ordinal(); + int dataTypeOrdinal = storageType.ordinal(); if (dataTypeOrdinal > 256) { throw new IllegalStateException("Data type is too large. Too many data types defined"); } @@ -75,7 +74,7 @@ public static ColumnDefinition fromBytes(ByteBuffer bytes) { } bytes.rewind(); int dataTypeOrdinal = bytes.get(); - DataType dataType = DataType.fromOrdinal(dataTypeOrdinal); + StorageDataType dataType = StorageDataType.fromOrdinal(dataTypeOrdinal); int fieldLength = bytes.getInt(); String columnName = ByteBufferUtils.fromASCIIBytes(bytes); return new ColumnDefinition(dataType, fieldLength, columnName.trim()); @@ -87,12 +86,12 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; ColumnDefinition that = (ColumnDefinition) o; return fieldLength == that.fieldLength && - dataType == that.dataType && + storageType == that.storageType && Objects.equals(columnName, that.columnName); } @Override public int hashCode() { - return Objects.hash(dataType, fieldLength, columnName); + return Objects.hash(storageType, fieldLength, columnName); } } diff --git a/src/com/mrhampson/database/ColumnValue.java b/src/com/mrhampson/database/ColumnValue.java index 2370fc0..4a3c754 100644 --- a/src/com/mrhampson/database/ColumnValue.java +++ b/src/com/mrhampson/database/ColumnValue.java @@ -21,14 +21,14 @@ */ public interface ColumnValue { ColumnDefinition getColumnDefinition(); - void setValue(T value); + void setValue(Object value); T getValue(); byte[] toBytes(); @SuppressWarnings("unchecked") static ColumnValue fromColumnDefinition(ColumnDefinition definition) { Objects.requireNonNull(definition); - switch (definition.getDataType()) { + switch (definition.getStorageType()) { case VARCHAR: return (ColumnValue)new VarCharColumnValue(definition); } diff --git a/src/com/mrhampson/database/Record.java b/src/com/mrhampson/database/Record.java new file mode 100644 index 0000000..705dbc4 --- /dev/null +++ b/src/com/mrhampson/database/Record.java @@ -0,0 +1,77 @@ +/* + * Record.java + * Created on Nov 15, 2018, 11:41 AM + * + * Copyright 2008-2018 LiveAction, Incorporated. All Rights Reserved. + * 3500 W Bayshore Road, Palo Alto, California 94303, U.S.A. + * + * This software is the confidential and proprietary information + * of LiveAction ("Confidential Information"). + * You shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with LiveAction. + */ +package com.mrhampson.database; + +import java.nio.ByteBuffer; +import java.util.*; + +/** + * @author Marshall Hampson + */ +public class Record { + private final int recordBytes; + private final Map> columnValues; + + private Record(Builder builder) { + this.recordBytes = builder.recordBytes; + this.columnValues = Collections.unmodifiableMap(builder.values); + } + + public Map> getColumnValues() { + return this.columnValues; + } + + public int getRecordBytes() { + return recordBytes; + } + + public byte[] toBytes() { + ByteBuffer buffer = ByteBuffer.allocate(recordBytes); + for (ColumnValue value : columnValues.values()) { + buffer.put(value.toBytes()); + } + return buffer.array(); + } + + public static final class Builder { + private final int recordBytes; + private final TableDefinition tableDefinition; + private final Map> values; + + public Builder(TableDefinition tableDefinition) { + Objects.requireNonNull(tableDefinition); + this.tableDefinition = tableDefinition; + this.values = new LinkedHashMap<>(); + int byteCounter = 0; + for (ColumnDefinition columnDefinition : this.tableDefinition.getColumns()) { + byteCounter += columnDefinition.getFieldLength(); + this.values.put(columnDefinition.getColumnName(), ColumnValue.fromColumnDefinition(columnDefinition)); + } + this.recordBytes = byteCounter; + } + + public void setColumnValue(String columnName, Object value) { + Objects.requireNonNull(columnName); + ColumnValue columnValue = this.values.get(columnName); + if (columnValue == null) { + throw new IllegalArgumentException("Column not defined"); + } + columnValue.setValue(value); + } + + public Record build() { + return new Record(this); + } + } +} diff --git a/src/com/mrhampson/database/DataType.java b/src/com/mrhampson/database/StorageDataType.java similarity index 53% rename from src/com/mrhampson/database/DataType.java rename to src/com/mrhampson/database/StorageDataType.java index f1c2334..6c0de65 100644 --- a/src/com/mrhampson/database/DataType.java +++ b/src/com/mrhampson/database/StorageDataType.java @@ -1,5 +1,5 @@ /* - * DataType.java + * StorageDataType.java * Created on Nov 10, 2018, 6:31 PM * * Copyright 2008-2018 LiveAction, Incorporated. All Rights Reserved. @@ -13,25 +13,38 @@ */ package com.mrhampson.database; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + /** * * NOTE: Cannot define more than 256 datatypes * @author Marshall Hampson */ -public enum DataType { +public enum StorageDataType { INTEGER, VARCHAR; - private static final DataType[] VALUES; + private static final StorageDataType[] VALUES; + private static final Map> STORAGE_TO_JAVA_TYPE; static { - VALUES = DataType.values(); + VALUES = StorageDataType.values(); + Map> storageToJavaType = new HashMap<>(); + storageToJavaType.put(INTEGER, Integer.class); + storageToJavaType.put(VARCHAR, String.class); + STORAGE_TO_JAVA_TYPE = Collections.unmodifiableMap(storageToJavaType); } - public static DataType fromOrdinal(int ordinal) { + public static StorageDataType fromOrdinal(int ordinal) { if (ordinal < 0 || ordinal >= VALUES.length) { throw new IllegalArgumentException("Ordinal out of bounds"); } return VALUES[ordinal]; } + + public Class getJavaType() { + return STORAGE_TO_JAVA_TYPE.get(this); + } } diff --git a/src/com/mrhampson/database/TableStorageManager.java b/src/com/mrhampson/database/TableStorageManager.java index c4e6720..d2e9bbc 100644 --- a/src/com/mrhampson/database/TableStorageManager.java +++ b/src/com/mrhampson/database/TableStorageManager.java @@ -19,6 +19,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.READ; @@ -43,6 +44,10 @@ public void create(TableDefinition tableDefinition) throws IOException { writeHeader(tableDefinition); } + public void storeRecord(Record record) throws IOException { + fileChannel.write(ByteBuffer.wrap(record.toBytes())); + } + public void load() throws IOException { fileChannel = FileChannel.open(tableFilePath, READ, WRITE); TableDefinition tableDefinition = readHeader(); diff --git a/src/com/mrhampson/database/TestMain.java b/src/com/mrhampson/database/TestMain.java index 8b184e1..cf8cc4a 100644 --- a/src/com/mrhampson/database/TestMain.java +++ b/src/com/mrhampson/database/TestMain.java @@ -23,8 +23,8 @@ */ public class TestMain { public static void main(String[] args) { - ColumnDefinition nameColumn = new ColumnDefinition(DataType.VARCHAR, 100, "NAME"); - ColumnDefinition ageColumn = new ColumnDefinition(DataType.VARCHAR, 100, "CITY"); + ColumnDefinition nameColumn = new ColumnDefinition(StorageDataType.VARCHAR, 100, "NAME"); + ColumnDefinition ageColumn = new ColumnDefinition(StorageDataType.VARCHAR, 100, "CITY"); TableDefinition tableDefinition = new TableDefinition( "PEOPLE", diff --git a/src/com/mrhampson/database/VarCharColumnValue.java b/src/com/mrhampson/database/VarCharColumnValue.java index fb28844..f87b51f 100644 --- a/src/com/mrhampson/database/VarCharColumnValue.java +++ b/src/com/mrhampson/database/VarCharColumnValue.java @@ -20,6 +20,7 @@ * @author Marshall Hampson */ public class VarCharColumnValue implements ColumnValue { + private static final Class CLASS = String.class; private final ColumnDefinition columnDefinition; private String value = null; @@ -34,11 +35,15 @@ public ColumnDefinition getColumnDefinition() { } @Override - public void setValue(String value) { - if (value != null && value.length() > columnDefinition.getFieldLength()) { + public void setValue(Object value) { + if (value != null && !CLASS.equals(value.getClass())) { + throw new IllegalArgumentException("Wrong type for column"); + } + String stringValue = (String)value; + if (stringValue != null && stringValue.length() > columnDefinition.getFieldLength()) { throw new IllegalArgumentException("Length out of range"); } - this.value = value; + this.value = stringValue; } @Override