001    package org.apache.ojb.broker.metadata.torque;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import org.apache.ojb.broker.metadata.ClassDescriptor;
019    import org.apache.ojb.broker.metadata.CollectionDescriptor;
020    import org.apache.ojb.broker.metadata.DescriptorRepository;
021    import org.apache.ojb.broker.metadata.FieldDescriptor;
022    import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
023    import org.apache.ojb.broker.util.logging.Logger;
024    import org.apache.ojb.broker.util.logging.LoggerFactory;
025    
026    import java.util.HashMap;
027    import java.util.Iterator;
028    import java.util.Vector;
029    
030    public class TorqueForeignKeyGenerator {
031            private final Logger logger = LoggerFactory.getLogger(this.getClass());
032        private DescriptorRepository repository;
033        private HashMap mappingTables = new HashMap();
034        private HashMap foreignKeyVectors = new HashMap();
035    
036        public TorqueForeignKeyGenerator(DescriptorRepository repository) {
037            this.repository = repository;
038        }
039    
040        public void buildConstraintsMap() {
041            Iterator classDescriptorIterators = this.repository.iterator();
042            while (classDescriptorIterators.hasNext()) {
043                ClassDescriptor cd = (ClassDescriptor) classDescriptorIterators.next();
044                if(cd.isAbstract() || cd.isInterface())
045                {
046                                    logger.debug( "Skip constraint build for abstract class/ interface " + cd.getClassNameOfObject() );
047                }
048                else
049                {
050                    buildConstraints(cd);
051                    buildOneToOneConstraints(cd);
052                }
053            }
054        }
055    
056        public Vector getForeignKeysForTable(String tableName) {
057            return (Vector) this.foreignKeyVectors.get(tableName);
058        }
059    
060        public HashMap getMappingTables() {
061            return this.mappingTables;
062        }
063    
064        private void buildTableFieldDescriptors(FieldDescriptor fieldDescriptors[], TableDescriptor tableDescriptor) {
065            for (int i = 0; i < fieldDescriptors.length; i++) {
066                tableDescriptor.addColumn(fieldDescriptors[i]);
067            }
068        }
069    
070        private void buildConstraints(ClassDescriptor cd) {
071            Vector collectionDescriptors = cd.getCollectionDescriptors();
072            for (int i = 0; i < collectionDescriptors.size(); i++) {
073                CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptors.get(i);
074                if (collectionDescriptor.isMtoNRelation()) {
075                    buildManyToManyConstraints(cd, collectionDescriptor);
076                } else {
077                    buildOneToManyReferences(cd, collectionDescriptor);
078                }
079            }
080        }
081    
082        private void buildManyToManyConstraints(ClassDescriptor cd, CollectionDescriptor collectionDescriptor) {
083            Vector columns = new Vector();
084    
085            ClassDescriptor itemDescriptor = this.repository.getDescriptorFor(collectionDescriptor.getItemClass());
086            buildManyToManyReferences(cd, collectionDescriptor, collectionDescriptor.getFksToThisClass(), columns);
087            buildManyToManyReferences(itemDescriptor, collectionDescriptor, collectionDescriptor.getFksToItemClass(), columns);
088    
089            if (isImplicitlyMapped(collectionDescriptor.getIndirectionTable())) {
090                TableDescriptor mappingTable = new TableDescriptor();
091                buildTableFieldDescriptors((FieldDescriptor[]) columns.toArray(new FieldDescriptor[0]), mappingTable);
092                mappingTable.setName(collectionDescriptor.getIndirectionTable());
093                this.mappingTables.put(mappingTable.getName(), mappingTable);
094            }
095        }
096    
097        private void buildManyToManyReferences(ClassDescriptor cd, CollectionDescriptor collectionDescriptor,
098                                               Object keys[], Vector columns)
099        {
100            if(cd.isAbstract() || cd.isInterface())
101            {
102                            logger.debug( "Skip foreign key build for MtoM, found abstract base class or interface " + cd.getClassNameOfObject() );
103                return;
104            }
105            StringBuffer buffer = new StringBuffer(256);
106            buildForeignKeyHeader(cd.getFullTableName(), buffer);
107    
108            for (int i = 0; i < keys.length; i++) {
109                String columnName = (String) keys[i];
110    
111                FieldDescriptor foreignColumn = cd.getPkFields()[i];
112                String foreignColumnName = foreignColumn.getPersistentField().getName();
113                buildReferenceForColumn(buffer, columnName, foreignColumnName);
114                FieldDescriptor fieldDescriptor = (FieldDescriptor)foreignColumn.clone();
115                fieldDescriptor.setColumnName(columnName);
116                columns.add(fieldDescriptor);
117            }
118            buffer.append("        </foreign-key>\n");
119    
120            addReferenceToTable(collectionDescriptor.getIndirectionTable(), buffer.toString());
121        }
122    
123        private void buildOneToManyReferences(ClassDescriptor cd, CollectionDescriptor collectionDescriptor) {
124            Vector foreignKeyIndices = collectionDescriptor.getForeignKeyFields();
125            ClassDescriptor foreignKeyClassDescriptor = this.repository.getDescriptorFor(collectionDescriptor.getItemClass());
126            buildForeignKey(cd, foreignKeyIndices, foreignKeyClassDescriptor);
127        }
128    
129    
130        private void buildOneToOneConstraints(ClassDescriptor classDescriptor) {
131            Vector referenceDescriptors = classDescriptor.getObjectReferenceDescriptors();
132            for (int i = 0; i < referenceDescriptors.size(); i++) {
133                ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) referenceDescriptors.get(i);
134    
135                Vector foreignKeyIndices = ord.getForeignKeyFields();
136                ClassDescriptor foreignClassDescriptor = this.repository.getDescriptorFor(ord.getItemClass());
137                buildForeignKey(foreignClassDescriptor, foreignKeyIndices, classDescriptor);
138            }
139        }
140    
141        private void buildForeignKey(ClassDescriptor foreignClassDescriptor, Vector foreignKeyIndices, ClassDescriptor classDescriptor) {
142    
143            if(classDescriptor.isAbstract() || classDescriptor.isInterface())
144            {
145                            logger.debug( "Skip foreign key build, found abstract base class or interface " + classDescriptor.getClassNameOfObject() );
146                return;
147            }
148            StringBuffer buffer = new StringBuffer(256);
149            buildForeignKeyHeader(foreignClassDescriptor.getFullTableName(), buffer);
150    
151            for (int i = 0; i < foreignKeyIndices.size(); i++) {
152                String columnName = null;
153                Object obj = foreignKeyIndices.get(i);
154    
155                if (obj instanceof Integer)
156                {
157                    int foreignKeyIndex = ((Integer) obj).intValue();
158                    columnName = classDescriptor.getFieldDescriptorByIndex(foreignKeyIndex).getColumnName();
159                }
160                else
161                {
162                        FieldDescriptor fld = classDescriptor.getFieldDescriptorByName((String) obj);
163                        if(fld == null)
164                        {
165                                                    logger.debug( "FieldDescriptor for foreign key parameter \n" + obj + " was not found in ClassDescriptor \n" + classDescriptor );
166                        }
167                        else columnName = fld.getColumnName();
168                }
169    
170                FieldDescriptor foreignColumn = foreignClassDescriptor.getPkFields()[i];
171                String foreignColumnName = foreignColumn.getColumnName();
172                buildReferenceForColumn(buffer, columnName, foreignColumnName);
173            }
174            buffer.append("        </foreign-key>\n");
175            addReferenceToTable(classDescriptor.getFullTableName(), buffer.toString());
176        }
177    
178        private void buildForeignKeyHeader(String foreignClassName, StringBuffer buffer) {
179            buffer.append("        <foreign-key foreignTable=\"");
180            buffer.append(foreignClassName);
181            buffer.append("\">\n");
182        }
183    
184        private void buildReferenceForColumn(StringBuffer buffer, String columnName, String foreignColumnName) {
185            buffer.append("            <reference local=\"");
186            buffer.append(columnName);
187            buffer.append("\" foreign=\"");
188            buffer.append(foreignColumnName);
189            buffer.append("\"/>\n");
190        }
191    
192    
193        private boolean isImplicitlyMapped(String tableName) {
194            Iterator classDescriptorIterator = repository.iterator();
195    
196            while (classDescriptorIterator.hasNext()) {
197                ClassDescriptor cd = (ClassDescriptor) classDescriptorIterator.next();
198                if (tableName.equals(cd.getFullTableName())) {
199                    return false;
200                }
201            }
202    
203            return true;
204        }
205    
206        private void addReferenceToTable(String tableName, String reference) {
207            Vector tableReferences = (Vector) this.foreignKeyVectors.get(tableName);
208            if (tableReferences == null) {
209                tableReferences = new Vector();
210                this.foreignKeyVectors.put(tableName, tableReferences);
211    
212            }
213            if (!tableReferences.contains(reference)) {
214                tableReferences.add(reference);
215            }
216        }
217    }