blob: a52bbc5e4bf83c78694ea3e15a0d4b8c9c938018 [file] [log] [blame]
/*
* Copyright (c) 2013, 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.db.ddl.delta;
import org.eclipse.net4j.db.ddl.delta.IDBDelta.ChangeKind;
import org.eclipse.net4j.db.ddl.delta.IDBDelta.DeltaType;
import org.eclipse.net4j.internal.db.ddl.delta.DBDelta;
import org.eclipse.net4j.internal.db.ddl.delta.DBDeltaWithProperties;
import org.eclipse.net4j.internal.db.ddl.delta.DBFieldDelta;
import org.eclipse.net4j.internal.db.ddl.delta.DBIndexDelta;
import org.eclipse.net4j.internal.db.ddl.delta.DBIndexFieldDelta;
import org.eclipse.net4j.internal.db.ddl.delta.DBPropertyDelta;
import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta;
import org.eclipse.net4j.internal.db.ddl.delta.DBTableDelta;
import org.eclipse.net4j.util.collection.Pair;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @since 4.2
* @author Eike Stepper
*/
public interface IDBDeltaVisitor
{
public void visit(IDBSchemaDelta delta);
public void visit(IDBTableDelta delta);
public void visit(IDBFieldDelta delta);
public void visit(IDBIndexDelta delta);
public void visit(IDBIndexFieldDelta delta);
public void visit(IDBPropertyDelta<?> delta);
/**
* @author Eike Stepper
*/
public static final class StopRecursion extends RuntimeException
{
private static final long serialVersionUID = 1L;
}
/**
* @author Eike Stepper
*/
public static class Default implements IDBDeltaVisitor
{
public void visit(IDBSchemaDelta delta)
{
if (handle(delta))
{
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
added(delta);
break;
case REMOVE:
removed(delta);
break;
case CHANGE:
changed(delta);
break;
default:
illegalChangeKind(changeKind);
}
}
else
{
stopRecursion();
}
}
protected void added(IDBSchemaDelta delta)
{
visitDefault(delta);
}
protected void removed(IDBSchemaDelta delta)
{
visitDefault(delta);
}
protected void changed(IDBSchemaDelta delta)
{
visitDefault(delta);
}
public void visit(IDBTableDelta delta)
{
if (handle(delta))
{
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
added(delta);
break;
case REMOVE:
removed(delta);
break;
case CHANGE:
changed(delta);
break;
default:
illegalChangeKind(changeKind);
}
}
else
{
stopRecursion();
}
}
protected void added(IDBTableDelta delta)
{
visitDefault(delta);
}
protected void removed(IDBTableDelta delta)
{
visitDefault(delta);
}
protected void changed(IDBTableDelta delta)
{
visitDefault(delta);
}
public void visit(IDBFieldDelta delta)
{
if (handle(delta))
{
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
added(delta);
break;
case REMOVE:
removed(delta);
break;
case CHANGE:
changed(delta);
break;
default:
illegalChangeKind(changeKind);
}
}
else
{
stopRecursion();
}
}
protected void added(IDBFieldDelta delta)
{
visitDefault(delta);
}
protected void removed(IDBFieldDelta delta)
{
visitDefault(delta);
}
protected void changed(IDBFieldDelta delta)
{
visitDefault(delta);
}
public void visit(IDBIndexDelta delta)
{
if (handle(delta))
{
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
added(delta);
break;
case REMOVE:
removed(delta);
break;
case CHANGE:
changed(delta);
break;
default:
illegalChangeKind(changeKind);
}
}
else
{
stopRecursion();
}
}
protected void added(IDBIndexDelta delta)
{
visitDefault(delta);
}
protected void removed(IDBIndexDelta delta)
{
visitDefault(delta);
}
protected void changed(IDBIndexDelta delta)
{
visitDefault(delta);
}
public void visit(IDBIndexFieldDelta delta)
{
if (handle(delta))
{
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
added(delta);
break;
case REMOVE:
removed(delta);
break;
case CHANGE:
changed(delta);
break;
default:
illegalChangeKind(changeKind);
}
}
else
{
stopRecursion();
}
}
protected void added(IDBIndexFieldDelta delta)
{
visitDefault(delta);
}
protected void removed(IDBIndexFieldDelta delta)
{
visitDefault(delta);
}
protected void changed(IDBIndexFieldDelta delta)
{
visitDefault(delta);
}
public void visit(IDBPropertyDelta<?> delta)
{
if (handle(delta))
{
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
added(delta);
break;
case REMOVE:
removed(delta);
break;
case CHANGE:
changed(delta);
break;
default:
illegalChangeKind(changeKind);
}
}
else
{
stopRecursion();
}
}
protected void added(IDBPropertyDelta<?> delta)
{
visitDefault(delta);
}
protected void removed(IDBPropertyDelta<?> delta)
{
visitDefault(delta);
}
protected void changed(IDBPropertyDelta<?> delta)
{
visitDefault(delta);
}
protected void visitDefault(IDBDelta delta)
{
}
protected boolean handle(IDBDelta delta)
{
return true;
}
protected final void stopRecursion()
{
throw new StopRecursion();
}
private void illegalChangeKind(ChangeKind changeKind)
{
throw new IllegalStateException("Illegal change kind: " + changeKind);
}
}
/**
* @author Eike Stepper
*/
public static class Filter extends Default
{
public static final Policy DEFAULT_POLICY = new Policy().allowAll().freeze();
private Policy policy;
public Filter()
{
this(null);
}
public Filter(Policy policy)
{
this.policy = policy == null ? DEFAULT_POLICY : policy;
}
public final Policy getPolicy()
{
return policy;
}
@Override
protected void added(IDBSchemaDelta delta)
{
doVisit(delta);
}
@Override
protected void added(IDBTableDelta delta)
{
doVisit(delta);
}
@Override
protected void added(IDBFieldDelta delta)
{
doVisit(delta);
}
@Override
protected void added(IDBIndexDelta delta)
{
doVisit(delta);
}
@Override
protected void added(IDBIndexFieldDelta delta)
{
doVisit(delta);
}
@Override
protected void added(IDBPropertyDelta<?> delta)
{
doVisit(delta);
}
@Override
protected void removed(IDBSchemaDelta delta)
{
doVisit(delta);
}
@Override
protected void removed(IDBTableDelta delta)
{
doVisit(delta);
}
@Override
protected void removed(IDBFieldDelta delta)
{
doVisit(delta);
}
@Override
protected void removed(IDBIndexDelta delta)
{
doVisit(delta);
}
@Override
protected void removed(IDBIndexFieldDelta delta)
{
doVisit(delta);
}
@Override
protected void removed(IDBPropertyDelta<?> delta)
{
doVisit(delta);
}
@Override
protected void changed(IDBSchemaDelta delta)
{
doVisit(delta);
}
@Override
protected void changed(IDBTableDelta delta)
{
doVisit(delta);
}
@Override
protected void changed(IDBFieldDelta delta)
{
doVisit(delta);
}
@Override
protected void changed(IDBIndexDelta delta)
{
doVisit(delta);
}
@Override
protected void changed(IDBIndexFieldDelta delta)
{
doVisit(delta);
}
@Override
protected void changed(IDBPropertyDelta<?> delta)
{
doVisit(delta);
}
protected void doVisit(IDBSchemaDelta delta)
{
visitDefault(delta);
}
protected void doVisit(IDBTableDelta delta)
{
visitDefault(delta);
}
protected void doVisit(IDBFieldDelta delta)
{
visitDefault(delta);
}
protected void doVisit(IDBIndexDelta delta)
{
visitDefault(delta);
}
protected void doVisit(IDBIndexFieldDelta delta)
{
visitDefault(delta);
}
protected void doVisit(IDBPropertyDelta<?> delta)
{
visitDefault(delta);
}
@Override
protected final boolean handle(IDBDelta delta)
{
if (policy.isForbidden(delta))
{
throw new ForbiddenChangeException(delta);
}
if (policy.isAllowed(delta))
{
return true;
}
return false;
}
/**
* @author Eike Stepper
*/
public static final class Policy implements Serializable
{
public static final Object ALLOWED = "ALLOWED";
public static final Object FORBIDDEN = "FORBIDDEN";
public static final Object IGNORED = "IGNORED";
private static final long serialVersionUID = 1L;
private Map<Object, Object> clauses = new HashMap<Object, Object>();
private transient boolean frozen;
public Policy()
{
}
public boolean isAllowed(DeltaType deltaType)
{
Object value = clauses.get(deltaType);
return value == ALLOWED;
}
public boolean isAllowed(ChangeKind changeKind)
{
Object value = clauses.get(changeKind);
return value == ALLOWED;
}
public boolean isAllowed(DeltaType deltaType, ChangeKind changeKind)
{
Object value = clauses.get(Pair.create(deltaType, changeKind));
if (value == null)
{
value = clauses.get(deltaType);
if (value == null)
{
value = clauses.get(changeKind);
}
}
return value == ALLOWED;
}
public boolean isAllowed(IDBDelta delta)
{
return isAllowed(delta.getDeltaType(), delta.getChangeKind());
}
public boolean isForbidden(DeltaType deltaType)
{
Object value = clauses.get(deltaType);
return value == FORBIDDEN;
}
public boolean isForbidden(ChangeKind changeKind)
{
Object value = clauses.get(changeKind);
return value == FORBIDDEN;
}
public boolean isForbidden(DeltaType deltaType, ChangeKind changeKind)
{
Object value = clauses.get(Pair.create(deltaType, changeKind));
if (value == null)
{
value = clauses.get(deltaType);
if (value == null)
{
value = clauses.get(changeKind);
}
}
return value == FORBIDDEN;
}
public boolean isForbidden(IDBDelta delta)
{
return isForbidden(delta.getDeltaType(), delta.getChangeKind());
}
public boolean isIgnored(DeltaType deltaType)
{
Object value = clauses.get(deltaType);
return value == null || value == IGNORED;
}
public boolean isIgnored(ChangeKind changeKind)
{
Object value = clauses.get(changeKind);
return value == null || value == IGNORED;
}
public boolean isIgnored(DeltaType deltaType, ChangeKind changeKind)
{
Object value = clauses.get(Pair.create(deltaType, changeKind));
if (value == null)
{
value = clauses.get(deltaType);
if (value == null)
{
value = clauses.get(changeKind);
}
}
return value == null || value == IGNORED;
}
public boolean isIgnored(IDBDelta delta)
{
return isIgnored(delta.getDeltaType(), delta.getChangeKind());
}
public Policy allow(DeltaType deltaType)
{
return addClause(deltaType, ALLOWED);
}
public Policy allow(ChangeKind changeKind)
{
return addClause(changeKind, ALLOWED);
}
public Policy allow(DeltaType deltaType, ChangeKind changeKind)
{
return addClause(Pair.create(deltaType, changeKind), ALLOWED);
}
public Policy allowAll()
{
return ignoreAll().allow(ChangeKind.ADD).allow(ChangeKind.REMOVE).allow(ChangeKind.CHANGE);
}
public Policy forbid(DeltaType deltaType)
{
return addClause(deltaType, FORBIDDEN);
}
public Policy forbid(ChangeKind changeKind)
{
return addClause(changeKind, FORBIDDEN);
}
public Policy forbid(DeltaType deltaType, ChangeKind changeKind)
{
return addClause(Pair.create(deltaType, changeKind), FORBIDDEN);
}
public Policy forbidAll()
{
return ignoreAll().forbid(ChangeKind.ADD).forbid(ChangeKind.REMOVE).forbid(ChangeKind.CHANGE);
}
public Policy ignore(DeltaType deltaType)
{
return removeClause(deltaType);
}
public Policy ignore(ChangeKind changeKind)
{
return removeClause(changeKind);
}
public Policy ignore(DeltaType deltaType, ChangeKind changeKind)
{
return removeClause(Pair.create(deltaType, changeKind));
}
public Policy ignoreAll()
{
checkFrozen();
clauses.clear();
return this;
}
@Override
public String toString()
{
return "Policy" + clauses;
}
public Policy freeze()
{
frozen = true;
return this;
}
private void checkFrozen()
{
if (frozen)
{
throw new IllegalStateException("Policy is frozen: " + this);
}
}
private Policy addClause(Object key, Object value)
{
checkFrozen();
clauses.put(key, value);
return this;
}
private Policy removeClause(Object key)
{
checkFrozen();
clauses.remove(key);
return this;
}
}
/**
* @author Eike Stepper
*/
public static final class ForbiddenChangeException extends RuntimeException
{
private static final long serialVersionUID = 1L;
private final IDBDelta delta;
public ForbiddenChangeException(IDBDelta delta)
{
super("Forbidden change: " + delta);
this.delta = delta;
}
public IDBDelta getDelta()
{
return delta;
}
}
}
/**
* @author Eike Stepper
*/
public static class Copier extends Filter
{
private DBSchemaDelta result;
public Copier()
{
}
public Copier(Policy policy)
{
super(policy);
}
public final IDBSchemaDelta getResult()
{
return result;
}
@Override
protected void doVisit(IDBSchemaDelta delta)
{
result = new DBSchemaDelta(delta.getName(), delta.getChangeKind());
}
@Override
protected void doVisit(IDBTableDelta delta)
{
DBTableDelta copy = new DBTableDelta(result, delta.getName(), delta.getChangeKind());
result.addTableDelta(copy);
}
@Override
protected void doVisit(IDBFieldDelta delta)
{
DBTableDelta parentCopy = getParentCopy(delta);
DBFieldDelta copy = new DBFieldDelta(parentCopy, delta.getName(), delta.getChangeKind());
parentCopy.addFieldDelta(copy);
}
@Override
protected void doVisit(IDBIndexDelta delta)
{
DBTableDelta parentCopy = getParentCopy(delta);
DBIndexDelta copy = new DBIndexDelta(parentCopy, delta.getName(), delta.getChangeKind());
parentCopy.addIndexDelta(copy);
}
@Override
protected void doVisit(IDBIndexFieldDelta delta)
{
DBIndexDelta parentCopy = getParentCopy(delta);
DBIndexFieldDelta copy = new DBIndexFieldDelta(parentCopy, delta.getName(), delta.getChangeKind());
parentCopy.addIndexFieldDelta(copy);
}
@Override
protected void doVisit(IDBPropertyDelta<?> delta)
{
DBDeltaWithProperties parentCopy = getParentCopy(delta);
@SuppressWarnings({ "rawtypes", "unchecked" })
DBPropertyDelta<?> copy = new DBPropertyDelta(parentCopy, delta.getName(), delta.getType(), delta.getValue(),
delta.getOldValue());
parentCopy.addPropertyDelta(copy);
}
@SuppressWarnings("unchecked")
private <T extends DBDelta> T getParentCopy(IDBDelta delta)
{
if (result == null)
{
throw new IllegalStateException("Copier can only be accepted by schema deltas");
}
DBDelta parent = (DBDelta)delta.getParent();
DeltaType deltaType = parent.getDeltaType();
switch (deltaType)
{
case SCHEMA:
return (T)result;
case TABLE:
return (T)result.getTableDelta(parent.getName());
case FIELD:
return (T)result.getTableDelta(parent.getParent().getName()).getFieldDelta(parent.getName());
case INDEX:
return (T)result.getTableDelta(parent.getParent().getName()).getIndexDelta(parent.getName());
case INDEX_FIELD:
return (T)result.getTableDelta(parent.getParent().getParent().getName())
.getIndexDelta(parent.getParent().getName()).getIndexFieldDelta(parent.getName());
default:
throw new IllegalStateException("Illegal delta type: " + deltaType);
}
}
}
}