You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/19 23:35:07 UTC
svn commit: r423615 [15/44] - in /incubator/openjpa/trunk: ./
openjpa-jdbc-5/ openjpa-jdbc-5/src/ openjpa-jdbc-5/src/main/
openjpa-jdbc-5/src/main/java/ openjpa-jdbc-5/src/main/java/org/
openjpa-jdbc-5/src/main/java/org/apache/ openjpa-jdbc-5/src/main/...
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,1104 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.meta;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+import org.apache.openjpa.jdbc.kernel.JDBCSeq;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.DynamicSchemaFactory;
+import org.apache.openjpa.jdbc.schema.LazySchemaFactory;
+import org.apache.openjpa.jdbc.schema.Schema;
+import org.apache.openjpa.jdbc.schema.SchemaGenerator;
+import org.apache.openjpa.jdbc.schema.SchemaGroup;
+import org.apache.openjpa.jdbc.schema.SchemaSerializer;
+import org.apache.openjpa.jdbc.schema.SchemaTool;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.jdbc.schema.XMLSchemaSerializer;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.kernel.Seq;
+import org.apache.openjpa.lib.conf.Configurations;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.meta.ClassArgParser;
+import org.apache.openjpa.lib.util.Files;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Options;
+import org.apache.openjpa.lib.util.Services;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.MetaDataFactory;
+import org.apache.openjpa.meta.MetaDataModes;
+import org.apache.openjpa.meta.QueryMetaData;
+import org.apache.openjpa.meta.SequenceMetaData;
+import org.apache.openjpa.meta.ValueStrategies;
+import org.apache.openjpa.util.GeneralException;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.MetaDataException;
+
+/**
+ * Tool for manipulating class mappings and associated schema.
+ *
+ * @author Abe White
+ */
+public class MappingTool
+ implements MetaDataModes {
+
+ public static final String SCHEMA_ACTION_NONE = "none";
+
+ public static final String ACTION_ADD = "add";
+ public static final String ACTION_REFRESH = "refresh";
+ public static final String ACTION_BUILD_SCHEMA = "buildSchema";
+ public static final String ACTION_DROP = "drop";
+ public static final String ACTION_VALIDATE = "validate";
+ public static final String ACTION_EXPORT = "export";
+ public static final String ACTION_IMPORT = "import";
+
+ public static final String[] ACTIONS = new String[]{
+ ACTION_ADD,
+ ACTION_REFRESH,
+ ACTION_BUILD_SCHEMA,
+ ACTION_DROP,
+ ACTION_VALIDATE,
+ ACTION_EXPORT,
+ ACTION_IMPORT,
+ };
+
+ private static Localizer _loc = Localizer.forPackage(MappingTool.class);
+
+ private final JDBCConfiguration _conf;
+ private final Log _log;
+ private final String _action;
+ private final boolean _meta;
+ private final int _mode;
+
+ private MappingRepository _repos = null;
+ private SchemaGroup _schema = null;
+ private SchemaTool _schemaTool = null;
+ private String _schemaAction = SchemaTool.ACTION_ADD;
+ private boolean _readSchema = false;
+ private boolean _pks = false;
+ private boolean _fks = false;
+ private boolean _indexes = false;
+ private boolean _seqs = true;
+ private boolean _dropUnused = true;
+ private boolean _ignoreErrors = false;
+ private File _file = null;
+ private Writer _mappingWriter = null;
+ private Writer _schemaWriter = null;
+
+ // buffer metadatas to be dropped
+ private Set _dropCls = null;
+ private Set _dropMap = null;
+ private boolean _flush = false;
+ private boolean _flushSchema = false;
+
+ /**
+ * Constructor. Supply configuration and action.
+ */
+ public MappingTool(JDBCConfiguration conf, String action, boolean meta) {
+ _conf = conf;
+ _log = conf.getLog(JDBCConfiguration.LOG_METADATA);
+ _meta = meta;
+
+ if (action == null)
+ _action = ACTION_REFRESH;
+ else if (!Arrays.asList(ACTIONS).contains(action))
+ throw new IllegalArgumentException("action == " + action);
+ else
+ _action = action;
+
+ if (meta && ACTION_ADD.equals(_action))
+ _mode = MODE_META;
+ else if (meta && ACTION_DROP.equals(_action))
+ _mode = MODE_META | MODE_MAPPING | MODE_QUERY;
+ else
+ _mode = MODE_MAPPING;
+ }
+
+ /**
+ * The action supplied on construction.
+ */
+ public String getAction() {
+ return _action;
+ }
+
+ /**
+ * Whether the action works on metadata as well as mappings.
+ */
+ public boolean isMetaDataAction() {
+ return _meta;
+ }
+
+ /**
+ * The schema modification policy, or <code>none</code>. See the
+ * ACTION constants in {@link SchemaTool}. Defaults to
+ * {@link SchemaTool#ACTION_ADD}.
+ */
+ public String getSchemaAction() {
+ return _schemaAction;
+ }
+
+ /**
+ * The schema modification policy, or <code>none</code>. See the
+ * ACTION constants in {@link SchemaTool}. Defaults to
+ * {@link SchemaTool#ACTION_ADD}.
+ */
+ public void setSchemaAction(String schemaAction) {
+ _schemaAction = schemaAction;
+ }
+
+ /**
+ * Set to true to read the entire schema before mapping.
+ * Leaving this option false saves time, but is dangerous when adding
+ * new mappings, because without full knowledge of the existing schema the
+ * mapping tool might create tables or indexes that conflict with
+ * existing components.
+ */
+ public boolean getReadSchema() {
+ return _readSchema;
+ }
+
+ /**
+ * Set to true to read the entire schema before mapping.
+ * Leaving this option false saves time, but is dangerous when adding
+ * new mappings, because without full knowledge of the existing schema the
+ * mapping tool might create tables or indexes that conflict with
+ * existing components.
+ */
+ public void setReadSchema(boolean readSchema) {
+ _readSchema = readSchema;
+ }
+
+ /**
+ * Whether to manipulate sequences. Defaults to true.
+ */
+ public boolean getSequences() {
+ return _seqs;
+ }
+
+ /**
+ * Whether to manipulate sequences. Defaults to true.
+ */
+ public void setSequences(boolean seqs) {
+ _seqs = seqs;
+ }
+
+ /**
+ * Whether indexes on existing tables should be manipulated.
+ * Defaults to false.
+ */
+ public boolean getIndexes() {
+ return _indexes;
+ }
+
+ /**
+ * Whether indexes on existing tables should be manipulated.
+ * Defaults to false.
+ */
+ public void setIndexes(boolean indexes) {
+ _indexes = indexes;
+ }
+
+ /**
+ * Whether foreign keys on existing tables should be manipulated.
+ * Defaults to false.
+ */
+ public boolean getForeignKeys() {
+ return _fks;
+ }
+
+ /**
+ * Whether foreign keys on existing tables should be manipulated.
+ * Defaults to false.
+ */
+ public void setForeignKeys(boolean fks) {
+ _fks = fks;
+ }
+
+ /**
+ * Whether primary keys on existing tables should be manipulated.
+ * Defaults to false.
+ */
+ public boolean getPrimaryKeys() {
+ return _pks;
+ }
+
+ /**
+ * Whether primary keys on existing tables should be manipulated.
+ * Defaults to false.
+ */
+ public void setPrimaryKeys(boolean pks) {
+ _pks = pks;
+ }
+
+ /**
+ * Whether schema components that are unused by any mapping will be
+ * dropped from this tool's {@link SchemaGroup}, and, depending on
+ * the schema action, from the database. Defaults to true.
+ */
+ public boolean getDropUnusedComponents() {
+ return _dropUnused;
+ }
+
+ /**
+ * Whether schema components that are unused by any mapping will be
+ * dropped from this tool's {@link SchemaGroup}, and, depending on
+ * the schema action, from the database. Defaults to true.
+ */
+ public void setDropUnusedComponents(boolean dropUnused) {
+ _dropUnused = dropUnused;
+ }
+
+ /**
+ * Whether and SQL errors should cause a failure or just issue a warning.
+ */
+ public void setIgnoreErrors(boolean ignoreErrors) {
+ _ignoreErrors = ignoreErrors;
+ }
+
+ /**
+ * Whether and SQL errors should cause a failure or just issue a warning.
+ */
+ public boolean getIgnoreErrors() {
+ return _ignoreErrors;
+ }
+
+ /**
+ * Return the schema tool to use for schema modification.
+ */
+ public SchemaTool getSchemaTool() {
+ if (_schemaTool == null)
+ _schemaTool = newSchemaTool(_schemaAction);
+ return _schemaTool;
+ }
+
+ /**
+ * Return the schema tool to use for schema modification.
+ */
+ private SchemaTool newSchemaTool(String action) {
+ if (SCHEMA_ACTION_NONE.equals(action))
+ action = null;
+ SchemaTool tool = new SchemaTool(_conf, action);
+ tool.setIgnoreErrors(getIgnoreErrors());
+ tool.setPrimaryKeys(getPrimaryKeys());
+ tool.setForeignKeys(getForeignKeys());
+ tool.setIndexes(getIndexes());
+ tool.setSequences(getSequences());
+ return tool;
+ }
+
+ /**
+ * Set the schema tool to use for schema modification.
+ */
+ public void setSchemaTool(SchemaTool tool) {
+ _schemaTool = tool;
+ }
+
+ /**
+ * The stream to export the planned schema to as an XML document.
+ * If non-null, then the database schema will not be altered.
+ */
+ public Writer getSchemaWriter() {
+ return _schemaWriter;
+ }
+
+ /**
+ * The stream to export the planned schema to as an XML document.
+ * If non-null, then the database schema will not be altered.
+ */
+ public void setSchemaWriter(Writer schemaWriter) {
+ _schemaWriter = schemaWriter;
+ }
+
+ /**
+ * The stream to export the planned mappings to as an XML document.
+ * If non-null, then the mapping repository will not be altered.
+ */
+ public Writer getMappingWriter() {
+ return _mappingWriter;
+ }
+
+ /**
+ * The stream to export the planned mappings to as an XML document.
+ * If non-null, then the mapping repository will not be altered.
+ */
+ public void setMappingWriter(Writer mappingWriter) {
+ _mappingWriter = mappingWriter;
+ }
+
+ /**
+ * If adding metadata, the metadata file to add to.
+ */
+ public File getMetaDataFile() {
+ return _file;
+ }
+
+ /**
+ * If adding metadata, the metadata file to add to.
+ */
+ public void setMetaDataFile(File file) {
+ _file = file;
+ }
+
+ /**
+ * Return the repository to use to access mapping information.
+ * Defaults to a new {@link MappingRepository}.
+ */
+ public MappingRepository getRepository() {
+ if (_repos == null) {
+ _repos = _conf.newMappingRepositoryInstance();
+ _repos.setSchemaGroup(getSchemaGroup());
+ _repos.setValidate(_repos.VALIDATE_UNENHANCED, false);
+ }
+ return _repos;
+ }
+
+ /**
+ * Set the repository to use to access mapping information.
+ */
+ public void setRepository(MappingRepository repos) {
+ _repos = repos;
+ }
+
+ /**
+ * Return the schema group to use in mapping. If none has been set, the
+ * schema will be generated from the database.
+ */
+ public SchemaGroup getSchemaGroup() {
+ if (_schema == null) {
+ if (ACTION_BUILD_SCHEMA.equals(_action)) {
+ DynamicSchemaFactory factory = new DynamicSchemaFactory();
+ factory.setConfiguration(_conf);
+ _schema = factory;
+ } else if (_readSchema
+ || SchemaTool.ACTION_RETAIN.equals(_schemaAction)
+ || SchemaTool.ACTION_REFRESH.equals(_schemaAction)) {
+ _schema = (SchemaGroup) getSchemaTool().getDBSchemaGroup().
+ clone();
+ } else {
+ // with this we'll just read tables as different mappings
+ // look for them
+ LazySchemaFactory factory = new LazySchemaFactory();
+ factory.setConfiguration(_conf);
+ factory.setPrimaryKeys(getPrimaryKeys());
+ factory.setForeignKeys(getForeignKeys());
+ factory.setIndexes(getIndexes());
+ _schema = factory;
+ }
+
+ if (_schema.getSchemas().length == 0)
+ _schema.addSchema();
+ }
+ return _schema;
+ }
+
+ /**
+ * Set the schema to use in mapping.
+ */
+ public void setSchemaGroup(SchemaGroup schema) {
+ _schema = schema;
+ }
+
+ /**
+ * Reset the internal repository. This is called automatically after
+ * every {@link #record}.
+ */
+ public void clear() {
+ _repos = null;
+ _schema = null;
+ _schemaTool = null;
+ _flush = false;
+ _flushSchema = false;
+ if (_dropCls != null)
+ _dropCls.clear();
+ if (_dropMap != null)
+ _dropMap.clear();
+ }
+
+ /**
+ * Records the changes that have been made to both the mappings and the
+ * associated schema, and clears the tool for further use. This also
+ * involves clearing the internal mapping repository.
+ */
+ public void record() {
+ MappingRepository repos = getRepository();
+ MetaDataFactory io = repos.getMetaDataFactory();
+ ClassMapping[] mappings;
+ if (!ACTION_DROP.equals(_action))
+ mappings = repos.getMappings();
+ else if (_dropMap != null)
+ mappings = (ClassMapping[]) _dropMap.toArray
+ (new ClassMapping[_dropMap.size()]);
+ else
+ mappings = new ClassMapping[0];
+
+ try {
+ if (_dropCls != null && !_dropCls.isEmpty()) {
+ Class[] cls = (Class[]) _dropCls.toArray
+ (new Class[_dropCls.size()]);
+ if (!io.drop(cls, _mode, null))
+ _log.warn(_loc.get("bad-drop", _dropCls));
+ }
+
+ if (_flushSchema) {
+ // drop portions of the known schema that no mapping uses, and
+ // add sequences used for value generation
+ if (_dropUnused)
+ dropUnusedSchemaComponents(mappings);
+ addSequenceComponents(mappings);
+
+ // now run the schematool as long as we're doing some schema
+ // action and the user doesn't just want an xml output
+ if (!SCHEMA_ACTION_NONE.equals(_schemaAction)
+ && (_schemaWriter == null || (_schemaTool != null
+ && _schemaTool.getWriter() != null))) {
+ SchemaTool tool = getSchemaTool();
+ tool.setSchemaGroup(getSchemaGroup());
+ tool.run();
+ tool.record();
+ }
+
+ // xml output of schema?
+ if (_schemaWriter != null) {
+ // serialize the planned schema to the stream
+ SchemaSerializer ser = new XMLSchemaSerializer(_conf);
+ ser.addAll(getSchemaGroup());
+ ser.serialize(_schemaWriter, ser.PRETTY);
+ _schemaWriter.flush();
+ }
+ }
+ if (!_flush)
+ return;
+
+ QueryMetaData[] queries = repos.getQueryMetaDatas();
+ SequenceMetaData[] seqs = repos.getSequenceMetaDatas();
+ Map output = null;
+
+ // if we're outputting to stream, set all metas to same file so
+ // they get placed in single string
+ if (_mappingWriter != null) {
+ output = new HashMap();
+ File tmp = new File("openjpatmp");
+ for (int i = 0; i < mappings.length; i++)
+ mappings[i].setSource(tmp, mappings[i].SRC_OTHER);
+ for (int i = 0; i < queries.length; i++)
+ queries[i].setSource(tmp, queries[i].getSourceScope(),
+ queries[i].SRC_OTHER);
+ for (int i = 0; i < seqs.length; i++)
+ seqs[i].setSource(tmp, seqs[i].getSourceScope(),
+ seqs[i].SRC_OTHER);
+ }
+
+ // store
+ if (!io.store(mappings, queries, seqs, _mode, output))
+ throw new MetaDataException(_loc.get("bad-store"));
+
+ // write to stream
+ if (_mappingWriter != null) {
+ PrintWriter out = new PrintWriter(_mappingWriter);
+ for (Iterator itr = output.values().iterator();
+ itr.hasNext();)
+ out.println((String) itr.next());
+ out.flush();
+ }
+ }
+ catch (RuntimeException re) {
+ throw re;
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ } finally {
+ clear();
+ }
+ }
+
+ /**
+ * Drops schema components that appear to be unused from the local
+ * copy of the schema group.
+ */
+ private void dropUnusedSchemaComponents(ClassMapping[] mappings) {
+ FieldMapping[] fields;
+ for (int i = 0; i < mappings.length; i++) {
+ mappings[i].refSchemaComponents();
+ mappings[i].getDiscriminator().refSchemaComponents();
+ mappings[i].getVersion().refSchemaComponents();
+ fields = mappings[i].getDefinedFieldMappings();
+ for (int j = 0; j < fields.length; j++)
+ fields[j].refSchemaComponents();
+ }
+
+ // also allow the dbdictionary to ref any schema components that
+ // it adds apart from mappings
+ SchemaGroup group = getSchemaGroup();
+ Schema[] schemas = group.getSchemas();
+ Table[] tables;
+ DBDictionary dict = _conf.getDBDictionaryInstance();
+ for (int i = 0; i < schemas.length; i++) {
+ tables = schemas[i].getTables();
+ for (int j = 0; j < tables.length; j++)
+ dict.refSchemaComponents(tables[j]);
+ }
+
+ group.removeUnusedComponents();
+ }
+
+ /**
+ * Add tables used by sequences to the given schema.
+ */
+ private void addSequenceComponents(ClassMapping[] mappings) {
+ SchemaGroup group = getSchemaGroup();
+ for (int i = 0; i < mappings.length; i++)
+ addSequenceComponents(mappings[i], group);
+ }
+
+ /**
+ * Add tables used by sequences to the given schema.
+ */
+ private void addSequenceComponents(ClassMapping mapping,
+ SchemaGroup group) {
+ SequenceMetaData smd = mapping.getIdentitySequenceMetaData();
+ Seq seq = null;
+ if (smd != null)
+ seq = smd.getInstance(null);
+ else if (mapping.getIdentityStrategy() == ValueStrategies.NATIVE
+ || (mapping.getIdentityStrategy() == ValueStrategies.NONE
+ && mapping.getIdentityType() == ClassMapping.ID_DATASTORE))
+ seq = _conf.getSequenceInstance();
+
+ if (seq instanceof JDBCSeq)
+ ((JDBCSeq) seq).addSchema(mapping, group);
+
+ FieldMapping[] fmds;
+ if (mapping.getEmbeddingMetaData() == null)
+ fmds = mapping.getDefinedFieldMappings();
+ else
+ fmds = mapping.getFieldMappings();
+ for (int i = 0; i < fmds.length; i++) {
+ smd = fmds[i].getValueSequenceMetaData();
+ if (smd != null) {
+ seq = smd.getInstance(null);
+ if (seq instanceof JDBCSeq)
+ ((JDBCSeq) seq).addSchema(mapping, group);
+ } else if (fmds[i].getEmbeddedMapping() != null)
+ addSequenceComponents(fmds[i].getEmbeddedMapping(), group);
+ }
+ }
+
+ ///////////
+ // Actions
+ ///////////
+
+ /**
+ * Run the configured action on the given instance.
+ */
+ public void run(Class cls) {
+ if (ACTION_ADD.equals(_action)) {
+ if (_meta)
+ addMeta(cls);
+ else
+ add(cls);
+ } else if (ACTION_REFRESH.equals(_action))
+ refresh(cls);
+ else if (ACTION_BUILD_SCHEMA.equals(_action))
+ buildSchema(cls);
+ else if (ACTION_DROP.equals(_action))
+ drop(cls);
+ else if (ACTION_VALIDATE.equals(_action))
+ validate(cls);
+ }
+
+ /**
+ * Add the mapping for the given instance.
+ */
+ private void add(Class cls) {
+ if (cls == null)
+ return;
+
+ _flush = true;
+ _flushSchema = true;
+ MappingRepository repos = getRepository();
+ repos.setStrategyInstaller(new MappingStrategyInstaller(repos));
+ repos.getMapping(cls, null, true);
+ }
+
+ /**
+ * Create a metadata for the given instance.
+ */
+ private void addMeta(Class cls) {
+ if (cls == null)
+ return;
+
+ _flush = true;
+ MappingRepository repos = getRepository();
+ repos.setResolve(MODE_MAPPING, false);
+ MetaDataFactory factory = repos.getMetaDataFactory();
+ factory.getDefaults().setIgnoreNonPersistent(false);
+ factory.setStoreMode(MetaDataFactory.STORE_VERBOSE);
+
+ ClassMetaData meta = repos.addMetaData(cls);
+ FieldMetaData[] fmds = meta.getDeclaredFields();
+ for (int i = 0; i < fmds.length; i++) {
+ if (fmds[i].getDeclaredTypeCode() == JavaTypes.OBJECT
+ && fmds[i].getDeclaredType() != Object.class)
+ fmds[i].setDeclaredTypeCode(JavaTypes.PC);
+ }
+ meta.setSource(_file, meta.getSourceType());
+ meta.setResolve(MODE_META, true);
+ }
+
+ /**
+ * Refresh or add the mapping for the given instance.
+ */
+ private void refresh(Class cls) {
+ if (cls == null)
+ return;
+
+ _flush = true;
+ _flushSchema = true;
+ MappingRepository repos = getRepository();
+ repos.setStrategyInstaller(new RefreshStrategyInstaller(repos));
+ repos.getMapping(cls, null, true);
+ }
+
+ /**
+ * Validate the mappings for the given class and its fields.
+ */
+ private void validate(Class cls) {
+ if (cls == null)
+ return;
+
+ _flushSchema = !SCHEMA_ACTION_NONE.equals(_schemaAction)
+ && !SchemaTool.ACTION_ADD.equals(_schemaAction);
+ MappingRepository repos = getRepository();
+ repos.setStrategyInstaller(new RuntimeStrategyInstaller(repos));
+ repos.getMapping(cls, null, true);
+ }
+
+ /**
+ * Create the schema using the mapping for the given instance.
+ */
+ private void buildSchema(Class cls) {
+ if (cls == null)
+ return;
+
+ _flushSchema = true;
+ MappingRepository repos = getRepository();
+ repos.setStrategyInstaller(new RuntimeStrategyInstaller(repos));
+ repos.getMapping(cls, null, true);
+
+ // set any logical pks to non-logical so they get flushed
+ Schema[] schemas = _schema.getSchemas();
+ Table[] tables;
+ Column[] cols;
+ for (int i = 0; i < schemas.length; i++) {
+ tables = schemas[i].getTables();
+ for (int j = 0; j < tables.length; j++) {
+ if (tables[j].getPrimaryKey() == null)
+ continue;
+
+ tables[j].getPrimaryKey().setLogical(false);
+ cols = tables[j].getPrimaryKey().getColumns();
+ for (int k = 0; k < cols.length; k++)
+ cols[k].setNotNull(true);
+ }
+ }
+ }
+
+ /**
+ * Drop mapping for given class.
+ */
+ private void drop(Class cls) {
+ if (cls == null)
+ return;
+
+ if (_dropCls == null)
+ _dropCls = new HashSet();
+ _dropCls.add(cls);
+ if (!SchemaTool.ACTION_DROP.equals(_schemaAction))
+ return;
+
+ MappingRepository repos = getRepository();
+ repos.setStrategyInstaller(new RuntimeStrategyInstaller(repos));
+ ClassMapping mapping = null;
+ try {
+ mapping = repos.getMapping(cls, null, false);
+ } catch (Exception e) {
+ }
+
+ if (mapping != null) {
+ _flushSchema = true;
+ if (_dropMap == null)
+ _dropMap = new HashSet();
+ _dropMap.add(mapping);
+ } else
+ _log.warn(_loc.get("no-drop-meta", cls));
+ }
+
+ ////////
+ // Main
+ ////////
+
+ /**
+ * Usage: java org.apache.openjpa.jdbc.meta.MappingTool [option]* [-action/-a
+ * <refresh | add | buildSchema | drop | validate | import | export>]
+ * <class name | .java file | .class file | .jdo file>*
+ * Where the following options are recognized.
+ * <ul>
+ * <li><i>-properties/-p <properties file or resource></i>: The
+ * path or resource name of a OpenJPA properties file containing
+ * information as outlined in {@link OpenJPAConfiguration}. Optional.</li>
+ * <li><i>-<property name> <property value></i>: All bean
+ * properties of the OpenJPA {@link JDBCConfiguration} can be set by
+ * using their names and supplying a value. For example:
+ * <code>-licenseKey adslfja83r3lkadf</code></li>
+ * <li><i>-file/-f <stdout | output file or resource></i>: Use
+ * this option to write the planned mappings to an XML document rather
+ * than store them in the repository. This option also specifies the
+ * metadata file to write to if using the <code>add</code> action with
+ * the <code>-meta true</code> flag, or the file to dump to if using
+ * the <code>export</code> action.</li>
+ * <li><i>-schemaAction/-sa <schema action | none></i>: The
+ * {@link SchemaTool} defines the actions possible. The actions will
+ * apply to all schema components used by the mappings involved.
+ * Unless you are running the mapping tool on all of your persistent
+ * types at once, be careful running schema actions that can drop data.
+ * It is possible to accidentally drop schema components that are
+ * used by classes you aren't currently running the tool over. The
+ * action defaults to <code>add</code>.</li>
+ * <li><i>-schemaFile/-sf <stdout | output file or resource></i>: Use
+ * this option to write the planned schema to an XML document rather
+ * than modify the data store.</li>
+ * <li><i>-sqlFile/-sql <stdout | output file or resource></i>: Use
+ * this option rather to write the planned schema changes as a SQL
+ * script rather than modifying the data store.</li>
+ * <li><i>-dropTables/-dt <true/t | false/f></i>: Corresponds to the
+ * same-named option in the {@link SchemaTool}.</li>
+ * <li><i>-dropSequences/-dsq <true/t | false/f></i>: Corresponds
+ * to the same-named option in the {@link SchemaTool}.</li>
+ * <li><i>-openjpaTables/-kt <true/t | false/f></i>: Corresponds to the
+ * same-named option in the {@link SchemaTool}.</li>
+ * <li><i>-ignoreErrors/-i <true/t | false/f></i>: Corresponds to the
+ * same-named option in the {@link SchemaTool}.</li>
+ * <li><i>-readSchema/-rs <true/t | false/f></i>: Set this to true
+ * to read the entire existing schema (even when false the parts of
+ * the schema used by classes the tool is run on will still be read).
+ * Turning on schema reading can ensure that no naming conflicts will
+ * occur, but it can take a long time.</li>
+ * <li><i>-primaryKeys/-pk <true/t | false/f></i>: Whether primary
+ * keys on existing tables are manipulated. Defaults to false.</li>
+ * <li><i>-foreignKeys/-fk <true/t | false/f></i>: Whether foreign
+ * keys on existing tables are manipulated. Defaults to false.</li>
+ * <li><i>-indexes/-ix <true/t | false/f></i>: Whether indexes on
+ * existing tables are manipulated. Defaults to false.</li>
+ * <li><i>-sequences/-sq <true/t | false/f></i>: Whether sequences
+ * are manipulated. Defaults to true.</li>
+ * <li><i>-schemas/-s <schema and table names></i>: A list of schemas
+ * and/or tables to read. Corresponds to the
+ * same-named option in the {@link SchemaGenerator}. This option
+ * is ignored if <code>readSchema</code> is false.</li>
+ * <li><i>-meta/-m <true/t | false/f></i>: Whether the given action
+ * applies to metadata as well as mappings.</li>
+ * </ul>
+ * The various actions are as follows.
+ * <ul>
+ * <li><i>refresh</i>: Bring the mapping information up-to-date
+ * with the class definitions. OpenJPA will attempt to use any provided
+ * mapping information, and fill in missing information. If the
+ * provided information conflicts with the class definition, the
+ * conflicting information will be discarded and the class/field will
+ * be re-mapped to new columns/tables. This is the default action.</li>
+ * <li><i>add</i>: If used with the <code>-meta</code> option, adds new
+ * default metadata for the given class(es). Otherwise, brings the
+ * mapping information up-to-date with the class
+ * definitions. OpenJPA will attempt to use any provided mapping
+ * information, and fill in missing information. OpenJPA will fail if
+ * the provided information conflicts with the class definition.</li>
+ * <li><i>buildSchema</i>: Create the schema matching the existing
+ * mappings for the given class(es). Any invalid mapping information
+ * will cause an exception.</li>
+ * <li><i>drop</i>: Delete mappings for the given classes. If used with
+ * the <code>-meta</code> option, also deletes metadata.</li>
+ * <li><i>validate</i>: Validate the given mappings. The mapping
+ * repository and schema will not be affected.</li>
+ * <li><i>import</i>: Import mappings from an XML document and store
+ * them as the current system mappings.</li>
+ * <li><i>export</i>: Dump the current mappings for the given classes to
+ * an XML document specified by the <code>file</code> option.</li>
+ * If used with the <code>-meta</code> option, the metadata will be
+ * included in the export.
+ * </ul>
+ * Each class supplied as an argument must have valid metadata. If
+ * no class arguments are given, the tool runs on all metadata files in
+ * the CLASSPATH.
+ * Examples:
+ * <ul>
+ * <li>Refresh the mappings for given package, without dropping any
+ * schema components:<br />
+ * <code>java org.apache.openjpa.jdbc.meta.MappingTool mypackage.jdo</code></li>
+ * <li>Refresh the mappings for all persistent classes in the classpath,
+ * dropping any unused columns and even tables:<br />
+ * <code>java org.apache.openjpa.jdbc.meta.MappingTool -sa refresh
+ * -dt true</code></li>
+ * <li>Make sure the mappings you've created by hand match the object
+ * model and schema:<br />
+ * <code>java org.apache.openjpa.jbdc.meta.MappingTool
+ * -a validate Person.java</code></li>
+ * <li>Remove the recorded mapping for a given class:<br />
+ * <code>java org.apache.openjpa.jbdc.meta.MappingTool
+ * -a drop Person.java</code></li>
+ * <li>Record the current mappings in an XML file:<br />
+ * <code>java org.apache.openjpa.jdbc.meta.MappingTool
+ * -f mypackage.orm -a export mypackage.jdo</code></li>
+ * </ul>
+ */
+ public static void main(String[] args)
+ throws IOException, SQLException {
+ Options opts = new Options();
+ args = opts.setFromCmdLine(args);
+ JDBCConfiguration conf = new JDBCConfigurationImpl();
+ try {
+ if (!run(conf, args, opts))
+ System.err.println(_loc.get("tool-usage"));
+ } finally {
+ conf.close();
+ }
+ }
+
+ /**
+ * Run the tool. Returns false if invalid options are given.
+ *
+ * @see #main
+ */
+ public static boolean run(JDBCConfiguration conf, String[] args,
+ Options opts)
+ throws IOException, SQLException {
+ if (opts.containsKey("help") || opts.containsKey("-help"))
+ return false;
+
+ // flags
+ Flags flags = new Flags();
+ flags.action = opts.removeProperty("action", "a", flags.action);
+ flags.schemaAction = opts.removeProperty("schemaAction", "sa",
+ flags.schemaAction);
+ flags.dropTables = opts.removeBooleanProperty
+ ("dropTables", "dt", flags.dropTables);
+ flags.openjpaTables = opts.removeBooleanProperty
+ ("openjpaTables", "kt", flags.openjpaTables);
+ flags.dropSequences = opts.removeBooleanProperty
+ ("dropSequences", "dsq", flags.dropSequences);
+ flags.readSchema = opts.removeBooleanProperty
+ ("readSchema", "rs", flags.readSchema);
+ flags.primaryKeys = opts.removeBooleanProperty
+ ("primaryKeys", "pk", flags.primaryKeys);
+ flags.indexes = opts.removeBooleanProperty("indexes", "ix",
+ flags.indexes);
+ flags.foreignKeys = opts.removeBooleanProperty("foreignKeys", "fk",
+ flags.foreignKeys);
+ flags.sequences = opts.removeBooleanProperty("sequences", "sq",
+ flags.sequences);
+ flags.ignoreErrors = opts.removeBooleanProperty
+ ("ignoreErrors", "i", flags.ignoreErrors);
+ flags.meta = opts.removeBooleanProperty("meta", "m", flags.meta);
+ String fileName = opts.removeProperty("file", "f", null);
+ String schemaFileName = opts.removeProperty("schemaFile", "sf", null);
+ String sqlFileName = opts.removeProperty("sqlFile", "sql", null);
+ String schemas = opts.removeProperty("s");
+ if (schemas != null)
+ opts.setProperty("schemas", schemas);
+
+ Configurations.populateConfiguration(conf, opts);
+ ClassLoader loader = conf.getClassResolverInstance().
+ getClassLoader(MappingTool.class, null);
+ if (flags.meta && ACTION_ADD.equals(flags.action))
+ flags.metaDataFile = Files.getFile(fileName, loader);
+ else
+ flags.mappingWriter = Files.getWriter(fileName, loader);
+ flags.schemaWriter = Files.getWriter(schemaFileName, loader);
+ flags.sqlWriter = Files.getWriter(sqlFileName, loader);
+
+ return run(conf, args, flags, loader);
+ }
+
+ /**
+ * Run the tool. Return false if an invalid option was given.
+ */
+ public static boolean run(JDBCConfiguration conf, String[] args,
+ Flags flags, ClassLoader loader)
+ throws IOException, SQLException {
+ // default action based on whether the mapping defaults fills in
+ // missing info
+ if (flags.action == null) {
+ if (conf.getMappingDefaultsInstance().defaultMissingInfo())
+ flags.action = ACTION_BUILD_SCHEMA;
+ else
+ flags.action = ACTION_REFRESH;
+ }
+
+ // collect the classes to act on
+ Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
+ Collection classes = null;
+ if (args.length == 0) {
+ if (ACTION_IMPORT.equals(flags.action))
+ return false;
+ log.info(_loc.get("running-all-classes"));
+ classes = conf.getMappingRepository().loadPersistentTypes(true,
+ loader);
+ } else {
+ classes = new HashSet();
+ ClassArgParser classParser = conf.getMetaDataRepository().
+ getMetaDataFactory().newClassArgParser();
+ classParser.setClassLoader(loader);
+ Class[] parsed;
+ for (int i = 0; args != null && i < args.length; i++) {
+ parsed = classParser.parseTypes(args[i]);
+ classes.addAll(Arrays.asList(parsed));
+ }
+ }
+
+ Class[] act = (Class[]) classes.toArray(new Class[classes.size()]);
+ if (ACTION_EXPORT.equals(flags.action)) {
+ // run exports until the first export succeeds
+ ImportExport[] instances = newImportExports();
+ for (int i = 0; i < instances.length; i++) {
+ if (instances[i].exportMappings(conf, act, flags.meta, log,
+ flags.mappingWriter))
+ return true;
+ }
+ return false;
+ }
+ if (ACTION_IMPORT.equals(flags.action)) {
+ // run exports until the first export succeeds
+ ImportExport[] instances = newImportExports();
+ for (int i = 0; i < instances.length; i++) {
+ if (instances[i].importMappings(conf, act, args, flags.meta,
+ log, loader))
+ return true;
+ }
+ return false;
+ }
+
+ MappingTool tool;
+ try {
+ tool = new MappingTool(conf, flags.action, flags.meta);
+ } catch (IllegalArgumentException iae) {
+ return false;
+ }
+
+ // setup the tool
+ tool.setIgnoreErrors(flags.ignoreErrors);
+ tool.setMetaDataFile(flags.metaDataFile);
+ tool.setMappingWriter(flags.mappingWriter);
+ tool.setSchemaAction(flags.schemaAction);
+ tool.setSchemaWriter(flags.schemaWriter);
+ tool.setReadSchema(flags.readSchema
+ && !ACTION_VALIDATE.equals(flags.action));
+ tool.setPrimaryKeys(flags.primaryKeys);
+ tool.setForeignKeys(flags.foreignKeys);
+ tool.setIndexes(flags.indexes);
+ tool.setSequences(flags.sequences || flags.dropSequences);
+
+ // make sure to do this after other settings so that other settings
+ // are passed on to schema tool
+ tool.getSchemaTool().setDropTables(flags.dropTables);
+ tool.getSchemaTool().setDropSequences(flags.dropSequences);
+ tool.getSchemaTool().setWriter(flags.sqlWriter);
+ tool.getSchemaTool().setOpenJPATables(flags.openjpaTables);
+
+ // and run the action
+ for (int i = 0; i < act.length; i++) {
+ log.info(_loc.get("tool-running", act[i], flags.action));
+ if (i == 0 && flags.readSchema)
+ log.info(_loc.get("tool-time"));
+ tool.run(act[i]);
+ }
+ log.info(_loc.get("tool-record"));
+ tool.record();
+ return true;
+ }
+
+ /**
+ * Create an {@link ImportExport} instance.
+ */
+ private static ImportExport[] newImportExports() {
+ try {
+ Class[] types = Services.getImplementorClasses(ImportExport.class);
+ ImportExport[] instances = new ImportExport[types.length];
+ for (int i = 0; i < types.length; i++)
+ instances[i] = (ImportExport) types[i].newInstance();
+ return instances;
+ } catch (Throwable t) {
+ throw new InternalException(_loc.get("importexport-instantiate"),
+ t);
+ }
+ }
+
+ /**
+ * Run flags.
+ */
+ public static class Flags {
+
+ public String action = null;
+ public boolean meta = false;
+ public String schemaAction = SchemaTool.ACTION_ADD;
+ public File metaDataFile = null;
+ public Writer mappingWriter = null;
+ public Writer schemaWriter = null;
+ public Writer sqlWriter = null;
+ public boolean ignoreErrors = false;
+ public boolean readSchema = false;
+ public boolean dropTables = false;
+ public boolean openjpaTables = false;
+ public boolean dropSequences = false;
+ public boolean sequences = true;
+ public boolean primaryKeys = false;
+ public boolean foreignKeys = false;
+ public boolean indexes = false;
+ }
+
+ /**
+ * Helper used to import and export mapping data.
+ */
+ public static interface ImportExport {
+
+ /**
+ * Import mappings for the given classes based on the given arguments.
+ */
+ public boolean importMappings(JDBCConfiguration conf, Class[] act,
+ String[] args, boolean meta, Log log, ClassLoader loader)
+ throws IOException;
+
+ /**
+ * Export mappings for the given classes based on the given arguments.
+ */
+ public boolean exportMappings(JDBCConfiguration conf, Class[] act,
+ boolean meta, Log log, Writer writer)
+ throws IOException;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MetaDataPlusMappingFactory.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MetaDataPlusMappingFactory.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MetaDataPlusMappingFactory.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MetaDataPlusMappingFactory.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.meta;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.DelegatingMetaDataFactory;
+import org.apache.openjpa.meta.MetaDataFactory;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.QueryMetaData;
+import org.apache.openjpa.meta.SequenceMetaData;
+
+/**
+ * Combines two internal {@link MetaDataFactory} instances -- one for
+ * metadata, one for mappings -- into a single {@link MetaDataFactory} facade.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class MetaDataPlusMappingFactory
+ extends DelegatingMetaDataFactory {
+
+ private final MetaDataFactory _map;
+
+ /**
+ * Constructor; supply delegates.
+ */
+ public MetaDataPlusMappingFactory(MetaDataFactory meta,
+ MetaDataFactory map) {
+ super(meta);
+ _map = map;
+
+ meta.setStrict(true);
+ map.setStrict(true);
+ }
+
+ /**
+ * Mapping factory delegate.
+ */
+ public MetaDataFactory getMappingDelegate() {
+ return _map;
+ }
+
+ /**
+ * Innermost mapping delegate.
+ */
+ public MetaDataFactory getInnermostMappingDelegate() {
+ if (_map instanceof DelegatingMetaDataFactory)
+ return ((DelegatingMetaDataFactory) _map).getInnermostDelegate();
+ return _map;
+ }
+
+ public void setRepository(MetaDataRepository repos) {
+ super.setRepository(repos);
+ _map.setRepository(repos);
+ }
+
+ public void setStoreDirectory(File dir) {
+ super.setStoreDirectory(dir);
+ _map.setStoreDirectory(dir);
+ }
+
+ public void setStoreMode(int store) {
+ super.setStoreMode(store);
+ _map.setStoreMode(store);
+ }
+
+ public void setStrict(boolean strict) {
+ // always in strict mode
+ }
+
+ public void load(Class cls, int mode, ClassLoader envLoader) {
+ if ((mode & ~MODE_MAPPING) != MODE_NONE)
+ super.load(cls, mode & ~MODE_MAPPING, envLoader);
+ if (cls != null && (mode & MODE_MAPPING) != 0)
+ _map.load(cls, mode & ~MODE_META, envLoader);
+ }
+
+ public boolean store(ClassMetaData[] metas, QueryMetaData[] queries,
+ SequenceMetaData[] seqs, int mode, Map output) {
+ boolean store = true;
+ if ((mode & ~MODE_MAPPING) != MODE_NONE)
+ store &= super.store(metas, queries, seqs, mode & ~MODE_MAPPING,
+ output);
+ if ((mode & MODE_MAPPING) != 0)
+ store &= _map.store(metas, queries, seqs, mode & ~MODE_META,
+ output);
+ return store;
+ }
+
+ public boolean drop(Class[] cls, int mode, ClassLoader envLoader) {
+ boolean drop = true;
+ if ((mode & ~MODE_MAPPING) != MODE_NONE)
+ drop &= super.drop(cls, mode & ~MODE_MAPPING, envLoader);
+ if ((mode & MODE_MAPPING) != 0)
+ drop &= _map.drop(cls, mode & ~MODE_META, envLoader);
+ return drop;
+ }
+
+ public Set getPersistentTypeNames(boolean classpath,
+ ClassLoader envLoader) {
+ Set names = super.getPersistentTypeNames(classpath, envLoader);
+ if (names != null && !names.isEmpty())
+ return names;
+ return _map.getPersistentTypeNames(classpath, envLoader);
+ }
+
+ public void clear() {
+ super.clear();
+ _map.clear();
+ }
+
+ public void addClassExtensionKeys(Collection exts) {
+ super.addClassExtensionKeys(exts);
+ _map.addClassExtensionKeys(exts);
+ }
+
+ public void addFieldExtensionKeys(Collection exts) {
+ super.addFieldExtensionKeys(exts);
+ _map.addFieldExtensionKeys(exts);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MetaDataPlusMappingFactory.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneMappingDefaults.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneMappingDefaults.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneMappingDefaults.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneMappingDefaults.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.meta;
+
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Index;
+import org.apache.openjpa.jdbc.schema.Schema;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.jdbc.schema.Unique;
+import serp.util.Strings;
+
+/**
+ * No-op mapping defaults.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class NoneMappingDefaults
+ implements MappingDefaults {
+
+ private static final NoneMappingDefaults _instance =
+ new NoneMappingDefaults();
+
+ public static NoneMappingDefaults getInstance() {
+ return _instance;
+ }
+
+ public boolean defaultMissingInfo() {
+ return false;
+ }
+
+ public boolean useClassCriteria() {
+ return false;
+ }
+
+ public Object getStrategy(ClassMapping cls, boolean adapt) {
+ return null;
+ }
+
+ public Object getStrategy(Version vers, boolean adapt) {
+ return null;
+ }
+
+ public Object getStrategy(Discriminator disc, boolean adapt) {
+ return null;
+ }
+
+ public Object getStrategy(ValueMapping vm, Class type, boolean adapt) {
+ return null;
+ }
+
+ public Object getDiscriminatorValue(Discriminator disc, boolean adapt) {
+ return null;
+ }
+
+ public String getTableName(ClassMapping cls, Schema schema) {
+ return Strings.getClassName(cls.getDescribedType()).replace('$', '_');
+ }
+
+ public String getTableName(FieldMapping fm, Schema schema) {
+ return fm.getName();
+ }
+
+ public void populateDataStoreIdColumns(ClassMapping cls, Table table,
+ Column[] cols) {
+ }
+
+ public void populateColumns(Version vers, Table table, Column[] cols) {
+ }
+
+ public void populateColumns(Discriminator disc, Table table,
+ Column[] cols) {
+ }
+
+ public void populateJoinColumn(ClassMapping cm, Table local, Table foreign,
+ Column col, Object target, int pos, int cols) {
+ }
+
+ public void populateJoinColumn(FieldMapping fm, Table local, Table foreign,
+ Column col, Object target, int pos, int cols) {
+ }
+
+ public void populateForeignKeyColumn(ValueMapping vm, String name,
+ Table local, Table foreign, Column col, Object target, boolean inverse,
+ int pos, int cols) {
+ }
+
+ public void populateColumns(ValueMapping vm, String name, Table table,
+ Column[] cols) {
+ }
+
+ public boolean populateOrderColumns(FieldMapping fm, Table table,
+ Column[] cols) {
+ return false;
+ }
+
+ public boolean populateNullIndicatorColumns(ValueMapping vm, String name,
+ Table table, Column[] cols) {
+ return false;
+ }
+
+ public ForeignKey getJoinForeignKey(ClassMapping cls, Table local,
+ Table foreign) {
+ return null;
+ }
+
+ public ForeignKey getJoinForeignKey(FieldMapping fm, Table local,
+ Table foreign) {
+ return null;
+ }
+
+ public ForeignKey getForeignKey(ValueMapping vm, String name, Table local,
+ Table foreign, boolean inverse) {
+ return null;
+ }
+
+ public Index getJoinIndex(FieldMapping fm, Table table, Column[] cols) {
+ return null;
+ }
+
+ public Index getIndex(ValueMapping vm, String name, Table table,
+ Column[] cols) {
+ return null;
+ }
+
+ public Index getIndex(Version vers, Table table, Column[] cols) {
+ return null;
+ }
+
+ public Index getIndex(Discriminator disc, Table table, Column[] cols) {
+ return null;
+ }
+
+ public Unique getJoinUnique(FieldMapping fm, Table table, Column[] cols) {
+ return null;
+ }
+
+ public Unique getUnique(ValueMapping vm, String name, Table table,
+ Column[] cols) {
+ return null;
+ }
+
+ public String getPrimaryKeyName(ClassMapping cm, Table table) {
+ return null;
+ }
+
+ public void installPrimaryKey(FieldMapping fm, Table table) {
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneMappingDefaults.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneStrategyInstaller.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneStrategyInstaller.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneStrategyInstaller.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneStrategyInstaller.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.meta;
+
+import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.NoneDiscriminatorStrategy;
+import org.apache.openjpa.jdbc.meta.strats.NoneFieldStrategy;
+import org.apache.openjpa.jdbc.meta.strats.NoneVersionStrategy;
+
+/**
+ * Clears all mapping information from classes and installs none strategies.
+ *
+ * @author Abe White
+ * @nojavadoc
+ * @since 4.0
+ */
+public class NoneStrategyInstaller
+ extends StrategyInstaller {
+
+ /**
+ * Constructor; supply configuration.
+ */
+ public NoneStrategyInstaller(MappingRepository repos) {
+ super(repos);
+ }
+
+ public void installStrategy(ClassMapping cls) {
+ cls.clearMapping();
+ cls.setStrategy(NoneClassStrategy.getInstance(), null);
+ cls.setSourceMode(cls.MODE_MAPPING, true);
+ }
+
+ public void installStrategy(FieldMapping field) {
+ field.clearMapping();
+ field.setStrategy(NoneFieldStrategy.getInstance(), null);
+ }
+
+ public void installStrategy(Version version) {
+ version.clearMapping();
+ version.setStrategy(NoneVersionStrategy.getInstance(), null);
+ }
+
+ public void installStrategy(Discriminator discrim) {
+ discrim.clearMapping();
+ discrim.setStrategy(NoneDiscriminatorStrategy.getInstance(), null);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/NoneStrategyInstaller.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/PropertiesReverseCustomizer.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/PropertiesReverseCustomizer.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/PropertiesReverseCustomizer.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/PropertiesReverseCustomizer.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.meta;
+
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.lib.util.Localizer;
+import serp.util.Strings;
+
+/**
+ * Simple {@link ReverseCustomizer} that uses a properties file to
+ * to allow customization of basic class and field properties. The
+ * customizer uses the following keys:
+ * <ul>
+ * <li><i><table name>.table-type</i>: Override the default type of the
+ * given table. Legal values are: <code>base, secondary,
+ * secondary-outer, association, subclass, none</code>. See
+ * the TABLE_XXX constants in {@link ReverseMappingTool} for descriptions.</li>
+ * <li><i><class name>.rename</i>: Override the tool-generated class name
+ * with the given value. Use full class names, including package. Use a
+ * value of <code>none</code> to reject the class and leave the
+ * corresponding table unmapped.</li>
+ * <li><i><table name>.class-name</i>: Assign the given fully-qualified
+ * class name to the type created from the given table. Use a value of
+ * <code>none</code> to prevent mapping this table. This property can be
+ * used in place of the <code>rename</code> property.
+ * <li><i><class name>.identity</i>: Set this property to
+ * <code>datastore</code>, <code>builtin</code>, or the desired
+ * fully-qualified application identity class name to override the
+ * reverse mapping tool's default identity settings. If the class has been
+ * renamed, use the new name.</li>
+ * <li><i><class name>.<field name>.rename</i>: Override the
+ * tool-generated field name with the given value. Use the field owner's
+ * full class name in the property key. The property value should be the
+ * new field name, without the preceding class name. Use a value of
+ * <code>none</code> to reject the generated mapping.</li>
+ * <li><i><table name>.<column name>.field-name</i>: Assign the
+ * field name to use for the mapping of a particular column. If this is
+ * a multi-column mapping, any one of the columns can be used. Use a value
+ * of <code>none</code> to prevent the column (and associated columns)
+ * from being mapped. This property can be used in place of the
+ * <code>rename</code> property.
+ * <li><i><class name>.<field name>.type</i>: The class name of
+ * the type to give the named field. Use full class names. If the field
+ * has been renamed, use the new name.</li>
+ * <li><i><class name>.<field name>.value</i>: The initial value
+ * for the named field. The given string will be placed as-is in the
+ * generated Java code, so be sure to add quotes to strings, etc. If the
+ * field has been renamed, use the new name.</li>
+ * </ul> All keys are optional; if not specified, the customizer keeps the
+ * default value generated by the reverse mapping tool.
+ */
+public class PropertiesReverseCustomizer
+ implements ReverseCustomizer {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (PropertiesReverseCustomizer.class);
+
+ protected ReverseMappingTool tool = null;
+ private Properties _props = null;
+ private Set _unaccessed = null;
+
+ public void setConfiguration(Properties props) {
+ _props = props;
+ _unaccessed = new TreeSet(props.keySet());
+ }
+
+ public void setTool(ReverseMappingTool tool) {
+ this.tool = tool;
+ }
+
+ public int getTableType(Table table, int defaultType) {
+ String type = getProperty(table.getName() + ".table-type");
+ if (type == null && table.getSchemaName() != null)
+ type = getProperty(table.getFullName() + ".table-type");
+ if (type == null)
+ return defaultType;
+ if ("none".equals(type))
+ return ReverseMappingTool.TABLE_NONE;
+ if ("base".equals(type))
+ return ReverseMappingTool.TABLE_BASE;
+ if ("secondary".equals(type))
+ return ReverseMappingTool.TABLE_SECONDARY;
+ if ("secondary-outer".equals(type))
+ return ReverseMappingTool.TABLE_SECONDARY_OUTER;
+ if ("association".equals(type))
+ return ReverseMappingTool.TABLE_ASSOCIATION;
+ if ("subclass".equals(type))
+ return ReverseMappingTool.TABLE_SUBCLASS;
+ throw new IllegalArgumentException(table.getName() + ".table-type: "
+ + type);
+ }
+
+ public String getClassName(Table table, String defaultName) {
+ // check for a rename property or a table-naming property
+ String name = getProperty(defaultName + ".rename");
+ if (name == null) {
+ name = getProperty(table.getName() + ".class-name");
+ if (name == null && table.getSchemaName() != null)
+ name = getProperty(table.getFullName() + ".class-name");
+ }
+
+ if (name == null) {
+ if (tool.getLog().isTraceEnabled())
+ tool.getLog().trace(_loc.get("custom-no-class",
+ defaultName, table));
+ return defaultName;
+ }
+
+ if ("none".equals(name)) {
+ if (tool.getLog().isInfoEnabled())
+ tool.getLog().info(_loc.get("custom-rm-class",
+ defaultName, table));
+ return null;
+ }
+
+ if (tool.getLog().isInfoEnabled())
+ tool.getLog().info(_loc.get("custom-class", defaultName, name));
+ return name;
+ }
+
+ public void customize(ClassMapping cls) {
+ // customize identity type
+ String id = getProperty(cls.getDescribedType().getName()
+ + ".identity");
+ if ("datastore".equals(id)) {
+ cls.setObjectIdType(null, false);
+ cls.setIdentityType(ClassMapping.ID_DATASTORE);
+ } else if ("builtin".equals(id)) {
+ cls.setObjectIdType(null, false);
+ cls.setIdentityType(ClassMapping.ID_APPLICATION);
+ } else if (id != null)
+ cls.setObjectIdType(tool.generateClass(id, null), false);
+ }
+
+ public String getClassCode(ClassMapping mapping) {
+ return null;
+ }
+
+ public void customize(FieldMapping field) {
+ String type = getProperty(field.getFullName() + ".type");
+ if (type != null)
+ field.setDeclaredType(Strings.toClass(type, null));
+ }
+
+ public String getFieldName(ClassMapping dec, Column[] cols, ForeignKey fk,
+ String defaultName) {
+ // check for a rename property or a column-naming property
+ String name = getProperty(dec.getDescribedType().getName() + "."
+ + defaultName + ".rename");
+ for (int i = 0; name == null && i < cols.length; i++) {
+ name = getProperty(cols[i].getTableName() + "."
+ + cols[i].getName() + "." + "field-name");
+ if (name == null && cols[i].getTable().getSchemaName() != null)
+ name = getProperty(cols[i].getTable().getFullName()
+ + "." + cols[i].getName() + "." + "field-name");
+ }
+
+ if (name == null) {
+ if (tool.getLog().isTraceEnabled())
+ tool.getLog().trace(_loc.get("custom-no-field", defaultName,
+ dec));
+ return defaultName;
+ }
+
+ if ("none".equals(name)) {
+ if (tool.getLog().isInfoEnabled())
+ tool.getLog().info(_loc.get("custom-rm-field", defaultName,
+ dec));
+ return null;
+ }
+
+ if (tool.getLog().isInfoEnabled())
+ tool.getLog().info(_loc.get("custom-field", defaultName, dec,
+ name));
+ return name;
+ }
+
+ public String getInitialValue(FieldMapping field) {
+ return getProperty(field.getFullName() + ".value");
+ }
+
+ public String getDeclaration(FieldMapping field) {
+ return null;
+ }
+
+ public String getFieldCode(FieldMapping field) {
+ return null;
+ }
+
+ public boolean unmappedTable(Table table) {
+ return false;
+ }
+
+ public void close() {
+ if (!_unaccessed.isEmpty() && tool.getLog().isTraceEnabled())
+ tool.getLog().trace(_loc.get("custom-unused-props", _unaccessed));
+ }
+
+ /**
+ * Return the property value for the given key, or null if none.
+ */
+ protected String getProperty(String key) {
+ String val = _props.getProperty(key);
+ if (val != null) {
+ val = val.trim();
+ if (val.length() == 0)
+ val = null;
+ }
+ _unaccessed.remove(key);
+ return val;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/PropertiesReverseCustomizer.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.meta;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.lib.meta.SourceTracker;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.xml.Commentable;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.MetaDataModes;
+import org.apache.openjpa.util.MetaDataException;
+import serp.util.Strings;
+
+/**
+ * Mapping of a query result set to scalar and/or persistence-capable
+ * object-level values.
+ *
+ * @author Pinaki Poddar
+ * @author Abe White
+ */
+public class QueryResultMapping
+ implements MetaDataModes, SourceTracker, Commentable {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (QueryResultMapping.class);
+
+ private final String _name;
+ private final MappingRepository _repos;
+ private File _file = null;
+ private Object _scope = null;
+ private int _srcType = SRC_OTHER;
+ private int _mode = MODE_QUERY;
+ private Class _class = null;
+ private int _idx = 0;
+ private String[] _comments = null;
+ private List _colList = null;
+ private List _pcList = null;
+
+ private PCResult[] _pcs = null;
+ private Object[] _cols = null;
+
+ /**
+ * Construct with the given name.
+ */
+ QueryResultMapping(String name, MappingRepository repos) {
+ _name = name;
+ _repos = repos;
+ }
+
+ /**
+ * Return the name for this query result.
+ */
+ public String getName() {
+ return _name;
+ }
+
+ /**
+ * The class that defines this query result, or null if none.
+ */
+ public Class getDefiningType() {
+ return _class;
+ }
+
+ /**
+ * The class that defines this query result, or null if none.
+ */
+ public void setDefiningType(Class cls) {
+ _class = cls;
+ }
+
+ /**
+ * Ids of mapped scalar columns in the result. These will typically be
+ * column names.
+ *
+ * @see org.apache.openjpa.jdbc.sql.Result
+ */
+ public Object[] getColumnResults() {
+ if (_cols == null) {
+ Object[] cols;
+ if (_colList == null)
+ cols = new Object[0];
+ else
+ cols = _colList.toArray();
+ _cols = cols;
+ }
+ return _cols;
+ }
+
+ /**
+ * Add the id of a mapped column in the query result. This will typically
+ * be a column name.
+ *
+ * @see org.apache.openjpa.jdbc.sql.Result
+ */
+ public void addColumnResult(Object id) {
+ _cols = null;
+ if (_colList == null)
+ _colList = new ArrayList();
+ _colList.add(id);
+ }
+
+ /**
+ * Return the mapped persistence-capable types in the query result.
+ */
+ public PCResult[] getPCResults() {
+ if (_pcs == null) {
+ PCResult[] pcs;
+ if (_pcList == null)
+ pcs = new PCResult[0];
+ else
+ pcs = (PCResult[]) _pcList.toArray
+ (new PCResult[_pcList.size()]);
+ _pcs = pcs;
+ }
+ return _pcs;
+ }
+
+ /**
+ * Add a mapped persistence-capable result with the given candidate type.
+ */
+ public PCResult addPCResult(Class candidate) {
+ _pcs = null;
+ PCResult pc = new PCResult(candidate);
+ if (_pcList == null)
+ _pcList = new ArrayList();
+ _pcList.add(pc);
+ return pc;
+ }
+
+ /**
+ * The source mode of this query result.
+ */
+ public int getSourceMode() {
+ return _mode;
+ }
+
+ /**
+ * The source mode of this query result.
+ */
+ public void setSourceMode(int mode) {
+ _mode = mode;
+ }
+
+ /**
+ * Relative order of result mapping in metadata.
+ */
+ public int getListingIndex() {
+ return _idx;
+ }
+
+ /**
+ * Relative order of result mapping in metadata.
+ */
+ public void setListingIndex(int idx) {
+ _idx = idx;
+ }
+
+ public String toString() {
+ return _name;
+ }
+
+ ///////////////
+ // Commentable
+ ///////////////
+
+ public String[] getComments() {
+ return (_comments == null) ? EMPTY_COMMENTS : _comments;
+ }
+
+ public void setComments(String[] comments) {
+ _comments = comments;
+ }
+
+ ////////////////////////////////
+ // SourceTracker implementation
+ ////////////////////////////////
+
+ public File getSourceFile() {
+ return _file;
+ }
+
+ public Object getSourceScope() {
+ return _scope;
+ }
+
+ public int getSourceType() {
+ return _srcType;
+ }
+
+ public void setSource(File file, Object scope, int srcType) {
+ _file = file;
+ _scope = scope;
+ _srcType = srcType;
+ }
+
+ public String getResourceName() {
+ return (_class == null) ? _name : _class.getName() + ":" + _name;
+ }
+
+ /**
+ * A persistence-capable result.
+ */
+ public class PCResult {
+
+ /**
+ * Path token to represent a discriminator.
+ */
+ public static final String DISCRIMINATOR = "<discriminator>";
+
+ private final Class _candidate;
+ private ClassMapping _candidateMap = null;
+ private Map _rawMappings = null; // string->object
+ private Map _mappings = null; // list->columnmap
+ private Map _eager = null; // list->fetchinfo
+ private FetchInfo _fetchInfo = null; // for top-level
+
+ /**
+ * Supply candidate type on construction.
+ */
+ private PCResult(Class candidate) {
+ _candidate = candidate;
+ }
+
+ /**
+ * The result candidate class.
+ */
+ public Class getCandidateType() {
+ return _candidate;
+ }
+
+ /**
+ * Candidate mapping.
+ */
+ public ClassMapping getCandidateTypeMapping() {
+ if (_candidateMap == null)
+ _candidateMap = _repos.getMapping(_candidate, null, true);
+ return _candidateMap;
+ }
+
+ /**
+ * Return the raw mapping paths supplied with {@link #addMapping}, or
+ * empty array if none.
+ */
+ public String[] getMappingPaths() {
+ if (_rawMappings == null)
+ return new String[0];
+ Collection keys = _rawMappings.keySet();
+ return (String[]) keys.toArray(new String[keys.size()]);
+ }
+
+ /**
+ * Return the mapping id for the given path supplied with
+ * {@link #addMapping}, or null if none.
+ */
+ public Object getMapping(String path) {
+ return (_rawMappings == null) ? null : _rawMappings.get(path);
+ }
+
+ /**
+ * Map the given path to the given result id.
+ */
+ public void addMapping(String path, Object id) {
+ if (path == null || path.length() == 0)
+ throw new MetaDataException(_loc.get("null-path",
+ QueryResultMapping.this, _candidate));
+
+ _mappings = null;
+ _eager = null;
+ _fetchInfo = null;
+ if (_rawMappings == null)
+ _rawMappings = new HashMap();
+ _rawMappings.put(path, id);
+ }
+
+ /**
+ * Map the given request onto a result id.
+ *
+ * @param path stack of data requests (see
+ * {@link org.apache.openjpa.jdbc.sql.Result#startDataRequest})
+ * @param id requested id or column (see
+ * {@link org.apache.openjpa.jdbc.sql.Result} APIs)
+ * @param joins requested joins, or null
+ * @return the id or column to fetch from the result
+ * (typically a column name)
+ */
+ public Object map(List path, Object id, Joins joins) {
+ if (_rawMappings == null || !(id instanceof Column))
+ return id;
+
+ resolve();
+ ColumnMap cm = (ColumnMap) _mappings.get(path);
+ return (cm == null) ? id : cm.map((Column) id);
+ }
+
+ /**
+ * Return true if the mapped result contains eager data for the given
+ * field at the given path.
+ *
+ * @param path stack of data requests (see
+ * {@link org.apache.openjpa.jdbc.sql.Result#startDataRequest})
+ */
+ public boolean hasEager(List path, FieldMapping field) {
+ if (_rawMappings == null)
+ return false;
+
+ resolve();
+ if (path.isEmpty())
+ return _fetchInfo.eager.get(field.getIndex());
+ if (_eager == null)
+ return false;
+ FetchInfo info = (FetchInfo) _eager.get(path);
+ return info != null && info.eager.get(field.getIndex());
+ }
+
+ /**
+ * Return the field indexes to exclude when loading data for the
+ * given path.
+ */
+ public BitSet getExcludes(List path) {
+ if (_rawMappings == null)
+ return null;
+
+ resolve();
+ if (path.isEmpty())
+ return _fetchInfo.excludes;
+ if (_eager == null)
+ return null;
+ FetchInfo info = (FetchInfo) _eager.get(path);
+ return (info == null) ? null : info.excludes;
+ }
+
+ /**
+ * Resolve internal datastructures from raw mappings.
+ */
+ private synchronized void resolve() {
+ if (_rawMappings == null || _mappings != null)
+ return;
+
+ _mappings = new HashMap();
+ _fetchInfo = new FetchInfo(getCandidateTypeMapping());
+
+ Map.Entry entry;
+ for (Iterator itr = _rawMappings.entrySet().iterator();
+ itr.hasNext();) {
+ entry = (Map.Entry) itr.next();
+ resolveMapping((String) entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Resolve the given mapping path.
+ */
+ private void resolveMapping(String path, Object id) {
+ // build up path to second-to-last token
+ String[] tokens = Strings.split(path, ".", 0);
+ List rpath = new ArrayList(tokens.length);
+ ClassMapping candidate = getCandidateTypeMapping();
+ FieldMapping fm = null;
+ Set eagers;
+ for (int i = 0; i < tokens.length - 1; i++) {
+ fm = candidate.getFieldMapping(tokens[i]);
+ if (fm == null)
+ throw new MetaDataException(_loc.get("bad-path",
+ QueryResultMapping.this, _candidate, path));
+
+ if (fm.getEmbeddedMapping() != null) {
+ recordIncluded(candidate, rpath, fm);
+ candidate = fm.getEmbeddedMapping();
+ } else
+ candidate = fm.getTypeMapping();
+ if (candidate == null)
+ throw new MetaDataException(_loc.get("untraversable-path",
+ QueryResultMapping.this, _candidate, path));
+ rpath.add(fm);
+ }
+
+ String lastToken = tokens[tokens.length - 1];
+ if (DISCRIMINATOR.equals(lastToken)) {
+ Discriminator discrim = candidate.getDiscriminator();
+ rpath.add(discrim);
+ assertSingleColumn(discrim.getColumns(), path);
+ _mappings.put(rpath, new SingleColumnMap(id));
+ } else {
+ FieldMapping last = candidate.getFieldMapping(lastToken);
+ if (last == null)
+ throw new MetaDataException(_loc.get("untraversable-path",
+ QueryResultMapping.this, _candidate, path));
+ assertSingleColumn(last.getColumns(), path);
+ Column col = last.getColumns()[0];
+
+ // special-case oid fields, since path lists supplied for
+ // them at runtime don't include the embedded fields
+ if (fm != null && fm.getDeclaredTypeCode() == JavaTypes.OID) {
+ addComplexColumnMapping(fm, rpath, col, id);
+ return;
+ }
+
+ if (fm != null && fm.getForeignKey() != null) {
+ // if the last field is one of the joinables used in the
+ // relation's foreign key, map to relation field path.
+ // otherwise, record that we have an eager result
+ Column fkCol = fm.getForeignKey().getColumn(col);
+ if (fkCol != null)
+ addComplexColumnMapping(fm, new ArrayList(rpath),
+ fkCol, id);
+ else {
+ recordEager(candidate, rpath, fm);
+ recordIncluded(candidate, rpath, last);
+ }
+ } else
+ recordIncluded(candidate, rpath, last);
+
+ // map to related field path. because the SingleColumnMap
+ // doesn't test the requested column, it will accept
+ // requests for both the fk col or the related field col
+ rpath.add(last);
+ _mappings.put(rpath, new SingleColumnMap(id));
+ }
+ }
+
+ /**
+ * Create an appropriate column mapping for the given field.
+ */
+ private void addComplexColumnMapping(FieldMapping fm, List rpath,
+ Column col, Object id) {
+ if (fm.getColumns().length == 1)
+ _mappings.put(rpath, new SingleColumnMap(id));
+ else {
+ MultiColumnMap mcm = (MultiColumnMap) _mappings.get(rpath);
+ if (mcm == null) {
+ mcm = new MultiColumnMap(fm.getColumns());
+ _mappings.put(rpath, mcm);
+ }
+ mcm.set(col, id);
+ }
+ }
+
+ /**
+ * For now, we only allow mappings with a single column. In the
+ * future we might introduce a syntax to map multiple columns.
+ */
+ private void assertSingleColumn(Column[] cols, String path) {
+ if (cols.length != 1)
+ throw new MetaDataException(_loc.get("num-cols-path",
+ QueryResultMapping.this, _candidate, path));
+ }
+
+ /**
+ * Record that there may be eager data for the given field at the given
+ * path.
+ */
+ private void recordEager(ClassMapping candidate, List path,
+ FieldMapping fm) {
+ if (path.size() == 1) {
+ _fetchInfo.eager.set(fm.getIndex());
+ _fetchInfo.excludes.clear(fm.getIndex());
+ } else {
+ // record at previous path
+ List copy = new ArrayList(path.size() - 1);
+ for (int i = 0; i < copy.size(); i++)
+ copy.add(path.get(i));
+
+ if (_eager == null)
+ _eager = new HashMap();
+ FetchInfo info = (FetchInfo) _eager.get(copy);
+ if (info == null) {
+ info = new FetchInfo(candidate);
+ _eager.put(copy, info);
+ }
+ info.eager.set(fm.getIndex());
+ info.excludes.clear(fm.getIndex());
+ }
+ }
+
+ /**
+ * Record that the field at the given path is included in the results.
+ */
+ private void recordIncluded(ClassMapping candidate, List path,
+ FieldMapping fm) {
+ if (path.isEmpty())
+ _fetchInfo.excludes.clear(fm.getIndex());
+ else {
+ if (_eager == null)
+ _eager = new HashMap();
+ FetchInfo info = (FetchInfo) _eager.get(path);
+ if (info == null) {
+ info = new FetchInfo(candidate);
+ _eager.put(new ArrayList(path), info);
+ }
+ info.excludes.clear(fm.getIndex());
+ }
+ }
+ }
+
+ /**
+ * Fetch information.
+ */
+ private static class FetchInfo {
+
+ /**
+ * Indexes of fields to exclude from loading.
+ */
+ public final BitSet excludes;
+
+ /**
+ * Indexes of eager fields.
+ */
+ public final BitSet eager;
+
+ public FetchInfo(ClassMapping type) {
+ FieldMapping[] fms = type.getFieldMappings();
+ eager = new BitSet(fms.length);
+ excludes = new BitSet(fms.length);
+ for (int i = 0; i < fms.length; i++)
+ if (!fms[i].isPrimaryKey())
+ excludes.set(i);
+ }
+ }
+
+ /**
+ * Mapping of columns to result ids.
+ */
+ private static interface ColumnMap {
+
+ /**
+ * Return the result id for the given column, or the given colum
+ * if none.
+ */
+ public Object map(Column col);
+ }
+
+ /**
+ * {@link ColumnMap} specialized for a single column.
+ */
+ private static class SingleColumnMap
+ implements ColumnMap {
+
+ private final Object _id;
+
+ public SingleColumnMap(Object id) {
+ _id = id;
+ }
+
+ public Object map(Column col) {
+ return _id;
+ }
+
+ public String toString() {
+ return _id.toString();
+ }
+ }
+
+ /**
+ * {@link ColumnMap} specialized for a multiple columns.
+ * Maps columns in linear time.
+ */
+ private static class MultiColumnMap
+ implements ColumnMap {
+
+ private final List _cols;
+ private final Object[] _ids;
+
+ public MultiColumnMap(Column[] cols) {
+ _cols = Arrays.asList(cols);
+ _ids = new Object[cols.length];
+ }
+
+ public Object map(Column col) {
+ int idx = _cols.indexOf(col);
+ return (idx == -1) ? col : _ids[idx];
+ }
+
+ public void set(Column col, Object id) {
+ int idx = _cols.indexOf(col);
+ if (idx != -1)
+ _ids[idx] = id;
+ }
+
+ public String toString() {
+ return _cols + "=" + Arrays.asList(_ids);
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java
------------------------------------------------------------------------------
svn:executable = *