/* * Copyright 2007 - 2017 the original author or authors. * * 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. */ package net.sf.jailer.extractionmodel; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import net.sf.jailer.ExecutionContext; import net.sf.jailer.datamodel.AggregationSchema; import net.sf.jailer.datamodel.Association; import net.sf.jailer.datamodel.Column; import net.sf.jailer.datamodel.DataModel; import net.sf.jailer.datamodel.Filter; import net.sf.jailer.datamodel.ParameterHandler; import net.sf.jailer.datamodel.Table; import net.sf.jailer.datamodel.filter_template.Clause; import net.sf.jailer.datamodel.filter_template.Clause.Predicate; import net.sf.jailer.datamodel.filter_template.Clause.Subject; import net.sf.jailer.datamodel.filter_template.FilterTemplate; import net.sf.jailer.restrictionmodel.RestrictionModel; import net.sf.jailer.util.CsvFile; import net.sf.jailer.util.CsvFile.Line; import net.sf.jailer.util.SqlUtil; /** * Extraction-model, defines the subject and the {@link RestrictionModel} * of an extraction. * * @author Ralf Wisser */ public class ExtractionModel { /** * The logger. */ private static final Logger _log = Logger.getLogger(ExtractionModel.class); /** * The table to read from. */ public final Table subject; /** * Additional Subject. */ public static class AdditionalSubject { private Table subject; private String condition; /** * @return the subject */ public Table getSubject() { return subject; } /** * @param subject the subject to set */ public void setSubject(Table subject) { this.subject = subject; } /** * @return the condition */ public String getCondition() { return condition; } /** * @param condition the condition to set */ public void setCondition(String condition) { this.condition = condition; } public AdditionalSubject(Table subject, String condition) { this.subject = subject; this.condition = condition; } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((condition == null) ? 0 : condition.hashCode()); result = prime * result + ((subject == null) ? 0 : subject.hashCode()); return result; } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AdditionalSubject other = (AdditionalSubject) obj; if (condition == null) { if (other.condition != null) return false; } else if (!condition.equals(other.condition)) return false; if (subject == null) { if (other.subject != null) return false; } else if (!subject.equals(other.subject)) return false; return true; } } /** * Additional Subjects. */ public List<AdditionalSubject> additionalSubjects = new ArrayList<AdditionalSubject>(); /** * The SQL-condition. */ private final String condition; /** * A limit for the number of subject-entities. (-1 for unlimited) */ public final long limit; /** * The restricted data-model to be used for extraction. */ public final DataModel dataModel; /** * Constructor for empty restriction models. * * @param dataModel the data model to restrict */ public ExtractionModel(DataModel dataModel, ExecutionContext executionContext) { this.dataModel = dataModel; subject = dataModel.getTables().iterator().hasNext()? dataModel.getTables().iterator().next() : null; condition = ""; dataModel.setRestrictionModel(new RestrictionModel(dataModel, executionContext)); limit = -1; dataModel.deriveFilters(); } /** * Constructor. * * @param the name of the model-file * @param parameters apply this parameter-value mapping to all restriction conditions, XML templates and filters */ public ExtractionModel(String fileName, Map<String, String> sourceSchemaMapping, Map<String, String> parameters, ExecutionContext executionContext) throws IOException { this(new File(fileName).toURI().toURL(), sourceSchemaMapping, parameters, executionContext, false); } /** * Constructor. * * @param the name of the model-file * @param parameters apply this parameter-value mapping to all restriction conditions, XML templates and filters */ public ExtractionModel(URL modelURL, Map<String, String> sourceSchemaMapping, Map<String, String> parameters, ExecutionContext executionContext, boolean failOnMissingSubject) throws IOException { String csvLocation = modelURL.toString(); List<CsvFile.Line> csv = new CsvFile(modelURL.openStream(), null, csvLocation, null).getLines(); if (csv.isEmpty()) { throw new RuntimeException(modelURL + "' is empty"); } CsvFile.Line subjectLine = csv.get(0); String location = subjectLine.location; DataModel dataModel = new DataModel(sourceSchemaMapping, executionContext, true); Table subject = dataModel.getTable(SqlUtil.mappedSchema(sourceSchemaMapping, subjectLine.cells.get(0))); if (subject == null) { String message = location + ": unknown subject table " + subjectLine.cells.get(0); if (failOnMissingSubject) { throw new RuntimeException(message); } else { _log.warn(message); } } String condition = subjectLine.cells.get(1); if ("".equals(condition)) { condition = "1=1"; } long limit = 0; if (!"".equals(subjectLine.cells.get(2))) { try { limit = Long.parseLong(subjectLine.cells.get(2)); } catch (NumberFormatException e) { throw new RuntimeException(location, e); } } if (dataModel.getRestrictionModel() == null) { dataModel.setRestrictionModel(new RestrictionModel(dataModel, executionContext)); } try { dataModel.getRestrictionModel().addRestrictionDefinition(modelURL, parameters); } catch (Exception e) { throw new RuntimeException(location, e); } this.subject = subject; this.condition = condition; this.limit = limit; this.dataModel = dataModel; // read xml mapping List<CsvFile.Line> xmlMapping = new CsvFile(modelURL.openStream(), "xml-mapping", csvLocation, null).getLines(); for (CsvFile.Line xmLine: xmlMapping) { location = subjectLine.location; String name = xmLine.cells.get(0); String tag = xmLine.cells.get(1); AggregationSchema aggregationSchema = AggregationSchema.valueOf(xmLine.cells.get(2)); Association association = dataModel.namedAssociations.get(name); if (association == null) { _log.warn("unknown association '" + name + "'"); } else { if (aggregationSchema != null) { association.setAggregationSchema(aggregationSchema); } association.setAggregationTagName(tag); } } // read upserts List<CsvFile.Line> upserts = new CsvFile(modelURL.openStream(), "upserts", csvLocation, null).getLines(); for (CsvFile.Line upsert: upserts) { location = subjectLine.location; String name = upsert.cells.get(0); String tag = upsert.cells.get(1); Table table = dataModel.getTable(name); if (table == null) { _log.warn("unknown table '" + name + "'"); } else { table.upsert = Boolean.parseBoolean(tag); } } // read "exclude from deletion" List<CsvFile.Line> excludes = new CsvFile(modelURL.openStream(), "exclude from deletion", csvLocation, null).getLines(); for (CsvFile.Line excludesLine: excludes) { location = subjectLine.location; String name = excludesLine.cells.get(0); String tag = excludesLine.cells.get(1); Table table = dataModel.getTable(name); if (table == null) { _log.warn("unknown table '" + name + "'"); } else { table.excludeFromDeletion = Boolean.parseBoolean(tag); } } // read export modus List<CsvFile.Line> exportModusFile = new CsvFile(modelURL.openStream(), "export modus", csvLocation, null).getLines(); Iterator<CsvFile.Line> i = exportModusFile.iterator(); if (i.hasNext()) { dataModel.setExportModus(i.next().cells.get(0)); } // read column mapping List<CsvFile.Line> columnMappingFile = new CsvFile(modelURL.openStream(), "xml column mapping", csvLocation, null).getLines(); for (CsvFile.Line xmLine: columnMappingFile) { String name = xmLine.cells.get(0); String mapping = xmLine.cells.get(1); Table table = dataModel.getTable(SqlUtil.mappedSchema(sourceSchemaMapping, name)); if (table == null) { _log.warn("unknown table " + name); } else { table.setXmlTemplate(ParameterHandler.assignParameterValues(mapping, parameters)); } } // read filters List<CsvFile.Line> filtersFile = new CsvFile(modelURL.openStream(), "filters", csvLocation, null).getLines(); for (CsvFile.Line xmLine: filtersFile) { String name = xmLine.cells.get(0); String column = xmLine.cells.get(1); String filter = xmLine.cells.get(2); Table table = dataModel.getTable(SqlUtil.mappedSchema(sourceSchemaMapping, name)); if (table == null) { _log.warn("unknown table " + name); } else { Column col = null; for (Column c: table.getColumns()) { if (c.name.equals(column)) { col = c; break; } } if (col == null) { _log.warn("unknown table" + name + "." + column); } else { String type = xmLine.cells.get(4); if (type.trim().length() == 0) { type = null; } Filter theFilter = new Filter(ParameterHandler.assignParameterValues(filter, parameters), type, false, null); theFilter.setApplyAtExport(!"Import".equalsIgnoreCase(xmLine.cells.get(3))); col.setFilter(theFilter); } } } // read filter templates List<CsvFile.Line> templatesFile = new CsvFile(modelURL.openStream(), "filter templates", csvLocation, null).getLines(); int lineNr = 0; FilterTemplate template = null; while (lineNr < templatesFile.size()) { CsvFile.Line xmLine = templatesFile.get(lineNr); if (xmLine.cells.get(0).equals("T")) { template = new FilterTemplate(); template.setName(xmLine.cells.get(1)); template.setExpression(xmLine.cells.get(2)); template.setEnabled("enabled".equals(xmLine.cells.get(3))); template.setApplyAtExport(!"Import".equalsIgnoreCase(xmLine.cells.get(4))); String type = xmLine.cells.get(5); if (type.trim().length() == 0) { type = null; } template.setType(type); dataModel.getFilterTemplates().add(template); } else if (xmLine.cells.get(0).equals("C") && template != null) { Clause clause = new Clause(); clause.setSubject(Enum.valueOf(Subject.class, xmLine.cells.get(1))); clause.setPredicate(Enum.valueOf(Predicate.class, xmLine.cells.get(2))); clause.setObject(xmLine.cells.get(3)); template.getClauses().add(clause); } ++lineNr; } // read xml settings List<CsvFile.Line> xmlSettingsFile = new CsvFile(modelURL.openStream(), "xml settings", csvLocation, null).getLines(); i = xmlSettingsFile.iterator(); if (i.hasNext()) { List<String> cells = i.next().cells; dataModel.getXmlSettings().datePattern = cells.get(0); dataModel.getXmlSettings().timestampPattern = cells.get(1); dataModel.getXmlSettings().rootTag = cells.get(2); } // read version int[] version = null; List<CsvFile.Line> versionBlock = new CsvFile(modelURL.openStream(), "version", csvLocation, null).getLines(); if (!versionBlock.isEmpty()) { String vCell = versionBlock.get(0).cells.get(0); String[] versionLine = vCell.split("[^0-9]+"); version = new int[Math.max(4, versionLine.length)]; int p = 0; for (String number: versionLine) { if (number.length() > 0) { try { version[p++] = Integer.parseInt(number); } catch (NumberFormatException e) { // version is unknown version = null; break; } } } } // read additional subjects List<CsvFile.Line> additionalSubsLines = new CsvFile(modelURL.openStream(), "additional subjects", csvLocation, null).getLines(); for (CsvFile.Line line: additionalSubsLines) { Table additSubject = dataModel.getTable(SqlUtil.mappedSchema(sourceSchemaMapping, line.cells.get(0))); if (additSubject != null) { additionalSubjects.add(new AdditionalSubject(additSubject, line.cells.get(1))); } } dataModel.deriveFilters(); disableUnknownChildren(new CsvFile(modelURL.openStream(), "known", csvLocation, null).getLines()); } private void disableUnknownChildren(List<Line> lines) { Set<String> known = new HashSet<String>(); for (Line line: lines) { known.add(line.cells.get(0)); } if (known.isEmpty()) { return; } for (Association a: dataModel.namedAssociations.values()) { String name = a.reversed? a.reversalAssociation.getName() : a.getName(); if (!known.contains(name) && a.isInsertSourceBeforeDestination()) { dataModel.getRestrictionModel().addRestriction(a.source, a, "false", "SYSTEM", true, new HashMap<String, String>()); } } } public static String loadDatamodelFolder(String fileName, ExecutionContext executionContext) throws IOException { List<CsvFile.Line> dmf = new CsvFile(new File(fileName), "datamodelfolder").getLines(); if (dmf.size() > 0) { return dmf.get(0).cells.get(0); } return null; } /** * @return the subject condition */ public String getCondition() { return condition; } }