/* * (c) Copyright 2010-2011 by Volker Bergmann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is permitted under the terms of the * GNU General Public License (GPL). * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS, * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.databene.benerator.engine.statement; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.databene.benerator.engine.BeneratorContext; import org.databene.benerator.engine.Statement; import org.databene.commons.CollectionUtil; import org.databene.commons.ConfigurationError; import org.databene.commons.ErrorHandler; import org.databene.commons.IOUtil; import org.databene.commons.collection.OrderedNameMap; import org.databene.commons.xml.XMLUtil; import org.databene.jdbacl.identity.IdentityModel; import org.databene.jdbacl.identity.IdentityProvider; import org.databene.jdbacl.identity.KeyMapper; import org.databene.jdbacl.identity.NoIdentity; import org.databene.jdbacl.identity.mem.MemKeyMapper; import org.databene.jdbacl.identity.xml.IdentityParseContext; import org.databene.jdbacl.identity.xml.IdentityParser; import org.databene.jdbacl.model.DBTable; import org.databene.jdbacl.model.Database; import org.databene.model.data.ComplexTypeDescriptor; import org.databene.model.data.ReferenceDescriptor; import org.databene.platform.db.DBSystem; import org.databene.script.Expression; import org.databene.script.expression.ExpressionUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Groups {@link TranscodeStatement}s and provides common features like * {@link IdentityProvider} and {@link KeyMapper} objects.<br/><br/> * Created: 10.09.2010 18:25:18 * @since 0.6.4 * @author Volker Bergmann */ public class TranscodingTaskStatement extends SequentialStatement { Expression<DBSystem> sourceEx; Expression<DBSystem> targetEx; Expression<String> identityEx; Expression<Long> pageSizeEx; Expression<ErrorHandler> errorHandlerExpression; IdentityProvider identityProvider; KeyMapper mapper; Map<String, Boolean> tableNkRequirements = OrderedNameMap.createCaseIgnorantMap(); public TranscodingTaskStatement(Expression<DBSystem> sourceEx, Expression<DBSystem> targetEx, Expression<String> identityEx, Expression<Long> pageSizeEx, Expression<ErrorHandler> errorHandlerExpression) { this.sourceEx = cache(sourceEx); this.targetEx = cache(targetEx); this.identityEx = cache(identityEx); this.pageSizeEx = cache(pageSizeEx); this.errorHandlerExpression = cache(errorHandlerExpression); this.identityProvider = new IdentityProvider(); } public Expression<DBSystem> getSourceEx() { return sourceEx; } public Expression<DBSystem> getTargetEx() { return targetEx; } public Expression<Long> getPageSizeEx() { return pageSizeEx; } public Expression<ErrorHandler> getErrorHandlerEx() { return errorHandlerExpression; } public IdentityProvider getIdentityProvider() { return identityProvider; } KeyMapper getKeyMapper() { return mapper; } @Override public boolean execute(BeneratorContext context) { DBSystem target = getTarget(context); Database database = target.getDbMetaData(); mapper = new MemKeyMapper(null, null, target.getConnection(), target.getId(), identityProvider, database); checkPrecoditions(context); super.execute(context); return true; } private void checkPrecoditions(BeneratorContext context) { DBSystem target = targetEx.evaluate(context); boolean identitiesRequired = collectPreconditions(subStatements, context); // check that each table for which an identity definition is required has one if (identitiesRequired) readIdentityDefinition(context); for (Entry<String, Boolean> req : tableNkRequirements.entrySet()) { String tableName = req.getKey(); Boolean required = req.getValue(); IdentityModel identity = identityProvider.getIdentity(tableName, false); if (identity == null) { if (required) { throw new ConfigurationError("For transcoding, an identity definition of table '" + tableName + "' is required"); } else { DBTable table = target.getDbMetaData().getTable(tableName); identity = new NoIdentity(table.getName()); identityProvider.registerIdentity(identity, tableName); } } } } // helpers --------------------------------------------------------------------------------------------------------- private boolean collectPreconditions(List<Statement> subStatements, BeneratorContext context) { boolean identitiesRequired = false; List<CascadeParent> children = CollectionUtil.extractItemsOfCompatibleType(CascadeParent.class, subStatements); for (CascadeParent statement : children) { ComplexTypeDescriptor type = statement.getType(getSourceEx().evaluate(context), context); String tableName = type.getName(); // items to be transcoded do not need NK definition tableNkRequirements.put(tableName, false); for (ReferenceDescriptor ref : type.getReferenceComponents()) { String targetTable = ref.getTargetType(); if (!tableNkRequirements.containsKey(targetTable) && statement.getSource(context).countEntities(targetTable) > 0) { tableNkRequirements.put(targetTable, true); identitiesRequired = true; } } identitiesRequired |= collectPreconditions(statement.getSubStatements(), context); } return identitiesRequired; } private void readIdentityDefinition(BeneratorContext context) { try { // check identity definition String identityUri = ExpressionUtil.evaluate(identityEx, context); if (identityUri == null) throw new ConfigurationError("No 'identity' definition file defined"); String idFile = context.resolveRelativeUri(identityUri); IdentityParser parser = new IdentityParser(); IdentityParseContext parseContext = new IdentityParseContext(identityProvider); Document idXml = XMLUtil.parse(IOUtil.getInputStreamForURI(idFile)); Object[] parentPath = new Object[0]; for (Element child : XMLUtil.getChildElements(idXml.getDocumentElement())) parser.parse(child, parentPath, parseContext); } catch (Exception e) { throw new ConfigurationError("Error setting up transcoding task", e); } } private DBSystem getTarget(BeneratorContext context) { DBSystem target = ExpressionUtil.evaluate(targetEx, context); if (target == null) throw new ConfigurationError("No 'target' database defined in <transcodingTask>"); return target; } public boolean needsNkMapping(String tableName) { Boolean required = tableNkRequirements.get(tableName); if (required == null) throw new RuntimeException("Assertion failed: Not clear if an identity definition is necessary for table " + tableName); return required; } }