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.labor.service.impl;
0017:
0018: import java.io.BufferedOutputStream;
0019: import java.io.BufferedReader;
0020: import java.io.File;
0021: import java.io.FileInputStream;
0022: import java.io.FileOutputStream;
0023: import java.io.FileReader;
0024: import java.io.IOException;
0025: import java.io.OutputStream;
0026: import java.util.ArrayList;
0027: import java.util.Collection;
0028: import java.util.Collections;
0029: import java.util.Iterator;
0030: import java.util.List;
0031:
0032: import org.apache.log4j.Logger;
0033: import org.kuali.core.dao.DocumentDao;
0034: import org.kuali.core.service.KualiConfigurationService;
0035: import org.kuali.core.web.comparator.NumericValueComparator;
0036: import org.kuali.core.web.comparator.StringValueComparator;
0037: import org.kuali.core.web.comparator.TemporalValueComparator;
0038: import org.kuali.core.web.ui.Column;
0039: import org.kuali.core.workflow.service.KualiWorkflowDocument;
0040: import org.kuali.kfs.KFSPropertyConstants;
0041: import org.kuali.module.gl.bo.CorrectionChangeGroup;
0042: import org.kuali.module.gl.bo.OriginEntryGroup;
0043: import org.kuali.module.gl.dao.CorrectionChangeDao;
0044: import org.kuali.module.gl.dao.CorrectionChangeGroupDao;
0045: import org.kuali.module.gl.dao.CorrectionCriteriaDao;
0046: import org.kuali.module.gl.service.OriginEntryGroupService;
0047: import org.kuali.module.gl.service.impl.CorrectionDocumentServiceImpl;
0048: import org.kuali.module.gl.util.CorrectionDocumentEntryMetadata;
0049: import org.kuali.module.gl.util.CorrectionDocumentUtils;
0050: import org.kuali.module.gl.util.OriginEntryStatistics;
0051: import org.kuali.module.labor.LaborPropertyConstants;
0052: import org.kuali.module.labor.bo.LaborOriginEntry;
0053: import org.kuali.module.labor.document.LaborCorrectionDocument;
0054: import org.kuali.module.labor.service.LaborCorrectionDocumentService;
0055: import org.kuali.module.labor.service.LaborOriginEntryService;
0056: import org.kuali.module.labor.util.LaborOriginEntryFileIterator;
0057: import org.springframework.transaction.annotation.Transactional;
0058:
0059: /**
0060: * Service implementation of LaborCorrectionDocumentService.
0061: */
0062: @Transactional
0063: public class LaborCorrectionDocumentServiceImpl extends
0064: CorrectionDocumentServiceImpl implements
0065: LaborCorrectionDocumentService {
0066: private static Logger LOG = Logger
0067: .getLogger(LaborCorrectionDocumentServiceImpl.class);
0068:
0069: protected OriginEntryGroupService originEntryGroupService;
0070:
0071: private LaborOriginEntryService laborOriginEntryService;
0072: private String llcpDirectoryName;
0073:
0074: protected static final String INPUT_ORIGIN_ENTRIES_FILE_SUFFIX = "-input.txt";
0075: protected static final String OUTPUT_ORIGIN_ENTRIES_FILE_SUFFIX = "-output.txt";
0076:
0077: public final static int UNLIMITED_ABORT_THRESHOLD = CorrectionDocumentUtils.RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_UNLIMITED;
0078:
0079: /**
0080: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#findByDocumentNumberAndCorrectionChangeGroupNumber(java.lang.String,
0081: * int)
0082: */
0083: public CorrectionChangeGroup findByDocumentNumberAndCorrectionChangeGroupNumber(
0084: String docId, int i) {
0085:
0086: return correctionChangeGroupDao
0087: .findByDocumentNumberAndCorrectionChangeGroupNumber(
0088: docId, i);
0089: }
0090:
0091: /**
0092: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#findByDocumentHeaderIdAndCorrectionGroupNumber(java.lang.String,
0093: * int)
0094: */
0095: public List findByDocumentHeaderIdAndCorrectionGroupNumber(
0096: String docId, int i) {
0097:
0098: return correctionChangeDao
0099: .findByDocumentHeaderIdAndCorrectionGroupNumber(docId,
0100: i);
0101: }
0102:
0103: /**
0104: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#findByDocumentNumberAndCorrectionGroupNumber(java.lang.String,
0105: * int)
0106: */
0107: public List findByDocumentNumberAndCorrectionGroupNumber(
0108: String docId, int i) {
0109:
0110: return correctionCriteriaDao
0111: .findByDocumentNumberAndCorrectionGroupNumber(docId, i);
0112: }
0113:
0114: /**
0115: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#findByCorrectionDocumentHeaderId(java.lang.String)
0116: */
0117: public LaborCorrectionDocument findByCorrectionDocumentHeaderId(
0118: String docId) {
0119:
0120: return (LaborCorrectionDocument) documentDao
0121: .findByDocumentHeaderId(LaborCorrectionDocument.class,
0122: docId);
0123: }
0124:
0125: /**
0126: * Sets the correctionChangeDao attribute value.
0127: *
0128: * @param correctionChangeDao The correctionChangeDao to set.
0129: */
0130:
0131: public void setCorrectionChangeDao(
0132: CorrectionChangeDao correctionChangeDao) {
0133: this .correctionChangeDao = correctionChangeDao;
0134: }
0135:
0136: /**
0137: * Sets the correctionChangeDao attribute value.
0138: *
0139: * @param correctionChangeGroupDao The correctionChangeDao to set.
0140: */
0141: public void setCorrectionChangeGroupDao(
0142: CorrectionChangeGroupDao correctionChangeGroupDao) {
0143: this .correctionChangeGroupDao = correctionChangeGroupDao;
0144: }
0145:
0146: /**
0147: * Sets the correctionCriteriaDao attribute value.
0148: *
0149: * @param correctionCriteriaDao The correctionCriteriaDao to set.
0150: */
0151: public void setCorrectionCriteriaDao(
0152: CorrectionCriteriaDao correctionCriteriaDao) {
0153: this .correctionCriteriaDao = correctionCriteriaDao;
0154: }
0155:
0156: /**
0157: * Sets the documentDao attribute value.
0158: *
0159: * @param documentDao The documentDao to set.
0160: */
0161: public void setDocumentDao(DocumentDao documentDao) {
0162: this .documentDao = documentDao;
0163: }
0164:
0165: private List<Column> cachedColumns = null;
0166:
0167: /**
0168: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#generateInputOriginEntryFileName(java.lang.String)
0169: */
0170: protected String generateInputOriginEntryFileName(
0171: LaborCorrectionDocument document) {
0172: String docId = document.getDocumentHeader().getDocumentNumber();
0173: return generateInputOriginEntryFileName(docId);
0174: }
0175:
0176: /**
0177: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#generateOutputOriginEntryFileName(java.lang.String)
0178: */
0179: protected String generateOutputOriginEntryFileName(
0180: LaborCorrectionDocument document) {
0181: String docId = document.getDocumentHeader().getDocumentNumber();
0182: return generateOutputOriginEntryFileName(docId);
0183: }
0184:
0185: /**
0186: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#generateInputOriginEntryFileName(java.lang.String)
0187: */
0188: protected String generateInputOriginEntryFileName(String docId) {
0189: return getOriginEntryStagingDirectoryPath() + File.separator
0190: + docId + INPUT_ORIGIN_ENTRIES_FILE_SUFFIX;
0191: }
0192:
0193: /**
0194: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#generateOutputOriginEntryFileName(java.lang.String)
0195: */
0196: protected String generateOutputOriginEntryFileName(String docId) {
0197: return getOriginEntryStagingDirectoryPath() + File.separator
0198: + docId + OUTPUT_ORIGIN_ENTRIES_FILE_SUFFIX;
0199: }
0200:
0201: /**
0202: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#persistOriginEntriesToFile(org.kuali.module.labor.document.LaborCorrectionDocument,
0203: * java.util.Iterator)
0204: */
0205: public void persistInputOriginEntriesForInitiatedOrSavedDocument(
0206: LaborCorrectionDocument document,
0207: Iterator<LaborOriginEntry> entries) {
0208: KualiWorkflowDocument workflowDocument = document
0209: .getDocumentHeader().getWorkflowDocument();
0210: if (!workflowDocument.stateIsInitiated()
0211: && !workflowDocument.stateIsSaved()) {
0212: LOG
0213: .error("This method may only be called when the document is in the initiated or saved state.");
0214: }
0215: String fullPathUniqueFileName = generateInputOriginEntryFileName(document);
0216: persistLaborOriginEntries(fullPathUniqueFileName, entries);
0217: }
0218:
0219: /**
0220: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#persistOutputLaborOriginEntriesForInitiatedOrSavedDocument(org.kuali.module.labor.document.LaborCorrectionDocument,
0221: * java.util.Iterator)
0222: */
0223: public void persistOutputLaborOriginEntriesForInitiatedOrSavedDocument(
0224: LaborCorrectionDocument document,
0225: Iterator<LaborOriginEntry> entries) {
0226: KualiWorkflowDocument workflowDocument = document
0227: .getDocumentHeader().getWorkflowDocument();
0228: if (!workflowDocument.stateIsInitiated()
0229: && !workflowDocument.stateIsSaved()) {
0230: LOG
0231: .error("This method may only be called when the document is in the initiated or saved state.");
0232: }
0233: String fullPathUniqueFileName = generateOutputOriginEntryFileName(document);
0234: persistLaborOriginEntries(fullPathUniqueFileName, entries);
0235: }
0236:
0237: /**
0238: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#persistLaborOriginEntries(java.lang.String,
0239: * java.util.Iterator)
0240: */
0241: protected void persistLaborOriginEntries(
0242: String fullPathUniqueFileName,
0243: Iterator<LaborOriginEntry> entries) {
0244: File fileOut = new File(fullPathUniqueFileName);
0245: FileOutputStream streamOut = null;
0246: BufferedOutputStream bufferedStreamOut = null;
0247: try {
0248: streamOut = new FileOutputStream(fileOut);
0249: bufferedStreamOut = new BufferedOutputStream(streamOut);
0250:
0251: byte[] newLine = "\n".getBytes();
0252: while (entries.hasNext()) {
0253: LaborOriginEntry entry = entries.next();
0254: bufferedStreamOut.write(entry.getLine().getBytes());
0255: bufferedStreamOut.write(newLine);
0256: }
0257: } catch (IOException e) {
0258: LOG.error(
0259: "unable to persist labor origin entries to file: "
0260: + fullPathUniqueFileName, e);
0261: throw new RuntimeException(
0262: "unable to persist origin entries to file.");
0263: } finally {
0264: try {
0265: bufferedStreamOut.close();
0266: streamOut.close();
0267: } catch (IOException e) {
0268: LOG.error("unable to close output streams for file: "
0269: + fullPathUniqueFileName, e);
0270: throw new RuntimeException(
0271: "unable to close output streams");
0272: }
0273: }
0274: }
0275:
0276: /**
0277: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#openEntryOutputStreamForOutputGroup(org.kuali.module.labor.document.LaborCorrectionDocument)
0278: */
0279: protected BufferedOutputStream openEntryOutputStreamForOutputGroup(
0280: LaborCorrectionDocument document) throws IOException {
0281: String fullPathUniqueFileName = generateOutputOriginEntryFileName(document);
0282: return new BufferedOutputStream(new FileOutputStream(
0283: fullPathUniqueFileName));
0284: }
0285:
0286: /**
0287: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#removePersistedInputOriginEntriesForInitiatedOrSavedDocument(org.kuali.module.labor.document.LaborCorrectionDocument)
0288: */
0289: public void removePersistedInputOriginEntries(
0290: LaborCorrectionDocument document) {
0291: String fullPathUniqueFileName = generateInputOriginEntryFileName(document);
0292: removePersistedOriginEntries(fullPathUniqueFileName);
0293: }
0294:
0295: /**
0296: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#removePersistedOutputOriginEntriesForInitiatedOrSavedDocument(org.kuali.module.labor.document.LaborCorrectionDocument)
0297: */
0298: public void removePersistedOutputOriginEntries(
0299: LaborCorrectionDocument document) {
0300: String fullPathUniqueFileName = generateOutputOriginEntryFileName(document);
0301: removePersistedOriginEntries(fullPathUniqueFileName);
0302: }
0303:
0304: /**
0305: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#removePersistedInputOriginEntries(java.lang.String)
0306: */
0307: public void removePersistedInputOriginEntries(String docId) {
0308: removePersistedOriginEntries(generateInputOriginEntryFileName(docId));
0309: }
0310:
0311: /**
0312: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#removePersistedOutputOriginEntries(java.lang.String)
0313: */
0314: public void removePersistedOutputOriginEntries(String docId) {
0315: removePersistedOriginEntries(generateOutputOriginEntryFileName(docId));
0316: }
0317:
0318: /**
0319: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#removePersistedOriginEntries(java.lang.String)
0320: */
0321: protected void removePersistedOriginEntries(
0322: String fullPathUniqueFileName) {
0323: File fileOut = new File(fullPathUniqueFileName);
0324: if (fileOut.exists() && fileOut.isFile()) {
0325: fileOut.delete();
0326: }
0327: }
0328:
0329: /**
0330: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#retrievePersistedInputOriginEntries(org.kuali.module.labor.document.LaborCorrectionDocument,
0331: * int)
0332: */
0333: public List<LaborOriginEntry> retrievePersistedInputOriginEntries(
0334: LaborCorrectionDocument document, int abortThreshold) {
0335: return retrievePersistedLaborOriginEntries(
0336: generateInputOriginEntryFileName(document),
0337: abortThreshold);
0338: }
0339:
0340: /**
0341: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#retrievePersistedOutputOriginEntries(org.kuali.module.labor.document.LaborCorrectionDocument,
0342: * int)
0343: */
0344: public List<LaborOriginEntry> retrievePersistedOutputOriginEntries(
0345: LaborCorrectionDocument document, int abortThreshold) {
0346: return retrievePersistedLaborOriginEntries(
0347: generateOutputOriginEntryFileName(document),
0348: abortThreshold);
0349: }
0350:
0351: /**
0352: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#retrievePersistedLaborOriginEntries(java.lang.String, int)
0353: */
0354: protected List<LaborOriginEntry> retrievePersistedLaborOriginEntries(
0355: String fullPathUniqueFileName, int abortThreshold) {
0356: File fileIn = new File(fullPathUniqueFileName);
0357: if (!fileIn.exists()) {
0358: LOG.error("File " + fullPathUniqueFileName
0359: + " does not exist.");
0360: throw new RuntimeException("File does not exist");
0361: }
0362: BufferedReader reader = null;
0363: FileReader fReader = null;
0364:
0365: List<LaborOriginEntry> entries = new ArrayList<LaborOriginEntry>();
0366: int lineNumber = 0;
0367: try {
0368: fReader = new FileReader(fileIn);
0369: reader = new BufferedReader(fReader);
0370: String line;
0371: while ((line = reader.readLine()) != null) {
0372: LaborOriginEntry entry = new LaborOriginEntry();
0373: entry.setFromTextFile(line, lineNumber);
0374: if (abortThreshold != UNLIMITED_ABORT_THRESHOLD
0375: && lineNumber >= abortThreshold) {
0376: return null;
0377: }
0378: lineNumber++;
0379: entries.add(entry);
0380: }
0381: } catch (IOException e) {
0382: LOG.error(
0383: "retrievePersistedOriginEntries() Error reading file "
0384: + fileIn.getAbsolutePath(), e);
0385: throw new RuntimeException("Error reading file");
0386: } finally {
0387: try {
0388: if (fReader != null) {
0389: fReader.close();
0390: }
0391: if (reader != null) {
0392: reader.close();
0393: }
0394: } catch (IOException e) {
0395: LOG.error("Unable to close file "
0396: + fileIn.getAbsolutePath(), e);
0397: throw new RuntimeException("Error closing file");
0398: }
0399: }
0400: return entries;
0401: }
0402:
0403: /**
0404: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#retrievePersistedInputOriginEntriesAsIterator(org.kuali.module.labor.document.LaborCorrectionDocument)
0405: */
0406: public Iterator<LaborOriginEntry> retrievePersistedInputOriginEntriesAsIterator(
0407: LaborCorrectionDocument document) {
0408: String fullPathUniqueFileName = generateInputOriginEntryFileName(document);
0409: return retrievePersistedLaborOriginEntriesAsIterator(fullPathUniqueFileName);
0410: }
0411:
0412: /**
0413: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#retrievePersistedOutputOriginEntriesAsIterator(org.kuali.module.labor.document.LaborCorrectionDocument)
0414: */
0415: public Iterator<LaborOriginEntry> retrievePersistedOutputOriginEntriesAsIterator(
0416: LaborCorrectionDocument document) {
0417: String fullPathUniqueFileName = generateOutputOriginEntryFileName(document);
0418: return retrievePersistedLaborOriginEntriesAsIterator(fullPathUniqueFileName);
0419: }
0420:
0421: /**
0422: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#retrievePersistedLaborOriginEntriesAsIterator(java.lang.String)
0423: */
0424: protected Iterator<LaborOriginEntry> retrievePersistedLaborOriginEntriesAsIterator(
0425: String fullPathUniqueFileName) {
0426: File fileIn = new File(fullPathUniqueFileName);
0427: if (!fileIn.exists()) {
0428: LOG.error("File " + fullPathUniqueFileName
0429: + " does not exist.");
0430: throw new RuntimeException("File does not exist");
0431: }
0432: BufferedReader reader = null;
0433: FileReader fReader = null;
0434:
0435: try {
0436: fReader = new FileReader(fileIn);
0437: reader = new BufferedReader(fReader);
0438:
0439: return new LaborOriginEntryFileIterator(reader);
0440: } catch (IOException e) {
0441: LOG.error(
0442: "retrievePersistedOriginEntries() Error opening file "
0443: + fileIn.getAbsolutePath(), e);
0444: throw new RuntimeException("Error opening file");
0445: }
0446: // don't close the reader, the iterator will take care of that
0447: }
0448:
0449: /**
0450: * Returns true if and only if the file corresponding to this document's input origin entries are on the file system.
0451: *
0452: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#areInputOriginEntriesPersisted(org.kuali.module.labor.document.LaborCorrectionDocument)
0453: */
0454: public boolean areInputOriginEntriesPersisted(
0455: LaborCorrectionDocument document) {
0456: String fullPathUniqueFileName = generateInputOriginEntryFileName(document);
0457: File file = new File(fullPathUniqueFileName);
0458: return file.exists();
0459: }
0460:
0461: /**
0462: * Returns true if and only if the file corresponding to this document's output origin entries are on the file system.
0463: *
0464: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#areOutputOriginEntriesPersisted(org.kuali.module.labor.document.LaborCorrectionDocument)
0465: */
0466: public boolean areOutputOriginEntriesPersisted(
0467: LaborCorrectionDocument document) {
0468: String fullPathUniqueFileName = generateOutputOriginEntryFileName(document);
0469: File file = new File(fullPathUniqueFileName);
0470: return file.exists();
0471: }
0472:
0473: /**
0474: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#writePersistedInputOriginEntriesToStream(java.io.OutputStream)
0475: */
0476: public void writePersistedInputOriginEntriesToStream(
0477: LaborCorrectionDocument document, OutputStream out)
0478: throws IOException {
0479: String fullPathUniqueFileName = generateInputOriginEntryFileName(document);
0480: writePersistedOriginEntriesToStream(fullPathUniqueFileName, out);
0481: }
0482:
0483: /**
0484: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#writePersistedOutputOriginEntriesToStream(java.io.OutputStream)
0485: */
0486: public void writePersistedOutputOriginEntriesToStream(
0487: LaborCorrectionDocument document, OutputStream out)
0488: throws IOException {
0489: String fullPathUniqueFileName = generateOutputOriginEntryFileName(document);
0490: writePersistedOriginEntriesToStream(fullPathUniqueFileName, out);
0491: }
0492:
0493: /**
0494: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#writePersistedOriginEntriesToStream(java.lang.String,
0495: * java.io.OutputStream)
0496: */
0497: protected void writePersistedOriginEntriesToStream(
0498: String fullPathUniqueFileName, OutputStream out)
0499: throws IOException {
0500: FileInputStream fileIn = new FileInputStream(
0501: fullPathUniqueFileName);
0502:
0503: try {
0504: byte[] buf = new byte[1000];
0505: int bytesRead;
0506:
0507: while ((bytesRead = fileIn.read(buf)) != -1) {
0508: out.write(buf, 0, bytesRead);
0509: }
0510: } finally {
0511: fileIn.close();
0512: }
0513: }
0514:
0515: /**
0516: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#writePersistedOriginEntriesToStream(org.kuali.module.labor.document.LaborCorrectionDocument,
0517: * org.kuali.module.gl.util)
0518: */
0519: public void persistOriginEntryGroupsForDocumentSave(
0520: LaborCorrectionDocument document,
0521: CorrectionDocumentEntryMetadata correctionDocumentEntryMetadata) {
0522: if (correctionDocumentEntryMetadata.getAllEntries() == null
0523: && !correctionDocumentEntryMetadata
0524: .isRestrictedFunctionalityMode()) {
0525: // if we don't have origin entries loaded and not in restricted functionality mode, then there's nothing worth
0526: // persisting
0527: removePersistedInputOriginEntries(document);
0528: removePersistedOutputOriginEntries(document);
0529: return;
0530: }
0531:
0532: if (!correctionDocumentEntryMetadata.getDataLoadedFlag()
0533: && !correctionDocumentEntryMetadata
0534: .isRestrictedFunctionalityMode()) {
0535: // data is not loaded (maybe user selected a new group with no rows)
0536: // clear out existing data
0537: removePersistedInputOriginEntries(document);
0538: removePersistedOutputOriginEntries(document);
0539: return;
0540: }
0541:
0542: // reload the group from the origin entry service
0543: Iterator<LaborOriginEntry> inputGroupEntries;
0544: KualiWorkflowDocument workflowDocument = document
0545: .getDocumentHeader().getWorkflowDocument();
0546: if ((workflowDocument.stateIsSaved() && !(correctionDocumentEntryMetadata
0547: .getInputGroupIdFromLastDocumentLoad() != null && correctionDocumentEntryMetadata
0548: .getInputGroupIdFromLastDocumentLoad().equals(
0549: document.getCorrectionInputGroupId())))
0550: || workflowDocument.stateIsInitiated()) {
0551: // we haven't saved the origin entry group yet, so let's load the entries from the DB and persist them for the document
0552: // this could be because we've previously saved the doc, but now we are now using a new input group, so we have to
0553: // repersist the input group
0554: OriginEntryGroup group = originEntryGroupService
0555: .getExactMatchingEntryGroup(document
0556: .getCorrectionInputGroupId());
0557: inputGroupEntries = laborOriginEntryService
0558: .getEntriesByGroup(group);
0559: persistInputOriginEntriesForInitiatedOrSavedDocument(
0560: document, inputGroupEntries);
0561:
0562: // we've exhausted the iterator for the origin entries group
0563: // reload the iterator from the file
0564: inputGroupEntries = retrievePersistedInputOriginEntriesAsIterator(document);
0565: } else if (workflowDocument.stateIsSaved()
0566: && correctionDocumentEntryMetadata
0567: .getInputGroupIdFromLastDocumentLoad().equals(
0568: document.getCorrectionInputGroupId())) {
0569: // we've saved the origin entries before, so just retrieve them
0570: inputGroupEntries = retrievePersistedInputOriginEntriesAsIterator(document);
0571: } else {
0572: LOG
0573: .error("Unexpected state while trying to persist/retrieve GLCP origin entries during document save: document status is "
0574: + workflowDocument.getStatusDisplayValue()
0575: + " selected input group: "
0576: + document.getCorrectionInputGroupId()
0577: + " last saved input group: "
0578: + correctionDocumentEntryMetadata
0579: .getInputGroupIdFromLastDocumentLoad());
0580: throw new RuntimeException(
0581: "Error persisting GLCP document origin entries.");
0582: }
0583:
0584: OriginEntryStatistics statistics;
0585: if (LaborCorrectionDocumentService.CORRECTION_TYPE_MANUAL
0586: .equals(correctionDocumentEntryMetadata.getEditMethod())) {
0587: // persist the allEntries element as the output group, since it has all of the modifications made by during the manual
0588: // edits
0589: Collection allEntries = new ArrayList();
0590: allEntries.addAll(correctionDocumentEntryMetadata
0591: .getAllEntries());
0592: persistOutputLaborOriginEntriesForInitiatedOrSavedDocument(
0593: document, allEntries.iterator());
0594:
0595: // even though the struts action handler may have computed the doc totals, let's recompute them
0596: statistics = CorrectionDocumentUtils
0597: .getStatistics(correctionDocumentEntryMetadata
0598: .getAllEntries());
0599: } else if (LaborCorrectionDocumentService.CORRECTION_TYPE_CRITERIA
0600: .equals(correctionDocumentEntryMetadata.getEditMethod())) {
0601: // we want to persist the values of the output group. So reapply all of the criteria on each entry, one at a time
0602:
0603: BufferedOutputStream bufferedOutputStream = null;
0604: try {
0605: bufferedOutputStream = openEntryOutputStreamForOutputGroup(document);
0606: statistics = new OriginEntryStatistics();
0607: byte[] newLine = "\n".getBytes();
0608:
0609: while (inputGroupEntries.hasNext()) {
0610: LaborOriginEntry entry = inputGroupEntries.next();
0611:
0612: entry = (LaborOriginEntry) CorrectionDocumentUtils
0613: .applyCriteriaToEntry(entry,
0614: correctionDocumentEntryMetadata
0615: .getMatchCriteriaOnly(),
0616: document.getCorrectionChangeGroup());
0617: if (entry != null) {
0618: CorrectionDocumentUtils
0619: .updateStatisticsWithEntry(entry,
0620: statistics);
0621: bufferedOutputStream.write(entry.getLine()
0622: .getBytes());
0623: bufferedOutputStream.write(newLine);
0624: }
0625: // else it was null, which means that the match criteria only flag was set, and the entry didn't match the
0626: // criteria
0627: }
0628: } catch (IOException e) {
0629: LOG
0630: .error(
0631: "Unable to persist persisted output entry",
0632: e);
0633: throw new RuntimeException(
0634: "Unable to persist output entry");
0635: } finally {
0636: if (bufferedOutputStream != null) {
0637: try {
0638: bufferedOutputStream.close();
0639: } catch (IOException e) {
0640: LOG
0641: .error(
0642: "Unable to close output stream for persisted output entries",
0643: e);
0644: throw new RuntimeException(
0645: "Unable to close output entry file");
0646: }
0647: }
0648: }
0649: } else if (LaborCorrectionDocumentService.CORRECTION_TYPE_REMOVE_GROUP_FROM_PROCESSING
0650: .equals(correctionDocumentEntryMetadata.getEditMethod())) {
0651: // just wipe out the previous output entries
0652: removePersistedOutputOriginEntries(document);
0653: statistics = new OriginEntryStatistics();
0654: } else {
0655: throw new RuntimeException("Unrecognized edit method: "
0656: + correctionDocumentEntryMetadata.getEditMethod());
0657: }
0658:
0659: CorrectionDocumentUtils.copyStatisticsToDocument(statistics,
0660: document);
0661: }
0662:
0663: /**
0664: * Gets the OriginEntryStagingDirectoryPath attribute.
0665: *
0666: * @return Returns the getLlcpDirectoryName.
0667: */
0668: protected String getOriginEntryStagingDirectoryPath() {
0669: return getLlcpDirectoryName();
0670: }
0671:
0672: /**
0673: * Gets the kualiConfigurationService attribute.
0674: *
0675: * @return Returns the kualiConfigurationService.
0676: */
0677: public KualiConfigurationService getKualiConfigurationService() {
0678: return kualiConfigurationService;
0679: }
0680:
0681: /**
0682: * Sets the kualiConfigurationService attribute value.
0683: *
0684: * @param kualiConfigurationService The kualiConfigurationService to set.
0685: */
0686: public void setKualiConfigurationService(
0687: KualiConfigurationService kualiConfigurationService) {
0688: this .kualiConfigurationService = kualiConfigurationService;
0689: }
0690:
0691: /**
0692: * Sets the originEntryService attribute value.
0693: *
0694: * @param originEntryService The originEntryService to set.
0695: */
0696: public void setLaborOriginEntryService(
0697: LaborOriginEntryService laborOriginEntryService) {
0698: this .laborOriginEntryService = laborOriginEntryService;
0699: }
0700:
0701: /**
0702: * Gets the llcpDirectoryName attribute.
0703: *
0704: * @return Returns the llcpDirectoryName.
0705: */
0706: public String getLlcpDirectoryName() {
0707: return llcpDirectoryName;
0708: }
0709:
0710: /**
0711: * Sets the llcpDirectoryName attribute value.
0712: *
0713: * @param llcpDirectoryName The llcpDirectoryName to set.
0714: */
0715: public void setLlcpDirectoryName(String llcpDirectoryName) {
0716: this .llcpDirectoryName = llcpDirectoryName;
0717: }
0718:
0719: /**
0720: * Gets the originEntryGroupService attribute.
0721: *
0722: * @return Returns the originEntryGroupService.
0723: */
0724: public OriginEntryGroupService getOriginEntryGroupService() {
0725: return originEntryGroupService;
0726: }
0727:
0728: /**
0729: * Sets the originEntryGroupService attribute value.
0730: *
0731: * @param originEntryGroupService The originEntryGroupService to set.
0732: */
0733: public void setOriginEntryGroupService(
0734: OriginEntryGroupService originEntryGroupService) {
0735: this .originEntryGroupService = originEntryGroupService;
0736: }
0737:
0738: /**
0739: * @see org.kuali.module.labor.service.LaborCorrectionDocumentService#getTableRenderColumnMetadata(java.lang.String)
0740: */
0741: public List<Column> getTableRenderColumnMetadata(String docId) {
0742: synchronized (this ) {
0743: if (cachedColumns == null) {
0744: cachedColumns = new ArrayList<Column>();
0745: Column columnToAdd;
0746:
0747: columnToAdd = new Column();
0748: columnToAdd.setColumnTitle("Fiscal Year");
0749: columnToAdd
0750: .setPropertyName(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR);
0751: columnToAdd.setValueComparator(NumericValueComparator
0752: .getInstance());
0753: cachedColumns.add(columnToAdd);
0754:
0755: columnToAdd = new Column();
0756: columnToAdd.setColumnTitle("Chart Code");
0757: columnToAdd
0758: .setPropertyName(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE);
0759: columnToAdd.setValueComparator(StringValueComparator
0760: .getInstance());
0761: cachedColumns.add(columnToAdd);
0762:
0763: columnToAdd = new Column();
0764: columnToAdd.setColumnTitle("Account Number");
0765: columnToAdd
0766: .setPropertyName(KFSPropertyConstants.ACCOUNT_NUMBER);
0767: columnToAdd.setValueComparator(StringValueComparator
0768: .getInstance());
0769: cachedColumns.add(columnToAdd);
0770:
0771: columnToAdd = new Column();
0772: columnToAdd.setColumnTitle("Sub Account Number");
0773: columnToAdd
0774: .setPropertyName(KFSPropertyConstants.SUB_ACCOUNT_NUMBER);
0775: columnToAdd.setValueComparator(StringValueComparator
0776: .getInstance());
0777: cachedColumns.add(columnToAdd);
0778:
0779: columnToAdd = new Column();
0780: columnToAdd.setColumnTitle("Object Code");
0781: columnToAdd
0782: .setPropertyName(KFSPropertyConstants.FINANCIAL_OBJECT_CODE);
0783: columnToAdd.setValueComparator(StringValueComparator
0784: .getInstance());
0785: cachedColumns.add(columnToAdd);
0786:
0787: columnToAdd = new Column();
0788: columnToAdd.setColumnTitle("Sub Object Code");
0789: columnToAdd
0790: .setPropertyName(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE);
0791: columnToAdd.setValueComparator(StringValueComparator
0792: .getInstance());
0793: cachedColumns.add(columnToAdd);
0794:
0795: columnToAdd = new Column();
0796: columnToAdd.setColumnTitle("Balance Type");
0797: columnToAdd
0798: .setPropertyName(KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE);
0799: columnToAdd.setValueComparator(StringValueComparator
0800: .getInstance());
0801: cachedColumns.add(columnToAdd);
0802:
0803: columnToAdd = new Column();
0804: columnToAdd.setColumnTitle("Object Type");
0805: columnToAdd
0806: .setPropertyName(KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE);
0807: columnToAdd.setValueComparator(StringValueComparator
0808: .getInstance());
0809: cachedColumns.add(columnToAdd);
0810:
0811: columnToAdd = new Column();
0812: columnToAdd.setColumnTitle("Fiscal Period");
0813: columnToAdd
0814: .setPropertyName(KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_CODE);
0815: columnToAdd.setValueComparator(StringValueComparator
0816: .getInstance());
0817: cachedColumns.add(columnToAdd);
0818:
0819: columnToAdd = new Column();
0820: columnToAdd.setColumnTitle("Document Type");
0821: columnToAdd
0822: .setPropertyName(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE);
0823: columnToAdd.setValueComparator(StringValueComparator
0824: .getInstance());
0825: cachedColumns.add(columnToAdd);
0826:
0827: columnToAdd = new Column();
0828: columnToAdd.setColumnTitle("Origin Code");
0829: columnToAdd
0830: .setPropertyName(KFSPropertyConstants.FINANCIAL_SYSTEM_ORIGINATION_CODE);
0831: columnToAdd.setValueComparator(StringValueComparator
0832: .getInstance());
0833: cachedColumns.add(columnToAdd);
0834:
0835: columnToAdd = new Column();
0836: columnToAdd.setColumnTitle("Document Number");
0837: columnToAdd
0838: .setPropertyName(KFSPropertyConstants.DOCUMENT_NUMBER);
0839: columnToAdd.setValueComparator(StringValueComparator
0840: .getInstance());
0841: cachedColumns.add(columnToAdd);
0842:
0843: columnToAdd = new Column();
0844: columnToAdd.setColumnTitle("Sequence Number");
0845: columnToAdd.setValueComparator(NumericValueComparator
0846: .getInstance());
0847: columnToAdd
0848: .setPropertyName(KFSPropertyConstants.TRANSACTION_ENTRY_SEQUENCE_NUMBER);
0849: cachedColumns.add(columnToAdd);
0850:
0851: columnToAdd = new Column();
0852: columnToAdd.setColumnTitle("Position Number");
0853: columnToAdd.setValueComparator(StringValueComparator
0854: .getInstance());
0855: columnToAdd
0856: .setPropertyName(KFSPropertyConstants.POSITION_NUMBER);
0857: cachedColumns.add(columnToAdd);
0858:
0859: columnToAdd = new Column();
0860: columnToAdd.setColumnTitle("Project Code");
0861: columnToAdd
0862: .setPropertyName(KFSPropertyConstants.PROJECT_CODE);
0863: columnToAdd.setValueComparator(StringValueComparator
0864: .getInstance());
0865: cachedColumns.add(columnToAdd);
0866:
0867: columnToAdd = new Column();
0868: columnToAdd.setColumnTitle("Description");
0869: columnToAdd
0870: .setPropertyName(KFSPropertyConstants.TRANSACTION_LEDGER_ENTRY_DESC);
0871: columnToAdd.setValueComparator(StringValueComparator
0872: .getInstance());
0873: cachedColumns.add(columnToAdd);
0874:
0875: columnToAdd = new Column();
0876: columnToAdd.setColumnTitle("Amount");
0877: columnToAdd.setValueComparator(NumericValueComparator
0878: .getInstance());
0879: columnToAdd
0880: .setPropertyName(KFSPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT);
0881: cachedColumns.add(columnToAdd);
0882:
0883: columnToAdd = new Column();
0884: columnToAdd.setColumnTitle("Debit Credit Indicator");
0885: columnToAdd
0886: .setPropertyName(KFSPropertyConstants.TRANSACTION_DEBIT_CREDIT_CODE);
0887: columnToAdd.setValueComparator(StringValueComparator
0888: .getInstance());
0889: cachedColumns.add(columnToAdd);
0890:
0891: columnToAdd = new Column();
0892: columnToAdd.setColumnTitle("Transaction Date");
0893: columnToAdd
0894: .setPropertyName(KFSPropertyConstants.TRANSACTION_DATE);
0895: columnToAdd.setValueComparator(TemporalValueComparator
0896: .getInstance());
0897: cachedColumns.add(columnToAdd);
0898:
0899: columnToAdd = new Column();
0900: columnToAdd.setColumnTitle("Org Doc Number");
0901: columnToAdd
0902: .setPropertyName(KFSPropertyConstants.ORGANIZATION_DOCUMENT_NUMBER);
0903: columnToAdd.setValueComparator(StringValueComparator
0904: .getInstance());
0905: cachedColumns.add(columnToAdd);
0906:
0907: columnToAdd = new Column();
0908: columnToAdd.setColumnTitle("Org Ref ID");
0909: columnToAdd
0910: .setPropertyName(KFSPropertyConstants.ORGANIZATION_REFERENCE_ID);
0911: columnToAdd.setValueComparator(StringValueComparator
0912: .getInstance());
0913: cachedColumns.add(columnToAdd);
0914:
0915: columnToAdd = new Column();
0916: columnToAdd.setColumnTitle("Ref Doc Type");
0917: columnToAdd
0918: .setPropertyName(KFSPropertyConstants.REFERENCE_FIN_DOCUMENT_TYPE_CODE);
0919: columnToAdd.setValueComparator(StringValueComparator
0920: .getInstance());
0921: cachedColumns.add(columnToAdd);
0922:
0923: columnToAdd = new Column();
0924: columnToAdd.setColumnTitle("Ref Origin Code");
0925: columnToAdd
0926: .setPropertyName(KFSPropertyConstants.REFERENCE_FINANCIAL_SYSTEM_ORIGINATION_CODE);
0927: columnToAdd.setValueComparator(StringValueComparator
0928: .getInstance());
0929: cachedColumns.add(columnToAdd);
0930:
0931: columnToAdd = new Column();
0932: columnToAdd.setColumnTitle("Ref Doc Number");
0933: columnToAdd
0934: .setPropertyName(KFSPropertyConstants.FINANCIAL_DOCUMENT_REFERENCE_NBR);
0935: columnToAdd.setValueComparator(StringValueComparator
0936: .getInstance());
0937: cachedColumns.add(columnToAdd);
0938:
0939: columnToAdd = new Column();
0940: columnToAdd.setColumnTitle("Reversal Date");
0941: columnToAdd
0942: .setPropertyName(KFSPropertyConstants.FINANCIAL_DOCUMENT_REVERSAL_DATE);
0943: columnToAdd.setValueComparator(StringValueComparator
0944: .getInstance());
0945: cachedColumns.add(columnToAdd);
0946:
0947: columnToAdd = new Column();
0948: columnToAdd.setColumnTitle("Enc Update Code");
0949: columnToAdd
0950: .setPropertyName(KFSPropertyConstants.TRANSACTION_ENCUMBRANCE_UPDT_CD);
0951: columnToAdd.setValueComparator(StringValueComparator
0952: .getInstance());
0953: cachedColumns.add(columnToAdd);
0954:
0955: columnToAdd = new Column();
0956: columnToAdd.setColumnTitle("Transaction Posting Date");
0957: columnToAdd.setValueComparator(TemporalValueComparator
0958: .getInstance());
0959: columnToAdd
0960: .setPropertyName(KFSPropertyConstants.TRANSACTION_POSTING_DATE);
0961: cachedColumns.add(columnToAdd);
0962:
0963: columnToAdd = new Column();
0964: columnToAdd.setColumnTitle("Pay Period End Date");
0965: columnToAdd.setValueComparator(TemporalValueComparator
0966: .getInstance());
0967: columnToAdd
0968: .setPropertyName(LaborPropertyConstants.PAY_PERIOD_END_DATE);
0969: cachedColumns.add(columnToAdd);
0970:
0971: columnToAdd = new Column();
0972: columnToAdd.setColumnTitle("Trn Total Hours");
0973: columnToAdd.setValueComparator(NumericValueComparator
0974: .getInstance());
0975: columnToAdd
0976: .setPropertyName(LaborPropertyConstants.TRANSACTION_TOTAL_HOURS);
0977: cachedColumns.add(columnToAdd);
0978:
0979: columnToAdd = new Column();
0980: columnToAdd
0981: .setColumnTitle("Payroll EndDate Fiscal Year");
0982: columnToAdd.setValueComparator(NumericValueComparator
0983: .getInstance());
0984: columnToAdd
0985: .setPropertyName(LaborPropertyConstants.PAYROLL_END_DATE_FISCAL_YEAR);
0986: cachedColumns.add(columnToAdd);
0987:
0988: columnToAdd = new Column();
0989: columnToAdd
0990: .setColumnTitle("Payroll EndDate Fiscal Period Code");
0991: columnToAdd.setValueComparator(StringValueComparator
0992: .getInstance());
0993: columnToAdd
0994: .setPropertyName(LaborPropertyConstants.PAYROLL_END_DATE_FISCAL_PERIOD_CODE);
0995: cachedColumns.add(columnToAdd);
0996:
0997: columnToAdd = new Column();
0998: columnToAdd.setColumnTitle("Empl Id");
0999: columnToAdd.setValueComparator(StringValueComparator
1000: .getInstance());
1001: columnToAdd
1002: .setPropertyName(KFSPropertyConstants.EMPLID);
1003: cachedColumns.add(columnToAdd);
1004:
1005: columnToAdd = new Column();
1006: columnToAdd.setColumnTitle("Empl Record");
1007: columnToAdd.setValueComparator(NumericValueComparator
1008: .getInstance());
1009: columnToAdd
1010: .setPropertyName(KFSPropertyConstants.EMPLOYEE_RECORD);
1011: cachedColumns.add(columnToAdd);
1012:
1013: columnToAdd = new Column();
1014: columnToAdd.setColumnTitle("Earn Code");
1015: columnToAdd.setValueComparator(StringValueComparator
1016: .getInstance());
1017: columnToAdd
1018: .setPropertyName(LaborPropertyConstants.EARN_CODE);
1019: cachedColumns.add(columnToAdd);
1020:
1021: columnToAdd = new Column();
1022: columnToAdd.setColumnTitle("Pay Group");
1023: columnToAdd.setValueComparator(StringValueComparator
1024: .getInstance());
1025: columnToAdd
1026: .setPropertyName(LaborPropertyConstants.PAY_GROUP);
1027: cachedColumns.add(columnToAdd);
1028:
1029: columnToAdd = new Column();
1030: columnToAdd.setColumnTitle("Salary Admin Plan");
1031: columnToAdd.setValueComparator(StringValueComparator
1032: .getInstance());
1033: columnToAdd
1034: .setPropertyName(LaborPropertyConstants.SALARY_ADMINISTRATION_PLAN);
1035: cachedColumns.add(columnToAdd);
1036:
1037: columnToAdd = new Column();
1038: columnToAdd.setColumnTitle("Grade");
1039: columnToAdd.setValueComparator(StringValueComparator
1040: .getInstance());
1041: columnToAdd
1042: .setPropertyName(LaborPropertyConstants.GRADE);
1043: cachedColumns.add(columnToAdd);
1044:
1045: columnToAdd = new Column();
1046: columnToAdd.setColumnTitle("Run Id");
1047: columnToAdd.setValueComparator(StringValueComparator
1048: .getInstance());
1049: columnToAdd
1050: .setPropertyName(LaborPropertyConstants.RUN_IDENTIFIER);
1051: cachedColumns.add(columnToAdd);
1052:
1053: columnToAdd = new Column();
1054: columnToAdd.setColumnTitle("Original Chart Code");
1055: columnToAdd.setValueComparator(StringValueComparator
1056: .getInstance());
1057: columnToAdd
1058: .setPropertyName(LaborPropertyConstants.LABORLEDGER_ORIGINAL_CHART_OF_ACCOUNTS_CODE);
1059: cachedColumns.add(columnToAdd);
1060:
1061: columnToAdd = new Column();
1062: columnToAdd.setColumnTitle("Original Account Number");
1063: columnToAdd.setValueComparator(StringValueComparator
1064: .getInstance());
1065: columnToAdd
1066: .setPropertyName(LaborPropertyConstants.LABORLEDGER_ORIGINAL_ACCOUNT_NUMBER);
1067: cachedColumns.add(columnToAdd);
1068:
1069: columnToAdd = new Column();
1070: columnToAdd
1071: .setColumnTitle("Original Sub-Account Number");
1072: columnToAdd.setValueComparator(StringValueComparator
1073: .getInstance());
1074: columnToAdd
1075: .setPropertyName(LaborPropertyConstants.LABORLEDGER_ORIGINAL_SUB_ACCOUNT_NUMBER);
1076: cachedColumns.add(columnToAdd);
1077:
1078: columnToAdd = new Column();
1079: columnToAdd.setColumnTitle("Original Object Code");
1080: columnToAdd.setValueComparator(StringValueComparator
1081: .getInstance());
1082: columnToAdd
1083: .setPropertyName(LaborPropertyConstants.LABORLEDGER_ORIGINAL_FINANCIAL_OBJECT_CODE);
1084: cachedColumns.add(columnToAdd);
1085:
1086: columnToAdd = new Column();
1087: columnToAdd.setColumnTitle("Original Sub-Object Code");
1088: columnToAdd.setValueComparator(StringValueComparator
1089: .getInstance());
1090: columnToAdd
1091: .setPropertyName(LaborPropertyConstants.LABORLEDGER_ORIGINAL_FINANCIAL_SUB_OBJECT_CODE);
1092: cachedColumns.add(columnToAdd);
1093:
1094: columnToAdd = new Column();
1095: columnToAdd.setColumnTitle("Company");
1096: columnToAdd.setValueComparator(StringValueComparator
1097: .getInstance());
1098: columnToAdd
1099: .setPropertyName(LaborPropertyConstants.HRMS_COMPANY);
1100: cachedColumns.add(columnToAdd);
1101:
1102: columnToAdd = new Column();
1103: columnToAdd.setColumnTitle("SetId");
1104: columnToAdd.setValueComparator(StringValueComparator
1105: .getInstance());
1106: columnToAdd
1107: .setPropertyName(LaborPropertyConstants.SET_ID);
1108: cachedColumns.add(columnToAdd);
1109:
1110: cachedColumns = Collections
1111: .unmodifiableList(cachedColumns);
1112: }
1113: }
1114: return cachedColumns;
1115: }
1116:
1117: }
|