Source Code Cross Referenced for FiscalYearMakersDaoOjb.java in  » ERP-CRM-Financial » Kuali-Financial-System » org » kuali » module » chart » dao » ojb » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » ERP CRM Financial » Kuali Financial System » org.kuali.module.chart.dao.ojb 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2007 The Kuali Foundation.
0003:         * 
0004:         * Licensed under the Educational Community License, Version 1.0 (the "License");
0005:         * you may not use this file except in compliance with the License.
0006:         * You may obtain a copy of the License at
0007:         * 
0008:         * http://www.opensource.org/licenses/ecl1.php
0009:         * 
0010:         * Unless required by applicable law or agreed to in writing, software
0011:         * distributed under the License is distributed on an "AS IS" BASIS,
0012:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         * See the License for the specific language governing permissions and
0014:         * limitations under the License.
0015:         */
0016:        package org.kuali.module.chart.dao.ojb;
0017:
0018:        import java.lang.reflect.InvocationTargetException;
0019:        import java.sql.Date;
0020:        import java.util.ArrayList;
0021:        import java.util.Calendar;
0022:        import java.util.Collection;
0023:        import java.util.GregorianCalendar;
0024:        import java.util.HashMap;
0025:        import java.util.HashSet;
0026:        import java.util.Iterator;
0027:        import java.util.LinkedHashMap;
0028:        import java.util.Map;
0029:        import java.util.regex.Matcher;
0030:        import java.util.regex.Pattern;
0031:
0032:        import org.apache.commons.beanutils.PropertyUtils;
0033:        import org.apache.log4j.Logger;
0034:        import org.apache.ojb.broker.metadata.ClassDescriptor;
0035:        import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
0036:        import org.apache.ojb.broker.metadata.CollectionDescriptor;
0037:        import org.apache.ojb.broker.metadata.DescriptorRepository;
0038:        import org.apache.ojb.broker.metadata.MetadataManager;
0039:        import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
0040:        import org.apache.ojb.broker.query.Criteria;
0041:        import org.apache.ojb.broker.query.QueryByCriteria;
0042:        import org.apache.ojb.broker.query.ReportQueryByCriteria;
0043:        import org.kuali.core.dao.ojb.PlatformAwareDaoBaseOjb;
0044:        import org.kuali.core.service.DateTimeService;
0045:        import org.kuali.core.service.PersistenceStructureService;
0046:        import org.kuali.core.util.TransactionalServiceUtils;
0047:        import org.kuali.kfs.KFSConstants;
0048:        import org.kuali.kfs.KFSPropertyConstants;
0049:        import org.kuali.kfs.KFSConstants.BudgetConstructionConstants;
0050:        import org.kuali.kfs.bo.Options;
0051:        import org.kuali.module.chart.bo.AccountingPeriod;
0052:        import org.kuali.module.chart.bo.IcrAutomatedEntry;
0053:        import org.kuali.module.chart.bo.ObjectCode;
0054:        import org.kuali.module.chart.bo.OffsetDefinition;
0055:        import org.kuali.module.chart.bo.OrganizationReversion;
0056:        import org.kuali.module.chart.bo.OrganizationReversionDetail;
0057:        import org.kuali.module.chart.bo.SubObjCd;
0058:        import org.kuali.module.chart.dao.FiscalYearMakersCopyAction;
0059:        import org.kuali.module.chart.dao.FiscalYearMakersDao;
0060:        import org.kuali.module.chart.dao.FiscalYearMakersFieldChangeAction;
0061:        import org.kuali.module.chart.dao.FiscalYearMakersFilterAction;
0062:        import org.kuali.module.gl.bo.UniversityDate;
0063:        import org.kuali.module.labor.bo.BenefitsCalculation;
0064:        import org.kuali.module.labor.bo.LaborObject;
0065:        import org.kuali.module.labor.bo.PositionObjectBenefit;
0066:
0067:        /**
0068:         * This class...
0069:         */
0070:        public class FiscalYearMakersDaoOjb extends PlatformAwareDaoBaseOjb
0071:                implements  FiscalYearMakersDao {
0072:
0073:            /*
0074:             * These routines are designed to create rows for the next fiscal year for reference tables, based on the rows in those tables
0075:             * for the current fiscal year. The idea is to relieve people of the responsibility for typing in hundreds of new rows in a
0076:             * maintenance document, and to preclude having to auto-generate reference rows for x years in the future, maintaining them as
0077:             * things change. There are two modes used by routines in this module. (1) slash-and-burn: if any rows for the target year
0078:             * exist, they are deleted, and replaced with copies of the current year's rows (2) warm-and-fuzzy: any rows for the new year
0079:             * that already exist are left in place, and only those rows whose keys are missing in the new fiscal year are copied from the
0080:             * current year There are two versions of each method (using overloading). To get the slash-and_burn version, one uses the
0081:             * method where there is a second parameter, and passes its value as the static variable "replaceMode".
0082:             */
0083:
0084:            /* turn on the logger for the persistence broker */
0085:            private static Logger LOG = org.apache.log4j.Logger
0086:                    .getLogger(FiscalYearMakersDaoOjb.class);
0087:
0088:            private DateTimeService dateTimeService;
0089:            private PersistenceStructureService persistenceStructureService;
0090:
0091:            private UniversityDate universityDate;
0092:
0093:            /*
0094:             * these fields are used for setting up the UniversityDate data they are here because RI in the database requires that
0095:             * UniversityDate be treated just like any other table in fiscal year makers there will be one start for each month the code
0096:             * will adjust the year from the reference year these values are intended ONLY to reset the fiscal year beginning date
0097:             */
0098:            private Integer BeginYear;
0099:            private String FiscalYear;
0100:            private static final Integer ReferenceYear = 1900;
0101:            public static final GregorianCalendar START_JANUARY = new GregorianCalendar(
0102:                    ReferenceYear, Calendar.JANUARY, 1);
0103:            public static final GregorianCalendar START_FEBRUARY = new GregorianCalendar(
0104:                    ReferenceYear, Calendar.FEBRUARY, 1);
0105:            public static final GregorianCalendar START_MARCH = new GregorianCalendar(
0106:                    ReferenceYear, Calendar.MARCH, 1);
0107:            public static final GregorianCalendar START_APRIL = new GregorianCalendar(
0108:                    ReferenceYear, Calendar.APRIL, 1);
0109:            public static final GregorianCalendar START_MAY = new GregorianCalendar(
0110:                    ReferenceYear, Calendar.MAY, 1);
0111:            public static final GregorianCalendar START_JUNE = new GregorianCalendar(
0112:                    ReferenceYear, Calendar.JUNE, 1);
0113:            public static final GregorianCalendar START_JULY = new GregorianCalendar(
0114:                    ReferenceYear, Calendar.JULY, 1);
0115:            public static final GregorianCalendar START_AUGUST = new GregorianCalendar(
0116:                    ReferenceYear, Calendar.AUGUST, 1);
0117:            public static final GregorianCalendar START_SEPTEMBER = new GregorianCalendar(
0118:                    ReferenceYear, Calendar.SEPTEMBER, 1);
0119:            public static final GregorianCalendar START_OCTOBER = new GregorianCalendar(
0120:                    ReferenceYear, Calendar.OCTOBER, 1);
0121:            public static final GregorianCalendar START_NOVEMBER = new GregorianCalendar(
0122:                    ReferenceYear, Calendar.NOVEMBER, 1);
0123:            public static final GregorianCalendar START_DECEMBER = new GregorianCalendar(
0124:                    ReferenceYear, Calendar.DECEMBER, 1);
0125:            private GregorianCalendar fiscalYearStartDate;
0126:
0127:            public static final boolean replaceMode = true;
0128:
0129:            /**
0130:             * delete all the rows (if any) for the request year for all the classes in the ordered delete list the delete order is set so
0131:             * that referential integrity will not cause an exception: children first, then parents
0132:             * 
0133:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#deleteNewYearRows(java.lang.Integer)
0134:             */
0135:            public void deleteNewYearRows(Integer requestYear) {
0136:
0137:                for (Map.Entry<String, Class> classesToDelete : getDeleteOrder()
0138:                        .entrySet()) {
0139:                    Integer RequestYear = requestYear;
0140:                    if (laggingCopyCycle.contains(classesToDelete.getKey())) {
0141:                        // this object is copied from LAST PERIOD into the CURRENT PERIOD
0142:                        RequestYear = RequestYear - 1;
0143:                    }
0144:                    deleteNewYearRows(RequestYear, classesToDelete.getValue());
0145:                }
0146:                getPersistenceBrokerTemplate().clearCache();
0147:            }
0148:
0149:            /**
0150:             * this routine gets rid of existing rows for the request year + 1 for the parents of the child passed as a parameter it is uses
0151:             * when, for some classes, we want to create two years' worth of rows on each run
0152:             * 
0153:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#deleteYearAfterNewYearRowsForParents(java.lang.Integer, java.lang.Class)
0154:             */
0155:            public void deleteYearAfterNewYearRowsForParents(
0156:                    Integer RequestYear, Class childClass) {
0157:                RequestYear = RequestYear + 1;
0158:                // first we have to delete the child rows
0159:                deleteNewYearRows(RequestYear, childClass);
0160:                // now we loop through the delete order, and delete each parent in turn
0161:                for (Map.Entry<String, Class> classesToDelete : getDeleteOrder()
0162:                        .entrySet()) {
0163:                    // better compare the names just to be safe
0164:                    Class deleteClass = classesToDelete.getValue();
0165:                    if (isAParentOf(deleteClass.getName(), childClass)) {
0166:                        deleteNewYearRows(RequestYear, deleteClass);
0167:                    }
0168:                }
0169:            }
0170:
0171:            /**
0172:             * This method checks to see if a given child class is a parent of another class (denoted by a String)
0173:             * 
0174:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#isAParentOf(java.lang.String, java.lang.Class)
0175:             */
0176:            public boolean isAParentOf(String testClassName, Class childClass) {
0177:                ArrayList<Class> parentClasses = childParentMap.get(childClass
0178:                        .getName());
0179:                Iterator<Class> parents = parentClasses.iterator();
0180:                // we compare names to be safe
0181:                while (parents.hasNext()) {
0182:                    if (testClassName.compareTo(parents.next().getName()) == 0) {
0183:                        return true;
0184:                    }
0185:                }
0186:                return false;
0187:            }
0188:
0189:            /**
0190:             * this is the routine where you designate which objects should participate and whether they should use customized field setters
0191:             * or customized query filters the objects participating MUST match the object list configured in the XML
0192:             * 
0193:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#setUpRun(java.lang.Integer, boolean)
0194:             */
0195:            public LinkedHashMap<String, FiscalYearMakersCopyAction> setUpRun(
0196:                    Integer BaseYear, boolean replaceMode) {
0197:
0198:                // added October, 2007, to remove all OJB auto-update and auto-delete codes
0199:                // which would alter the delete and copy order set by the XML-encoded parent-child relationships
0200:                // this code changes the settings in memory for this run only
0201:                // this implies that fiscal year makers should run in its own container, with no
0202:                // other jobs which might depend on the auto-xxx settings.
0203:                turnOffCascades();
0204:
0205:                /***************************************************************************************************************************
0206:                 * AccountingPeriod *
0207:                 **************************************************************************************************************************/
0208:                FiscalYearMakersCopyAction copyActionAcctPrd = new FiscalYearMakersCopyAction() {
0209:                    FiscalYearMakersFieldChangeAction<AccountingPeriod> fieldAction = new FiscalYearMakersFieldChangeAction<AccountingPeriod>() {
0210:                        public void customFieldChangeMethod(
0211:                                Integer currentFiscalYear,
0212:                                Integer newFiscalYear,
0213:                                AccountingPeriod candidateRow) {
0214:                            // there is a four-character year in periods 01 through 12, and a two-character
0215:                            // year in period 13. we need to update these for the new year. instead of
0216:                            // hard-wiring in the periods, we just try to make both changes
0217:                            Integer startThisYear = currentFiscalYear - 1;
0218:                            String startThisYearString = startThisYear
0219:                                    .toString();
0220:                            String currentFiscalYearString = currentFiscalYear
0221:                                    .toString();
0222:                            String newFiscalYearString = newFiscalYear
0223:                                    .toString();
0224:                            String nameString = candidateRow
0225:                                    .getUniversityFiscalPeriodName();
0226:                            candidateRow
0227:                                    .setUniversityFiscalPeriodName(updateStringField(
0228:                                            newFiscalYearString,
0229:                                            currentFiscalYearString,
0230:                                            candidateRow
0231:                                                    .getUniversityFiscalPeriodName()));
0232:                            candidateRow
0233:                                    .setUniversityFiscalPeriodName(updateStringField(
0234:                                            currentFiscalYearString,
0235:                                            startThisYearString,
0236:                                            candidateRow
0237:                                                    .getUniversityFiscalPeriodName()));
0238:                            candidateRow
0239:                                    .setUniversityFiscalPeriodName(updateTwoDigitYear(
0240:                                            newFiscalYearString.substring(2, 4),
0241:                                            currentFiscalYearString.substring(
0242:                                                    2, 4),
0243:                                            candidateRow
0244:                                                    .getUniversityFiscalPeriodName()));
0245:                            candidateRow
0246:                                    .setUniversityFiscalPeriodName(updateTwoDigitYear(
0247:                                            currentFiscalYearString.substring(
0248:                                                    2, 4),
0249:                                            startThisYearString.substring(2, 4),
0250:                                            candidateRow
0251:                                                    .getUniversityFiscalPeriodName()));
0252:                            // we have to update the ending date, increasing it by one year
0253:                            candidateRow
0254:                                    .setUniversityFiscalPeriodEndDate(addYearToDate(candidateRow
0255:                                            .getUniversityFiscalPeriodEndDate()));
0256:                            // we set all of the fiscal period status codes to "closed" before the
0257:                            // start of the coming year
0258:                            candidateRow
0259:                                    .setUniversityFiscalPeriodStatusCode(KFSConstants.ACCOUNTING_PERIOD_STATUS_OPEN);
0260:                        }
0261:                    };
0262:
0263:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0264:                        MakersMethods<AccountingPeriod> makersMethod = new MakersMethods<AccountingPeriod>();
0265:                        makersMethod.makeMethod(AccountingPeriod.class,
0266:                                baseYear, replaceMode, fieldAction);
0267:                    }
0268:                };
0269:                addCopyAction(AccountingPeriod.class, copyActionAcctPrd);
0270:
0271:                /***************************************************************************************************************************
0272:                 * BenefitsCalculation *
0273:                 **************************************************************************************************************************/
0274:                FiscalYearMakersCopyAction copyActionBenCalc = new FiscalYearMakersCopyAction() {
0275:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0276:                        MakersMethods<BenefitsCalculation> makersMethod = new MakersMethods<BenefitsCalculation>();
0277:                        makersMethod.makeMethod(BenefitsCalculation.class,
0278:                                baseYear, replaceMode);
0279:                    }
0280:                };
0281:                addCopyAction(BenefitsCalculation.class, copyActionBenCalc);
0282:
0283:                /***************************************************************************************************************************
0284:                 * IcrAutomatedEntry *
0285:                 **************************************************************************************************************************/
0286:                FiscalYearMakersCopyAction copyActionIcrAuto = new FiscalYearMakersCopyAction() {
0287:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0288:                        MakersMethods<IcrAutomatedEntry> makersMethod = new MakersMethods<IcrAutomatedEntry>();
0289:                        makersMethod.makeMethod(IcrAutomatedEntry.class,
0290:                                baseYear, replaceMode);
0291:                    }
0292:                };
0293:                addCopyAction(IcrAutomatedEntry.class, copyActionIcrAuto);
0294:
0295:                /***************************************************************************************************************************
0296:                 * LaborObject *
0297:                 **************************************************************************************************************************/
0298:                FiscalYearMakersCopyAction copyActionLabObj = new FiscalYearMakersCopyAction() {
0299:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0300:                        MakersMethods<LaborObject> makersMethod = new MakersMethods<LaborObject>();
0301:                        makersMethod.makeMethod(LaborObject.class, baseYear,
0302:                                replaceMode);
0303:                    }
0304:                };
0305:                addCopyAction(LaborObject.class, copyActionLabObj);
0306:
0307:                /***************************************************************************************************************************
0308:                 * ObjectCode *
0309:                 **************************************************************************************************************************/
0310:                FiscalYearMakersCopyAction copyActionObjectCode = new FiscalYearMakersCopyAction() {
0311:                    FiscalYearMakersFilterAction filterObjectCode = new FiscalYearMakersFilterAction() {
0312:                        public Criteria customCriteriaMethod() {
0313:                            // this method allows us to add any filters needed on the current
0314:                            // year rows--for example, we might not want any marked deleted.
0315:                            // for ObjectCode, we don't want any invalid objects--UNLESS they
0316:                            // are the dummy object used in budget construction
0317:                            Criteria criteriaID = new Criteria();
0318:                            criteriaID
0319:                                    .addEqualTo(
0320:                                            KFSPropertyConstants.FINANCIAL_OBJECT_ACTIVE_CODE,
0321:                                            true);
0322:                            Criteria criteriaBdg = new Criteria();
0323:                            criteriaBdg
0324:                                    .addEqualTo(
0325:                                            KFSPropertyConstants.FINANCIAL_OBJECT_CODE,
0326:                                            BudgetConstructionConstants.OBJECT_CODE_2PLG);
0327:                            criteriaID.addOrCriteria(criteriaBdg);
0328:                            return criteriaID;
0329:                        }
0330:                    };
0331:
0332:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0333:                        MakersMethods<ObjectCode> makersMethod = new MakersMethods<ObjectCode>();
0334:                        makersMethod.makeMethod(ObjectCode.class, baseYear,
0335:                                replaceMode, filterObjectCode);
0336:                    }
0337:                };
0338:                addCopyAction(ObjectCode.class, copyActionObjectCode);
0339:
0340:                /***************************************************************************************************************************
0341:                 * OffsetDefinition *
0342:                 **************************************************************************************************************************/
0343:                FiscalYearMakersCopyAction copyActionOffDef = new FiscalYearMakersCopyAction() {
0344:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0345:                        MakersMethods<OffsetDefinition> makersMethod = new MakersMethods<OffsetDefinition>();
0346:                        makersMethod.makeMethod(OffsetDefinition.class,
0347:                                baseYear, replaceMode);
0348:                    }
0349:                };
0350:                addCopyAction(OffsetDefinition.class, copyActionOffDef);
0351:
0352:                /***************************************************************************************************************************
0353:                 * Options *
0354:                 **************************************************************************************************************************/
0355:                FiscalYearMakersCopyAction copyActionOptions = new FiscalYearMakersCopyAction() {
0356:                    FiscalYearMakersFieldChangeAction<Options> fieldAction = new FiscalYearMakersFieldChangeAction<Options>() {
0357:                        public void customFieldChangeMethod(
0358:                                Integer currentFiscalYear,
0359:                                Integer newFiscalYear, Options candidateRow) {
0360:                            // some ineffeciency in set up is traded for easier maintenance
0361:                            Integer currentYearStart = currentFiscalYear - 1;
0362:                            String endNextYearString = newFiscalYear.toString();
0363:                            String endCurrentYearString = currentFiscalYear
0364:                                    .toString();
0365:                            String startCurrentYearString = currentYearStart
0366:                                    .toString();
0367:                            candidateRow
0368:                                    .setUniversityFiscalYearStartYr(currentFiscalYear);
0369:                            // here we allow for a substring of XXXX-YYYY as well as XXXX and YYYY
0370:                            String holdIt = updateStringField(
0371:                                    endNextYearString, endCurrentYearString,
0372:                                    candidateRow.getUniversityFiscalYearName());
0373:                            candidateRow
0374:                                    .setUniversityFiscalYearName(updateStringField(
0375:                                            endCurrentYearString,
0376:                                            startCurrentYearString, holdIt));
0377:                        }
0378:                    };
0379:
0380:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0381:                        MakersMethods<Options> makersMethod = new MakersMethods<Options>();
0382:                        makersMethod.makeMethod(Options.class, baseYear,
0383:                                replaceMode, fieldAction);
0384:                    }
0385:                };
0386:                addCopyAction(Options.class, copyActionOptions);
0387:
0388:                /***************************************************************************************************************************
0389:                 * OrganizationReversion *
0390:                 **************************************************************************************************************************/
0391:                FiscalYearMakersCopyAction copyActionOrgRev = new FiscalYearMakersCopyAction() {
0392:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0393:                        MakersMethods<OrganizationReversion> makersMethod = new MakersMethods<OrganizationReversion>();
0394:                        makersMethod.makeMethod(OrganizationReversion.class,
0395:                                baseYear, replaceMode);
0396:                    }
0397:                };
0398:                addCopyAction(OrganizationReversion.class, copyActionOrgRev);
0399:
0400:                /***************************************************************************************************************************
0401:                 * OrganizationReversionDetail *
0402:                 **************************************************************************************************************************/
0403:                FiscalYearMakersCopyAction copyActionOrgRevDtl = new FiscalYearMakersCopyAction() {
0404:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0405:                        MakersMethods<OrganizationReversionDetail> makersMethod = new MakersMethods<OrganizationReversionDetail>();
0406:                        makersMethod.makeMethod(
0407:                                OrganizationReversionDetail.class, baseYear,
0408:                                replaceMode);
0409:                    }
0410:                };
0411:                addCopyAction(OrganizationReversionDetail.class,
0412:                        copyActionOrgRevDtl);
0413:
0414:                /***************************************************************************************************************************
0415:                 * PositionObjectBenefit *
0416:                 **************************************************************************************************************************/
0417:                FiscalYearMakersCopyAction copyActionPosObjBen = new FiscalYearMakersCopyAction() {
0418:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0419:                        MakersMethods<PositionObjectBenefit> makersMethod = new MakersMethods<PositionObjectBenefit>();
0420:                        makersMethod.makeMethod(PositionObjectBenefit.class,
0421:                                baseYear, replaceMode);
0422:                    }
0423:                };
0424:                addCopyAction(PositionObjectBenefit.class, copyActionPosObjBen);
0425:
0426:                /***************************************************************************************************************************
0427:                 * SubObjCd *
0428:                 **************************************************************************************************************************/
0429:                FiscalYearMakersCopyAction copyActionSubObjCd = new FiscalYearMakersCopyAction() {
0430:                    /*
0431:                     * not for phase II FiscalYearMakersFilterAction filterSubObjectCode = new FiscalYearMakersFilterAction() { public
0432:                     * Criteria customCriteriaMethod() { // this method allows us to add any filters needed on the current // year rows--for
0433:                     * example, we might not want any marked deleted. // for SubObjectCode, we don't want any invalid objects Criteria
0434:                     * criteriaID = new Criteria(); criteriaID.addEqualTo(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_ACTIVE_INDICATOR,true);
0435:                     * return criteriaID; } };
0436:                     */
0437:                    public void copyMethod(Integer baseYear, boolean replaceMode) {
0438:                        MakersMethods<SubObjCd> makersMethod = new MakersMethods<SubObjCd>();
0439:                        makersMethod.makeMethod(SubObjCd.class, baseYear,
0440:                                replaceMode);
0441:                        /*
0442:                         * not for phase II replaceMode, filterSubObjectCode);
0443:                         */
0444:                    }
0445:                };
0446:                addCopyAction(SubObjCd.class, copyActionSubObjCd);
0447:                /***************************************************************************************************************************
0448:                 * University Date *
0449:                 **************************************************************************************************************************/
0450:                FiscalYearMakersCopyAction copyActionUniversityDate = new FiscalYearMakersCopyAction() {
0451:                    public void copyMethod(Integer currentFiscalYear,
0452:                            boolean replaceMode) {
0453:                        // this is the routine to call to build the new year's university date tables
0454:                        // the start month of the fiscal year is passed in with the reference date
0455:                        // the year in the reference date is updated to the year of the current fiscal
0456:                        // year
0457:                        // if we start on January 1, the year of the beginning date should in fact be
0458:                        // the year FOLLOWING the currentFiscalYear. Otherwise, the first date of the
0459:                        // new fiscal year falls within the year in which the current fiscal year ends.
0460:                        if (fiscalYearStartDate.equals(START_JANUARY)) {
0461:                            currentFiscalYear = currentFiscalYear + 1;
0462:                        }
0463:                        GregorianCalendar newYearStartDate = new GregorianCalendar(
0464:                                fiscalYearStartDate.get(Calendar.YEAR),
0465:                                fiscalYearStartDate.get(Calendar.MONTH),
0466:                                fiscalYearStartDate.get(Calendar.DAY_OF_MONTH));
0467:                        int yearDifference = currentFiscalYear
0468:                                - newYearStartDate.get(Calendar.YEAR);
0469:                        newYearStartDate.add(Calendar.YEAR, yearDifference);
0470:                        makeUniversityDate(newYearStartDate);
0471:                    }
0472:                };
0473:                addCopyAction(UniversityDate.class, copyActionUniversityDate);
0474:                /** ***************************************************************** */
0475:                //
0476:                // this is the routine that sets up and and returns the runtime call order
0477:                // addCopyAction must have been called on every object to be copied
0478:                // before this routine is called
0479:                return (getCopyOrder());
0480:            }
0481:
0482:            /*******************************************************************************************************************************
0483:             * University Date Database Access *
0484:             ******************************************************************************************************************************/
0485:
0486:            /**
0487:             * this is the only routine that simply replaces what is there, if anything but, we have to do a delete--otherwise, we can get
0488:             * an optimistic locking exception when we try to store a new row on top of something already in the database. we will delete by
0489:             * fiscal year. the accounting period is assumed to correspond to the month, with the month of the start date being the first
0490:             * period and the month of the last day of the fiscal year being the twelfth. the fiscal year tag is always the year of the
0491:             * ending date of the fiscal year
0492:             * 
0493:             * @param fiscalYearStartDate
0494:             */
0495:            public void makeUniversityDate(GregorianCalendar fiscalYearStartDate) {
0496:                // loop through a year's worth of dates for the new year
0497:                GregorianCalendar shunivdate = new GregorianCalendar(
0498:                        fiscalYearStartDate.get(Calendar.YEAR),
0499:                        fiscalYearStartDate.get(Calendar.MONTH),
0500:                        fiscalYearStartDate.get(Calendar.DAY_OF_MONTH));
0501:                // set up the end date
0502:                GregorianCalendar enddate = new GregorianCalendar(
0503:                        fiscalYearStartDate.get(Calendar.YEAR),
0504:                        fiscalYearStartDate.get(Calendar.MONTH),
0505:                        fiscalYearStartDate.get(Calendar.DAY_OF_MONTH));
0506:                enddate.add(Calendar.MONTH, 12);
0507:                enddate.add(Calendar.DAY_OF_MONTH, -1);
0508:                // the fiscal year is always the year of the ending date of the fiscal year
0509:                Integer nextFiscalYear = (Integer) enddate.get(Calendar.YEAR);
0510:                // get rid of anything already there
0511:                deleteNewYearRows(nextFiscalYear, UniversityDate.class);
0512:                // initialize the period variables
0513:                int period = 1;
0514:                String periodString = String.format("%02d", period);
0515:                int compareMonth = shunivdate.get(Calendar.MONTH);
0516:                int currentMonth = shunivdate.get(Calendar.MONTH);
0517:                // loop through the dates until we hit the last one
0518:                while (!(shunivdate.equals(enddate))) {
0519:                    // TODO: temporary debugging code
0520:                    LOG.debug(String.format("\n%s %s %tD:%tT", nextFiscalYear,
0521:                            periodString, shunivdate, shunivdate));
0522:                    // store these values--we will update whatever is there
0523:                    UniversityDate universityDate = new UniversityDate();
0524:                    universityDate.setUniversityFiscalYear(nextFiscalYear);
0525:                    universityDate.setUniversityDate(new Date(shunivdate
0526:                            .getTimeInMillis()));
0527:                    universityDate
0528:                            .setUniversityFiscalAccountingPeriod(periodString);
0529:                    getPersistenceBrokerTemplate().store(universityDate);
0530:                    // next day
0531:                    shunivdate.add(Calendar.DAY_OF_MONTH, 1);
0532:                    // does this kick us into a new month and therefore a new accounting period?
0533:                    compareMonth = shunivdate.get(Calendar.MONTH);
0534:                    if (currentMonth != compareMonth) {
0535:                        period = period + 1;
0536:                        periodString = String.format("%02d", period);
0537:                        currentMonth = compareMonth;
0538:                        // TODO: debugging code
0539:                        if (period == 13) {
0540:                            LOG
0541:                                    .warn("the date comparison is not working properly");
0542:                            break;
0543:                        }
0544:                    }
0545:                }
0546:                // store the end date
0547:                UniversityDate universityDate = new UniversityDate();
0548:                universityDate.setUniversityFiscalYear(nextFiscalYear);
0549:                universityDate.setUniversityDate(new Date(shunivdate
0550:                        .getTimeInMillis()));
0551:                universityDate
0552:                        .setUniversityFiscalAccountingPeriod(periodString);
0553:                getPersistenceBrokerTemplate().store(universityDate);
0554:                // TODO: temporary debugging code
0555:                LOG.debug(String.format("\n%s %s %tD:%tT\n", nextFiscalYear,
0556:                        periodString, shunivdate, shunivdate));
0557:            }
0558:
0559:            /**
0560:             * This method is a private utility method to perform some date operations
0561:             * 
0562:             * @param inDate
0563:             * @return date with one year added
0564:             */
0565:            private java.sql.Date addYearToDate(Date inDate) {
0566:                // OK. Apparently the JDK is trying to offer a generic calendar to all
0567:                // users. java.sql.Date (which extends java.util.Date) is trying to create
0568:                // a DB independent date value. both have settled on milliseconds since
0569:                // midnight, January 1, 1970, Greenwich Mean Time. This value is then
0570:                // "normalized" to the local time zone when it is converted to a date. But,
0571:                // the constructors are based on the original millisecond value, and this
0572:                // value is recoverable via the "time" methods in the classes.
0573:                GregorianCalendar currentCalendarDate = new GregorianCalendar();
0574:                // create a calendar object with no values set
0575:                currentCalendarDate.clear();
0576:                // set the calendar values using the "standard" millisecond value
0577:                // this should represent the java.sql.Date value in the local time zone
0578:                currentCalendarDate.setTimeInMillis(inDate.getTime());
0579:                // add a year to the SQL date
0580:                currentCalendarDate.add(GregorianCalendar.YEAR, 1);
0581:                // return the "standardized" value of the orginal date + 1 year
0582:                return (new Date(currentCalendarDate.getTimeInMillis()));
0583:            }
0584:
0585:            /**
0586:             * This method builds and returns a hash set containing the composite key string of rows that already exist in the relevant
0587:             * table for the new fiscal year (we assume all the members of the composite key are strings except the fiscal year.
0588:             * 
0589:             * @param requestYear
0590:             * @param businessObject
0591:             * @return a hash set with the composite key string of rows
0592:             */
0593:            private HashSet<String> buildMapOfExistingKeys(Integer requestYear,
0594:                    Class businessObject) {
0595:
0596:                Criteria criteriaID = new Criteria();
0597:                criteriaID.addEqualTo(
0598:                        KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
0599:                        requestYear);
0600:                // get space for the map
0601:                HashSet<String> returnHash = new HashSet<String>(
0602:                        hashObjectSize(businessObject, criteriaID));
0603:                // set up to query for the key fields
0604:                String[] attrib = { "" }; // we'll reorient this pointer when we know the size
0605:                attrib = (String[]) persistenceStructureService.getPrimaryKeys(
0606:                        businessObject).toArray(attrib);
0607:                ReportQueryByCriteria queryID = new ReportQueryByCriteria(
0608:                        businessObject, attrib, criteriaID);
0609:                Iterator keyValues = getPersistenceBrokerTemplate()
0610:                        .getReportQueryIteratorByQuery(queryID);
0611:                while (keyValues.hasNext()) {
0612:                    Object[] keyObject = (Object[]) keyValues.next();
0613:                    // we assume the fiscal year is an integer, and the other keys are strings
0614:                    // OJB always returns BigDecimal for a number (including fiscal year),
0615:                    // but we apply toString directly to the object, so we should be OK.
0616:                    StringBuffer concatKey = new StringBuffer(keyObject[0]
0617:                            .toString());
0618:                    for (int i = 1; i < keyObject.length; i++) {
0619:                        concatKey = concatKey.append(keyObject[i]);
0620:                    }
0621:                    returnHash.add(concatKey.toString());
0622:                }
0623:                return returnHash;
0624:            }
0625:
0626:            /**
0627:             * This method gets rid of all the rows in the new fiscal year
0628:             * 
0629:             * @param requestYear
0630:             * @param businessObject
0631:             */
0632:            private void deleteNewYearRows(Integer requestYear,
0633:                    Class businessObject) {
0634:                LOG.warn(String.format("\ndeleting %s for %d", businessObject
0635:                        .getName(), requestYear));
0636:                Criteria criteriaID = new Criteria();
0637:                criteriaID.addEqualTo(
0638:                        KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
0639:                        requestYear);
0640:                QueryByCriteria queryID = new QueryByCriteria(businessObject,
0641:                        criteriaID);
0642:                getPersistenceBrokerTemplate().deleteByQuery(queryID);
0643:                LOG.warn(String.format("\n rows for %d deleted", requestYear));
0644:                getPersistenceBrokerTemplate().clearCache();
0645:            }
0646:
0647:            /**
0648:             * we look up the fiscal year for today's date, and return it we return 0 if nothing is found
0649:             * 
0650:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#fiscalYearFromToday()
0651:             */
0652:            public Integer fiscalYearFromToday() {
0653:
0654:                Integer currentFiscalYear = new Integer(0);
0655:                Date lookUpDate = dateTimeService.getCurrentSqlDateMidnight();
0656:                Criteria criteriaID = new Criteria();
0657:                criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_DATE,
0658:                        lookUpDate);
0659:                String[] attrb = { KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR };
0660:                ReportQueryByCriteria queryID = new ReportQueryByCriteria(
0661:                        UniversityDate.class, attrb, criteriaID);
0662:                Iterator resultRow = getPersistenceBrokerTemplate()
0663:                        .getReportQueryIteratorByQuery(queryID);
0664:                if (resultRow.hasNext()) {
0665:                    currentFiscalYear = (Integer) ((Number) ((Object[]) TransactionalServiceUtils
0666:                            .retrieveFirstAndExhaustIterator(resultRow))[0])
0667:                            .intValue();
0668:                }
0669:                // TODO:
0670:                LOG.debug(String.format(
0671:                        "\nreturned from fiscalYearFromToday: %d",
0672:                        currentFiscalYear));
0673:                // TODO:
0674:                return currentFiscalYear;
0675:            }
0676:
0677:            // @@TODO:
0678:            // this code is duplicated from GenesisDaoOjb. we don't need to overload the
0679:            // hashObjectSize method here
0680:            // if this thing catches on, maybe we should make the hashObjectSize method
0681:            // a public method in a service
0682:            //
0683:            /**
0684:             * This method determines a hash capacity given by a hash size this corresponds to a little more than the default load factor of
0685:             * .75 a rehash supposedly occurs when the actual number of elements exceeds (load factor)*capacity N rows < .75 capacity ==>
0686:             * capacity > 4N/3 or 1.3333N. We add a little slop.
0687:             * 
0688:             * @param hashSize
0689:             * @return recommended hash capacity based on hash size
0690:             */
0691:            private Integer hashCapacity(Integer hashSize) {
0692:
0693:                Double tempValue = hashSize.floatValue() * (1.45);
0694:                return (Integer) tempValue.intValue();
0695:            }
0696:
0697:            /**
0698:             * This method calculates a given hash set size based on objects retrieved or 1 if no objects
0699:             * 
0700:             * @param classID
0701:             * @param criteriaID
0702:             * @return hash set size
0703:             */
0704:            private Integer hashObjectSize(Class classID, Criteria criteriaID) {
0705:                // this counts all rows
0706:                String[] selectList = new String[] { "COUNT(*)" };
0707:                ReportQueryByCriteria queryID = new ReportQueryByCriteria(
0708:                        classID, selectList, criteriaID);
0709:                Iterator resultRows = getPersistenceBrokerTemplate()
0710:                        .getReportQueryIteratorByQuery(queryID);
0711:                while (resultRows.hasNext()) {
0712:                    return (hashCapacity(((Number) ((Object[]) TransactionalServiceUtils
0713:                            .retrieveFirstAndExhaustIterator(resultRows))[0])
0714:                            .intValue()));
0715:                }
0716:                return (new Integer(1));
0717:            }
0718:
0719:            /**
0720:             * this routine is reminiscent of computing in 1970, when disk space was scarce and every byte was fraught with meaning. some
0721:             * fields are captions and titles, and they contain things like the fiscal year. for the new year, we have to update these
0722:             * substrings in place, so they don't have to be updated by hand to display correct information in the application. we use the
0723:             * regular expression utilities in java
0724:             * 
0725:             * @param newYearString
0726:             * @param oldYearString
0727:             * @param currentField
0728:             * @return the updated string
0729:             */
0730:            private String updateStringField(String newYearString,
0731:                    String oldYearString, String currentField) {
0732:                Pattern pattern = Pattern.compile(oldYearString);
0733:                Matcher matcher = pattern.matcher(currentField);
0734:                return matcher.replaceAll(newYearString);
0735:            }
0736:
0737:            /**
0738:             * this routine is provided to update string fields which contain two-digit years that need to be updated for display. it is
0739:             * very specific, but it's necessary. "two-digit year" means the two numeric characters preceded by a non-numeric character.
0740:             * 
0741:             * @param newYear
0742:             * @param oldYear
0743:             * @param currentString
0744:             * @return the updated string for a two digit year
0745:             */
0746:            private String updateTwoDigitYear(String newYear, String oldYear,
0747:                    String currentString) {
0748:                // group 1 is the bounded by the outermost set of parentheses
0749:                // group 2 is the first inner set
0750:                // group 3 is the second inner set--a two-digit year at the beginning of the line
0751:                String regExpString = "(([^0-9]{1}" + oldYear + ")|^("
0752:                        + oldYear + "))";
0753:                Pattern pattern = Pattern.compile(regExpString);
0754:                Matcher matcher = pattern.matcher(currentString);
0755:                // start looking for a match
0756:                boolean matched = matcher.find();
0757:                if (!matched) {
0758:                    // just return if nothing is found
0759:                    return currentString;
0760:                }
0761:                // we found something
0762:                // we have to process it
0763:                String returnString = currentString;
0764:                StringBuffer outString = new StringBuffer();
0765:                // is there a match at the beginning of the line (a match with group 3)?
0766:                if (matcher.group(3) != null) {
0767:                    // there is a two-digit-year string at the beginning of the line
0768:                    // we want to replace it
0769:                    matcher.appendReplacement(outString, newYear);
0770:                    // find the next match if there is one
0771:                    matched = matcher.find();
0772:                }
0773:                while (matched) {
0774:                    // the new string will no longer match with group 3
0775:                    // if there is still a match, it will be with group 2
0776:                    // now we have to prefix the new year string with the same
0777:                    // non-numeric character as the next match (hyphen, space, whatever)
0778:                    String newYearString = matcher.group(2).substring(0, 1)
0779:                            + newYear;
0780:                    matcher.appendReplacement(outString, newYearString);
0781:                    matched = matcher.find();
0782:                }
0783:                // dump whatever detritus is left into the new string
0784:                matcher.appendTail(outString);
0785:                return outString.toString();
0786:            }
0787:
0788:            public void setDateTimeService(DateTimeService dateTimeService) {
0789:                this .dateTimeService = dateTimeService;
0790:            }
0791:
0792:            public void setPersistenceStructureService(
0793:                    PersistenceStructureService persistenceStructureService) {
0794:                this .persistenceStructureService = persistenceStructureService;
0795:            }
0796:
0797:            public void setFiscalYearStartDate(
0798:                    GregorianCalendar fiscalYearStartDate) {
0799:                // this routine can be used to reset the default start date for the
0800:                // fiscal year
0801:                this .fiscalYearStartDate = fiscalYearStartDate;
0802:            }
0803:
0804:            /**
0805:             * This class is a generic class to pass in types to the generic routines
0806:             */
0807:            private class MakersMethods<T> {
0808:                // this is the signature used for an object that requires no
0809:                // special processing
0810:                private void makeMethod(Class ojbMappedClass,
0811:                        Integer currentFiscalYear, boolean replaceMode) {
0812:                    FiscalYearMakersFieldChangeAction changeAction = null;
0813:                    FiscalYearMakersFilterAction filterAction = null;
0814:                    makeMethod(ojbMappedClass, currentFiscalYear, replaceMode,
0815:                            changeAction, filterAction);
0816:                }
0817:
0818:                // this is the signature used for an object which has special
0819:                // filter criteria in the WHERE clause
0820:                private void makeMethod(Class ojbMappedClass,
0821:                        Integer currentFiscalYear, boolean replaceMode,
0822:                        FiscalYearMakersFilterAction filterAction) {
0823:                    FiscalYearMakersFieldChangeAction changeAction = null;
0824:                    makeMethod(ojbMappedClass, currentFiscalYear, replaceMode,
0825:                            changeAction, filterAction);
0826:                }
0827:
0828:                // this is the signature used for an object which requires changes
0829:                // to fields other than KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR
0830:                private void makeMethod(Class ojbMappedClass,
0831:                        Integer currentFiscalYear, boolean replaceMode,
0832:                        FiscalYearMakersFieldChangeAction changeAction) {
0833:                    FiscalYearMakersFilterAction filterAction = null;
0834:                    makeMethod(ojbMappedClass, currentFiscalYear, replaceMode,
0835:                            changeAction, filterAction);
0836:                }
0837:
0838:                /**
0839:                 * this is the signature used for an object which has both special filter criteria and required changes to additional fields
0840:                 * 
0841:                 * @param ojbMappedClass
0842:                 * @param currentFiscalYear
0843:                 * @param replaceMode
0844:                 * @param changeAction
0845:                 * @param filterAction
0846:                 */
0847:                private void makeMethod(Class ojbMappedClass,
0848:                        Integer currentFiscalYear, boolean replaceMode,
0849:                        FiscalYearMakersFieldChangeAction changeAction,
0850:                        FiscalYearMakersFilterAction filterAction) {
0851:                    if (laggingCopyCycle.contains(ojbMappedClass.getName())) {
0852:                        // this object is copied from LAST PERIOD into the CURRENT PERIOD
0853:                        currentFiscalYear = currentFiscalYear - 1;
0854:                    }
0855:                    if (replaceMode) {
0856:                        // we will replace any new year rows that exist
0857:                        try {
0858:                            genericSlashAndBurn(ojbMappedClass,
0859:                                    currentFiscalYear, changeAction,
0860:                                    filterAction);
0861:                        } catch (IllegalAccessException ex) {
0862:                            ex.printStackTrace();
0863:                            RuntimeException newEx = new RuntimeException(
0864:                                    String.format("\n failed for %s",
0865:                                            ojbMappedClass.getName()));
0866:                            throw (newEx);
0867:                        } catch (InvocationTargetException ex) {
0868:                            ex.printStackTrace();
0869:                            RuntimeException newEx = new RuntimeException(
0870:                                    String.format("\n failed for %s",
0871:                                            ojbMappedClass.getName()));
0872:                            throw (newEx);
0873:                        } catch (NoSuchFieldException ex) {
0874:                            ex.printStackTrace();
0875:                            RuntimeException newEx = new RuntimeException(
0876:                                    String.format("\n failed for %s",
0877:                                            ojbMappedClass.getName()));
0878:                            throw (newEx);
0879:                        } catch (NoSuchMethodException ex) {
0880:                            ex.printStackTrace();
0881:                            RuntimeException newEx = new RuntimeException(
0882:                                    String.format("\n failed for %s",
0883:                                            ojbMappedClass.getName()));
0884:                            throw (newEx);
0885:                        }
0886:                    } else {
0887:                        // we will only add any new year rows that do not already exist
0888:                        try {
0889:                            genericWarmAndFuzzy(ojbMappedClass,
0890:                                    currentFiscalYear, changeAction,
0891:                                    filterAction);
0892:                        } catch (IllegalAccessException ex) {
0893:                            ex.printStackTrace();
0894:                            RuntimeException newEx = new RuntimeException(
0895:                                    String.format("\n failed for %s",
0896:                                            ojbMappedClass.getName()));
0897:                            throw (newEx);
0898:                        } catch (InvocationTargetException ex) {
0899:                            ex.printStackTrace();
0900:                            RuntimeException newEx = new RuntimeException(
0901:                                    String.format("\n failed for %s",
0902:                                            ojbMappedClass.getName()));
0903:                            throw (newEx);
0904:                        } catch (NoSuchFieldException ex) {
0905:                            ex.printStackTrace();
0906:                            RuntimeException newEx = new RuntimeException(
0907:                                    String.format("\n failed for %s",
0908:                                            ojbMappedClass.getName()));
0909:                            throw (newEx);
0910:                        } catch (NoSuchMethodException ex) {
0911:                            ex.printStackTrace();
0912:                            RuntimeException newEx = new RuntimeException(
0913:                                    String.format("\n failed for %s",
0914:                                            ojbMappedClass.getName()));
0915:                            throw (newEx);
0916:                        }
0917:                    }
0918:                }
0919:
0920:                /**
0921:                 * routine to build rows for the coming fiscal year, replacing any that already exist
0922:                 * 
0923:                 * @param ojbMappedClass
0924:                 * @param currentFiscalYear
0925:                 * @param changeAction
0926:                 * @param filterAction
0927:                 * @throws NoSuchFieldException
0928:                 * @throws NoSuchMethodException
0929:                 * @throws IllegalAccessException
0930:                 * @throws InvocationTargetException
0931:                 */
0932:                private void genericSlashAndBurn(Class ojbMappedClass,
0933:                        Integer currentFiscalYear,
0934:                        FiscalYearMakersFieldChangeAction changeAction,
0935:                        FiscalYearMakersFilterAction filterAction)
0936:                        throws NoSuchFieldException, NoSuchMethodException,
0937:                        IllegalAccessException, InvocationTargetException {
0938:                    Integer newFiscalYear = currentFiscalYear + 1;
0939:                    String requestYearString = newFiscalYear.toString();
0940:                    Integer rowsRead = new Integer(0);
0941:                    Integer rowsWritten = new Integer(0);
0942:                    Integer rowsFailingRI = new Integer(0);
0943:                    LOG.warn(String.format("\n copying %s from %d to %d",
0944:                            ojbMappedClass.getName(), currentFiscalYear,
0945:                            newFiscalYear));
0946:                    // some parents have auto-update other than none in their relationship
0947:                    // with a child. in this case, the child's rows were written when the
0948:                    // parent's rows were written, and writing the child's rows again now will
0949:                    // cause an optimistic locking exception. we have already deleted any rows
0950:                    // the existed for the target year, so any rows there now have been written
0951:                    // by an auto-update, are therefore correct, and should not be recopied.
0952:                    // we check here for the existence of rows.
0953:                    Criteria checkCriteria = new Criteria();
0954:                    checkCriteria.addEqualTo(
0955:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
0956:                            newFiscalYear);
0957:                    if (hashObjectSize(ojbMappedClass, checkCriteria) > 0) {
0958:                        LOG
0959:                                .warn(String
0960:                                        .format(
0961:                                                "\n    %s rows for %d exist already from an auto-update",
0962:                                                ojbMappedClass.getName(),
0963:                                                newFiscalYear));
0964:                        return;
0965:                    }
0966:                    // build the list of parent keys already copied to the new year (if any)
0967:                    // the appropriate child foreign keys must exist in each parent
0968:                    // if they do not, this means that the parent row in the current fiscal
0969:                    // year was filtered out in the copy to the new fiscal year, and therefore
0970:                    // the corresponding child rows should not be copied either
0971:                    ParentKeyChecker<T> parentKeyChecker = new ParentKeyChecker<T>(
0972:                            ojbMappedClass, newFiscalYear);
0973:                    // get the rows from the previous year
0974:                    Criteria criteriaID = new Criteria();
0975:                    criteriaID.addEqualTo(
0976:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
0977:                            currentFiscalYear);
0978:                    if (filterAction != null) {
0979:                        criteriaID.addAndCriteria(filterAction
0980:                                .customCriteriaMethod());
0981:                    }
0982:                    QueryByCriteria queryID = new QueryByCriteria(
0983:                            ojbMappedClass, criteriaID);
0984:                    Iterator<T> oldYearObjects = getPersistenceBrokerTemplate()
0985:                            .getIteratorByQuery(queryID);
0986:                    while (oldYearObjects.hasNext()) {
0987:                        rowsRead = rowsRead + 1;
0988:                        T ourBO = oldYearObjects.next();
0989:                        // we have to set the fiscal year and the version number
0990:                        setCommonFields(ourBO, newFiscalYear);
0991:                        // we also set all the custom fields (which presumably are NOT
0992:                        // keys). they may be foreign keys into the parent, and if they
0993:                        // are the parent check should reflect the new year values.
0994:                        if (!(changeAction == null)) {
0995:                            changeAction.customFieldChangeMethod(
0996:                                    currentFiscalYear, newFiscalYear, ourBO);
0997:                        }
0998:                        // check to see if the row exists in all the parents
0999:                        if (!parentKeyChecker.childRowSatisfiesRI(ourBO)) {
1000:                            rowsFailingRI = rowsFailingRI + 1;
1001:                            continue;
1002:                        }
1003:                        // store the result
1004:                        getPersistenceBrokerTemplate().store(ourBO);
1005:                        rowsWritten = rowsWritten + 1;
1006:                    }
1007:                    LOG
1008:                            .warn(String
1009:                                    .format(
1010:                                            "\n%s:\n%d read = %d\n%d written = %d\nfailed RI = %d",
1011:                                            ojbMappedClass.getName(),
1012:                                            currentFiscalYear, rowsRead,
1013:                                            newFiscalYear, rowsWritten,
1014:                                            rowsFailingRI));
1015:                    // if a parent has inverse foreign keys into the child, when we copy the
1016:                    // parent the child rows will be in the cache as well of auto-retrieve is
1017:                    // true and proxy is false. then, when we build the child rows later these
1018:                    // cached rows cause an OJB "optimistic locking" error. we therefore remove
1019:                    // any cached rows after all the rows for each object have been copied to
1020:                    // the database
1021:                    getPersistenceBrokerTemplate().clearCache();
1022:                }
1023:
1024:                /**
1025:                 * routine to only add, not replace, rows for the coming fiscal year
1026:                 * 
1027:                 * @param ojbMappedClass
1028:                 * @param currentFiscalYear
1029:                 * @param changeAction
1030:                 * @param filterAction
1031:                 * @throws NoSuchFieldException
1032:                 * @throws NoSuchMethodException
1033:                 * @throws IllegalAccessException
1034:                 * @throws InvocationTargetException
1035:                 */
1036:                private void genericWarmAndFuzzy(Class ojbMappedClass,
1037:                        Integer currentFiscalYear,
1038:                        FiscalYearMakersFieldChangeAction changeAction,
1039:                        FiscalYearMakersFilterAction filterAction)
1040:                        throws NoSuchFieldException, NoSuchMethodException,
1041:                        IllegalAccessException, InvocationTargetException {
1042:                    Integer newFiscalYear = currentFiscalYear + 1;
1043:                    String requestYearString = newFiscalYear.toString();
1044:                    Integer rowsRead = new Integer(0);
1045:                    Integer rowsWritten = new Integer(0);
1046:                    Integer rowsFailingRI = new Integer(0);
1047:                    LOG.warn(String.format("\n copying %s from %d to %d",
1048:                            ojbMappedClass.getName(), currentFiscalYear,
1049:                            newFiscalYear));
1050:                    // build the list of parent keys already copied to the new year (if any)
1051:                    // the appropriate child foreign keys must exist in each parent
1052:                    // if they do not, this means that the parent row in the current fiscal
1053:                    // year was filtered out in the copy to the new fiscal year, and therefore
1054:                    // the corresponding child rows should not be copied either
1055:                    ParentKeyChecker<T> parentKeyChecker = new ParentKeyChecker<T>(
1056:                            ojbMappedClass, newFiscalYear);
1057:                    // get the hash set of keys of objects which already exist for the new
1058:                    // year and will not be replaced
1059:                    HashSet existingKeys = buildMapOfExistingKeys(
1060:                            newFiscalYear, ojbMappedClass);
1061:                    //
1062:                    String[] keyFields = { "" }; // reorient this pointer when we know the size
1063:                    keyFields = (String[]) persistenceStructureService
1064:                            .getPrimaryKeys(ojbMappedClass).toArray(keyFields);
1065:                    // get the rows from the previous year
1066:                    Criteria criteriaID = new Criteria();
1067:                    criteriaID.addEqualTo(
1068:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
1069:                            currentFiscalYear);
1070:                    if (filterAction != null) {
1071:                        criteriaID.addAndCriteria(filterAction
1072:                                .customCriteriaMethod());
1073:                    }
1074:                    QueryByCriteria queryID = new QueryByCriteria(
1075:                            ojbMappedClass, criteriaID);
1076:                    Iterator<T> oldYearObjects = getPersistenceBrokerTemplate()
1077:                            .getIteratorByQuery(queryID);
1078:                    while (oldYearObjects.hasNext()) {
1079:                        rowsRead = rowsRead + 1;
1080:                        StringBuffer hashChecker = new StringBuffer(
1081:                                requestYearString);
1082:                        T ourBO = oldYearObjects.next();
1083:                        for (int i = 1; i < keyFields.length; i++) {
1084:                            // 10/2007 some primary keys may not be strings:
1085:                            // we assume that the same value of a non-string will be converted to a string consistently
1086:                            hashChecker.append(PropertyUtils.getSimpleProperty(
1087:                                    ourBO, keyFields[i].toString()));
1088:                        }
1089:                        // TODO:
1090:                        // if (rowsRead%1007 == 1)
1091:                        // {
1092:                        // LOG.warn(String.format("\n%s: row %d hash key = %s\n",
1093:                        // ojbMappedClass.getName(),
1094:                        // rowsRead,hashChecker.toString()));
1095:                        // }
1096:                        // TODO:
1097:                        if (existingKeys.contains(hashChecker.toString())) {
1098:                            continue;
1099:                        }
1100:                        // we have to set the fiscal year and the version number
1101:                        setCommonFields(ourBO, newFiscalYear);
1102:                        // we also set all the custom fields (which presumably are NOT
1103:                        // keys). they may be foreign keys into the parent, and if they
1104:                        // are the parent check should reflect the new year values.
1105:                        if (!(changeAction == null)) {
1106:                            changeAction.customFieldChangeMethod(
1107:                                    currentFiscalYear, newFiscalYear, ourBO);
1108:                        }
1109:                        // check to see if the row exists in all the parents
1110:                        if (!parentKeyChecker.childRowSatisfiesRI(ourBO)) {
1111:                            rowsFailingRI = rowsFailingRI + 1;
1112:                            continue;
1113:                        }
1114:                        // store the result
1115:                        getPersistenceBrokerTemplate().store(ourBO);
1116:                        rowsWritten = rowsWritten + 1;
1117:                    }
1118:                    LOG
1119:                            .warn(String
1120:                                    .format(
1121:                                            "\n%s:\n%d read = %d\n%d written = %d\nfailed RI = %d",
1122:                                            ojbMappedClass.getName(),
1123:                                            currentFiscalYear, rowsRead,
1124:                                            newFiscalYear, rowsWritten,
1125:                                            rowsFailingRI));
1126:                    // if a parent has inverse foreign keys into the child, when we copy the
1127:                    // parent the child rows will be in the cache as well of auto-retrieve is
1128:                    // true and proxy is false. then, when we build the child rows later these
1129:                    // cached rows cause an OJB "optimistic locking" error. we therefore remove
1130:                    // any cached rows after all the rows for each object have been copied to
1131:                    // the database
1132:                    getPersistenceBrokerTemplate().clearCache();
1133:                }
1134:
1135:                /**
1136:                 * the compiler doesn't know the class of the object at this point, so we can't use the methods contained in the object the
1137:                 * compiler would not be able to find them PropertyUtils uses reflection at run time to find the correct set method
1138:                 * 
1139:                 * @param ourBO
1140:                 * @param newFiscalYear
1141:                 * @throws NoSuchFieldException
1142:                 * @throws IllegalAccessException
1143:                 * @throws NoSuchMethodException
1144:                 * @throws InvocationTargetException
1145:                 */
1146:                private void setCommonFields(T ourBO, Integer newFiscalYear)
1147:                        throws NoSuchFieldException, IllegalAccessException,
1148:                        NoSuchMethodException, InvocationTargetException {
1149:
1150:                    // set the fiscal year
1151:                    PropertyUtils.setSimpleProperty(ourBO,
1152:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
1153:                            newFiscalYear);
1154:                    // set the version number (to avoid running up the meter as the
1155:                    // years fly by)
1156:                    // the version number is a field of the base class common to all
1157:                    // persistable business objects
1158:                    PropertyUtils.setSimpleProperty(ourBO,
1159:                            KFSPropertyConstants.VERSION_NUMBER, new Long(0));
1160:                    // fld =
1161:                    // ourBO.getClass().getSuperclass().getDeclaredField(KFSPropertyConstants.VERSION_NUMBER);
1162:                    // fld.setAccessible(true);
1163:                    // fld.set(ourBO, (Object) (new Long(0)));
1164:                }
1165:
1166:            };
1167:
1168:            /*******************************************************************************************************************************
1169:             * This section handles RI *
1170:             ******************************************************************************************************************************/
1171:
1172:            // list of objects (from XML) that need to be copied for a new fiscal period
1173:            private HashMap<String, Class> makerObjectsList = new HashMap<String, Class>(
1174:                    25);
1175:            // list of objects that need to be copied coupled with a list of
1176:            // other objects to be copied on which they have an RI dependency
1177:            private HashMap<String, ArrayList<Class>> childParentMap = new HashMap<String, ArrayList<Class>>(
1178:                    25);
1179:            // this is the source of the above map. it is loaded from XML through a set
1180:            // method. if there is no RI, initializing it here should make it empty.
1181:            // since the code works for things that have no parents, the code should
1182:            // still work if there is no RI and everybody is an orphan
1183:            private HashMap<String, Class[]> childParentArrayMap = new HashMap<String, Class[]>(
1184:                    25);
1185:            // this is the list of copy actions, one for each object to be copied
1186:            // it is built in the setUp method
1187:            private HashMap<String, FiscalYearMakersCopyAction> classSpecificActions;
1188:            // this is the set of objects which are copied from LAST YEAR to the
1189:            // current year, instead of from the CURRENT YEAR to the next year
1190:            private HashSet<String> laggingCopyCycle = new HashSet<String>(20);
1191:
1192:            /**
1193:             * the list of all the fiscal year makers objects
1194:             * 
1195:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#getMakerObjectsList()
1196:             */
1197:            public HashMap<String, Class> getMakerObjectsList() {
1198:                return this .makerObjectsList;
1199:            }
1200:
1201:            /**
1202:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#setMakerObjectsList(java.util.HashMap)
1203:             */
1204:            public void setMakerObjectsList(
1205:                    HashMap<String, Class> makerObjectsList) {
1206:                this .makerObjectsList = makerObjectsList;
1207:                classSpecificActions = new HashMap<String, FiscalYearMakersCopyAction>(
1208:                        makerObjectsList.size());
1209:            }
1210:
1211:            /**
1212:             * this list of child/parent relationships for the fiscal year makers objects
1213:             * 
1214:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#getChildParentMap()
1215:             */
1216:            public HashMap<String, ArrayList<Class>> getChildParentMap() {
1217:                return this .childParentMap;
1218:            }
1219:
1220:            /**
1221:             * Spring did not do the conversions of the XML necessary to create HashMap<String,ArrayList<Class>>. (We got an ArrayList of
1222:             * strings.) Since everything was written for an ArrayList, we will convert the Class[] version (which Spring can handle) to an
1223:             * ArrayList here. (There is a way to get a "list" view of an array, and this view is an ArrayList. But we will create a new
1224:             * one, which will be extensible, unlike the view.)
1225:             * 
1226:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#setChildParentArrayMap(java.util.HashMap)
1227:             */
1228:            public void setChildParentArrayMap(
1229:                    HashMap<String, Class[]> childParentArrayMap) {
1230:                this .childParentArrayMap = childParentArrayMap;
1231:
1232:                childParentMap = new HashMap<String, ArrayList<Class>>(
1233:                        childParentArrayMap.size());
1234:                for (Map.Entry<String, Class[]> fromMap : childParentArrayMap
1235:                        .entrySet()) {
1236:                    Class[] sourceArray = fromMap.getValue();
1237:                    ArrayList<Class> targetList = new ArrayList<Class>(
1238:                            sourceArray.length);
1239:                    for (int i = 0; i < sourceArray.length; i++) {
1240:                        targetList.add(i, sourceArray[i]);
1241:                    }
1242:                    childParentMap.put(fromMap.getKey(), targetList);
1243:                }
1244:            }
1245:
1246:            /**
1247:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#setLaggingCopyCycle(java.util.HashSet)
1248:             */
1249:            public void setLaggingCopyCycle(HashSet<String> laggingCopyCycle) {
1250:                this .laggingCopyCycle = laggingCopyCycle;
1251:            }
1252:
1253:            /**
1254:             * this is a map of the execution order for the classes the class name is followed by a class ID which contains the copy method
1255:             * it is called at the end of setUp, and uses the classSpecificActions map built during setUp to get the copy action required
1256:             * 
1257:             * @return
1258:             * @throws RuntimeException
1259:             */
1260:            private LinkedHashMap<String, FiscalYearMakersCopyAction> getCopyOrder()
1261:                    throws RuntimeException {
1262:                // throw an exception if the lists don't match
1263:                if (findChildParentXMLErrors()) {
1264:                    RuntimeException ex = new RuntimeException(
1265:                            "\nXML class list and parent-child list are incompatible");
1266:                    throw (ex);
1267:                }
1268:                LinkedHashMap<String, FiscalYearMakersCopyAction> returnMap = new LinkedHashMap<String, FiscalYearMakersCopyAction>(
1269:                        makerObjectsList.size());
1270:                // make a copy of the child/parent map so we can iterate until all the
1271:                // elements in the copy have been added to the copy order list.
1272:                // we have to do a deep copy, not just a clone
1273:                HashMap<String, ArrayList<Class>> childParentWkngMap = new HashMap<String, ArrayList<Class>>(
1274:                        childParentMap.size());
1275:                for (Map.Entry<String, ArrayList<Class>> sourceMap : childParentMap
1276:                        .entrySet()) {
1277:                    ArrayList<Class> srceList = sourceMap.getValue();
1278:                    ArrayList<Class> targetList = new ArrayList<Class>(srceList
1279:                            .size());
1280:                    Iterator<Class> copyIt = srceList.iterator();
1281:                    while (copyIt.hasNext()) {
1282:                        targetList.add(copyIt.next());
1283:                    }
1284:                    childParentWkngMap.put(sourceMap.getKey(), targetList);
1285:                }
1286:                // first, add to the list all makers objects which aren't children of anything
1287:                for (Map.Entry<String, Class> orphans : makerObjectsList
1288:                        .entrySet()) {
1289:                    if (!childParentWkngMap.containsKey(orphans.getKey())) {
1290:                        // the object is supposed to be a call back action
1291:                        // we will fill it in later
1292:                        // for now, we set it to null
1293:                        returnMap.put(orphans.getKey(), classSpecificActions
1294:                                .get(orphans.getKey()));
1295:                    }
1296:                }
1297:                // since a child of parent X should not be a parent of a parent of
1298:                // parent X, this should work. if at any pass through the loop
1299:                // we don't add to the copy order, there has to be a circular
1300:                // relationship, and we will trow an exception
1301:                Integer numberAdded = new Integer(1);
1302:                // we need to keep track of parentChildMap elements to be removed
1303:                // we cannot do that in the for loop without a "concurrent modification exception"
1304:                HashSet<String> removeList = new HashSet<String>(
1305:                        childParentWkngMap.size());
1306:                while (!childParentWkngMap.isEmpty()) {
1307:                    // nothing was found on the last pass, so nothing will be found on
1308:                    // any of the subsequent passes either
1309:                    if (numberAdded.intValue() == 0) {
1310:                        RuntimeException ex = new RuntimeException(
1311:                                "child/parent XML map contains circular relationships");
1312:                        throw (ex);
1313:                    }
1314:                    numberAdded = 0;
1315:                    for (Map.Entry<String, ArrayList<Class>> parentList : childParentWkngMap
1316:                            .entrySet()) {
1317:                        Iterator<Class> parents = parentList.getValue()
1318:                                .iterator();
1319:                        if (!parents.hasNext()) {
1320:                            // all the parents have been added
1321:                            // add the child to the returnMap and delete this entry
1322:                            numberAdded = numberAdded + 1;
1323:                            // we will detect a null copy action later and throw an exception
1324:                            returnMap.put(parentList.getKey(),
1325:                                    classSpecificActions.get(parentList
1326:                                            .getKey()));
1327:                            removeList.add(parentList.getKey());
1328:                            continue;
1329:                        }
1330:                        // loop trough the parent list and try to add the parents to the
1331:                        // copy order map
1332:                        while (parents.hasNext()) {
1333:                            Class nextParent = parents.next();
1334:                            // check to see whether this parent is a child of someone else
1335:                            if ((!childParentWkngMap.containsKey(nextParent
1336:                                    .getName()))
1337:                                    || (removeList.contains(nextParent
1338:                                            .getName()))) {
1339:                                // we will detect a null copy action later and throw an exception
1340:                                returnMap.put(nextParent.getName(),
1341:                                        classSpecificActions.get(nextParent
1342:                                                .getName()));
1343:                                numberAdded = numberAdded + 1;
1344:                                parents.remove();
1345:                            }
1346:                        }
1347:                    }
1348:                    // now we want to remove the parents whose children are already
1349:                    // in the delete list from the parentChildWkngMap
1350:                    Iterator<String> goners = removeList.iterator();
1351:                    while (goners.hasNext()) {
1352:                        childParentWkngMap.remove(goners.next());
1353:                    }
1354:                    removeList.clear();
1355:                }
1356:                // now we have to verify that everything in the map has a valid copy action
1357:                boolean screwedUpCopyOrder = false;
1358:                for (Map.Entry<String, FiscalYearMakersCopyAction> copyActions : returnMap
1359:                        .entrySet()) {
1360:                    if (copyActions.getValue() == null) {
1361:                        LOG.error(String.format(
1362:                                "\n%s has no valid copy action", copyActions
1363:                                        .getKey()));
1364:                        screwedUpCopyOrder = true;
1365:                    }
1366:                }
1367:                if (screwedUpCopyOrder) {
1368:                    RuntimeException rex = new RuntimeException(
1369:                            "\ninvalid copy order list");
1370:                    throw (rex);
1371:                }
1372:                return returnMap;
1373:            }
1374:
1375:            /**
1376:             * this list specifies the delete order for the objects in the list
1377:             * 
1378:             * @return
1379:             * @throws RuntimeException
1380:             */
1381:            private LinkedHashMap<String, Class> getDeleteOrder()
1382:                    throws RuntimeException {
1383:                // throw an exception if the lists don't match
1384:                if (findChildParentXMLErrors()) {
1385:                    RuntimeException ex = new RuntimeException(
1386:                            "\nXML class list and parent-child list are incompatible");
1387:                    throw (ex);
1388:                }
1389:                // this should only be called once
1390:                // we'll rebuild it on each call
1391:                // first we need a parent child map
1392:                HashMap<String, ArrayList<Class>> parentChildMap = createParentChildMap();
1393:                // children must be deleted before parents
1394:                LinkedHashMap<String, Class> returnList = new LinkedHashMap<String, Class>(
1395:                        makerObjectsList.size());
1396:                // first, add to the list all makers objects which aren't parents of anything
1397:                for (Map.Entry<String, Class> childless : makerObjectsList
1398:                        .entrySet()) {
1399:                    if (!parentChildMap.containsKey(childless.getKey())) {
1400:                        returnList
1401:                                .put(childless.getKey(), childless.getValue());
1402:                    }
1403:                }
1404:                // since a child of parent X cannot be a parent to a parent of X,
1405:                // this loop should work. we continue until the list is empty
1406:                // if there is a pass in which nothing is written, this implies a
1407:                // circular relationship and we will throw an exception
1408:                Integer numberAdded = new Integer(1);
1409:                // we need to keep track of parentChildMap elements to be removed
1410:                // we cannot do that in the for loop without a "concurrent modification exception"
1411:                HashSet<String> removeList = new HashSet<String>(parentChildMap
1412:                        .size());
1413:                while (!parentChildMap.isEmpty()) {
1414:                    // if the last loop didn't do anything, then the
1415:                    // next pass won't either. it means the parent/child map has a circular
1416:                    // relationship
1417:                    if (numberAdded.intValue() == 0) {
1418:                        RuntimeException ex = new RuntimeException(
1419:                                "child/parent XML map contains circular relationships");
1420:                        throw (ex);
1421:                    }
1422:                    // do another iteration over the shrunken list, and see what else
1423:                    // we can stick in the delete order list
1424:                    numberAdded = 0;
1425:                    for (Map.Entry<String, ArrayList<Class>> parents : parentChildMap
1426:                            .entrySet()) {
1427:                        // try to add the children to the return list of classes
1428:                        Iterator<Class> children = parents.getValue()
1429:                                .iterator();
1430:                        // if there are no children, we have already added them all
1431:                        // it is safe to simply add the parent to the map and remove the row
1432:                        if (!children.hasNext()) {
1433:                            numberAdded = numberAdded + 1;
1434:                            returnList.put(parents.getKey(), makerObjectsList
1435:                                    .get(parents.getKey()));
1436:                            // add this to the remove list
1437:                            removeList.add(parents.getKey());
1438:                            continue;
1439:                        }
1440:                        while (children.hasNext()) {
1441:                            Class nextChild = children.next();
1442:                            // if the child is already in the map, remove the child
1443:                            if (returnList.containsKey(nextChild.getName())) {
1444:                                numberAdded = numberAdded + 1;
1445:                                children.remove();
1446:                                continue;
1447:                            }
1448:                            // if the child is not a parent of anything still in the list
1449:                            // we add it to the list and remove it
1450:                            if ((!parentChildMap.containsKey(nextChild
1451:                                    .getName()))
1452:                                    || (removeList
1453:                                            .contains(nextChild.getName()))) {
1454:                                numberAdded = numberAdded + 1;
1455:                                returnList.put(nextChild.getName(), nextChild);
1456:                                children.remove();
1457:                            }
1458:                        }
1459:                    }
1460:                    // now we want to remove the parents whose children are already
1461:                    // in the delete list from the parentChildMap
1462:                    Iterator<String> goners = removeList.iterator();
1463:                    while (goners.hasNext()) {
1464:                        parentChildMap.remove(goners.next());
1465:                    }
1466:                    removeList.clear();
1467:                }
1468:                return returnList;
1469:            }
1470:
1471:            /**
1472:             * this is an "action", or callback, class it allows us to build an instance at run time for each child, after the parents have
1473:             * already been built for the coming fiscal period (1) for each parent, store the values that exist for the child's foreign keys
1474:             * (2) provide a method that can be called by each child row read from the base period. the method will check that the child has
1475:             * the proper RI relationship with at least one row from each parent.
1476:             */
1477:            public class ParentKeyChecker<C> {
1478:                private ParentClass<C>[] parentClassList = null;
1479:
1480:                /**
1481:                 * Constructs a FiscalYearMakersDaoOjb.java.
1482:                 * 
1483:                 * @param childClass
1484:                 * @param requestYear
1485:                 */
1486:                public ParentKeyChecker(Class childClass, Integer requestYear) {
1487:                    String testString = childClass.getName();
1488:                    if (childParentMap.containsKey(testString)) {
1489:                        ArrayList<Class> parentClasses = childParentMap
1490:                                .get(testString);
1491:                        parentClassList = (ParentClass<C>[]) new ParentClass[parentClasses
1492:                                .size()];
1493:                        for (int i = 0; i < parentClasses.size(); i++) {
1494:                            parentClassList[i] = new ParentClass<C>(
1495:                                    parentClasses.get(i), childClass,
1496:                                    requestYear);
1497:                        }
1498:                    }
1499:                }
1500:
1501:                /**
1502:                 * This method...
1503:                 * 
1504:                 * @param ourBO
1505:                 * @return true if child row satisfies referential integrity
1506:                 * @throws IllegalAccessException
1507:                 * @throws InvocationTargetException
1508:                 * @throws NoSuchMethodException
1509:                 */
1510:                public boolean childRowSatisfiesRI(C ourBO)
1511:                        throws IllegalAccessException,
1512:                        InvocationTargetException, NoSuchMethodException {
1513:                    boolean returnValue = true;
1514:                    if (parentClassList == null) {
1515:                        return returnValue;
1516:                    }
1517:                    for (int i = 0; i < parentClassList.length; i++) {
1518:                        returnValue = returnValue
1519:                                && parentClassList[i].isInParent(ourBO);
1520:                    }
1521:                    return returnValue;
1522:                }
1523:            };
1524:
1525:            /**
1526:             * this class is used to construct a parent key hashmap, and provide a method to verify that a business object of type C matches
1527:             * on its foreign key fields with the parent
1528:             */
1529:            public class ParentClass<C> {
1530:                private String[] childKeyFields;
1531:                private String[] parentKeyFields;
1532:                private HashSet<String> parentKeys = new HashSet<String>(1);
1533:
1534:                /**
1535:                 * Constructs a FiscalYearMakersDaoOjb.java. the constructor will initialize the key hashmap for this parent object it will
1536:                 * also get the foreign key fields from the persistence data structure (the assumption is that the fields names returned are
1537:                 * the same in both the parent class and the child class). try to set this up so that if the parent/child relationship does
1538:                 * not exist in OJB, we can issue a warning message and go on, and all the methods will still behave properly
1539:                 * 
1540:                 * @param parentClass
1541:                 * @param childClass
1542:                 * @param requestYear
1543:                 */
1544:                public ParentClass(Class parentClass, Class childClass,
1545:                        Integer requestYear) {
1546:                    // fill in the key field names
1547:                    // TODO: fix this--we need the child class as well as the parentClass
1548:                    ReturnedPair<String[], String[]> keyArrays = fetchForeignKeysToParent(
1549:                            childClass, parentClass);
1550:                    childKeyFields = keyArrays.getFirst();
1551:                    parentKeyFields = keyArrays.getSecond();
1552:                    if (childKeyFields != null) {
1553:                        // build a query to get the keys already added to the parent
1554:                        Criteria criteriaID = new Criteria();
1555:                        criteriaID.addEqualTo(KFSConstants.UNIV_FISCAL_YR,
1556:                                requestYear);
1557:                        ReportQueryByCriteria queryID = new ReportQueryByCriteria(
1558:                                parentClass, parentKeyFields, criteriaID, true);
1559:                        // build a hash set of the keys in the parent
1560:                        parentKeys = new HashSet<String>(hashCapacity(queryID));
1561:                        Iterator parentRows = getPersistenceBrokerTemplate()
1562:                                .getReportQueryIteratorByQuery(queryID);
1563:                        while (parentRows.hasNext()) {
1564:                            parentKeys.add(buildKeyString((Object[]) parentRows
1565:                                    .next()));
1566:                        }
1567:                    }
1568:                }
1569:
1570:                /**
1571:                 * This method...
1572:                 * 
1573:                 * @param ourBO
1574:                 * @return
1575:                 * @throws IllegalAccessException
1576:                 * @throws InvocationTargetException
1577:                 * @throws NoSuchMethodException
1578:                 */
1579:                private String buildChildTestKey(C ourBO)
1580:                        throws IllegalAccessException,
1581:                        InvocationTargetException, NoSuchMethodException {
1582:                    StringBuffer returnKey = new StringBuffer("");
1583:                    // we will convert all the keys to strings
1584:                    for (int i = 0; i < childKeyFields.length; i++) {
1585:                        returnKey.append(PropertyUtils.getProperty(ourBO,
1586:                                childKeyFields[i].toString()));
1587:                    }
1588:                    return returnKey.toString();
1589:                }
1590:
1591:                /**
1592:                 * method to test whether a key of the child row matches one in parent
1593:                 * 
1594:                 * @param ourBO
1595:                 * @return
1596:                 * @throws IllegalAccessException
1597:                 * @throws InvocationTargetException
1598:                 * @throws NoSuchMethodException
1599:                 */
1600:                public boolean isInParent(C ourBO)
1601:                        throws IllegalAccessException,
1602:                        InvocationTargetException, NoSuchMethodException {
1603:                    if (childKeyFields == null) {
1604:                        return false;
1605:                    }
1606:                    return (parentKeys.contains(buildChildTestKey(ourBO)));
1607:                }
1608:
1609:            }
1610:
1611:            /*******************************************************************************************************************************
1612:             * private methods
1613:             ******************************************************************************************************************************/
1614:
1615:            /**
1616:             * This method takes an object to copy and a copy action and places it in our action list
1617:             * 
1618:             * @param objectToCopy
1619:             * @param copyAction
1620:             */
1621:            private void addCopyAction(Class objectToCopy,
1622:                    FiscalYearMakersCopyAction copyAction) {
1623:                classSpecificActions.put(objectToCopy.getName(), copyAction);
1624:            }
1625:
1626:            /**
1627:             * This method... we always assume the first key is the fiscal year OJB returns a BigDecimal for this (it's a numeric field, and
1628:             * in some databases--notably Oracle--every numeric field is stored as a number)
1629:             * 
1630:             * @param inKeys
1631:             * @return a string of keys from the array
1632:             */
1633:            private String buildKeyString(Object[] inKeys) {
1634:                StringBuffer stringBuilder = new StringBuffer();
1635:
1636:                // stringBuilder.append(((Integer)((BigDecimal) inKeys[0]).intValue()).toString());
1637:                // for (int i = 1; i < inKeys.length; i++)
1638:                // {
1639:                // stringBuilder.append((String)inKeys[i]);
1640:                // }
1641:                //
1642:                // when the parent rows were cached (not yet written to the data base), the
1643:                // loop commented out above gave a java.lang.String cast exception.
1644:                // this seems to indicate that BigDecimal is returned only from Oracle, and
1645:                // not from cached objects. we therefore made the assumption that toString
1646:                // will work properly for every type of field we are likely to encounter
1647:                for (int i = 0; i < inKeys.length; i++) {
1648:                    stringBuilder.append(inKeys[i].toString());
1649:                }
1650:                return stringBuilder.toString();
1651:            }
1652:
1653:            /**
1654:             * This method creates a HashMap of parent->child relationships
1655:             * 
1656:             * @return HashMap that contains a parent key with children as an array
1657:             */
1658:            private HashMap<String, ArrayList<Class>> createParentChildMap() {
1659:                HashMap<String, ArrayList<Class>> returnMap = new HashMap<String, ArrayList<Class>>(
1660:                        makerObjectsList.size());
1661:                // we've checked that every child has a list of unique parents
1662:                // so, all we have to do is read the list of children and reverse
1663:                // the map
1664:                for (Map.Entry<String, ArrayList<Class>> childMap : childParentMap
1665:                        .entrySet()) {
1666:                    String childName = childMap.getKey();
1667:                    Class childClass = makerObjectsList.get(childName);
1668:                    ArrayList parentArray = childMap.getValue();
1669:                    for (int j = 0; j < parentArray.size(); j++) {
1670:                        String parentName = ((Class) parentArray.get(j))
1671:                                .getName();
1672:                        if (returnMap.containsKey(parentName)) {
1673:                            ArrayList childArray = returnMap.get(parentName);
1674:                            childArray.add(childClass);
1675:                        } else {
1676:                            ArrayList childArray = new ArrayList(3);
1677:                            childArray.add(childClass);
1678:                            returnMap.put(parentName, childArray);
1679:                        }
1680:                    }
1681:                }
1682:                return returnMap;
1683:            }
1684:
1685:            /**
1686:             * this routine looks for two types of errors (1) a child or parent is NOT in the makers object list (fatal) from XML (2) some
1687:             * of the child's parents are listed more than once (warning)
1688:             * 
1689:             * @return true if there are problems in the XML data
1690:             */
1691:            private boolean findChildParentXMLErrors() {
1692:
1693:                boolean problemsInXML = false;
1694:                for (Map.Entry<String, ArrayList<Class>> childMap : childParentMap
1695:                        .entrySet()) {
1696:                    String childName = childMap.getKey();
1697:                    ArrayList parentList = childMap.getValue();
1698:                    ArrayList removeList = new ArrayList(parentList.size());
1699:                    // check for a problem child
1700:                    if (!makerObjectsList.containsKey(childName)) {
1701:                        problemsInXML = true;
1702:                        LOG
1703:                                .error(String
1704:                                        .format(
1705:                                                "\nchild %s is not in the fiscal period copy list",
1706:                                                childName));
1707:                    }
1708:                    for (int i = 0; i < parentList.size(); i++) {
1709:                        String parentName = ((Class) parentList.get(i))
1710:                                .getName();
1711:                        if (!makerObjectsList.containsKey(parentName)) {
1712:                            problemsInXML = true;
1713:                            LOG
1714:                                    .error(String
1715:                                            .format(
1716:                                                    "\nparent %s of child %s is not in the fiscal period copy list",
1717:                                                    parentName, childName));
1718:                        }
1719:                        for (int j = i + 1; j < parentList.size(); j++) {
1720:                            String anotherParent = ((Class) parentList.get(j))
1721:                                    .getName();
1722:                            if (parentName.compareTo(anotherParent) == 0) {
1723:                                // we have a duplicate--add it to the drop list
1724:                                LOG
1725:                                        .warn(String
1726:                                                .format(
1727:                                                        "\nchild %s has parent %s listed twice in XML\n",
1728:                                                        childName,
1729:                                                        anotherParent));
1730:
1731:                                removeList.add(j);
1732:                            }
1733:                        }
1734:                        // get rid of the duplicate elements
1735:                        for (int k = 0; k < removeList.size(); k++) {
1736:                            parentList.remove(removeList.get(k));
1737:                        }
1738:                    }
1739:                }
1740:                return problemsInXML;
1741:            }
1742:
1743:            /**
1744:             * we can have two kinds of relationships in Kuali (1) a child contains a foreign key to the primary keys of its RI parent (a
1745:             * 1:1 relationship in OJB). The parent object is coded in XML with a reference-descriptor (2) a child has a many:1 relationship
1746:             * with its RI parent. in this case, the parent has foreign keys into the primary keys of the child. The child object is coded
1747:             * in XML with a collection-descriptor this routine gets the parent keys and the child keys for the relationship, so we can
1748:             * build a map of all the values for those keys that have already been copied into the parent for the new year. as each child
1749:             * row is about to be copied, we check to see whether its key values match one of the sets of values in the parent. if they do
1750:             * not, we skip the child row. this routine gives us the key field names we need to accomplish that.
1751:             * 
1752:             * @param childClass
1753:             * @param parentClass
1754:             * @return foreign keys for parent class
1755:             */
1756:            private ReturnedPair<String[], String[]> fetchForeignKeysToParent(
1757:                    Class childClass, Class parentClass) {
1758:                ReturnedPair<String[], String[]> returnObject = new ReturnedPair<String[], String[]>();
1759:
1760:                /*
1761:                 * first look for the 1:1 relationship
1762:                 */
1763:                returnObject = fetchFKToParent(childClass, parentClass);
1764:                if (!(returnObject.getFirst() == null)) {
1765:                    return returnObject;
1766:                }
1767:                /*
1768:                 * assume it's a 1:m relationship, and look for a collection-descriptor if we can't find one, we'll issue a warning and
1769:                 * return an empty pair of arrays
1770:                 */
1771:                return (fetchFKToChild(parentClass, childClass));
1772:            }
1773:
1774:            /**
1775:             * This method...
1776:             * 
1777:             * @param parentClass
1778:             * @param childClass
1779:             * @return foreign keys for child class
1780:             */
1781:            private ReturnedPair<String[], String[]> fetchFKToChild(
1782:                    Class parentClass, Class childClass) {
1783:                String[] childKeyFields;
1784:                String[] parentKeyFields;
1785:                ReturnedPair<String[], String[]> returnObject = new ReturnedPair<String[], String[]>();
1786:                // first we have to find the attribute name of the parent reference to the child
1787:                // class
1788:                HashMap<String, Class> collectionObjects = (HashMap<String, Class>) persistenceStructureService
1789:                        .listCollectionObjectTypes(parentClass);
1790:                String attributeName = null;
1791:                String childClassID = childClass.getName();
1792:                for (Map.Entry<String, Class> attributeMap : collectionObjects
1793:                        .entrySet()) {
1794:                    if (childClassID.compareTo(attributeMap.getValue()
1795:                            .getName()) == 0) {
1796:                        // the name of the child class matches a collection class
1797:                        // this is the attribute we want
1798:                        attributeName = attributeMap.getKey();
1799:                        break;
1800:                    }
1801:                }
1802:                // now we have to use the attribute to look up the inverse foreign keys
1803:                if (attributeName == null) {
1804:                    // write a warning and return an empty key set
1805:                    LOG
1806:                            .warn(String
1807:                                    .format(
1808:                                            "\n%s does not have a collection reference to %s\n",
1809:                                            parentClass.getName(), childClassID));
1810:                    return returnObject;
1811:                }
1812:                HashMap<String, String> keyMap = (HashMap<String, String>) persistenceStructureService
1813:                        .getInverseForeignKeysForCollection(parentClass,
1814:                                attributeName);
1815:                childKeyFields = new String[keyMap.size()];
1816:                parentKeyFields = new String[keyMap.size()];
1817:                // the primary key names refer to child fields
1818:                // the foreign key names refer to parent fields
1819:                // (persistenceStructureService assumes that the child fields match the
1820:                // parent primary key fields in order, AND that the first child field
1821:                // corresponds to the first parent primary key field, the second to the
1822:                // second, etc. this is apparently OJB's assumption as well. in a 1:many
1823:                // relationship, all the parent primary key fields must be used (and
1824:                // possibly some other fields)--since the parent row must be unique, but only
1825:                // some of the child's primary keys will be used)
1826:                int i = 0;
1827:                for (Map.Entry<String, String> fkPkPair : keyMap.entrySet()) {
1828:                    parentKeyFields[i] = fkPkPair.getKey();
1829:                    childKeyFields[i] = fkPkPair.getValue();
1830:                    i = i + 1;
1831:                }
1832:                returnObject.setFirst(childKeyFields);
1833:                returnObject.setSecond(parentKeyFields);
1834:                return returnObject;
1835:            }
1836:
1837:            /**
1838:             * This method takes a child class and parent class and
1839:             * 
1840:             * @param childClass
1841:             * @param parentClass
1842:             * @return foreign keys for a given parent class
1843:             */
1844:            private ReturnedPair<String[], String[]> fetchFKToParent(
1845:                    Class childClass, Class parentClass) {
1846:                String[] childKeyFields;
1847:                String[] parentKeyFields;
1848:                ReturnedPair<String[], String[]> returnObject = new ReturnedPair<String[], String[]>();
1849:                // first we have to find the attribute name of the reference to the parent
1850:                // class
1851:                HashMap<String, Class> referenceObjects = (HashMap<String, Class>) persistenceStructureService
1852:                        .listReferenceObjectFields(childClass);
1853:                String attributeName = null;
1854:                String parentClassID = parentClass.getName();
1855:                for (Map.Entry<String, Class> attributeMap : referenceObjects
1856:                        .entrySet()) {
1857:                    if (parentClassID.compareTo(attributeMap.getValue()
1858:                            .getName()) == 0) {
1859:                        // the name of the parent class matches a reference class
1860:                        // this is the attribute we want
1861:                        attributeName = attributeMap.getKey();
1862:                        break;
1863:                    }
1864:                }
1865:                // now we have to use the attribute to look up the foreign keys
1866:                if (attributeName == null) {
1867:                    // write a warning and return an empty key set
1868:                    LOG.warn(String.format(
1869:                            "\n%s does not have a child reference to %s\n",
1870:                            childClass.getName(), parentClassID));
1871:                    return returnObject;
1872:                }
1873:                HashMap<String, String> keyMap = (HashMap<String, String>) persistenceStructureService
1874:                        .getForeignKeysForReference(childClass, attributeName);
1875:                childKeyFields = new String[keyMap.size()];
1876:                parentKeyFields = new String[keyMap.size()];
1877:                // the primary key names refer to parent fields
1878:                // the foreign key names refer to child fields
1879:                // (persistenceStructureService assumes that the child fields match the
1880:                // parent primary key fields in order, AND that the first child field
1881:                // corresponds to the first parent primary key field, the second to the
1882:                // second, etc. this is apparently OJB's assumption as well.)
1883:                int i = 0;
1884:                for (Map.Entry<String, String> fkPkPair : keyMap.entrySet()) {
1885:                    childKeyFields[i] = fkPkPair.getKey();
1886:                    parentKeyFields[i] = fkPkPair.getValue();
1887:                    i = i + 1;
1888:                }
1889:                returnObject.setFirst(childKeyFields);
1890:                returnObject.setSecond(parentKeyFields);
1891:                return returnObject;
1892:            }
1893:
1894:            /**
1895:             * This method determines the capacity of the hash based on the item count returned by the query
1896:             * 
1897:             * @param queryID
1898:             * @return hash capacity based on query result set size
1899:             */
1900:            private Integer hashCapacity(ReportQueryByCriteria queryID) {
1901:                // this corresponds to a load factor of a little more than the default load factor
1902:                // of .75
1903:                // (a rehash supposedly occurs when the actual number of elements exceeds
1904:                // hashcapacity*(load factor). we want to avoid a rehash)
1905:                // N rows < .75*capacity ==> capacity > 4N/3 or 1.3333N We add a little slop.
1906:                Integer actualCount = new Integer(
1907:                        getPersistenceBrokerTemplate().getCount(queryID));
1908:                return ((Integer) ((Double) (actualCount.floatValue() * (1.45)))
1909:                        .intValue());
1910:            }
1911:
1912:            private PersistenceStructureWindow persistenceStructureWindow = null;
1913:
1914:            /**
1915:             * turnOffCascades should always be called, but if it hasn't been, there is no need to call this
1916:             * 
1917:             * @see org.kuali.module.chart.dao.FiscalYearMakersDao#resetCascades()
1918:             */
1919:            public void resetCascades() {
1920:                if (persistenceStructureWindow == null) {
1921:                    return;
1922:                }
1923:                ;
1924:                persistenceStructureWindow.restoreCascading();
1925:            }
1926:
1927:            /**
1928:             * this routine is designed to solve a problem caused by auto-xxx settings in the OJB-repostiory auto-update or auto-delete
1929:             * settings other than "none" will cause row(s) for a linked object to be written or deleted as soon as the row for the linking
1930:             * object is. this circumvents our parent-child paradigm by which we ensure deletes and copies are done in an order that will
1931:             * not violate referential integrity constraints. for example, suppose a parent A is linked to a child B, which has
1932:             * auto-update="object". B may have an RI constraint on C, while A has nothing to do with C. our copy order will allow A to be
1933:             * copied before C. auto-update="object" copies row(s) from B at the same time a row from A is copied. since no rows from C have
1934:             * been copied yet (C follows A in the copy order, the attempt to store the rows of B will violate RI--the required rows from C
1935:             * are not in the DB yet. (an example as of October, 2007 is A = OrganizationReversion, B = OrganizationReversionDetail, and C =
1936:             * ObjectCode) this routine dynamically switches off the auto-update and auto-delete in the OJB repository loaded in memory.
1937:             * this should affect only the current run, makes no permanent changes, and will not affect the performance of any documents.
1938:             * the assumption is that this code is running in its own Java container, which will go away when the run is complete.
1939:             */
1940:            private void turnOffCascades() {
1941:
1942:                // set up the window into the OJB persistence structure
1943:                persistenceStructureWindow = new PersistenceStructureWindow();
1944:                for (Map.Entry<String, ArrayList<Class>> childMap : childParentMap
1945:                        .entrySet()) {
1946:                    // get the class from the child name
1947:                    Class childClass = makerObjectsList.get(childMap.getKey());
1948:                    ArrayList<Class> parentList = childMap.getValue();
1949:                    for (int i = 0; i < parentList.size(); i++) {
1950:                        Class parentClass = parentList.get(i);
1951:                        persistenceStructureWindow.inhibitCascading(childClass,
1952:                                parentClass);
1953:                        persistenceStructureWindow.inhibitCascading(
1954:                                parentClass, childClass);
1955:                    }
1956:                }
1957:
1958:            }
1959:
1960:            /*******************************************************************************************************************************
1961:             * these classes belong in the persistence structure service. pending that, we use them here because we need them. we indicate
1962:             * below which should be public and which should not. this should only be used in BATCH, where nothing that needs to auto-update
1963:             * or auto-delete is likely to access the parent-child objects. for fiscal year makers, this condition is met. for batch
1964:             * routines that use a plug-in or create and store documents, it may not be.
1965:             ******************************************************************************************************************************/
1966:            /**
1967:             * This class
1968:             */
1969:            private class PersistenceStructureWindow {
1970:                private DescriptorRepository descriptorRepository;
1971:
1972:                // these save enough information from the repository so we can restore the auto-xxx fields we change
1973:                // the size of the hashmaps should be more than sufficient to change 16-17 tables
1974:                // we shouldn't have to change anywhere close to that many
1975:                private HashMap<CollectionDescriptor, String[]> collectionStore = new HashMap<CollectionDescriptor, String[]>(
1976:                        25);
1977:                private HashMap<CollectionDescriptor, String[]> collectionDelete = new HashMap<CollectionDescriptor, String[]>(
1978:                        25);
1979:                private HashMap<ObjectReferenceDescriptor, String[]> objectReferenceStore = new HashMap<ObjectReferenceDescriptor, String[]>(
1980:                        25);
1981:                private HashMap<ObjectReferenceDescriptor, String[]> objectReferenceDelete = new HashMap<ObjectReferenceDescriptor, String[]>(
1982:                        25);
1983:
1984:                public PersistenceStructureWindow() {
1985:                    MetadataManager metadataManager = MetadataManager
1986:                            .getInstance();
1987:                    descriptorRepository = metadataManager
1988:                            .getGlobalRepository();
1989:
1990:                }
1991:
1992:                /**
1993:                 * This method looks for reference descriptors and collection descriptors in the source class that refer to the target class
1994:                 * and specify an auto-delete or auto-update. it turns those functions off for the remainder of the batch run.
1995:                 * 
1996:                 * @param referencingClass
1997:                 * @param targetClass
1998:                 */
1999:                public void inhibitCascading(Class referencingClass,
2000:                        Class targetClass) {
2001:                    /* this method should be public in the persistence structure service */
2002:
2003:                    /* a given class will not have a 1:1 reference and a 1:m reference to the same target class */
2004:                    if (fixReferences(referencingClass, targetClass)) {
2005:                        return;
2006:                    }
2007:                    fixCollections(referencingClass, targetClass);
2008:                }
2009:
2010:                /**
2011:                 * This method returns a specific ClassDescriptor based on a BusinessObject class
2012:                 * 
2013:                 * @param boClass
2014:                 * @return class descriptor for this BO class
2015:                 */
2016:                private ClassDescriptor getOJBDescriptor(Class boClass) {
2017:                    ClassDescriptor classDescriptor = null;
2018:                    try {
2019:                        classDescriptor = descriptorRepository
2020:                                .getDescriptorFor(boClass);
2021:                    } catch (ClassNotPersistenceCapableException ex) {
2022:                        LOG.warn(String.format(
2023:                                "\n\nClass %s is non-presistable\n--> %s",
2024:                                boClass.getName(), ex.getMessage()));
2025:                        // we'll just let things go at this point and hope for the best with RI
2026:                        return classDescriptor;
2027:                    }
2028:                    return classDescriptor;
2029:                }
2030:
2031:                /**
2032:                 * This method turns off cascading updates on the target class as it is referenced from the referencing class
2033:                 * 
2034:                 * @param referencingClass
2035:                 * @param targetClass
2036:                 * @return true if it finds the reference (or isn't persistable), false if it can't find the reference
2037:                 */
2038:                private boolean fixReferences(Class referencingClass,
2039:                        Class targetClass) {
2040:                    ClassDescriptor classDescriptor = getOJBDescriptor(referencingClass);
2041:                    if (classDescriptor == null) {
2042:                        // class isn't persistable--no reason to continue
2043:                        return true;
2044:                    }
2045:                    Collection<ObjectReferenceDescriptor> referenceIDAttributes = classDescriptor
2046:                            .getObjectReferenceDescriptors(false);
2047:                    for (ObjectReferenceDescriptor objReferenceDescriptor : referenceIDAttributes) {
2048:                        if (targetClass.getName().compareTo(
2049:                                objReferenceDescriptor.getItemClassName()) == 0) {
2050:                            LOG.debug(String.format(
2051:                                    "\n\nfound reference to %s in %s",
2052:                                    targetClass.getName(), referencingClass
2053:                                            .getName()));
2054:
2055:                            if (objReferenceDescriptor.getCascadingDelete() != ObjectReferenceDescriptor.CASCADE_NONE) {
2056:                                // we want to issue a message whenever we toggle--so store three things
2057:                                String[] infoString = {
2058:                                        targetClass.getName(),
2059:                                        referencingClass.getName(),
2060:                                        objReferenceDescriptor
2061:                                                .getCascadeAsString(objReferenceDescriptor
2062:                                                        .getCascadingDelete()) };
2063:                                objectReferenceDelete.put(
2064:                                        objReferenceDescriptor, infoString);
2065:                                objReferenceDescriptor
2066:                                        .setCascadingDelete(ObjectReferenceDescriptor.CASCADE_NONE);
2067:                                LOG
2068:                                        .warn(String
2069:                                                .format(
2070:                                                        "\nreset auto-delete = %s\nfor %s in %s",
2071:                                                        infoString[2],
2072:                                                        targetClass.getName(),
2073:                                                        referencingClass
2074:                                                                .getName()));
2075:                            }
2076:                            if (objReferenceDescriptor.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE) {
2077:                                // we want to issue a message whenever we toggle--so store three things
2078:                                String[] infoString = {
2079:                                        targetClass.getName(),
2080:                                        referencingClass.getName(),
2081:                                        objReferenceDescriptor
2082:                                                .getCascadeAsString(objReferenceDescriptor
2083:                                                        .getCascadingStore()) };
2084:                                objectReferenceStore.put(
2085:                                        objReferenceDescriptor, infoString);
2086:                                objReferenceDescriptor
2087:                                        .setCascadingStore(ObjectReferenceDescriptor.CASCADE_NONE);
2088:                                LOG
2089:                                        .warn(String
2090:                                                .format(
2091:                                                        "\nreset auto-update = %s\nfor %s in %s",
2092:                                                        infoString[2],
2093:                                                        targetClass.getName(),
2094:                                                        referencingClass
2095:                                                                .getName()));
2096:                            }
2097:                            return true;
2098:                            // a given class will not have a reference-id and a collection-ref-id to the same target class
2099:                        }
2100:                    }
2101:                    return false;
2102:                }
2103:
2104:                /**
2105:                 * This method turns off cascading updates on the target class as it is referenced from the referencing class as a
2106:                 * collection
2107:                 * 
2108:                 * @param referencingClass
2109:                 * @param targetClass
2110:                 * @return true if it finds the reference (or isn't persistable), false if it can't find the reference
2111:                 */
2112:                private boolean fixCollections(Class referencingClass,
2113:                        Class targetClass) {
2114:                    ClassDescriptor classDescriptor = getOJBDescriptor(referencingClass);
2115:                    if (classDescriptor == null) {
2116:                        // class isn't persistable--no reason to continue
2117:                        return true;
2118:                    }
2119:                    Collection<CollectionDescriptor> collectionIDAttributes = classDescriptor
2120:                            .getCollectionDescriptors(false);
2121:                    for (CollectionDescriptor collectionDescriptor : collectionIDAttributes) {
2122:                        if (targetClass.getName().compareTo(
2123:                                collectionDescriptor.getItemClassName()) == 0) {
2124:                            LOG
2125:                                    .debug(String
2126:                                            .format(
2127:                                                    "\n\nfound collection reference to %s in %s",
2128:                                                    targetClass.getName(),
2129:                                                    referencingClass.getName()));
2130:
2131:                            if (collectionDescriptor.getCascadingDelete() != CollectionDescriptor.CASCADE_NONE) {
2132:                                // we want to issue a message whenever we toggle--so store three things
2133:                                String[] infoString = {
2134:                                        targetClass.getName(),
2135:                                        referencingClass.getName(),
2136:                                        collectionDescriptor
2137:                                                .getCascadeAsString(collectionDescriptor
2138:                                                        .getCascadingDelete()) };
2139:                                collectionDelete.put(collectionDescriptor,
2140:                                        infoString);
2141:                                collectionDescriptor
2142:                                        .setCascadingDelete(CollectionDescriptor.CASCADE_NONE);
2143:                                LOG
2144:                                        .warn(String
2145:                                                .format(
2146:                                                        "\nreset auto-delete = %s \nfor %s in %s",
2147:                                                        infoString[2],
2148:                                                        targetClass.getName(),
2149:                                                        referencingClass
2150:                                                                .getName()));
2151:                            }
2152:                            if (collectionDescriptor.getCascadingStore() != CollectionDescriptor.CASCADE_NONE) {
2153:                                // we want to issue a message whenever we toggle--so store three things
2154:                                String[] infoString = {
2155:                                        targetClass.getName(),
2156:                                        referencingClass.getName(),
2157:                                        collectionDescriptor
2158:                                                .getCascadeAsString(collectionDescriptor
2159:                                                        .getCascadingStore()) };
2160:                                collectionStore.put(collectionDescriptor,
2161:                                        infoString);
2162:                                collectionDescriptor
2163:                                        .setCascadingStore(CollectionDescriptor.CASCADE_NONE);
2164:                                LOG
2165:                                        .warn(String
2166:                                                .format(
2167:                                                        "\nreset auto-update = %s \nfor %s in %s",
2168:                                                        infoString[2],
2169:                                                        targetClass.getName(),
2170:                                                        referencingClass
2171:                                                                .getName()));
2172:                            }
2173:                            return true;
2174:                            // a given class will not have a reference-id and a collection-ref-id to the same target class
2175:                        }
2176:                    }
2177:                    return false;
2178:                }
2179:
2180:                /**
2181:                 * This method restores the cascading saves and updates to what they were before the change
2182:                 */
2183:                public void restoreCascading() {
2184:                    // auto deletes in collections
2185:                    for (Map.Entry<CollectionDescriptor, String[]> restoreTargets : collectionDelete
2186:                            .entrySet()) {
2187:                        String[] infoString = restoreTargets.getValue();
2188:                        CollectionDescriptor collectionDesc = restoreTargets
2189:                                .getKey();
2190:                        collectionDesc.setCascadingDelete(infoString[2]);
2191:                        LOG.warn(String.format(
2192:                                "\nauto-delete reset to %s\nfor %s in %s",
2193:                                collectionDesc
2194:                                        .getCascadeAsString(collectionDesc
2195:                                                .getCascadingDelete()),
2196:                                infoString[0], infoString[1]));
2197:                    }
2198:                    // auto updates in collections
2199:                    for (Map.Entry<CollectionDescriptor, String[]> restoreTargets : collectionStore
2200:                            .entrySet()) {
2201:                        String[] infoString = restoreTargets.getValue();
2202:                        CollectionDescriptor collectionDesc = restoreTargets
2203:                                .getKey();
2204:                        collectionDesc.setCascadingStore(infoString[2]);
2205:                        LOG.warn(String.format(
2206:                                "\nauto-update reset to %s\nfor %s in %s",
2207:                                collectionDesc
2208:                                        .getCascadeAsString(collectionDesc
2209:                                                .getCascadingStore()),
2210:                                infoString[0], infoString[1]));
2211:                    }
2212:                    // auto deletes in references
2213:                    for (Map.Entry<ObjectReferenceDescriptor, String[]> restoreTargets : objectReferenceDelete
2214:                            .entrySet()) {
2215:                        String[] infoString = restoreTargets.getValue();
2216:                        ObjectReferenceDescriptor objReferenceDesc = restoreTargets
2217:                                .getKey();
2218:                        objReferenceDesc.setCascadingDelete(infoString[2]);
2219:                        LOG.warn(String.format(
2220:                                "\nauto-delete reset to %s\nfor %s in %s",
2221:                                objReferenceDesc
2222:                                        .getCascadeAsString(objReferenceDesc
2223:                                                .getCascadingDelete()),
2224:                                infoString[0], infoString[1]));
2225:                    }
2226:                    // auto updates in collections
2227:                    for (Map.Entry<ObjectReferenceDescriptor, String[]> restoreTargets : objectReferenceStore
2228:                            .entrySet()) {
2229:                        String[] infoString = restoreTargets.getValue();
2230:                        ObjectReferenceDescriptor objReferenceDesc = restoreTargets
2231:                                .getKey();
2232:                        objReferenceDesc.setCascadingStore(infoString[2]);
2233:                        LOG.warn(String.format(
2234:                                "\nauto-update reset to %s\nfor %s in %s",
2235:                                objReferenceDesc
2236:                                        .getCascadeAsString(objReferenceDesc
2237:                                                .getCascadingStore()),
2238:                                infoString[0], infoString[1]));
2239:                    }
2240:                }
2241:            }
2242:
2243:            /**
2244:             * this is a handy junk inner class that allows us to return two things from a method
2245:             */
2246:            private class ReturnedPair<S, T> {
2247:                S firstObject;
2248:                T secondObject;
2249:
2250:                public ReturnedPair() {
2251:                    this .firstObject = null;
2252:                    this .secondObject = null;
2253:                }
2254:
2255:                public S getFirst() {
2256:                    return this .firstObject;
2257:                }
2258:
2259:                public T getSecond() {
2260:                    return this .secondObject;
2261:                }
2262:
2263:                public void setFirst(S firstObject) {
2264:                    this .firstObject = firstObject;
2265:                }
2266:
2267:                public void setSecond(T secondObject) {
2268:                    this .secondObject = secondObject;
2269:                }
2270:            }
2271:
2272:            // @@TODO: remove this test routine
2273:            public void testUpdateTwoDigitYear() {
2274:                String oldYear = new String("08");
2275:                String newYear = new String("11");
2276:                String testString = new String(
2277:                        "08-09 x 08 07-08 08-09 09 08 08");
2278:                String newString = updateTwoDigitYear(newYear, oldYear,
2279:                        testString);
2280:                LOG
2281:                        .warn(String
2282:                                .format(
2283:                                        "\n test of updateTwoDigitYear:\n input = %s\n output = %s\n from: %s, to:%s  ",
2284:                                        testString, newString, oldYear, newYear));
2285:                testString = new String("x08-09 x 08 07-08 08-09 09 tail");
2286:                newString = updateTwoDigitYear(newYear, oldYear, testString);
2287:                LOG
2288:                        .warn(String
2289:                                .format(
2290:                                        "\n test of updateTwoDigitYear:\n input = %s\n output = %s\n from: %s, to:%s  ",
2291:                                        testString, newString, oldYear, newYear));
2292:                testString = new String(" nada ");
2293:                newString = updateTwoDigitYear(newYear, oldYear, testString);
2294:                LOG
2295:                        .warn(String
2296:                                .format(
2297:                                        "\n test of updateTwoDigitYear:\n input = %s\n output = %s\n from: %s, to:%s  ",
2298:                                        testString, newString, oldYear, newYear));
2299:            }
2300:
2301:            // TODO: remove these test routines
2302:
2303:            public void testRIRelationships() throws NoSuchMethodException,
2304:                    IllegalAccessException, InvocationTargetException {
2305:                // print the object list
2306:                LOG.warn(String.format("\n\nFiscalYearMakersObjects:\n\n"));
2307:                for (Map.Entry<String, Class> makerObjects : makerObjectsList
2308:                        .entrySet()) {
2309:                    LOG.warn(String.format("\nkey: %s, class name: %s",
2310:                            makerObjects.getKey(), makerObjects.getValue()
2311:                                    .getName()));
2312:                }
2313:                // print the child/parent list
2314:                LOG.warn(String.format("\n\nchild key, parent classes:\n\n"));
2315:                for (Map.Entry<String, ArrayList<Class>> childParents : childParentMap
2316:                        .entrySet()) {
2317:                    Iterator<Class> parents = childParents.getValue()
2318:                            .iterator();
2319:                    LOG.warn(String.format("\nchild: %s has %d parents",
2320:                            childParents.getKey(), childParents.getValue()
2321:                                    .size()));
2322:                    Class childClass = makerObjectsList.get(childParents
2323:                            .getKey());
2324:                    while (parents.hasNext()) {
2325:                        Class parentClass = parents.next();
2326:                        LOG.warn(String.format(
2327:                                "\n       parent class name: %s", parentClass
2328:                                        .getName()));
2329:                        ReturnedPair<String[], String[]> keySets = fetchForeignKeysToParent(
2330:                                childClass, parentClass);
2331:                        String[] childKeys = keySets.getFirst();
2332:                        String[] parentKeys = keySets.getSecond();
2333:                        LOG.warn(String
2334:                                .format("\n       ------child keys-----"));
2335:                        for (int i = 0; i < childKeys.length; i++) {
2336:                            LOG
2337:                                    .warn(String.format("\n       %s",
2338:                                            childKeys[i]));
2339:                        }
2340:                        LOG.warn(String
2341:                                .format("\n       ------parent keys-----"));
2342:                        for (int i = 0; i < parentKeys.length; i++) {
2343:                            LOG.warn(String
2344:                                    .format("\n       %s", parentKeys[i]));
2345:                        }
2346:                    }
2347:                }
2348:                // print the delete order
2349:                LOG.warn(String.format("\n\nDelete Order:\n\n"));
2350:                LinkedHashMap<String, Class> deleteOrder = getDeleteOrder();
2351:                for (Map.Entry<String, Class> orderedDeleteList : deleteOrder
2352:                        .entrySet()) {
2353:                    LOG.warn(String.format("\n  key: %s, class name: %s",
2354:                            orderedDeleteList.getKey(), orderedDeleteList
2355:                                    .getValue().getName()));
2356:                }
2357:                // print the copy order
2358:                // (this should fail with an exception if not all the objects have been
2359:                // added to setUpRun)
2360:                LOG.warn(String.format("\n\nCopy Order:\n\n"));
2361:                LinkedHashMap<String, FiscalYearMakersCopyAction> copyOrder = setUpRun(
2362:                        2010, false);
2363:                for (Map.Entry<String, FiscalYearMakersCopyAction> copyOrderList : copyOrder
2364:                        .entrySet()) {
2365:                    if (copyOrderList.getValue() == null) {
2366:                        LOG.warn(String.format(
2367:                                "\n  key: %s, object value is null",
2368:                                copyOrderList.getKey()));
2369:                    } else {
2370:                        LOG.warn(String.format("\n  key: %s, object value %s",
2371:                                copyOrderList.getKey(), copyOrderList
2372:                                        .getValue().toString()));
2373:                    }
2374:                }
2375:                // reprint this, to make sure it hasn't been corrupted in building
2376:                // the copy order data structure
2377:                LOG.warn(String.format("\n\nchild key, parent classes:\n\n"));
2378:                for (Map.Entry<String, ArrayList<Class>> childParents : childParentMap
2379:                        .entrySet()) {
2380:                    Iterator<Class> parents = childParents.getValue()
2381:                            .iterator();
2382:                    LOG.warn(String.format("\nchild: %s has %d parents",
2383:                            childParents.getKey(), childParents.getValue()
2384:                                    .size()));
2385:                    while (parents.hasNext()) {
2386:                        LOG.warn(String.format(
2387:                                "\n       parent class name: %s", parents
2388:                                        .next().getName()));
2389:                    }
2390:                }
2391:                // now print the objects which are a year behind
2392:                LOG
2393:                        .warn(String
2394:                                .format("\n\nLagging Copy Cycle (one year behind):\n\n"));
2395:                Iterator<String> laggardList = laggingCopyCycle.iterator();
2396:                while (laggardList.hasNext()) {
2397:                    LOG.warn(String.format("\n   %s", (String) laggardList
2398:                            .next()));
2399:                }
2400:                LOG.warn(String.format("\n\n"));
2401:                // we want to test the code that enforces RI copy integrity by checking
2402:                // that child keys to be copied exist in the (already copied) parent keys
2403:                // we use 2011. FS_OPTIONS_T exists, but CA_OBJECT_CODE_T does not
2404:                // so, CA_SUB_OBJECT_CD_T should fail, but CA_OBJECT_CODE_T should succeed.
2405:                // we add a third which should work, to test the need to specify the generic
2406:                // class on the constructor
2407:                Integer baseYear = new Integer(2009);
2408:                Integer requestYear = baseYear + 1;
2409:                ParentKeyChecker<ObjectCode> objCodeChk = new ParentKeyChecker<ObjectCode>(
2410:                        ObjectCode.class, requestYear);
2411:                Criteria yearCriteria = new Criteria();
2412:                yearCriteria.addEqualTo(
2413:                        KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, baseYear);
2414:                yearCriteria.addLessThan("ROWNUM", new Integer(3));
2415:                QueryByCriteria queryID = new QueryByCriteria(ObjectCode.class,
2416:                        yearCriteria);
2417:                Iterator<ObjectCode> objsReturned = getPersistenceBrokerTemplate()
2418:                        .getIteratorByQuery(queryID);
2419:                while (objsReturned.hasNext()) {
2420:                    ObjectCode rowReturned = objsReturned.next();
2421:                    PropertyUtils.setSimpleProperty(rowReturned,
2422:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
2423:                            requestYear);
2424:                    LOG.warn(String.format("\nObjectCode key in parents? %b",
2425:                            objCodeChk.childRowSatisfiesRI(rowReturned)));
2426:                }
2427:                // now we're going to do the sub object code
2428:                ParentKeyChecker<SubObjCd> subObjCdChk = new ParentKeyChecker<SubObjCd>(
2429:                        SubObjCd.class, requestYear);
2430:                queryID = new QueryByCriteria(SubObjCd.class, yearCriteria);
2431:                Iterator<SubObjCd> subObjsReturned = getPersistenceBrokerTemplate()
2432:                        .getIteratorByQuery(queryID);
2433:                while (subObjsReturned.hasNext()) {
2434:                    SubObjCd rowReturned = subObjsReturned.next();
2435:                    PropertyUtils.setSimpleProperty(rowReturned,
2436:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
2437:                            requestYear);
2438:                    LOG.warn(String.format(
2439:                            "\nsub object code key in parents? %b", subObjCdChk
2440:                                    .childRowSatisfiesRI(rowReturned)));
2441:                }
2442:                // we need to test OrganizationReversionDetail
2443:                ParentKeyChecker<OrganizationReversionDetail> OrgRevDtlChk = new ParentKeyChecker<OrganizationReversionDetail>(
2444:                        OrganizationReversionDetail.class, requestYear - 1);
2445:                Criteria laggingYearCriteria = new Criteria();
2446:                laggingYearCriteria.addEqualTo(
2447:                        KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
2448:                        baseYear - 1);
2449:                laggingYearCriteria.addLessThan("ROWNUM", new Integer(3));
2450:                queryID = new QueryByCriteria(
2451:                        OrganizationReversionDetail.class, laggingYearCriteria);
2452:                Iterator<OrganizationReversionDetail> OrgRevDtlReturned = getPersistenceBrokerTemplate()
2453:                        .getIteratorByQuery(queryID);
2454:                while (OrgRevDtlReturned.hasNext()) {
2455:                    OrganizationReversionDetail rowReturned = OrgRevDtlReturned
2456:                            .next();
2457:                    PropertyUtils.setSimpleProperty(rowReturned,
2458:                            KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
2459:                            requestYear - 1);
2460:                    LOG.warn(String.format(
2461:                            "\nOrganizationReversionDetail key in parents? %b",
2462:                            OrgRevDtlChk.childRowSatisfiesRI(rowReturned)));
2463:                }
2464:                LOG.warn(String.format("\n\nend RI test\n\n"));
2465:            }
2466:
2467:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.