001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/calendar/tags/sakai_2-4-1/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/readers/Reader.java $
003: * $Id: Reader.java 8050 2006-04-20 17:39:55Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.calendar.impl.readers;
021:
022: import java.io.BufferedReader;
023: import java.io.InputStream;
024: import java.io.InputStreamReader;
025: import java.util.ArrayList;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029:
030: import org.sakaiproject.exception.ImportException;
031: import org.sakaiproject.time.api.TimeService;
032:
033: /**
034: * This class provides common functionality for parsers of various
035: * calendar import formats.
036: */
037: public abstract class Reader {
038: protected Map columnHeaderMap;
039: protected TimeService timeService;
040:
041: /**
042: * Contains header information such as the text label used for the
043: * header and the calendar event property with which it is associated.
044: **/
045: public class ColumnHeader {
046: private String columnProperty;
047: private String columnHeader;
048:
049: /**
050: * Default constructor
051: */
052: public ColumnHeader() {
053: super ();
054: }
055:
056: /**
057: * Construct a ColumnHeader with a specified text label used in the import
058: * file and the Calendar Event property that is associated with it.
059: * @param columnHeader
060: * @param columnProperty
061: */
062: public ColumnHeader(String columnHeader, String columnProperty) {
063: this .columnHeader = columnHeader;
064: this .columnProperty = columnProperty;
065: }
066:
067: /**
068: * Gets the column header as it appears in the import file.
069: */
070: public String getColumnHeader() {
071: return columnHeader;
072: }
073:
074: /**
075: * Gets the calendar event property name associated with this header.
076: */
077: public String getColumnProperty() {
078: return columnProperty;
079: }
080:
081: }
082:
083: /**
084: * Default Constructor
085: */
086: public Reader() {
087: super ();
088:
089: // Use whatever the default column mapping.
090: this .setColumnHeaderToAtributeMapping(this
091: .getDefaultColumnMap());
092: }
093:
094: /**
095: * This class contains the information for a single cell in a given row/column.
096: */
097: static public class ReaderImportCell {
098: private String columnHeader;
099: private String value;
100: private int lineNumber;
101: private int column;
102: private String propertyName;
103:
104: /**
105: * @param row
106: * @param column
107: * @param value
108: * @param propertyName
109: * @param columnHeader
110: */
111: ReaderImportCell(int row, int column, String value,
112: String propertyName, String columnHeader) {
113: super ();
114: this .lineNumber = row;
115: this .column = column;
116: this .value = value;
117: this .propertyName = propertyName;
118: this .columnHeader = columnHeader;
119: }
120:
121: /**
122: * Gets the calendar event property name associated with this header.
123: */
124: public String getPropertyName() {
125: return propertyName;
126: }
127:
128: /**
129: * Gets the zero-based column number.
130: */
131: public int getColumnNumber() {
132: return column;
133: }
134:
135: /**
136: * Gets the one-based row number.
137: */
138: public int getLineNumber() {
139: return lineNumber;
140: }
141:
142: /**
143: * Gets the value of the cell as a string. No type conversion is performed,
144: * this is just the string read in from the stream.
145: */
146: public String getCellValue() {
147: return value;
148: }
149:
150: /**
151: * Gets the text header used in the import file for this column.
152: */
153: public String getColumnHeader() {
154: return columnHeader;
155: }
156:
157: }
158:
159: /**
160: * Users of this class need to define a callback that will be handled for
161: * each row.
162: */
163: public interface ReaderImportRowHandler {
164: /**
165: * This is the callback that is called for each row.
166: * @param columnIterator Iterator for a collection of CSVReaderImportCell for this row.
167: */
168: void handleRow(Iterator columnIterator) throws ImportException;
169: }
170:
171: /**
172: * Create meta-information from the first line of the "file" (actually stream)
173: * that contains the names of the columns.
174: * @param columns
175: */
176: protected ColumnHeader[] buildColumnDescriptionArray(
177: String[] columns)
178:
179: {
180: ColumnHeader[] columnDescriptionArray;
181: columnDescriptionArray = new ColumnHeader[columns.length];
182:
183: for (int i = 0; i < columns.length; i++) {
184: columnDescriptionArray[i] = new ColumnHeader(columns[i],
185: (String) columnHeaderMap.get(columns[i]));
186: }
187: return columnDescriptionArray;
188: }
189:
190: /**
191: * Remove leading/trailing quotes
192: * @param columnsReadFromFile
193: */
194: protected void trimLeadingTrailingQuotes(
195: String[] columnsReadFromFile) {
196: for (int i = 0; i < columnsReadFromFile.length; i++) {
197: String regex2 = "(?:\")*([^\"]+)(?:\")*";
198: columnsReadFromFile[i] = columnsReadFromFile[i].trim()
199: .replaceAll(regex2, "$1");
200: }
201: }
202:
203: /**
204: * Users of this class must define a map where the keys are the column headers
205: * that will appear in the first line of the CSV file (stream) and the values
206: * are the associated property names that the callback will receive.
207: * @param columnList
208: */
209: public void setColumnHeaderToAtributeMapping(Map columnHeaderMap) {
210: this .columnHeaderMap = columnHeaderMap;
211: }
212:
213: /**
214: * Split a line into a list of CSVReaderImportCell objects.
215: * @param columnDescriptionArray
216: * @param lineNumber
217: * @param columns
218: */
219: protected Iterator processLine(
220: ColumnHeader[] columnDescriptionArray, int lineNumber,
221: String[] columns) {
222: List list = new ArrayList();
223:
224: for (int i = 0; i < columns.length; i++) {
225: if (i >= columnDescriptionArray.length) {
226: continue;
227: } else {
228: list.add(new ReaderImportCell(lineNumber, i,
229: columns[i], columnDescriptionArray[i]
230: .getColumnProperty(),
231: columnDescriptionArray[i].getColumnHeader()));
232: }
233: }
234:
235: return list.iterator();
236: }
237:
238: /**
239: * Utility routine to get a BufferedReader
240: * @param stream
241: */
242: protected BufferedReader getReader(InputStream stream) {
243: InputStreamReader inStreamReader = new InputStreamReader(stream);
244: BufferedReader bufferedReader = new BufferedReader(
245: inStreamReader);
246: return bufferedReader;
247: }
248:
249: /**
250: * Import a CSV file from a stream and callback on each row.
251: * @param stream Stream of CSV (or other delimited data)
252: * @param handler Callback for each row.
253: */
254: abstract public void importStreamFromDelimitedFile(
255: InputStream stream, ReaderImportRowHandler handler)
256: throws ImportException;
257:
258: /**
259: * Gets the mapping of text column header labels in the import file to
260: * calendar event properties.
261: */
262: public Map getColumnHeaderMap()
263:
264: {
265: return columnHeaderMap;
266: }
267:
268: /**
269: * Each derived class must implement this filter to convert the properties as set by the
270: * reader into a common set of properties that will be used to create calendar events.
271: * Notably, this filter must create a ScheduleImporterService.ACTUAL_TIMERANGE property
272: * that will define the actual start time/date of the event.
273: * @param importStream
274: * @param customFieldNames
275: * @throws ImportException
276: */
277: abstract public List filterEvents(List events,
278: String[] customFieldNames) throws ImportException;
279:
280: /**
281: * Derived classes must provide a default mapping of text column header labels in the import file to
282: * calendar event properties.
283: * @throws ImportException
284: */
285: abstract public Map getDefaultColumnMap();
286:
287: /**
288: */
289: public TimeService getTimeService() {
290: return timeService;
291: }
292:
293: /**
294: * @param service
295: */
296: public void setTimeService(TimeService service) {
297: timeService = service;
298: }
299:
300: }
|