View Javadoc
1   /*
2    * Copyright 2012 Olivier Godineau
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at http://www.apache.org/licenses/LICENSE-2.0
7    * 
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11   * License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package olg.csv.base.ods;
15  
16  import java.io.File;
17  import java.io.FileInputStream;
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import olg.csv.base.AbstractSheetReader;
24  import olg.csv.base.Cell;
25  import olg.csv.base.Row;
26  import olg.csv.bean.formatter.Formatter;
27  
28  import org.odftoolkit.odfdom.doc.OdfSpreadsheetDocument;
29  import org.odftoolkit.odfdom.doc.table.OdfTable;
30  import org.odftoolkit.odfdom.doc.table.OdfTableCell;
31  import org.odftoolkit.odfdom.doc.table.OdfTableRow;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  import org.w3c.dom.Node;
35  import org.w3c.dom.NodeList;
36  
37  /**
38   * ODS Reader based on ODFDOM API.
39   * 
40   * @author Olivier Godineau
41   * 
42   */
43  public class ODSReader extends AbstractSheetReader {
44  	/**
45  	 * The class logger.
46  	 */
47  	private static final Logger LOGGER = LoggerFactory.getLogger(ODSReader.class);
48  	/**
49  	 * The cell formatter.
50  	 */
51  	private final Formatter<OdfTableCell> cellFormatter;
52  	/**
53  	 * Indicate if the stream must be closed on close invocation.
54  	 */
55  	private boolean closeInputStream; // NOPMD by olivier on 08/01/12 18:20
56  	/**
57  	 * the stream.
58  	 */
59  	private final InputStream input;
60  	/**
61  	 * the ODS Document read from the stream.
62  	 */
63  	private OdfSpreadsheetDocument odfDocument;
64  	/**
65  	 * The sheet on which read.
66  	 */
67  	private OdfTable sheet;
68  
69  	/**
70  	 * the nb of rows in the sheet.
71  	 */
72  	private int rowCount;
73  
74  	/**
75  	 * 
76  	 * @param odsFile
77  	 *            the ODS File to read.
78  	 * @param settings
79  	 *            ods settings. If sheetname setting is defined try to find a
80  	 *            sheet with this name otherwise try to read sheet at sheetNum
81  	 *            position in sheets list. Must be not null and have a cell
82  	 *            formatter.
83  	 * @throws IOException
84  	 *             if an error occurs during opening file,i.e the specified
85  	 *             sheet has been not found
86  	 */
87  	public ODSReader(File odsFile, ODSSettings settings) throws IOException {
88  		super(settings);
89  		if (settings.getCellFormatter() == null) {
90  			throw new IllegalArgumentException(
91  					"ODSReader constructor ODSReaderSettings argument must have not null cellFormatter attribut");
92  		}
93  
94  		this.input = new FileInputStream(odsFile);
95  		this.closeInputStream = true;
96  		this.cellFormatter = settings.getCellFormatter();
97  		this.initSheet();
98  	}
99  
100 	/**
101 	 * 
102 	 * @param input
103 	 *            the stream to read.
104 	 * @param settings
105 	 *            ods settings. If sheetname setting is defined try to find a
106 	 *            sheet with this name otherwise try to read sheet at sheetNum
107 	 *            position in sheets list. Must be not null and have a cell
108 	 *            formatter.
109 	 * @throws IOException
110 	 *             if an error occurs during opening file,i.e the specified
111 	 *             sheet has been not found
112 	 */
113 	public ODSReader(InputStream input, ODSSettings settings) throws IOException {
114 		super(settings);
115 		if (input == null) {
116 			throw new IllegalArgumentException("ODSReader constructor InputStream argument must not be null");
117 		}
118 		if (settings.getCellFormatter() == null) {
119 			throw new IllegalArgumentException(
120 					"ODSReader constructor ODSReaderSettings argument must have not null cellFormatter attribut");
121 		}
122 
123 		this.input = input;
124 		this.closeInputStream = false;
125 		this.cellFormatter = settings.getCellFormatter();
126 		this.initSheet();
127 	}
128 
129 	@Override
130 	protected void doOnInitSheet() throws IOException {
131 		try {
132 
133 			this.odfDocument = OdfSpreadsheetDocument.loadDocument(input);
134 			List<OdfTable> sheets = this.odfDocument.getTableList();
135 			if (sheetName != null) {
136 				for (OdfTable feuille : sheets) { // TODO à revoir
137 					if (sheetName.equals(feuille.getTableName())) {
138 						this.sheet = feuille;
139 						this.sheetNum = sheets.indexOf(feuille);
140 						break;
141 					}
142 				}
143 				if (sheet == null) {
144 					throw new IOException("Error on SpreadsheetDocument instanciation : no sheet[" + sheetName + "]");
145 				}
146 			} else {
147 				try {
148 					this.sheet = sheets.get(getSheetNum());
149 					this.sheetName = this.sheet.getTableName();
150 				} catch (IndexOutOfBoundsException ex) {
151 					throw new IOException("Error on SpreadsheetDocument instanciation : no sheet at this index["
152 							+ getSheetNum() + "]");
153 				}
154 			}
155 
156 			this.rowCount = sheet.getRowCount();
157 		} catch (Exception e) {
158 			close();
159 			throw new IOException("Error on SpreadsheetDocument instanciation", e);
160 		}
161 
162 	}
163 
164 	/**
165 	 * Closes this stream and catch and logs IOException if necessary. if the
166 	 * parent stream is provided and passed to ODSReader constructor by user,
167 	 * user must close it (closes what you opens!)
168 	 */
169 	@Override
170 	public void close() {
171 		if (this.odfDocument != null) {
172 			this.odfDocument.close();
173 		}
174 
175 		if (closeInputStream && input != null) {
176 			try {
177 				input.close();
178 			} catch (IOException ex) {
179 				LOGGER.info("Error on closing ODSReader", ex);
180 			}
181 		}
182 
183 	}
184 
185 	@Override
186 	protected Row setNext() {
187 		Row retour = null;
188 		List<Cell> cells = new ArrayList<Cell>();
189 		int column = 0;
190 		OdfTableRow odfRow = this.sheet.getRowByIndex(recordIndex - 1);
191 
192 		for (int i = 0; i < odfRow.getCellCount(); i++) {
193 			OdfTableCell cell = odfRow.getCellByIndex(i);
194 			String value = cellFormatter.toString(cell);
195 			if (beginAtColumn > cell.getColumnIndex()) {
196 				continue;
197 			}
198 			if (endAtColumn != null && endAtColumn < cell.getColumnIndex()) {
199 				continue;
200 			}
201 			padding(cells, column, cell.getColumnIndex() - beginAtColumn);
202 			cells.add(new Cell(cell.getColumnIndex() - beginAtColumn, ("".equals(value) ? null : value))); // NOPMD
203 																											// by
204 																											// olivier
205 																											// on
206 																											// 04/01/12
207 																											// 23:26
208 			column = cell.getColumnIndex() + 1 - beginAtColumn;
209 		}
210 		padding(cells, column, (endAtColumn == null ? rowSize : endAtColumn));
211 
212 		retour = new Row(recordIndex++, cells, rowSize);
213 		return retour;
214 	}
215 
216 	@Override
217 	protected int getRows() {
218 
219 		return this.rowCount;
220 	}
221 
222 	@Override
223 	protected int defineRowSize() {
224 
225 		return this.sheet.getColumnCount();
226 	}
227 
228 	/**
229 	 * Define a default cell format strategy.
230 	 * 
231 	 * ODS Cell can use different ways to represent content on several lines : a
232 	 * Cell could be composed with several paragraphs elements and each
233 	 * paragraph could be composed with line-break elements. To format a cell as
234 	 * a string, the default strategy is to extract paragraph content and use a
235 	 * breakline string to separate them and replace in each paragraph element,
236 	 * the line-break found element by a breakline. The breakline string used
237 	 * must be defined at ODSCellFormatter instanciation.
238 	 * 
239 	 * @author Olivier Godineau
240 	 * 
241 	 */
242 	public static final class ODSCellFormatter extends Formatter<OdfTableCell> {
243 		/**
244 		 * the breakline to use.
245 		 */
246 		private final String breakline;
247 
248 		/**
249 		 * 
250 		 * @param breakline
251 		 *            to use
252 		 */
253 		public ODSCellFormatter(String breakline) {
254 			super();
255 			this.breakline = breakline;
256 		}
257 
258 		@Override
259 		public String toString(OdfTableCell cell) {
260 			String value = null;
261 			NodeList nodes = cell.getOdfElement().getChildNodes();
262 			StringBuilder sbuild = new StringBuilder();
263 
264 			for (int j = 0; j < nodes.getLength(); j++) {
265 				Node node = nodes.item(j);
266 				NodeList childs = node.getChildNodes();
267 				for (int i = 0; i < childs.getLength(); i++) {
268 					Node child = childs.item(i);
269 					String name = child.getNodeName();
270 					String prefix = child.getPrefix();
271 					if (name.equals(prefix + ":line-break")) {
272 						sbuild.append(breakline);
273 					} else {
274 						sbuild.append(child.getTextContent());
275 					}
276 
277 				}
278 				sbuild.append(breakline);
279 
280 			}
281 			if (nodes.getLength() > 0) {
282 				value = sbuild.substring(0, sbuild.length() - breakline.length());
283 			}
284 			return value;
285 		}
286 
287 	}
288 
289 }