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
+     * &lt;refresh | add | buildSchema | drop | validate | import | export&gt;]
+     * &lt;class name | .java file | .class file | .jdo file&gt;*
+     *  Where the following options are recognized.
+     * <ul>
+     * <li><i>-properties/-p &lt;properties file or resource&gt;</i>: The
+     * path or resource name of a OpenJPA properties file containing
+     * information as outlined in {@link OpenJPAConfiguration}. Optional.</li>
+     * <li><i>-&lt;property name&gt; &lt;property value&gt;</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 &lt;stdout | output file or resource&gt;</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 &lt;schema action | none&gt;</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 &lt;stdout | output file or resource&gt;</i>: Use
+     * this option to write the planned schema to an XML document rather
+     * than modify the data store.</li>
+     * <li><i>-sqlFile/-sql &lt;stdout | output file or resource&gt;</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 &lt;true/t | false/f&gt;</i>: Corresponds to the
+     * same-named option in the {@link SchemaTool}.</li>
+     * <li><i>-dropSequences/-dsq &lt;true/t | false/f&gt;</i>: Corresponds
+     * to the same-named option in the {@link SchemaTool}.</li>
+     * <li><i>-openjpaTables/-kt &lt;true/t | false/f&gt;</i>: Corresponds to the
+     * same-named option in the {@link SchemaTool}.</li>
+     * <li><i>-ignoreErrors/-i &lt;true/t | false/f&gt;</i>: Corresponds to the
+     * same-named option in the {@link SchemaTool}.</li>
+     * <li><i>-readSchema/-rs &lt;true/t | false/f&gt;</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 &lt;true/t | false/f&gt;</i>: Whether primary
+     * keys on existing tables are manipulated. Defaults to false.</li>
+     * <li><i>-foreignKeys/-fk &lt;true/t | false/f&gt;</i>: Whether foreign
+     * keys on existing tables are manipulated. Defaults to false.</li>
+     * <li><i>-indexes/-ix &lt;true/t | false/f&gt;</i>: Whether indexes on
+     * existing tables are manipulated. Defaults to false.</li>
+     * <li><i>-sequences/-sq &lt;true/t | false/f&gt;</i>: Whether sequences
+     * are manipulated. Defaults to true.</li>
+     * <li><i>-schemas/-s &lt;schema and table names&gt;</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 &lt;true/t | false/f&gt;</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>&lt;table name&gt;.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>&lt;class name&gt;.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>&lt;table name&gt;.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>&lt;class name&gt;.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>&lt;class name&gt;.&lt;field name&gt;.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>&lt;table name&gt;.&lt;column name&gt;.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>&lt;class name&gt;.&lt;field name&gt;.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>&lt;class name&gt;.&lt;field name&gt;.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 = *