| /* |
| * 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); |
| } |
| } |
| } |
| } |