001: /*
002:
003: Derby - Class org.apache.derby.impl.load.ControlInfo
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
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: */
021:
022: package org.apache.derby.impl.load;
023:
024: import java.io.PrintStream;
025: import java.io.InputStream;
026: import java.io.InputStreamReader;
027: import java.util.Date;
028: import java.util.Properties;
029:
030: //read the control file properties. If the passed parameter for control file
031: //name is null, assigns default values to the properties. Also, if the control
032: //file has message property in it, it sends the errors to that file by
033: //redirecting system err to that message file
034: class ControlInfo {
035:
036: static final String ESCAPE = "Escape";
037: static final String DEFAULT_ESCAPE = "\\";
038: static final String QUOTE = "Quote";
039: static final String DEFAULT_QUOTE = "'";
040: static final String COMMIT_COUNT = "CommitCount";
041: static final String DEFAULT_COMMIT_COUNT = "0";
042: static final String START_ROW = "StartRow";
043: static final String DEFAULT_START_ROW = "1";
044: static final String STOP_ROW = "StopRow";
045: static final String DEFAULT_STOP_ROW = "0";
046:
047: static final String FIELD_SEPARATOR = "FieldSeparator";
048: static final String DEFAULT_FIELD_SEPARATOR = ",";
049: static final String RECORD_SEPARATOR = "RecordSeparator";
050: static final String DEFAULT_RECORD_SEPARATOR = System
051: .getProperty("line.separator");
052: static final String COLUMN_DEFINITION = "ColumnDefinition";
053: static final String DEFAULT_COLUMN_DEFINITION = "FALSE";
054: static final String NULL_STRING = "Null";
055: static final String DEFAULT_NULL_STRING = "NULL";
056: static final String FORMAT = "Format";
057: static final String DEFAULT_FORMAT = "ASCII_DELIMITED";
058: static final String DB2_DELIMITED_FORMAT = "DB2_DELIMITED"; //beetle 5007
059: static final String FIELD_START_DELIMITER = "FieldStartDelimiter";
060: static final String DEFAULT_FIELD_START_DELIMITER = "\"";
061: static final String FIELD_END_DELIMITER = "FieldEndDelimiter";
062: static final String DEFAULT_FIELD_END_DELIMITER = "\"";
063: static final String COLUMN_WIDTHS = "ColumnWidths";
064: static final String MESSAGE_FILE = "MessageFile";
065: static final String DEFAULT_VERSION = "1";
066: static final String VERSION = "Version";
067: static final String NEWLINE = "\n";
068: static final String COMMA = ",";
069: static final String SPACE = " ";
070: static final String TAB = "\t";
071: static final String CR = "\r";
072: static final String LF = "\n";
073: static final String CRLF = "\r\n";
074: static final String LFCR = "\n\r";
075: static final String FF = "\f";
076: static final String EMPTY_LINE = "\n\n";
077: static final String SEMICOLON = ";";
078: static final String DATA_CODESET = "DataCodeset";
079: static final String HAS_DELIMETER_AT_END = "HasDelimeterAtEnd";
080:
081: static final String INTERNAL_NONE = "None";
082: static final String INTERNAL_TRUE = "True";
083: static final String INTERNAL_FALSE = "False";
084: static final String INTERNAL_TAB = "Tab";
085: static final String INTERNAL_SPACE = "Space";
086: static final String INTERNAL_CR = "CR";
087: static final String INTERNAL_LF = "LF";
088: static final String INTERNAL_CRLF = "CR-LF";
089: static final String INTERNAL_LFCR = "LF-CR";
090: static final String INTERNAL_COMMA = "Comma";
091: static final String INTERNAL_SEMICOLON = "Semicolon";
092: static final String INTERNAL_NEWLINE = "New Line";
093: static final String INTERNAL_FF = "FF";
094: static final String INTERNAL_EMPTY_LINE = "Empty line";
095:
096: private Properties currentProperties;
097:
098: public ControlInfo() throws Exception {
099: getCurrentProperties();
100: //the field and record separators can't be subset of each other
101: if (getFieldSeparator().indexOf(getRecordSeparator()) != -1) {
102: throw LoadError.fieldAndRecordSeparatorsSubset();
103: }
104: }
105:
106: //read the value of a given property
107: String getPropertyValue(String aKey) throws Exception {
108: return getCurrentProperties().getProperty(aKey);
109: }
110:
111: //following are the default values for few of the properties
112: private void loadDefaultValues() {
113: currentProperties = new Properties();
114: currentProperties.put(FIELD_SEPARATOR, DEFAULT_FIELD_SEPARATOR);
115: currentProperties.put(RECORD_SEPARATOR,
116: DEFAULT_RECORD_SEPARATOR);
117: currentProperties.put(COLUMN_DEFINITION,
118: DEFAULT_COLUMN_DEFINITION);
119: currentProperties.put(NULL_STRING, DEFAULT_NULL_STRING);
120: currentProperties.put(FORMAT, DEFAULT_FORMAT);
121: currentProperties.put(FIELD_START_DELIMITER,
122: DEFAULT_FIELD_START_DELIMITER);
123: currentProperties.put(FIELD_END_DELIMITER,
124: DEFAULT_FIELD_END_DELIMITER);
125: currentProperties.put(VERSION, DEFAULT_VERSION);
126: currentProperties.put(HAS_DELIMETER_AT_END, INTERNAL_FALSE);
127: }
128:
129: //get control file version.
130: String getCurrentVersion() throws Exception {
131: return (DEFAULT_VERSION);
132: }
133:
134: //2 possible formats: fixed and delimited. default is ASCII_DELIMITED
135: String getFormat() throws Exception {
136: return (getCurrentProperties().getProperty(FORMAT));
137: }
138:
139: //read the column widths property which is comma delimited.
140: //In case of fixed format, if column widths are missing, it will
141: //throw an exception
142: int[] getColumnWidths() {
143: return null;
144: }
145:
146: //default is DEFAULT_FIELD_SEPARATOR
147: String getFieldSeparator() throws Exception {
148: String fieldSeparator = getCurrentProperties().getProperty(
149: FIELD_SEPARATOR);
150: fieldSeparator = mapFromUserFriendlyFieldDelimiters(fieldSeparator);
151: return fieldSeparator;
152: }
153:
154: String getFieldStartDelimiter() throws Exception {
155: return (getCurrentProperties()
156: .getProperty(FIELD_START_DELIMITER));
157: }
158:
159: String getFieldEndDelimiter() throws Exception {
160: return (getCurrentProperties().getProperty(FIELD_END_DELIMITER));
161: }
162:
163: String getRecordSeparator() throws Exception {
164: String recordSeparator = getCurrentProperties().getProperty(
165: RECORD_SEPARATOR);
166: recordSeparator = mapFromUserFriendlyRecordDelimiters(recordSeparator);
167: return recordSeparator;
168: }
169:
170: //to be used to cover cases where column delimeters are placed at the end of
171: //each column resulting in an extra delimeter at the end of a row.
172: boolean getHasDelimiterAtEnd() throws Exception {
173: String hasDelimeterAtEnd = getCurrentProperties().getProperty(
174: HAS_DELIMETER_AT_END);
175: return hasDelimeterAtEnd.equals(INTERNAL_TRUE);
176: }
177:
178: String getHasDelimeterAtEndString() throws Exception {
179: String hasDelimeterAtEnd = getCurrentProperties().getProperty(
180: HAS_DELIMETER_AT_END);
181: return hasDelimeterAtEnd;
182: }
183:
184: //if at the time of export, the column has null into it, we will spit
185: //nullString in the output file.
186: //If at the time of import, we see nullString for a column, we will
187: //send null as part of resultSet interface
188: String getNullString() throws Exception {
189: return (getCurrentProperties().getProperty(NULL_STRING));
190: }
191:
192: //for fixed format, get column definitions
193: String getColumnDefinition() throws Exception {
194: return (getCurrentProperties().getProperty(COLUMN_DEFINITION));
195: }
196:
197: private String mapFromUserFriendlyFieldDelimiters(String aDelimiter) {
198: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
199: INTERNAL_TAB.toUpperCase(java.util.Locale.ENGLISH)))
200: return TAB;
201: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
202: INTERNAL_SPACE.toUpperCase(java.util.Locale.ENGLISH)))
203: return SPACE;
204: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
205: INTERNAL_CR.toUpperCase(java.util.Locale.ENGLISH)))
206: return CR;
207: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
208: INTERNAL_LF.toUpperCase(java.util.Locale.ENGLISH)))
209: return LF;
210: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
211: INTERNAL_CRLF.toUpperCase(java.util.Locale.ENGLISH)))
212: return CRLF;
213: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
214: INTERNAL_LFCR.toUpperCase(java.util.Locale.ENGLISH)))
215: return LFCR;
216: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
217: INTERNAL_COMMA.toUpperCase(java.util.Locale.ENGLISH)))
218: return COMMA;
219: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
220: INTERNAL_SEMICOLON
221: .toUpperCase(java.util.Locale.ENGLISH)))
222: return SEMICOLON;
223:
224: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
225: "\\n", '\n');
226: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
227: "\\t", '\t');
228: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
229: "\\r", '\r');
230: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
231: "\\f", '\f');
232: return aDelimiter;
233: }
234:
235: //vjbms: when user types \n in vjbms, it comes as 2 characters \ and n
236: //and not just one character '\n' That's the reason for the following
237: //check. I look for "\n" and replace it with '\n'. Same thing for \t
238: // \r and \f
239: private String commonToFieldAndRecordDelimiters(String aDelimiter,
240: String specialChars, char replacementChar) {
241: String beforeSpecialChars;
242: String afterSpecialChars;
243: int specialCharsPosition;
244: while (aDelimiter.indexOf(specialChars) != -1) {
245: specialCharsPosition = aDelimiter.indexOf(specialChars);
246: beforeSpecialChars = aDelimiter.substring(0,
247: specialCharsPosition);
248: afterSpecialChars = aDelimiter
249: .substring(specialCharsPosition + 2);
250: aDelimiter = beforeSpecialChars + replacementChar
251: + afterSpecialChars;
252: }
253: return aDelimiter;
254: }
255:
256: private String mapFromUserFriendlyRecordDelimiters(String aDelimiter) {
257: if (aDelimiter.equals("\n"))
258: aDelimiter = INTERNAL_NEWLINE;
259: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
260: INTERNAL_NEWLINE.toUpperCase(java.util.Locale.ENGLISH)))
261: return NEWLINE;
262: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
263: INTERNAL_CR.toUpperCase(java.util.Locale.ENGLISH)))
264: return CR;
265: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
266: INTERNAL_LF.toUpperCase(java.util.Locale.ENGLISH)))
267: return LF;
268: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
269: INTERNAL_CRLF.toUpperCase(java.util.Locale.ENGLISH)))
270: return CRLF;
271: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
272: INTERNAL_LFCR.toUpperCase(java.util.Locale.ENGLISH)))
273: return LFCR;
274: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
275: INTERNAL_FF.toUpperCase(java.util.Locale.ENGLISH)))
276: return FF;
277: if (aDelimiter.toUpperCase(java.util.Locale.ENGLISH).equals(
278: INTERNAL_EMPTY_LINE
279: .toUpperCase(java.util.Locale.ENGLISH)))
280: return EMPTY_LINE;
281:
282: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
283: "\\n", '\n');
284: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
285: "\\t", '\t');
286: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
287: "\\r", '\r');
288: aDelimiter = commonToFieldAndRecordDelimiters(aDelimiter,
289: "\\f", '\f');
290: return aDelimiter;
291: }
292:
293: /**
294: *
295: * @return Code set, can return null for use the default code set.
296: * @throws Exception
297: */
298: String getDataCodeset() throws Exception {
299: return (getCurrentProperties().getProperty(DATA_CODESET));
300: }
301:
302: /**read the control file properties into a local variable which is used later on
303: *In case there is no control file, read the default values for these properties
304: * @exception Exception if there is an error
305: */
306: Properties getCurrentProperties() throws Exception {
307: if (currentProperties == null) {
308: loadDefaultValues();
309: }
310: return currentProperties;
311: }
312:
313: // Following set routines can be used to change the default properties
314:
315: public void setColumnWidths(String columnWidths) throws Exception {
316: if (columnWidths != null)
317: currentProperties.setProperty(COLUMN_WIDTHS, columnWidths);
318: }
319:
320: public void setFieldSeparator(String fieldSeperator)
321: throws Exception {
322: if (fieldSeperator != null)
323: currentProperties.setProperty(FIELD_SEPARATOR,
324: fieldSeperator);
325: }
326:
327: public void setFieldStartDelimiter(String fsdl) throws Exception {
328: if (fsdl != null)
329: currentProperties.setProperty(FIELD_START_DELIMITER, fsdl);
330: }
331:
332: public void setFieldEndDelimiter(String fedl) throws Exception {
333: if (fedl != null)
334: currentProperties.setProperty(FIELD_END_DELIMITER, fedl);
335: }
336:
337: public void setRecordSeparator(String recordSeperator)
338: throws Exception {
339: if (recordSeperator != null)
340: currentProperties.setProperty(RECORD_SEPARATOR,
341: recordSeperator);
342: }
343:
344: public void setHasDelimiterAtEnd(String hasDelimeterAtEnd)
345: throws Exception {
346: if (hasDelimeterAtEnd != null)
347: currentProperties.setProperty(HAS_DELIMETER_AT_END,
348: hasDelimeterAtEnd);
349: }
350:
351: public void setNullString(String nullString) throws Exception {
352: if (nullString != null)
353: currentProperties.setProperty(NULL_STRING, nullString);
354: }
355:
356: //for fixed format, set column definitions
357: public void setcolumnDefinition(String columnDefinition)
358: throws Exception {
359: if (columnDefinition != null)
360: currentProperties.setProperty(COLUMN_DEFINITION,
361: columnDefinition);
362: }
363:
364: public void setDataCodeset(String codeset) throws Exception {
365: if (codeset != null)
366: currentProperties.setProperty(DATA_CODESET, codeset);
367: }
368:
369: public void setCharacterDelimiter(String charDelimiter)
370: throws Exception {
371: if (charDelimiter != null) {
372: setFieldStartDelimiter(charDelimiter);
373: setFieldEndDelimiter(charDelimiter);
374: }
375: }
376:
377: public void setControlProperties(String characterDelimiter,
378: String columnDelimiter, String codeset) throws Exception {
379: setCharacterDelimiter(characterDelimiter);
380: setFieldSeparator(columnDelimiter);
381: setDataCodeset(codeset);
382: //check whether the delimiters are valid ones
383: validateDelimiters();
384: }
385:
386: private void validateDelimiters() throws Exception {
387:
388: char colDel = (getFieldSeparator()).charAt(0);
389: char charDel = (getFieldStartDelimiter()).charAt(0);
390:
391: //The period was specified as a character string delimiter.
392: if (charDel == '.') {
393: throw LoadError.periodAsCharDelimiterNotAllowed();
394: }
395:
396: //A delimiter is not valid or is used more than once.
397: if (colDel == charDel || colDel == '.'
398: || Character.isSpaceChar(colDel)
399: || Character.isSpaceChar(charDel)) {
400: throw LoadError.delimitersAreNotMutuallyExclusive();
401: }
402:
403: }
404: }
|