| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.sql.SQLException; |
| |
| import org.apache.openjpa.jdbc.kernel.JDBCStore; |
| import org.apache.openjpa.jdbc.schema.Column; |
| import org.apache.openjpa.jdbc.schema.ColumnIO; |
| import org.apache.openjpa.jdbc.schema.Index; |
| import org.apache.openjpa.jdbc.schema.Schemas; |
| import org.apache.openjpa.jdbc.sql.Joins; |
| import org.apache.openjpa.jdbc.sql.Result; |
| import org.apache.openjpa.jdbc.sql.RowManager; |
| import org.apache.openjpa.jdbc.sql.SQLBuffer; |
| import org.apache.openjpa.jdbc.sql.Select; |
| import org.apache.openjpa.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.meta.MetaDataContext; |
| import org.apache.openjpa.meta.MetaDataModes; |
| import org.apache.openjpa.meta.MetaDataRepository; |
| import org.apache.openjpa.util.InternalException; |
| |
| /** |
| * Handles determining the object class of database records. |
| * |
| * @author Abe White |
| */ |
| public class Discriminator |
| implements DiscriminatorStrategy, MetaDataContext, MetaDataModes { |
| |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * Null discriminator value marker. |
| */ |
| public static final Object NULL = new Object(); |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (Discriminator.class); |
| |
| private final ClassMapping _mapping; |
| private final DiscriminatorMappingInfo _info; |
| private DiscriminatorStrategy _strategy = null; |
| private int _resMode = MODE_NONE; |
| |
| private Column[] _cols = Schemas.EMPTY_COLUMNS; |
| private ColumnIO _io = null; |
| private Index _idx = null; |
| private boolean _subsLoaded = false; |
| private Object _value = null; |
| |
| private int _javaType = -1; |
| |
| /** |
| * Constructor. Supply owning mapping. |
| */ |
| public Discriminator(ClassMapping mapping) { |
| _mapping = mapping; |
| _info = getMappingRepository().newMappingInfo(this); |
| } |
| |
| @Override |
| public MetaDataRepository getRepository() { |
| return _mapping.getRepository(); |
| } |
| |
| public MappingRepository getMappingRepository() { |
| return _mapping.getMappingRepository(); |
| } |
| |
| /** |
| * Return the owning mapping. |
| */ |
| public ClassMapping getClassMapping() { |
| return _mapping; |
| } |
| |
| /** |
| * The strategy used for class discrimination. |
| */ |
| public DiscriminatorStrategy getStrategy() { |
| return _strategy; |
| } |
| |
| /** |
| * The strategy used for class discrimination. The <code>adapt</code> |
| * parameter determines whether to adapt when mapping the strategy; |
| * use null if the strategy should not be mapped. |
| */ |
| public void setStrategy(DiscriminatorStrategy strategy, Boolean adapt) { |
| // set strategy first so we can access it during mapping |
| DiscriminatorStrategy orig = _strategy; |
| _strategy = strategy; |
| if (strategy != null) { |
| try { |
| strategy.setDiscriminator(this); |
| if (adapt != null) |
| strategy.map(adapt); |
| } catch (RuntimeException re) { |
| // reset strategy |
| _strategy = orig; |
| throw re; |
| } |
| } |
| } |
| |
| /** |
| * The discriminator value. |
| */ |
| public Object getValue() { |
| return _value; |
| } |
| |
| /** |
| * The discriminator value. |
| */ |
| public void setValue(Object value) { |
| _value = value; |
| } |
| |
| /** |
| * Raw mapping data. |
| */ |
| public DiscriminatorMappingInfo getMappingInfo() { |
| return _info; |
| } |
| |
| /** |
| * Columns used by this Discriminator. |
| */ |
| public Column[] getColumns() { |
| return _cols; |
| } |
| |
| /** |
| * Columns used by this Discriminator. |
| */ |
| public void setColumns(Column[] cols) { |
| if (cols == null) |
| cols = Schemas.EMPTY_COLUMNS; |
| _cols = cols; |
| } |
| |
| /** |
| * I/O information on the discriminator columns. |
| */ |
| public ColumnIO getColumnIO() { |
| return (_io == null) ? ColumnIO.UNRESTRICTED : _io; |
| } |
| |
| /** |
| * I/O information on the discriminator columns. |
| */ |
| public void setColumnIO(ColumnIO io) { |
| _io = io; |
| } |
| |
| /** |
| * Index on the Discriminator columns, or null if none. |
| */ |
| public Index getIndex() { |
| return _idx; |
| } |
| |
| /** |
| * Index on the Discriminator columns, or null if none. |
| */ |
| public void setIndex(Index idx) { |
| _idx = idx; |
| } |
| |
| /** |
| * Increment the reference count of used schema components. |
| */ |
| public void refSchemaComponents() { |
| for (Column col : _cols) { |
| col.ref(); |
| } |
| } |
| |
| /** |
| * Clear mapping information, including strategy. |
| */ |
| public void clearMapping() { |
| _strategy = null; |
| _cols = Schemas.EMPTY_COLUMNS; |
| _idx = null; |
| _value = null; |
| _info.clear(); |
| setResolve(MODE_MAPPING | MODE_MAPPING_INIT, false); |
| } |
| |
| /** |
| * Update {@link MappingInfo} with our current mapping information. |
| */ |
| public void syncMappingInfo() { |
| _info.syncWith(this); |
| } |
| |
| /** |
| * Resolve mode. |
| */ |
| public int getResolve() { |
| return _resMode; |
| } |
| |
| /** |
| * Resolve mode. |
| */ |
| public void setResolve(int mode) { |
| _resMode = mode; |
| } |
| |
| /** |
| * Resolve mode. |
| */ |
| public void setResolve(int mode, boolean on) { |
| if (mode == MODE_NONE) |
| _resMode = mode; |
| else if (on) |
| _resMode |= mode; |
| else |
| _resMode &= ~mode; |
| } |
| |
| /** |
| * Resolve mapping information. |
| */ |
| public boolean resolve(int mode) { |
| if ((_resMode & mode) == mode) |
| return true; |
| int cur = _resMode; |
| _resMode |= mode; |
| if ((mode & MODE_MAPPING) != 0 && (cur & MODE_MAPPING) == 0) |
| resolveMapping(); |
| if ((mode & MODE_MAPPING_INIT) != 0 && (cur & MODE_MAPPING_INIT) == 0) |
| _strategy.initialize(); |
| return false; |
| } |
| |
| /** |
| * Setup mapping. |
| */ |
| private void resolveMapping() { |
| // map strategy |
| MappingRepository repos = getMappingRepository(); |
| if (_strategy == null) |
| repos.getStrategyInstaller().installStrategy(this); |
| Log log = repos.getLog(); |
| if (log.isTraceEnabled()) |
| log.trace(_loc.get("strategy", this, _strategy.getAlias())); |
| |
| // mark columns as mapped |
| Column[] cols = getColumns(); |
| ColumnIO io = getColumnIO(); |
| for (int i = 0; i < cols.length; i++) { |
| if (io.isInsertable(i, false)) |
| cols[i].setFlag(Column.FLAG_DIRECT_INSERT, true); |
| if (io.isUpdatable(i, false)) |
| cols[i].setFlag(Column.FLAG_DIRECT_UPDATE, true); |
| } |
| } |
| |
| /** |
| * Whether this Discriminator has loaded subclasses yet. |
| */ |
| public boolean getSubclassesLoaded() { |
| if (!_subsLoaded) { |
| ClassMapping sup = _mapping.getPCSuperclassMapping(); |
| if (sup != null && sup.getDiscriminator().getSubclassesLoaded()) |
| _subsLoaded = true; |
| } |
| return _subsLoaded; |
| } |
| |
| /** |
| * Whether this Discriminator has loaded subclasses yet. |
| */ |
| public void setSubclassesLoaded(boolean loaded) { |
| _subsLoaded = loaded; |
| } |
| |
| /** |
| * Add WHERE conditions to the given select limiting the returned results |
| * to our mapping type, possibly including subclasses. |
| */ |
| public boolean addClassConditions(Select sel, boolean subs, Joins joins) { |
| if (_mapping.getJoinablePCSuperclassMapping() == null |
| && _mapping.getJoinablePCSubclassMappings().length == 0) |
| return false; |
| if (!hasClassConditions(_mapping, subs)) |
| return false; |
| |
| // join down to base class where conditions will be added |
| ClassMapping from = _mapping; |
| ClassMapping sup = _mapping.getJoinablePCSuperclassMapping(); |
| for (; sup != null; from = sup, sup = from |
| .getJoinablePCSuperclassMapping()) { |
| if (from.getTable() != sup.getTable()) { |
| if (joins == null) |
| joins = sel.newJoins(); |
| joins = from.joinSuperclass(joins, false); |
| } |
| } |
| |
| sel.where(getClassConditions(sel, joins, _mapping, subs), joins); |
| return true; |
| } |
| |
| //////////////////////////////////////// |
| // DiscriminatorStrategy implementation |
| //////////////////////////////////////// |
| |
| @Override |
| public String getAlias() { |
| return assertStrategy().getAlias(); |
| } |
| |
| @Override |
| public void map(boolean adapt) { |
| assertStrategy().map(adapt); |
| } |
| |
| @Override |
| public void initialize() { |
| assertStrategy().initialize(); |
| } |
| |
| @Override |
| public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm) |
| throws SQLException { |
| assertStrategy().insert(sm, store, rm); |
| } |
| |
| @Override |
| public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm) |
| throws SQLException { |
| assertStrategy().update(sm, store, rm); |
| } |
| |
| @Override |
| public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm) |
| throws SQLException { |
| assertStrategy().delete(sm, store, rm); |
| } |
| |
| @Override |
| public Boolean isCustomInsert(OpenJPAStateManager sm, JDBCStore store) { |
| return assertStrategy().isCustomInsert(sm, store); |
| } |
| |
| @Override |
| public Boolean isCustomUpdate(OpenJPAStateManager sm, JDBCStore store) { |
| return assertStrategy().isCustomUpdate(sm, store); |
| } |
| |
| @Override |
| public Boolean isCustomDelete(OpenJPAStateManager sm, JDBCStore store) { |
| return assertStrategy().isCustomDelete(sm, store); |
| } |
| |
| @Override |
| public void customInsert(OpenJPAStateManager sm, JDBCStore store) |
| throws SQLException { |
| assertStrategy().customInsert(sm, store); |
| } |
| |
| @Override |
| public void customUpdate(OpenJPAStateManager sm, JDBCStore store) |
| throws SQLException { |
| assertStrategy().customUpdate(sm, store); |
| } |
| |
| @Override |
| public void customDelete(OpenJPAStateManager sm, JDBCStore store) |
| throws SQLException { |
| assertStrategy().customDelete(sm, store); |
| } |
| |
| @Override |
| public void setDiscriminator(Discriminator owner) { |
| assertStrategy().setDiscriminator(owner); |
| } |
| |
| @Override |
| public boolean select(Select sel, ClassMapping mapping) { |
| return assertStrategy().select(sel, mapping); |
| } |
| |
| @Override |
| public void loadSubclasses(JDBCStore store) |
| throws SQLException, ClassNotFoundException { |
| assertStrategy().loadSubclasses(store); |
| } |
| |
| @Override |
| public Class<?> getClass(JDBCStore store, ClassMapping base, Result result) |
| throws SQLException, ClassNotFoundException { |
| return assertStrategy().getClass(store, base, result); |
| } |
| |
| @Override |
| public boolean hasClassConditions(ClassMapping base, boolean subs) { |
| return assertStrategy().hasClassConditions(base, subs); |
| } |
| |
| @Override |
| public SQLBuffer getClassConditions(Select sel, Joins joins, |
| ClassMapping base, boolean subs) { |
| return assertStrategy().getClassConditions(sel, joins, base, subs); |
| } |
| |
| private DiscriminatorStrategy assertStrategy() { |
| if (_strategy == null) |
| throw new InternalException(); |
| return _strategy; |
| } |
| |
| @Override |
| public String toString() { |
| return _mapping + "<discriminator>"; |
| } |
| |
| public void setJavaType(int javaType) { |
| _javaType = javaType; |
| } |
| |
| public int getJavaType() { |
| if (_javaType == -1) { |
| ClassMapping superMapping = _mapping.getPCSuperclassMapping(); |
| |
| if (superMapping != null && |
| superMapping.getDiscriminator() != null) { |
| _javaType = superMapping.getDiscriminator().getJavaType(); |
| } |
| } |
| |
| return _javaType; |
| } |
| } |