001package io.ebeaninternal.dbmigration.model;
002
003import io.ebean.config.DbConstraintNaming;
004import io.ebean.config.dbplatform.DatabasePlatform;
005import io.ebeaninternal.api.SpiEbeanServer;
006import io.ebeaninternal.dbmigration.Detect;
007import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
008import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
009import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
010import io.ebeaninternal.dbmigration.migration.ChangeSet;
011import io.ebeaninternal.dbmigration.model.build.ModelBuildBeanVisitor;
012import io.ebeaninternal.dbmigration.model.build.ModelBuildContext;
013import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
014import io.ebeaninternal.extraddl.model.DdlScript;
015import io.ebeaninternal.extraddl.model.ExtraDdl;
016import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
017import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
018
019import java.io.IOException;
020import java.util.List;
021
022import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
023
024/**
025 * Reads EbeanServer bean descriptors to build the current model.
026 */
027public class CurrentModel {
028
029  private final SpiEbeanServer server;
030  private final DatabasePlatform databasePlatform;
031  private final DbConstraintNaming constraintNaming;
032  private final boolean platformTypes;
033  private final boolean jaxbPresent;
034  private final String ddlHeader;
035  private final DdlOptions ddlOptions = new DdlOptions();
036
037  private ModelContainer model;
038  private ChangeSet changeSet;
039  private DdlWrite write;
040
041  /**
042   * Construct with a given EbeanServer instance for DDL create all generation, not migration.
043   */
044  public CurrentModel(SpiEbeanServer server) {
045    this(server, server.config().getConstraintNaming(), true);
046  }
047
048  /**
049   * Construct with a given EbeanServer, platformDdl and constraintNaming convention.
050   * <p>
051   * Note the EbeanServer is just used to read the BeanDescriptors and platformDdl supplies
052   * the platform specific handling on
053   * </p>
054   */
055  public CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming) {
056    this(server, constraintNaming, false);
057  }
058
059  private CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming, boolean platformTypes) {
060    this.server = server;
061    this.databasePlatform = server.databasePlatform();
062    this.constraintNaming = constraintNaming;
063    this.platformTypes = platformTypes;
064    this.ddlHeader = server.config().getDdlHeader();
065    this.jaxbPresent = Detect.isJAXBPresent(server.config());
066  }
067
068  public DdlOptions getDdlOptions() {
069    return ddlOptions;
070  }
071
072  /**
073   * Return true if the model contains tables that are partitioned.
074   */
075  public boolean isTablePartitioning() {
076    return read().isTablePartitioning();
077  }
078
079  /**
080   * Return the tables that have partitioning.
081   */
082  public List<MTable> getPartitionedTables() {
083    return read().getPartitionedTables();
084  }
085
086  /**
087   * Return the current model by reading all the bean descriptors and properties.
088   */
089  public ModelContainer read() {
090    if (model == null) {
091      model = new ModelContainer();
092      ModelBuildContext context = new ModelBuildContext(model, databasePlatform, constraintNaming, platformTypes);
093      ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context);
094      VisitAllUsing visit = new VisitAllUsing(visitor, server);
095      visit.visitAllBeans();
096      // adjust the foreign keys on the 'draft' tables
097      context.adjustDraftReferences();
098    }
099    return model;
100  }
101
102  public void setChangeSet(ChangeSet changeSet) {
103    this.changeSet = changeSet;
104  }
105
106  /**
107   * Return as a ChangeSet.
108   */
109  public ChangeSet getChangeSet() {
110    read();
111    if (changeSet == null) {
112      changeSet = asChangeSet();
113    }
114    return changeSet;
115  }
116
117  /**
118   * Return the 'Create' DDL.
119   */
120  public String getCreateDdl() throws IOException {
121
122    createDdl();
123
124    StringBuilder ddl = new StringBuilder(2000);
125    if (ddlHeader != null && !ddlHeader.isEmpty()) {
126      ddl.append(ddlHeader).append('\n');
127    }
128    if (jaxbPresent) {
129      addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
130    }
131    ddl.append(write.apply().getBuffer());
132    ddl.append(write.applyForeignKeys().getBuffer());
133    ddl.append(write.applyHistoryView().getBuffer());
134    ddl.append(write.applyHistoryTrigger().getBuffer());
135    return ddl.toString();
136  }
137
138  private void addExtraDdl(StringBuilder ddl, ExtraDdl extraDdl, String prefix) {
139    if (extraDdl != null) {
140      List<DdlScript> ddlScript = extraDdl.getDdlScript();
141      for (DdlScript script : ddlScript) {
142        if (script.isInit() && matchPlatform(server.platform(), script.getPlatforms())) {
143          ddl.append(prefix).append(script.getName()).append('\n');
144          ddl.append(script.getValue());
145        }
146      }
147    }
148  }
149
150  /**
151   * Return the 'Drop' DDL.
152   */
153  public String getDropAllDdl() throws IOException {
154
155    createDdl();
156
157    StringBuilder ddl = new StringBuilder(2000);
158    if (ddlHeader != null && !ddlHeader.isEmpty()) {
159      ddl.append(ddlHeader).append('\n');
160    }
161    ddl.append(write.dropAllForeignKeys().getBuffer());
162    ddl.append(write.dropAll().getBuffer());
163    return ddl.toString();
164  }
165
166  /**
167   * Create all the DDL based on the changeSet.
168   */
169  private void createDdl() throws IOException {
170    if (write == null) {
171      ChangeSet createChangeSet = getChangeSet();
172      write = new DdlWrite(new MConfiguration(), model, ddlOptions);
173      DdlHandler handler = handler();
174      handler.generateProlog(write);
175      handler.generate(write, createChangeSet);
176      handler.generateEpilog(write);
177    }
178  }
179
180  /**
181   * Return the platform specific DdlHandler (to generate DDL).
182   */
183  private DdlHandler handler() {
184    return PlatformDdlBuilder.create(databasePlatform).createDdlHandler(server.config());
185  }
186
187  /**
188   * Convert the model into a ChangeSet.
189   */
190  private ChangeSet asChangeSet() {
191    // empty diff so changes will effectively all be create
192    ModelDiff diff = new ModelDiff();
193    diff.compareTo(model);
194    return diff.getApplyChangeSet();
195  }
196
197}