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