All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.teamapps.universaldb.model.TableModel Maven / Gradle / Ivy

/*-
 * ========================LICENSE_START=================================
 * UniversalDB
 * ---
 * Copyright (C) 2014 - 2023 TeamApps.org
 * ---
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =========================LICENSE_END==================================
 */
package org.teamapps.universaldb.model;

import org.teamapps.message.protocol.utils.MessageUtils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class TableModel {
	private static final int TABLE_MODEL_VERSION = 1;

	public static final String FIELD_CREATION_DATE = "metaCreationDate";
	public static final String FIELD_CREATED_BY = "metaCreatedBy";
	public static final String FIELD_MODIFICATION_DATE = "metaModificationDate";
	public static final String FIELD_MODIFIED_BY = "metaModifiedBy";
	public static final String FIELD_DELETION_DATE = "metaDeletionDate";
	public static final String FIELD_DELETED_BY = "metaDeletedBy";
	public static final String FIELD_RESTORE_DATE = "metaRestoreDate";
	public static final String FIELD_RESTORED_BY = "metaRestoredBy";
	public static final String FIELD_ID = "id";
	public static final String[] FORBIDDEN_COLUMN_NAMES = new String[]{FIELD_CREATION_DATE, FIELD_CREATED_BY, FIELD_MODIFICATION_DATE, FIELD_MODIFIED_BY, FIELD_DELETION_DATE, FIELD_DELETED_BY, FIELD_RESTORE_DATE, FIELD_RESTORED_BY, FIELD_ID, "coll-recs", "coll-del-recs", "versioning-pos", "matches"};
	private final DatabaseModel databaseModel;
	private final String name;
	private final String title;
	private final boolean remoteTable;
	private final String remoteTableName;
	private final String remoteDatabase;
	private final String remoteDatabaseNamespace;
	private final boolean trackModifications;
	private final boolean versioning;
	private final boolean recoverableRecords; //else: record id to index pos column
	private final List fields = new ArrayList<>();
	private int tableId;
	private boolean deprecated;
	private boolean deleted;
	private int dateCreated;
	private int dateModified;
	private int versionCreated;
	private int versionModified;
	protected TableModel(DatabaseModel databaseModel, String name, String title, boolean remoteTable, String remoteDatabase, String remoteDatabaseNamespace, boolean trackModifications, boolean versioning, boolean recoverableRecords) {
		this(databaseModel, name, title, remoteTable, null, remoteDatabase, remoteDatabaseNamespace, trackModifications, versioning, recoverableRecords);
	}

	protected TableModel(DatabaseModel databaseModel, String name, String title, boolean remoteTable, String remoteTableName, String remoteDatabase, String remoteDatabaseNamespace, boolean trackModifications, boolean versioning, boolean recoverableRecords) {
		NamingUtils.checkName(name, title);
		this.databaseModel = databaseModel;
		this.name = NamingUtils.createName(name);
		this.title = NamingUtils.createTitle(title);
		this.remoteTable = remoteTable;
		this.remoteTableName = remoteTableName != null ? remoteTableName : name;
		this.remoteDatabase = remoteDatabase;
		this.remoteDatabaseNamespace = remoteDatabaseNamespace;
		this.trackModifications = trackModifications;
		this.versioning = versioning;
		this.recoverableRecords = recoverableRecords;
		if (trackModifications) {
			addTimestamp(FIELD_CREATION_DATE);
			addInteger(FIELD_CREATED_BY);
			addTimestamp(FIELD_MODIFICATION_DATE);
			addInteger(FIELD_MODIFIED_BY);
		}
		if (recoverableRecords) {
			addTimestamp(FIELD_DELETION_DATE);
			addInteger(FIELD_DELETED_BY);
			addTimestamp(FIELD_RESTORE_DATE);
			addInteger(FIELD_RESTORED_BY);
		}
	}

	protected TableModel(DataInputStream dis, List> resolveFunctions, DatabaseModel databaseModel) throws IOException {
		this.databaseModel = databaseModel;
		int modelVersion = dis.readInt();
		name = MessageUtils.readString(dis);
		title = MessageUtils.readString(dis);
		remoteTable = dis.readBoolean();
		remoteTableName = remoteTable ? MessageUtils.readString(dis) : null;
		remoteDatabase = remoteTable ? MessageUtils.readString(dis) : null;
		remoteDatabaseNamespace = remoteTable ? MessageUtils.readString(dis) : null;
		trackModifications = dis.readBoolean();
		versioning = dis.readBoolean();
		recoverableRecords = dis.readBoolean();
		tableId = dis.readInt();
		deprecated = dis.readBoolean();
		deleted = dis.readBoolean();
		dateCreated = dis.readInt();
		dateModified = dis.readInt();
		versionCreated = dis.readInt();
		versionModified = dis.readInt();
		int fieldCount = dis.readInt();
		for (int i = 0; i < fieldCount; i++) {
			FieldType fieldType = FieldType.getTypeById(dis.readInt());
			if (fieldType == FieldType.ENUM) {
				addFieldModel(new EnumFieldModel(dis, this, databaseModel));
			} else if (fieldType == FieldType.SINGLE_REFERENCE || fieldType == FieldType.MULTI_REFERENCE) {
				addFieldModel(new ReferenceFieldModel(dis, this, resolveFunctions));
			} else if (fieldType == FieldType.FILE) {
				addFieldModel(new FileFieldModel(dis, this));
			} else {
				addFieldModel(new FieldModel(dis, this));
			}
		}
	}

	public static boolean isReservedMetaName(String name) {
		for (String columnName : FORBIDDEN_COLUMN_NAMES) {
			if (name.equals(columnName)) {
				return true;
			}
		}
		return false;
	}

	public void write(DataOutputStream dos) throws IOException {
		dos.writeInt(TABLE_MODEL_VERSION);
		MessageUtils.writeString(dos, name);
		MessageUtils.writeString(dos, title);
		dos.writeBoolean(remoteTable);
		if (remoteTable) {
			MessageUtils.writeString(dos, remoteTableName);
			MessageUtils.writeString(dos, remoteDatabase);
			MessageUtils.writeString(dos, remoteDatabaseNamespace);
		}
		dos.writeBoolean(trackModifications);
		dos.writeBoolean(versioning);
		dos.writeBoolean(recoverableRecords);
		dos.writeInt(tableId);
		dos.writeBoolean(deprecated);
		dos.writeBoolean(deleted);
		dos.writeInt(dateCreated);
		dos.writeInt(dateModified);
		dos.writeInt(versionCreated);
		dos.writeInt(versionModified);
		dos.writeInt(fields.size());
		for (FieldModel field : fields) {
			dos.writeInt(field.getFieldType().getId());
			field.write(dos);
		}
	}

	public FieldModel getField(String fieldName) {
		return fields.stream().filter(f -> f.getName().equals(fieldName)).findAny().orElse(null);
	}

	public ReferenceFieldModel getReferenceField(String fieldName) {
		FieldModel fieldModel = fields.stream().filter(f -> f.getName().equals(fieldName)).findAny().orElse(null);
		return fieldModel != null && fieldModel.getFieldType().isReference() ? (ReferenceFieldModel) fieldModel : null;
	}

	public ReferenceFieldModel addReference(String name, TableModel referencedTable) {
		return addReference(name, referencedTable, false);
	}

	public ReferenceFieldModel addReference(String name, TableModel referencedTable, boolean cascadeDelete) {
		return addReference(name, name, referencedTable, cascadeDelete);
	}

	public ReferenceFieldModel addReference(String name, String title, String referencedTable, boolean cascadeDelete) {
		TableModel tableModel = databaseModel.getTable(referencedTable);
		return addReference(name, title, tableModel, cascadeDelete);
	}

	public ReferenceFieldModel addReference(String name, String title, TableModel referencedTable, boolean cascadeDelete) {
		ReferenceFieldModel referenceFieldModel = new ReferenceFieldModel(name, title, this, referencedTable, false, cascadeDelete, null);
		addFieldModel(referenceFieldModel);
		return referenceFieldModel;
	}

	public ReferenceFieldModel addReference(String name, ReferenceFieldModel reverseReference) {
		return addReference(name, reverseReference, false);
	}

	public ReferenceFieldModel addReference(String name, ReferenceFieldModel reverseReference, boolean cascadeDelete) {
		return addReference(name, name, reverseReference, cascadeDelete);
	}

	public ReferenceFieldModel addReference(String name, String title, ReferenceFieldModel reverseReference, boolean cascadeDelete) {
		ReferenceFieldModel referenceFieldModel = new ReferenceFieldModel(name, title, this, reverseReference.getTableModel(), false, cascadeDelete, reverseReference);
		addFieldModel(referenceFieldModel);
		return referenceFieldModel;
	}

	public ReferenceFieldModel addMultiReference(String name, TableModel referencedTable) {
		return addMultiReference(name, referencedTable, false);
	}

	public ReferenceFieldModel addMultiReference(String name, TableModel referencedTable, boolean cascadeDelete) {
		return addMultiReference(name, name, referencedTable, cascadeDelete);
	}

	public ReferenceFieldModel addMultiReference(String name, String title, String referencedTable, boolean cascadeDelete) {
		TableModel tableModel = databaseModel.getTable(referencedTable);
		return addMultiReference(name, title, tableModel, cascadeDelete);
	}

	public ReferenceFieldModel addMultiReference(String name, String title, TableModel referencedTable, boolean cascadeDelete) {
		ReferenceFieldModel referenceFieldModel = new ReferenceFieldModel(name, title, this, referencedTable, true, cascadeDelete, null);
		addFieldModel(referenceFieldModel);
		return referenceFieldModel;
	}

	public ReferenceFieldModel addMultiReference(String name, ReferenceFieldModel reverseReference) {
		return addMultiReference(name, reverseReference, false);
	}

	public ReferenceFieldModel addMultiReference(String name, ReferenceFieldModel reverseReference, boolean cascadeDelete) {
		return addMultiReference(name, name, reverseReference, cascadeDelete);
	}

	public ReferenceFieldModel addMultiReference(String name, String title, ReferenceFieldModel reverseReference, boolean cascadeDelete) {
		ReferenceFieldModel referenceFieldModel = new ReferenceFieldModel(name, title, this, reverseReference.getTableModel(), true, cascadeDelete, reverseReference);
		addFieldModel(referenceFieldModel);
		return referenceFieldModel;
	}

	public EnumFieldModel addEnum(EnumModel enumModel) {
		return addEnum(enumModel.getName(), enumModel.getTitle(), enumModel);
	}

	public EnumFieldModel addEnum(String name, EnumModel enumModel) {
		return addEnum(name, name, enumModel);
	}

	public EnumFieldModel addEnum(String name, String title, String enumName) {
		EnumModel enumModel = databaseModel.getEnumModel(enumName);
		return addEnum(name, title, enumModel);
	}

	public EnumFieldModel addEnum(String name, String title, EnumModel enumModel) {
		if (enumModel == null) {
			throw new RuntimeException("Error: missing enum model for field:" + name);
		}
		EnumFieldModel enumFieldModel = new EnumFieldModel(name, title, this, enumModel);
		addFieldModel(enumFieldModel);
		return enumFieldModel;
	}

	public FileFieldModel addFile(String name) {
		return addFile(name, name, true, true);
	}

	public FileFieldModel addFile(String name, String title) {
		return addFile(name, title, true, true);
	}

	public FileFieldModel addFile(String name, String title, boolean indexContent, boolean detectLanguage) {
		return addFile(name, title, indexContent, 100_000, detectLanguage);
	}

	public FileFieldModel addFile(String name, String title, boolean indexContent, int maxIndexContentLength, boolean detectLanguage) {
		FileFieldModel fileFieldModel = new FileFieldModel(name, title, this, indexContent, maxIndexContentLength, detectLanguage);
		addFieldModel(fileFieldModel);
		return fileFieldModel;
	}

	public FieldModel addBoolean(String name) {
		return addFieldModel(name, FieldType.BOOLEAN);
	}

	public FieldModel addShort(String name) {
		return addFieldModel(name, FieldType.SHORT);
	}

	public FieldModel addInteger(String name) {
		return addFieldModel(name, FieldType.INT);
	}

	public FieldModel addLong(String name) {
		return addFieldModel(name, FieldType.LONG);
	}

	public FieldModel addFloat(String name) {
		return addFieldModel(name, FieldType.FLOAT);
	}

	public FieldModel addDouble(String name) {
		return addFieldModel(name, FieldType.DOUBLE);
	}

	public FieldModel addText(String name) {
		return addFieldModel(name, FieldType.TEXT);
	}

	public FieldModel addTranslatableText(String name) {
		return addFieldModel(name, FieldType.TRANSLATABLE_TEXT);
	}

	public FieldModel addByteArray(String name) {
		return addFieldModel(name, FieldType.BINARY);
	}

	public FieldModel addTimestamp(String name) {
		return addFieldModel(name, FieldType.TIMESTAMP);
	}

	public FieldModel addLocalDate(String name) {
		return addFieldModel(name, FieldType.LOCAL_DATE);
	}

	public FieldModel addDateTime(String name) {
		return addFieldModel(name, FieldType.DATE_TIME);
	}

	public FieldModel addDate(String name) {
		return addFieldModel(name, FieldType.DATE);
	}

	public FieldModel addTime(String name) {
		return addFieldModel(name, FieldType.TIME);
	}

	public FieldModel addBoolean(String name, String title) {
		return addFieldModel(name, title, FieldType.BOOLEAN);
	}

	public FieldModel addShort(String name, String title) {
		return addFieldModel(name, title, FieldType.SHORT);
	}

	public FieldModel addInteger(String name, String title) {
		return addFieldModel(name, title, FieldType.INT);
	}

	public FieldModel addLong(String name, String title) {
		return addFieldModel(name, title, FieldType.LONG);
	}

	public FieldModel addFloat(String name, String title) {
		return addFieldModel(name, title, FieldType.FLOAT);
	}

	public FieldModel addDouble(String name, String title) {
		return addFieldModel(name, title, FieldType.DOUBLE);
	}

	public FieldModel addText(String name, String title) {
		return addFieldModel(name, title, FieldType.TEXT);
	}

	public FieldModel addTranslatableText(String name, String title) {
		return addFieldModel(name, title, FieldType.TRANSLATABLE_TEXT);
	}

	public FieldModel addByteArray(String name, String title) {
		return addFieldModel(name, title, FieldType.BINARY);
	}

	public FieldModel addTimestamp(String name, String title) {
		return addFieldModel(name, title, FieldType.TIMESTAMP);
	}

	public FieldModel addLocalDate(String name, String title) {
		return addFieldModel(name, title, FieldType.LOCAL_DATE);
	}

	public FieldModel addDateTime(String name, String title) {
		return addFieldModel(name, title, FieldType.DATE_TIME);
	}

	public FieldModel addDate(String name, String title) {
		return addFieldModel(name, title, FieldType.DATE);
	}

	public FieldModel addTime(String name, String title) {
		return addFieldModel(name, title, FieldType.TIME);
	}

	private FieldModel addFieldModel(String title, FieldType fieldType) {
		return addFieldModel(title, title, fieldType);
	}

	private FieldModel addFieldModel(String name, String title, FieldType fieldType) {
		FieldModel fieldModel = new FieldModel(name, title, this, fieldType);
		addFieldModel(fieldModel);
		return fieldModel;
	}

	protected FieldModel addFieldModel(FieldModel fieldModel) {
		if (fields.stream().anyMatch(f -> f.getName().equals(fieldModel.getName()))) {
			throw  new RuntimeException("Adding duplicate field names not allowed, table:" + getName() + ", field-name:" + fieldModel.getName());
		}
		fields.add(fieldModel);
		return fieldModel;
	}


	public String getName() {
		return name;
	}

	public String getTitle() {
		return title;
	}

	public String getRemoteTableName() {
		return remoteTableName;
	}

	public boolean isRemoteTable() {
		return remoteTable;
	}

	public String getRemoteDatabase() {
		return remoteDatabase;
	}

	public String getRemoteDatabaseNamespace() {
		return remoteDatabaseNamespace;
	}

	public boolean isTrackModifications() {
		return trackModifications;
	}

	public boolean isVersioning() {
		return versioning;
	}

	public boolean isRecoverableRecords() {
		return recoverableRecords;
	}

	public List getFields() {
		return new ArrayList<>(fields);
	}

	public List getReferenceFields() {
		return fields.stream()
				.filter(f -> f.getFieldType().isReference())
				.map(f -> (ReferenceFieldModel) f)
				.collect(Collectors.toList());
	}

	public List getEnumFields() {
		return fields.stream()
				.filter(f -> f.getFieldType() == FieldType.ENUM)
				.map(f -> (EnumFieldModel) f)
				.collect(Collectors.toList());
	}

	public List getFileFields() {
		return fields.stream()
				.filter(f -> f.getFieldType() == FieldType.FILE)
				.map(f -> (FileFieldModel) f)
				.collect(Collectors.toList());
	}

	public int getTableId() {
		return tableId;
	}

	protected void setTableId(int tableId) {
		if (this.tableId != 0) {
			throw new RuntimeException("Error: table id already set:" + this.tableId + ", new:" + tableId);
		}
		this.tableId = tableId;
	}

	public boolean isDeprecated() {
		return deprecated;
	}

	protected void setDeprecated(boolean deprecated) {
		this.deprecated = deprecated;
	}

	public boolean isDeleted() {
		return deleted;
	}

	protected void setDeleted(boolean deleted) {
		this.deleted = deleted;
	}

	public int getDateCreated() {
		return dateCreated;
	}

	protected void setDateCreated(int dateCreated) {
		this.dateCreated = dateCreated;
	}

	public int getDateModified() {
		return dateModified;
	}

	protected void setDateModified(int dateModified) {
		this.dateModified = dateModified;
	}

	public int getVersionCreated() {
		return versionCreated;
	}

	protected void setVersionCreated(int versionCreated) {
		this.versionCreated = versionCreated;
	}

	public int getVersionModified() {
		return versionModified;
	}

	protected void setVersionModified(int versionModified) {
		this.versionModified = versionModified;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		String type = remoteTable ? "remote table (" + remoteDatabase + ")" : "table";
		sb.append(type)
				.append(": ")
				.append(name).append(" (").append(title).append(")")
				.append(" [").append(tableId).append("]\n");
		for (FieldModel field : fields) {
			sb.append("\t").append(field.toString()).append("\n");
		}
		return sb.toString();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy