1 /*
2 * ModeShape (http://www.modeshape.org)
3 * See the COPYRIGHT.txt file distributed with this work for information
4 * regarding copyright ownership. Some portions may be licensed
5 * to Red Hat, Inc. under one or more contributor license agreements.
6 * See the AUTHORS.txt file in the distribution for a full listing of
7 * individual contributors.
8 *
9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10 * is licensed to you under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * ModeShape is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this software; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23 */
24 package org.modeshape.sequencer.text;
25
26 import java.util.Arrays;
27 import org.modeshape.common.util.CheckArg;
28
29 /**
30 * An text sequencer implementation that uses a {@link #setColumnStartPositions(String) list of column numbers} to split incoming
31 * rows into fixed-width columns. By default, this class treats each row as a single column. There is an implicit column start
32 * index of 0 for the first column.
33 *
34 * @see AbstractTextSequencer
35 */
36 public class FixedWidthTextSequencer extends AbstractTextSequencer {
37
38 private int[] columnStartPositions = new int[] {};
39
40 /**
41 * Set the column start positions. The column start positions are 0-based. Everything before the first start position is
42 * treated as the first column.
43 * <p>
44 * As an example, if the column start positions were {3, 6, 15} and the incoming stream was:
45 *
46 * <pre>
47 * 1 2
48 * 012345678901234567890
49 * supercallifragilistic
50 * expialidocious
51 * </pre>
52 * This sequencer would return the following rows:
53 * <pre>
54 * row 1: "sup", "erc", "allifragi", "listic"
55 * row 2: "exp:, "ial", "idocious"
56 * </pre>
57 *
58 * Note that there are only three columns returned in the second row, as there were not enough characters to reach the third
59 * start position.
60 * </p>
61 *
62 * @param columnStartPositions the column startPositions; may not be null
63 */
64 public void setColumnStartPositions( int[] columnStartPositions ) {
65 CheckArg.isNotNull(columnStartPositions, "columnStartPositions");
66
67 this.columnStartPositions = columnStartPositions;
68 Arrays.sort(columnStartPositions);
69 }
70
71 /**
72 * Set the column start positions from a list of column start positions concatenated into a single, comma-delimited string.
73 *
74 * @param commaDelimitedColumnStartPositions a list of column start positions concatenated into a single, comma-delimited
75 * string; may not be null
76 * @see #setColumnStartPositions(int[])
77 */
78 public void setColumnStartPositions( String commaDelimitedColumnStartPositions ) {
79 CheckArg.isNotNull(commaDelimitedColumnStartPositions, "commaDelimitedColumnStartPositions");
80
81 String[] stringStartPositions = commaDelimitedColumnStartPositions.split(",");
82 int[] columnStartPositions = new int[stringStartPositions.length];
83
84 for (int i = 0; i < stringStartPositions.length; i++) {
85 columnStartPositions[i] = Integer.valueOf(stringStartPositions[i]);
86 }
87
88 setColumnStartPositions(columnStartPositions);
89 }
90
91 @Override
92 protected String[] parseLine( String line ) {
93 assert line != null;
94
95 int columnCount = columnStartPositions.length + 1;
96 int currentPos = 0;
97
98 String[] columns = new String[columnCount];
99
100 for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
101 int endPos = columnIndex >= columnCount - 1 ? Integer.MAX_VALUE : columnStartPositions[columnIndex];
102 String chunk = parseColumn(line, currentPos, endPos);
103
104 // The line was shorter than expected
105 if (chunk == null) {
106 assert columnIndex > 0 : "parseColumn failed to return the first column in string " + line;
107
108 String[] truncatedColumns = new String[columnIndex - 1];
109 System.arraycopy(columns, 0, truncatedColumns, 0, columnIndex - 1);
110 return truncatedColumns;
111 }
112 columns[columnIndex] = chunk;
113 currentPos = endPos;
114 }
115
116 return columns;
117 }
118
119 private String parseColumn( String line,
120 int startPos,
121 int endPos ) {
122 int length = line.length();
123
124 if (length < startPos) {
125 return null;
126 }
127
128 if (length < endPos) {
129 return line.substring(startPos);
130 }
131
132 return line.substring(startPos, endPos);
133 }
134
135 }