Package org.apache.isis.objectstore.sql.auto

Source Code of org.apache.isis.objectstore.sql.auto.AbstractAutoMapper

/*
*  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.isis.objectstore.sql.auto;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.isis.core.commons.config.IsisConfiguration;
import org.apache.isis.core.commons.exceptions.NotYetImplementedException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
import org.apache.isis.core.metamodel.spec.feature.Contributed;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.isis.core.runtime.system.context.IsisContext;
import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
import org.apache.isis.objectstore.sql.AbstractMapper;
import org.apache.isis.objectstore.sql.CollectionMapper;
import org.apache.isis.objectstore.sql.DatabaseConnector;
import org.apache.isis.objectstore.sql.Defaults;
import org.apache.isis.objectstore.sql.FieldMappingLookup;
import org.apache.isis.objectstore.sql.ObjectMappingLookup;
import org.apache.isis.objectstore.sql.Sql;
import org.apache.isis.objectstore.sql.SqlObjectStoreException;
import org.apache.isis.objectstore.sql.mapping.FieldMapping;

public abstract class AbstractAutoMapper extends AbstractMapper {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractAutoMapper.class);

    protected final Map<ObjectAssociation, FieldMapping> fieldMappingByField = Maps.newLinkedHashMap();

    protected CollectionMapper collectionMappers[];
    protected String collectionMapperFields[];
    protected boolean dbCreatesId;

    protected ObjectSpecification specification;
    protected String table;

    final String className;
    final String parameterBase;
    final FieldMappingLookup lookup;
    final ObjectMappingLookup objectMappingLookup;

    protected AbstractAutoMapper(final String className, final String parameterBase, final FieldMappingLookup lookup,
        final ObjectMappingLookup objectMappingLookup) {
        this.specification = specificationFor(className);
        this.className = className;
        this.parameterBase = parameterBase;
        this.lookup = lookup;
        this.objectMappingLookup = objectMappingLookup;
    }

    protected AbstractAutoMapper(final FieldMappingLookup lookup, final AbstractAutoMapper abstractAutoMapper,
        final String className) {

        this.specification = getSpecificationLoader().loadSpecification(className);
        this.className = className;

        this.parameterBase = null;
        this.lookup = null;
        this.objectMappingLookup = null;
    }

    private static ObjectSpecification specificationFor(final String className) {
        ObjectSpecification specification = IsisContext.getSpecificationLoader().loadSpecification(className);
        List<OneToOneAssociation> properties = specification.getProperties(Contributed.EXCLUDED);
        if (isNullOrEmpty(properties) && !specification.isAbstract()) {
            throw new SqlObjectStoreException(specification.getFullIdentifier() + " has no fields: " + specification);
        }
        return specification;
    }

    protected void setUpFieldMappers() {
        setUpFieldMappers(lookup, objectMappingLookup, className, parameterBase);
    }

    private void setUpFieldMappers(final FieldMappingLookup lookup, final ObjectMappingLookup objectMappingLookup,
        final String className, final String parameterBase) {
        final IsisConfiguration configParameters = getConfiguration();
        table = configParameters.getString(parameterBase + ".table." + className);
        if (table == null) {
            final String name = getTableNameFromSpecification(specification);
            table = name;
        } else {
            table = Sql.tableIdentifier(table);
        }

        dbCreatesId = configParameters.getBoolean(parameterBase + "db-ids", false);
        if (configParameters.getBoolean(parameterBase + "all-fields", true)) {
            setupFullMapping(lookup, objectMappingLookup, className, configParameters, parameterBase);
        } else {
            // setupSpecifiedMapping(specification, configParameters,
            // parameterBase);
        }

        LOG.info("table mapping: " + table + " (" + columnList(fieldMappingByField) + ")");
    }

    protected String getTableNameFromSpecification(final ObjectSpecification objectSpecification) {
        return Sql.tableIdentifier(Sql.sqlName(Defaults.getTablePrefix() + objectSpecification.getShortIdentifier()));
    }

    protected List<ObjectAssociation> fields = new ArrayList<ObjectAssociation>();

    protected void getExtraFields(final List<ObjectAssociation> fields) {
    }

    private void setupFullMapping(final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup,
        final String className, final IsisConfiguration configParameters, final String parameterBase) {

        fields.addAll(specification.getAssociations(Contributed.EXCLUDED));

        int simpleFieldCount = 0;
        int collectionFieldCount = 0;
        for (int i = 0; i < fields.size(); i++) {
            if (fields.get(i).isNotPersisted()) {
                continue;
            } else if (fields.get(i).isOneToManyAssociation()) {
                collectionFieldCount++;
            } else {
                simpleFieldCount++;
            }
        }

        final ObjectAssociation[] oneToOneProperties = new ObjectAssociation[simpleFieldCount];
        final ObjectAssociation[] oneToManyProperties = new ObjectAssociation[collectionFieldCount];
        collectionMappers = new CollectionMapper[collectionFieldCount];
        collectionMapperFields = new String[collectionFieldCount];
        final IsisConfiguration subset = getConfiguration().createSubset(parameterBase + ".mapper.");

        for (int i = 0, simpleFieldNo = 0, collectionFieldNo = 0; i < fields.size(); i++) {
            final ObjectAssociation field = fields.get(i);
            if (field.isNotPersisted()) {
                continue;
            } else if (field.isOneToManyAssociation()) {
                oneToManyProperties[collectionFieldNo] = field;

                // TODO: Replace "new ForeignKeyCollectionMapper" with a factory
                // method(?) to allow a different
                // default CollectionMapper

                // TODO: I think the default order should be changed - and I
                // think I (KAM) have dropped support for the
                // original "association-table" implementation. This means the
                // current checks are misleading.
                final String type = subset.getString(field.getId());
                if (type == null || type.equals("association-table")) {
                    // collectionMappers[collectionFieldNo] = new
                    // AutoCollectionMapper(specification,
                    // oneToManyProperties[collectionFieldNo], lookup);
                    // collectionMappers[collectionFieldNo] = new
                    // ForeignKeyCollectionMapper(oneToManyProperties[collectionFieldNo],
                    // parameterBase, lookup,
                    // objectMapperLookup);

                    CollectionMapper collectionMapper = null;

                    // Trying to detect recursion, here.
                    // Let ForeignKeyInChildCollectionMapper find itself when a
                    // field is a collection of the current
                    // field type.
                    if (this instanceof ForeignKeyInChildCollectionMapper) {
                        final ForeignKeyInChildCollectionMapper mc = (ForeignKeyInChildCollectionMapper) this;

                        if (mc.priorField == field) {
                            collectionMapper = mc;
                        }
                    }

                    if (collectionMapper == null) {
                        // TODO: Polymorphism - is it sufficient for the
                        // collectionMapper to handle the subclasses?
                        final ObjectSpecification fieldSpecification = field.getSpecification();
                        if (fieldSpecification.hasSubclasses() || fieldSpecification.isAbstract()) {
                            // PolymorphicForeignKeyInChildCollectionBaseMapper
                            // Or PolymorphicForeignKeyInChildCollectionMapper
                            collectionMapper =
                                new PolymorphicForeignKeyInChildCollectionBaseMapper(
                                    oneToManyProperties[collectionFieldNo], parameterBase, lookup, objectMapperLookup,
                                    this, field);
                        } else {
                            final ForeignKeyInChildCollectionMapper mapper =
                                new ForeignKeyInChildCollectionMapper(oneToManyProperties[collectionFieldNo],
                                    parameterBase, lookup, objectMapperLookup, this, field);
                            mapper.setUpFieldMappers();
                            collectionMapper = mapper;
                        }
                    }

                    collectionMappers[collectionFieldNo] = collectionMapper;
                    collectionMapperFields[collectionFieldNo] = field.getId();

                } else if (type.equals("fk-table")) {
                    final String property = parameterBase + field.getId() + ".element-type";
                    final String elementType = configParameters.getString(property);
                    if (elementType == null) {
                        throw new SqlObjectStoreException("Expected property " + property);
                    }
                    /*
                     * collectionMappers[collectionFieldNo] = new ForeignKeyCollectionMapper(elementType,
                     * oneToManyProperties[collectionFieldNo], parameterBase, lookup, objectMapperLookup);
                     */
                } else {
                    // TODO use other mappers where necessary
                    throw new NotYetImplementedException("for " + type);
                }

                collectionFieldNo++;
            } else if (field.isOneToOneAssociation()) {
                oneToOneProperties[simpleFieldNo] = field;
                simpleFieldNo++;
            } else {
                oneToOneProperties[simpleFieldNo] = field;
                simpleFieldNo++;
            }
        }

        for (final ObjectAssociation field : oneToOneProperties) {
            if (fieldMappingByField.containsKey(field)) {
                continue;
            }
            final FieldMapping mapping = lookup.createMapping(specification, field);
            fieldMappingByField.put(field, mapping);
        }
    }

    protected String columnList(final Map<ObjectAssociation, FieldMapping> fieldMappingByField) {
        return columnList(fieldMappingByField.values());
    }

    /*
     * private void setupSpecifiedMapping( final ObjectSpecification specification, final IsisConfiguration
     * configParameters, final String parameterBase) { IsisConfiguration columnMappings =
     * IsisContext.getConfiguration().createSubset(parameterBase + "column"); int columnsSize = columnMappings.size();
     * // columnNames = new String[columnsSize]; oneToOneProperties = new ObjectAssociation[columnsSize];
     *
     * int i = 0; for (Enumeration names = columnMappings.propertyNames(); names.hasMoreElements(); i++) { String
     * columnName = (String) names.nextElement(); String fieldName = columnMappings.getString(columnName);
     * oneToOneProperties[i] = specification.getAssociation(fieldName); // columnNames[i] = columnName; }
     *
     * IsisConfiguration collectionMappings = IsisContext.getConfiguration().createSubset( parameterBase +
     * "collection"); int collectionsSize = collectionMappings.size(); collectionMappers = new
     * AutoCollectionMapper[collectionsSize]; oneToManyProperties = new ObjectAssociation[collectionsSize];
     *
     * int j = 0; for (Enumeration names = collectionMappings.propertyNames(); names.hasMoreElements(); j++) { String
     * propertyName = (String) names.nextElement(); String collectionName = collectionMappings.getString(propertyName);
     * String type = collectionMappings.getString(collectionName);
     *
     * oneToManyProperties[j] = specification.getAssociation(collectionName); if (type.equals("auto")) {
     * collectionMappers[j] = new AutoCollectionMapper(this, specification, oneToManyProperties[j], getLookup()); } else
     * { // TODO use other mappers where necessary // new ReversedAutoAssociationMapper(specification, collectionName,
     * parameterBase);
     *
     * throw new NotYetImplementedException(); } } }
     */
    protected String columnList(final Collection<FieldMapping> fieldMappings) {
        final StringBuffer sql = new StringBuffer();
        for (final FieldMapping mapping : fieldMappings) {
            if (sql.length() > 0) {
                sql.append(",");
            }
            mapping.appendColumnNames(sql);
        }
        return sql.toString();
    }

    protected ObjectAdapter getAdapter(final ObjectSpecification spec, final Oid oid) {
        final ObjectAdapter adapter = getAdapterManager().getAdapterFor(oid);
        if (adapter != null) {
            return adapter;
        }

        // REVIEW: where the oid is a TypedOid, the following two lines could be replaced by
        // getPersistenceSession().recreatePersistentAdapter(oid)
        // is preferable, since then reuses the PojoRecreator impl defined within SqlPersistorInstaller
        final Object recreatedPojo = spec.createObject();
        return getPersistenceSession().mapRecreatedPojo(oid, recreatedPojo);
    }

    protected FieldMapping fieldMappingFor(final ObjectAssociation field) {
        return fieldMappingByField.get(field);
    }

    @Override
    public boolean needsTables(final DatabaseConnector connection) {
        for (int i = 0; collectionMappers != null && i < collectionMappers.length; i++) {
            if (collectionMappers[i].needsTables(connection)) {
                return true;
            }
        }
        return !connection.hasTable(table);
    }

    protected String values(final DatabaseConnector connector, final ObjectAdapter object) {
        final StringBuffer sql = new StringBuffer();
        for (final FieldMapping mapping : fieldMappingByField.values()) {
            mapping.appendInsertValues(connector, sql, object);
            sql.append(",");
        }
        return sql.toString();
    }

    private static boolean isNullOrEmpty(List<?> list) {
        return list == null || list.size() == 0;
    }

    @Override
    public String toString() {
        return "AbstractAutoMapper [table=" + table + ",noColumns=" + fieldMappingByField.size() + ",specification="
            + specification.getFullIdentifier() + "]";
    }

    protected SpecificationLoaderSpi getSpecificationLoader() {
        return IsisContext.getSpecificationLoader();
    }

    protected IsisConfiguration getConfiguration() {
        return IsisContext.getConfiguration();
    }

    protected PersistenceSession getPersistenceSession() {
        return IsisContext.getPersistenceSession();
    }

    protected AdapterManager getAdapterManager() {
        return getPersistenceSession().getAdapterManager();
    }

}
TOP

Related Classes of org.apache.isis.objectstore.sql.auto.AbstractAutoMapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.