private void mergeJoinColumn()

in openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java [1356:1585]


    private void mergeJoinColumn(MetaDataContext context, String prefix,
        Column given, Object[][] joins, int idx, Table table, ClassMapping cls,
        ClassMapping rel, ForeignKeyDefaults def, boolean inversable,
        boolean adapt, boolean fill) {
        // default to the primary key column name if this is a pk join
        DBIdentifier name = given.getIdentifier();
        if (DBIdentifier.isNull(name) && given.getFlag(Column.FLAG_PK_JOIN) && cls != null) {
            Column[] pks = cls.getPrimaryKeyColumns();
            if (pks.length == 1)
                name = pks[0].getIdentifier();
        }

        // if we can't adapt, then the user must at least give a column name
        if (DBIdentifier.isNull(name) && !adapt && !fill)
            throw new MetaDataException(_loc.get(prefix + "-no-fkcol-name",
                context));

        // check to see if the column isn't in the expected table; it might
        // be an inverse join or a join to a base class of the target type
        Table local = table;
        Table foreign = rel.getTable();
        boolean fullName = false;
        boolean inverse = false;
        if (!DBIdentifier.isNull(name)) {
            QualifiedDBIdentifier path = QualifiedDBIdentifier.getPath(name);
            if (!DBIdentifier.isNull(path.getObjectTableName())) {
                if (DBIdentifier.isEmpty(path.getObjectTableName()))
                    local = foreign;
                else
                    local = findTable(context, path.getObjectTableName(),
                        local, foreign, null);
                fullName = true;
                name = path.getIdentifier().getUnqualifiedName();

                // if inverse join, then swap local and foreign tables
                if (local != table) {
                    foreign = table;
                    inverse = true;
                }
            }
        }
        boolean forceInverse = !fullName && _join == JOIN_INVERSE;
        if (forceInverse) {
            local = foreign;
            foreign = table;
            inverse = true;
        }

        // determine target
        DBIdentifier targetName = given.getTargetIdentifier();
        Object target = null;
        Table ttable = null;
        boolean constant = false;
        boolean fullTarget = false;
        if (DBIdentifier.isNull(targetName) && given.getTargetField() != null) {
            ClassMapping tcls = (inverse) ? cls : rel;
            String fieldName = given.getTargetField();
            String[] names = Normalizer.splitName(fieldName);
            fullTarget = names.length > 1;

            if (names.length > 1 && StringUtil.isEmpty(names[0])) {
                // allow use of '.' without prefix to mean "use expected local
                // cls"; but if we already inversed no need to switch again
                if (!inverse)
                    tcls = cls;
                fieldName = names[1];
            } else if (names.length > 1) {
                // must be class + field name
                tcls = findClassMapping(context, names[0], cls, rel);
                fieldName = names[1];
            }
            if (tcls == null)
                throw new MetaDataException(_loc.get(prefix
                    + "-bad-fktargetcls", context, fieldName, name));

            FieldMapping field = tcls.getFieldMapping(fieldName);
            if (field == null)
                throw new MetaDataException(_loc.get(prefix
                    + "-bad-fktargetfield", new Object[]{ context, fieldName,
                    name, tcls }));
            if (field.getColumns().length != 1)
                throw new MetaDataException(_loc.get(prefix
                    + "-fktargetfield-cols", context, fieldName, name));
            ttable = (field.getJoinForeignKey() != null) ? field.getTable()
                : field.getDefiningMapping().getTable();
            targetName = field.getColumns()[0].getIdentifier();
        } else if (!DBIdentifier.isNull(targetName)) {
            String targetNameStr = targetName.getName();
            if (targetNameStr.charAt(0) == '\'') {
                constant = true;
                target = targetNameStr.substring(1, targetNameStr.length() - 1);
            } else if (targetNameStr.charAt(0) == '-'
                || targetNameStr.charAt(0) == '.'
                || Character.isDigit(targetNameStr.charAt(0))) {
                constant = true;
                try {
                    if (targetNameStr.indexOf('.') == -1)
                        target = new Integer(targetNameStr);
                    else
                        target = new Double(targetNameStr);
                } catch (RuntimeException re) {
                    throw new MetaDataException(_loc.get(prefix
                        + "-bad-fkconst", context, targetName, name));
                }
            } else if ("null".equalsIgnoreCase(targetNameStr))
                constant = true;
            else {
                QualifiedDBIdentifier path = QualifiedDBIdentifier.getPath(targetName);
                fullTarget = (!DBIdentifier.isNull(path.getObjectTableName()));
                if (!DBIdentifier.isNull(path.getObjectTableName()) &&
                    DBIdentifier.isEmpty(path.getObjectTableName())) {
                    // allow use of '.' without prefix to mean "use expected
                    // local table", but ignore if we're already inversed
                    if (!inverse)
                        ttable = local;
                    targetName = path.getIdentifier().getUnqualifiedName();
                } else if (!DBIdentifier.isNull(path.getObjectTableName())) {
                    ttable = findTable(context, path.getObjectTableName(), foreign, local, (inverse) ? cls : rel);
                    targetName = path.getIdentifier().getUnqualifiedName();
                }
            }
        }

        // use explicit target table if available
        if (ttable == local && local != foreign) {
            // swap, unless user gave incompatible table in column name
            if (fullName)
                throw new MetaDataException(_loc.get(prefix
                    + "-bad-fktarget-inverse", new Object[]{ context, name,
                    foreign, ttable }));
            local = foreign;
            foreign = ttable;
        } else if (ttable != null) {
            // ttable might be a table of a base class of the target
            foreign = ttable;
        }

        // check to see if we inversed; if this is a same-table join, then
        // consider it an implicit inverse if the user includes the table name
        // in the column name, but not in the column target, or if the user
        // gives no column name but a full target name
        inverse = inverse || local != table || (local == foreign
            && ((fullName && !fullTarget) || (DBIdentifier.isNull(name) && fullTarget)));
        if (!inversable && !constant && inverse) {
            if (local == foreign)
                throw new MetaDataException(_loc.get(prefix
                    + "-bad-fk-self-inverse", context, local));
            throw new MetaDataException(_loc.get(prefix + "-bad-fk-inverse",
                context, local, table));
        }
        if (DBIdentifier.isNull(name) && constant)
            throw new MetaDataException(_loc.get(prefix
                + "-no-fkcol-name-adapt", context));

        if (DBIdentifier.isNull(name) && DBIdentifier.isNull(targetName)) {
            // if no name or target is provided and there's more than one likely
            // join possibility, too ambiguous
            PrimaryKey pk = foreign.getPrimaryKey();
            if (joins.length != 1 || pk == null || pk.getColumns().length != 1)
                throw new MetaDataException(_loc.get(prefix
                    + "-no-fkcol-name-adapt", context));

            // assume target is pk column
            targetName = pk.getColumns()[0].getIdentifier();
        } else if (!DBIdentifier.isNull(name) && DBIdentifier.isNull(targetName)) {
            // if one primary key column use it for target; if multiple joins
            // look for a foreign column with same name as local column
            PrimaryKey pk = foreign.getPrimaryKey();
            if (joins.length == 1 && pk != null && pk.getColumns().length == 1) {
                targetName = pk.getColumns()[0].getIdentifier();
            }
            else if (foreign.getColumn(name) != null) {
                targetName = name;
            }
            else {
                throw new MetaDataException(_loc.get(prefix
                    + "-no-fkcol-target-adapt", context, name));
            }
        }

        // find the target column, and create template for local column based
        // on it
        Column tmplate = new Column();
        tmplate.setIdentifier(name);
        if (!constant) {
            Column tcol = foreign.getColumn(targetName, false);
            if (tcol == null) {
            	String schemaCase = rel.getMappingRepository().getDBDictionary().schemaCase;
            	if (DBDictionary.SCHEMA_CASE_LOWER.equals(schemaCase)) {
                	tcol = foreign.getColumn(DBIdentifier.toLower(targetName, true), false);
            	} else if (DBDictionary.SCHEMA_CASE_UPPER.equals(schemaCase)) {
            		tcol = foreign.getColumn(DBIdentifier.toUpper(targetName, true), false);
            	}
            }
        	if (tcol == null) {
        		// give up
        		throw new MetaDataException(_loc.get(prefix + "-bad-fktarget",
    				new Object[]{ context, targetName, name, foreign }));
        	}
            if (DBIdentifier.isNull(name))
                tmplate.setIdentifier(tcol.getIdentifier());
            tmplate.setJavaType(tcol.getJavaType());
            tmplate.setType(tcol.getType());
            tmplate.setTypeName(tcol.getTypeName());
            tmplate.setSize(tcol.getSize());
            tmplate.setDecimalDigits(tcol.getDecimalDigits());
            target = tcol;
        } else if (target instanceof String)
            tmplate.setJavaType(JavaTypes.STRING);
        else if (target instanceof Integer)
            tmplate.setJavaType(JavaTypes.INT);
        else if (target instanceof Double)
            tmplate.setJavaType(JavaTypes.DOUBLE);

        // populate template, but let user-given name override default name
        if (def != null)
            def.populate(local, foreign, tmplate, target, inverse, idx,
                joins.length);
        if (!DBIdentifier.isNull(name))
            tmplate.setIdentifier(name);

        // create or merge local column
        Column col = mergeColumn(context, prefix, tmplate, true, given, local,
            adapt, fill);

        joins[idx][0] = col;
        joins[idx][1] = target;
        if (inverse)
            joins[idx][2] = Boolean.TRUE;
    }