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.io.BufferedReader; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import net.jcip.annotations.ThreadSafe; 31 import org.modeshape.common.util.Logger; 32 import org.modeshape.graph.sequencer.SequencerOutput; 33 import org.modeshape.graph.sequencer.StreamSequencer; 34 import org.modeshape.graph.sequencer.StreamSequencerContext; 35 36 /** 37 * The base class for the text sequencers. This class treats the text to be sequenced as a series of rows, with each row delimited 38 * by a line terminator. Concrete subclasses provide their own mechanisms for splitting a row of data into a series of columns. 39 * <p> 40 * This class provides some fundamental capabilities, including the ability to set a {@link #setCommentMarker(String) comment 41 * marker}, {@link #setMaximumLinesToRead(int) limit the number of lines} to be read from a file, and 42 * {@link #setRowFactoryClassName(String) provide custom transformations} from the sets of columns to the graph structure. 43 * </p> 44 */ 45 @ThreadSafe 46 public abstract class AbstractTextSequencer implements StreamSequencer { 47 48 private final Logger logger = Logger.getLogger(getClass()); 49 private String rowFactoryClassName = null; 50 private String commentMarker = null; 51 private int maximumLinesToRead = -1; 52 53 /** 54 * {@inheritDoc} 55 */ 56 public void sequence( InputStream stream, 57 SequencerOutput output, 58 StreamSequencerContext context ) { 59 BufferedReader reader = null; 60 String line = null; 61 RowFactory rowFactory = null; 62 String rowFactoryClassName = this.rowFactoryClassName; 63 String commentMarker = this.commentMarker; 64 int maxLinesToRead = this.maximumLinesToRead; 65 66 int rowCount = 0; 67 68 try { 69 rowFactory = createRowFactory(rowFactoryClassName); 70 } catch (Exception ex) { 71 // This really shouldn't be able to happen, as we test-instantiate the class before it is set 72 throw new IllegalStateException(TextSequencerI18n.couldNotInstantiateRowFactory.text(rowFactoryClassName)); 73 } 74 75 try { 76 reader = new BufferedReader(new InputStreamReader(stream)); 77 78 while ((line = reader.readLine()) != null) { 79 if (commentMarker != null && line.startsWith(this.commentMarker)) continue; 80 if ((maxLinesToRead > 0) && (++rowCount > maxLinesToRead)) return; 81 String[] columns = parseLine(line); 82 83 rowFactory.recordRow(context, output, columns); 84 } 85 86 } catch (IOException ioe) { 87 logger.error(ioe, TextSequencerI18n.errorReadingLine, context.getInputPath()); 88 89 } finally { 90 try { 91 if (reader != null) reader.close(); 92 } catch (Exception ignore) { 93 } 94 } 95 96 } 97 98 /** 99 * Sets the comment marker to use. Any line that begins with the comment marker will be ignored and will not be counted as a 100 * read line for the purposes of the {@link #getMaximumLinesToRead() maximum line limitation}. 101 * 102 * @param commentMarker the string that indicates that the line is a comment and should be ignored; null indicates that there 103 * is no comment marker 104 */ 105 public void setCommentMarker( String commentMarker ) { 106 this.commentMarker = commentMarker; 107 } 108 109 /** 110 * @return the current comment marker; may be null 111 */ 112 public String getCommentMarker() { 113 return commentMarker; 114 } 115 116 /** 117 * @return the maximum number of lines to read when sequencing; non-positive numbers indicate that all lines should be read 118 * and sequenced 119 */ 120 public int getMaximumLinesToRead() { 121 return maximumLinesToRead; 122 } 123 124 /** 125 * Sets the maximum number of lines to read. When this number is reached during the sequencing of any particular stream, the 126 * stream will be closed and remaining lines (if any) will be ignored. {@link #setCommentMarker(String) Comment lines} do not 127 * count towards the number of lines read. 128 * 129 * @param maximumLinesToRead the maximum number of lines to read; a non-positive number indicates that all lines should be 130 * read and sequenced. 131 */ 132 public void setMaximumLinesToRead( int maximumLinesToRead ) { 133 this.maximumLinesToRead = maximumLinesToRead; 134 } 135 136 /** 137 * @return the current row factory class name; may not be null 138 */ 139 public String getRowFactoryClassName() { 140 return rowFactoryClassName; 141 } 142 143 /** 144 * Sets the custom row factory class name. This method attempts to instantiate an instance of the custom {@link RowFactory} 145 * class prior to modifying the row factory class name to ensure that the new value represents a valid implementation. 146 * 147 * @param rowFactoryClassName the fully-qualified class name of the new custom row factory implementation; null indicates that 148 * {@link DefaultRowFactory the default row factory} should be used. 149 * @throws ClassNotFoundException if the the named row factory class cannot be located 150 * @throws IllegalAccessException if the row factory class or its nullary constructor is not accessible. 151 * @throws InstantiationException if the row factory represents an abstract class, an interface, an array class, a primitive 152 * type, or void; or if the class has no nullary constructor; or if the instantiation fails for some other reason. 153 * @throws ClassCastException if the instantiated row factory does not implement the {@link RowFactory} interface 154 */ 155 public synchronized void setRowFactoryClassName( String rowFactoryClassName ) 156 throws ClassNotFoundException, IllegalAccessException, InstantiationException { 157 // Make sure that it's going to work 158 createRowFactory(rowFactoryClassName); 159 this.rowFactoryClassName = rowFactoryClassName; 160 } 161 162 /** 163 * Parse the given row into its constituent columns. 164 * 165 * @param row the row to be parsed 166 * @return an array of columns; never null 167 */ 168 protected abstract String[] parseLine( String row ); 169 170 /** 171 * Creates an instance of the {@link #getRowFactoryClassName() row factory} configured for this sequencer. 172 * 173 * @param className the name of the class to configure; null indicates that the {@link DefaultRowFactory default row factory} 174 * should be used. 175 * @return an implementation of the named class; never null 176 * @throws ClassNotFoundException if the the named row factory class cannot be located 177 * @throws IllegalAccessException if the row factory class or its nullary constructor is not accessible. 178 * @throws InstantiationException if the row factory represents an abstract class, an interface, an array class, a primitive 179 * type, or void; or if the class has no nullary constructor; or if the instantiation fails for some other reason. 180 */ 181 protected synchronized RowFactory createRowFactory( String className ) 182 throws ClassNotFoundException, IllegalAccessException, InstantiationException { 183 if (this.rowFactoryClassName == null) { 184 return new DefaultRowFactory(); 185 } 186 187 Class<?> rowFactoryClass = Class.forName(this.rowFactoryClassName); 188 RowFactory rowFactory = (RowFactory)rowFactoryClass.newInstance(); 189 190 return rowFactory; 191 } 192 }