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

org.fuchss.objectcasket.objectpacker.impl.JoinTableBuilder Maven / Gradle / Ivy

Go to download

Object Casket is a simple O/R mapper that can be used together with the Java Persistence API (JPA). The aim is to provide a simple solution for small projects to store multi-related entities in a simple manner.

There is a newer version: 0.20.17
Show newest version
package org.fuchss.objectcasket.objectpacker.impl;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.fuchss.objectcasket.common.CasketError.CE3;
import org.fuchss.objectcasket.common.CasketException;
import org.fuchss.objectcasket.objectpacker.port.Session;
import org.fuchss.objectcasket.tablemodule.port.Row;
import org.fuchss.objectcasket.tablemodule.port.Table;
import org.fuchss.objectcasket.tablemodule.port.TableModule;
import org.fuchss.objectcasket.tablemodule.port.TableObserver;

class JoinTableBuilder implements TableObserver {

	private static final String PK_ID = "pk@id";
	private static final String CLIENT_ID = "client@id";
	private static final String SUPPLIER_ID = "supplier@id";
	private static final Class PK_TYPE = Integer.class;

	private final SessionImpl session;
	private final TableModule tabMod;
	private Table joinTable;
	private final M2MInfo info;

	private final Map> joinTabEntryMap = new HashMap<>();
	private final Map rowToClientMap = new HashMap<>();
	private final Map rowToSupplierMap = new HashMap<>();
	private final Map supplierKeyMap = new HashMap<>();

	JoinTableBuilder(SessionImpl sessionImpl, TableModule tableModule, M2MInfo info) throws CasketException {
		this.info = info;
		this.session = sessionImpl;
		this.tabMod = tableModule;

		this.createTableOrView();
	}

	Class getSupplierClass() {
		return this.info.getSupplierClass();
	}

	int write(C client, Set suppliers, Object transaction) throws CasketException, IllegalArgumentException, IllegalAccessException {
		Set currentSuppliers = this.joinTabEntryMap.computeIfAbsent(client, k -> new HashMap<>()).keySet();
		boolean hasSuppliers = !currentSuppliers.isEmpty();
		Set newSuppliers = new HashSet<>(suppliers);
		newSuppliers.removeAll(currentSuppliers);
		Set removedSuppliers = new HashSet<>(currentSuppliers);
		removedSuppliers.removeAll(suppliers);

		this.deleteRow(client, removedSuppliers, transaction);
		this.createRows(client, newSuppliers, transaction);

		for (S supplier : newSuppliers)
			this.session.addClient(supplier);
		for (S supplier : removedSuppliers)
			this.session.removeClient(this.info.getSupplierClass(), this.info.getSupplierClassInfo().getPK(supplier));

		if (hasSuppliers)
			return (suppliers.isEmpty() ? -1 : 0);
		return (!suppliers.isEmpty() ? 1 : 0);
	}

	Set allSuppliers(C obj, Object transaction) throws CasketException {
		try {
			if (!this.joinTabEntryMap.containsKey(obj)) {
				Map newJoinTabEntries = new HashMap<>();
				this.readSuppliers(obj, newJoinTabEntries, transaction);
				this.joinTabEntryMap.put(obj, newJoinTabEntries);
			}
			return new HashSet<>(this.joinTabEntryMap.get(obj).keySet());
		} catch (Exception exc) {
			throw CasketException.build(exc);
		}
	}

	private void createTableOrView() throws CasketException {
		Map> signature = new HashMap<>();
		signature.put(SUPPLIER_ID, this.info.getSupplierClassInfo().getType());
		signature.put(CLIENT_ID, this.info.getClientClassInfo().getType());
		signature.put(PK_ID, PK_TYPE);

		if (this.tabMod.tableExists(this.info.getJoinTableName()))
			this.joinTable = this.tabMod.mkView(this.info.getJoinTableName(), PK_ID, signature, true);
		else
			this.joinTable = this.tabMod.createTable(this.info.getJoinTableName(), PK_ID, signature, true);
		this.joinTable.register(this);

	}

	private void createRows(C client, Set newSuppliers, Object transaction) throws IllegalArgumentException, IllegalAccessException, CasketException {
		Map joinTabEntries = this.joinTabEntryMap.get(client);
		Serializable clientPk = this.info.getClientClassInfo().getPK(client);
		for (S supplier : newSuppliers) {
			Map newValues = new HashMap<>();
			newValues.put(CLIENT_ID, clientPk);
			newValues.put(SUPPLIER_ID, this.info.getSupplierClassInfo().getPK(supplier));
			Row row = this.joinTable.createRow(newValues, transaction);
			joinTabEntries.put(supplier, row);
			this.rowToClientMap.put(row, client);
			this.rowToSupplierMap.put(row, supplier);
		}
	}

	private void deleteRow(C client, Set removedSuppliers, Object transaction) throws CasketException {
		Map joinTabEntries = this.joinTabEntryMap.get(client);

		for (S supplier : removedSuppliers) {
			Row joinTabRow = joinTabEntries.remove(supplier);
			this.joinTable.deleteRow(joinTabRow, transaction);
			this.rowToClientMap.remove(joinTabRow);
			this.rowToSupplierMap.remove(joinTabRow);
		}

	}

	private void readSuppliers(C obj, Map newJoinTabEntries, Object transaction) throws IllegalArgumentException, IllegalAccessException, CasketException {
		Set args = new HashSet<>();
		args.add(new Table.Exp(CLIENT_ID, Table.TabCMP.EQUAL, this.info.getClientClassInfo().getPK(obj)));
		List rows = this.joinTable.searchRows(args, transaction);
		for (Row row : rows) {
			Serializable fk = row.getValue(SUPPLIER_ID, this.info.getSupplierClassInfo().getType());
			Set arg = new HashSet<>();
			arg.add(new Session.Exp(this.info.getSupplierClassInfo().getFieldName(), "==", fk));
			S supplier = this.session.getObjects(this.info.getSupplierClass(), arg).iterator().next();
			this.rowToClientMap.put(row, obj);
			this.rowToSupplierMap.put(row, supplier);
			newJoinTabEntries.put(supplier, row);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public synchronized void update(Set changed, Set deleted, Set added) {
		synchronized (this.session) {
			if (this.session.ignore)
				return;
			try {
				ObjectBuilder clientBuilder = (ObjectBuilder) this.session.objectFactoryMap.getIfExists(this.info.getClientClass());
				ObjectBuilder supplierBuilder = (ObjectBuilder) this.session.objectFactoryMap.getIfExists(this.info.getSupplierClass());

				Set changedClients = new HashSet<>();
				Set changedSuppliers = new HashSet<>();
				Set suppliersToLoad = new HashSet<>();
				for (Row row : deleted) {
					S supplier = this.rowToSupplierMap.remove(row);
					C client = this.rowToClientMap.remove(row);
					if ((client != null) && (supplier != null)) {
						this.joinTabEntryMap.get(client).remove(supplier);
						changedClients.add(client);
					}
				}
				for (Row row : added) {
					Serializable clientKey = row.getValue(CLIENT_ID, this.info.getClientClassInfo().getType());
					C client = clientBuilder.getObjectByPk(clientKey);
					if (client == null)
						continue;
					Serializable supplierKey = row.getValue(SUPPLIER_ID, this.info.getSupplierClassInfo().getType());
					S supplier = supplierBuilder.getObjectByPk(supplierKey);
					if (supplier == null)
						suppliersToLoad.add(supplierKey);
					else
						changedSuppliers.add(supplier);
					changedClients.add(client);
					this.setMaps(row, client, supplier, supplierKey);

				}
				this.informBuilderAndSession(suppliersToLoad, changedClients, changedSuppliers, clientBuilder, supplierBuilder);
			} catch (Exception exc) {
				exc.printStackTrace();
			}
		}
	}

	private void setMaps(Row row, C client, S supplier, Serializable supplierKey) throws CasketException {
		if ((supplier == null) && (supplierKey == null))
			throw CE3.MISSING_SUPPLIER.defaultBuild(client, row, this.info.getJoinTableName());
		this.rowToClientMap.put(row, client);
		if (supplier == null)
			this.supplierKeyMap.put(supplierKey, row);
		else {
			this.rowToSupplierMap.put(row, supplier);
			this.joinTabEntryMap.get(client).put(supplier, row);
		}

	}

	private void informBuilderAndSession(Set suppliersToLoad, Set changedClients, Set changedSuppliers, ObjectBuilder clientBuilder, ObjectBuilder supplierBuilder) {
		if (changedClients.isEmpty() && changedSuppliers.isEmpty() && suppliersToLoad.isEmpty())
			return;
		if (!changedClients.isEmpty())
			clientBuilder.changedObjects(changedClients);
		if (!changedSuppliers.isEmpty())
			supplierBuilder.changedObjects(changedSuppliers);
		if (!suppliersToLoad.isEmpty())
			this.session.loadNewAssignedObjects(this, suppliersToLoad, this.info.getSupplierClassInfo().getFieldName());
		this.session.updateDone();
	}

	@SuppressWarnings("unchecked")
	protected void insertNewSuppliers(Map supplierMap) {
		for (Entry entry : supplierMap.entrySet()) {
			Row row = this.supplierKeyMap.remove(entry.getKey());
			C client = this.rowToClientMap.get(row);
			S supplier = (S) entry.getValue();
			this.rowToSupplierMap.put(row, supplier);
			this.joinTabEntryMap.computeIfAbsent(client, k -> new HashMap<>()).put(supplier, row);
		}
	}

}