1 | /* |
2 | * jDTAUS Banking RI DTAUS |
3 | * Copyright (C) 2005 Christian Schulte |
4 | * <cs@schulte.it> |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
19 | * |
20 | */ |
21 | package org.jdtaus.banking.dtaus.ri.zka; |
22 | |
23 | import java.io.IOException; |
24 | import java.util.Iterator; |
25 | import java.util.Map; |
26 | import org.jdtaus.banking.dtaus.Checksum; |
27 | import org.jdtaus.banking.dtaus.Header; |
28 | import org.jdtaus.banking.dtaus.LogicalFile; |
29 | import org.jdtaus.banking.dtaus.PhysicalFile; |
30 | import org.jdtaus.banking.dtaus.PhysicalFileFactory; |
31 | import org.jdtaus.banking.dtaus.spi.HeaderValidator; |
32 | import org.jdtaus.banking.dtaus.spi.IllegalHeaderException; |
33 | import org.jdtaus.banking.messages.AnalysesFileMessage; |
34 | import org.jdtaus.core.container.ContainerFactory; |
35 | import org.jdtaus.core.io.FileOperations; |
36 | import org.jdtaus.core.monitor.spi.Task; |
37 | import org.jdtaus.core.monitor.spi.TaskMonitor; |
38 | |
39 | /** |
40 | * Default {@code PhysicalFile} implementation. |
41 | * <p><b>Note:</b><br/> |
42 | * This implementation is not thread-safe.</p> |
43 | * |
44 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> |
45 | * @version $JDTAUS: DefaultPhysicalFile.java 8661 2012-09-27 11:29:58Z schulte $ |
46 | */ |
47 | public final class DefaultPhysicalFile implements PhysicalFile |
48 | { |
49 | |
50 | /** Index der logischen Dateien. */ |
51 | private AbstractLogicalFile[] index; |
52 | |
53 | /** Anzahl vorhandener logischer Dateien. */ |
54 | private int dtausCount = 0; |
55 | |
56 | /** Mapping of attribute names to theire values. */ |
57 | private final java.util.Properties properties; |
58 | |
59 | /** <code>FileOperations</code> requirement. **/ |
60 | private FileOperations fileOperations; |
61 | |
62 | /** Format of this instance. */ |
63 | private final int format; |
64 | |
65 | /** |
66 | * Creates a new {@code DefaultPhysicalFile} instance. |
67 | * |
68 | * @param format The format of the new instance. |
69 | * @param fileOperations The {@code FileOperations} implementation to operate on. |
70 | * @param properties Configuration properties. |
71 | * |
72 | * @throws NullPointerException if either {@code fileOperations} or {@code properties} is {@code null}. |
73 | * @throws IllegalArgumentException if {@code format} is not equal to {@code FORMAT_DISK} and {@code FORMAT_TAPE}. |
74 | * @throws IOException wenn nicht gelesen werden kann. |
75 | * |
76 | * @see PhysicalFileFactory#FORMAT_DISK |
77 | * @see PhysicalFileFactory#FORMAT_TAPE |
78 | */ |
79 | public DefaultPhysicalFile( |
80 | final int format, final FileOperations fileOperations, final java.util.Properties properties ) |
81 | throws IOException |
82 | { |
83 | super(); |
84 | |
85 | if ( fileOperations == null ) |
86 | { |
87 | throw new NullPointerException( "fileOperations" ); |
88 | } |
89 | if ( properties == null ) |
90 | { |
91 | throw new NullPointerException( "properties" ); |
92 | } |
93 | if ( format != PhysicalFileFactory.FORMAT_DISK && format != PhysicalFileFactory.FORMAT_TAPE ) |
94 | { |
95 | throw new IllegalArgumentException( Integer.toString( format ) ); |
96 | } |
97 | |
98 | this.properties = properties; |
99 | this.fileOperations = fileOperations; |
100 | this.format = format; |
101 | this.checksum(); |
102 | } |
103 | |
104 | public int count() |
105 | { |
106 | return this.dtausCount; |
107 | } |
108 | |
109 | public LogicalFile add( final Header header ) throws IOException |
110 | { |
111 | if ( header == null ) |
112 | { |
113 | throw new NullPointerException( "header" ); |
114 | } |
115 | |
116 | IllegalHeaderException result = null; |
117 | final HeaderValidator[] validators = this.getHeaderValidator(); |
118 | |
119 | for ( int i = validators.length - 1; i >= 0; i-- ) |
120 | { |
121 | result = validators[i].assertValidHeader( header, result ); |
122 | } |
123 | |
124 | if ( result != null && result.getMessages().length > 0 ) |
125 | { |
126 | throw result; |
127 | } |
128 | |
129 | this.resizeIndex( this.dtausCount ); |
130 | |
131 | final AbstractLogicalFile lFile = this.newLogicalFile( |
132 | ( this.dtausCount == 0 ? 0L : this.index[this.dtausCount - 1].getChecksumPosition() + |
133 | this.index[this.dtausCount - 1].getBlockSize() ) ); |
134 | |
135 | lFile.insertBytes( lFile.getHeaderPosition(), this.format * 2 ); |
136 | lFile.writeHeader( header ); |
137 | lFile.writeChecksum( new Checksum() ); |
138 | lFile.checksum(); |
139 | this.index[this.dtausCount] = lFile; |
140 | return this.index[this.dtausCount++]; |
141 | } |
142 | |
143 | public LogicalFile get( int dtausId ) |
144 | { |
145 | if ( !this.checkLogicalFileExists( dtausId ) ) |
146 | { |
147 | throw new IllegalArgumentException( "dtausId" ); |
148 | } |
149 | return this.index[dtausId]; |
150 | } |
151 | |
152 | public void remove( int dtausId ) throws IOException |
153 | { |
154 | if ( !this.checkLogicalFileExists( dtausId ) ) |
155 | { |
156 | throw new IllegalArgumentException( "dtausId" ); |
157 | } |
158 | |
159 | this.index[dtausId].removeBytes( |
160 | this.index[dtausId].getHeaderPosition(), this.index[dtausId].getChecksumPosition() - |
161 | this.index[dtausId].getHeaderPosition() + this.format ); |
162 | |
163 | System.arraycopy( this.index, dtausId + 1, this.index, dtausId, --this.dtausCount - dtausId ); |
164 | } |
165 | |
166 | public void commit() throws IOException |
167 | { |
168 | this.getFileOperations().close(); |
169 | } |
170 | |
171 | public int getLogicalFileCount() throws IOException |
172 | { |
173 | return this.count(); |
174 | } |
175 | |
176 | public LogicalFile addLogicalFile( final Header header ) throws IOException |
177 | { |
178 | return this.add( header ); |
179 | } |
180 | |
181 | public LogicalFile getLogicalFile( final int index ) throws IOException |
182 | { |
183 | return this.get( index ); |
184 | } |
185 | |
186 | public void removeLogicalFile( final int index ) throws IOException |
187 | { |
188 | this.remove( index ); |
189 | } |
190 | |
191 | /** FileOperations requirement getter method. */ |
192 | private FileOperations getFileOperations() |
193 | { |
194 | return this.fileOperations; |
195 | } |
196 | |
197 | private boolean checkLogicalFileExists( int dtausId ) |
198 | { |
199 | return dtausId < this.dtausCount && dtausId >= 0; |
200 | } |
201 | |
202 | private void checksum() throws IOException |
203 | { |
204 | this.dtausCount = 0; |
205 | int dtausIndex = 0; |
206 | final long length = this.getFileOperations().getLength(); |
207 | long maximumProgress = length; |
208 | long progressDivisor = 1L; |
209 | |
210 | while ( maximumProgress > Integer.MAX_VALUE ) |
211 | { |
212 | maximumProgress /= 2L; |
213 | progressDivisor *= 2L; |
214 | } |
215 | |
216 | final Task task = new Task(); |
217 | task.setIndeterminate( false ); |
218 | task.setCancelable( false ); |
219 | task.setDescription( new AnalysesFileMessage() ); |
220 | task.setMinimum( 0 ); |
221 | task.setProgress( 0 ); |
222 | task.setMaximum( (int) maximumProgress ); |
223 | |
224 | try |
225 | { |
226 | this.getTaskMonitor().monitor( task ); |
227 | |
228 | for ( long position = 0L; position < length; |
229 | position = this.index[dtausIndex].getChecksumPosition() + this.index[dtausIndex++].getBlockSize() ) |
230 | { |
231 | task.setProgress( (int) ( position / progressDivisor ) ); |
232 | this.resizeIndex( dtausIndex ); |
233 | this.index[dtausIndex] = this.newLogicalFile( position ); |
234 | this.index[dtausIndex].checksum(); |
235 | this.dtausCount++; |
236 | } |
237 | } |
238 | finally |
239 | { |
240 | this.getTaskMonitor().finish( task ); |
241 | } |
242 | } |
243 | |
244 | private AbstractLogicalFile newLogicalFile( final long headerPosition ) throws IOException |
245 | { |
246 | final AbstractLogicalFile ret; |
247 | |
248 | switch ( this.format ) |
249 | { |
250 | case PhysicalFileFactory.FORMAT_DISK: |
251 | ret = new DTAUSDisk(); |
252 | break; |
253 | case PhysicalFileFactory.FORMAT_TAPE: |
254 | ret = new DTAUSTape(); |
255 | break; |
256 | default: |
257 | throw new IllegalStateException(); |
258 | |
259 | } |
260 | |
261 | ret.setFileOperations( this.getFileOperations() ); |
262 | ret.setHeaderPosition( headerPosition ); |
263 | ret.setChecksumPosition( headerPosition + this.format ); |
264 | |
265 | for ( Iterator it = this.properties.entrySet().iterator(); it.hasNext(); ) |
266 | { |
267 | final Map.Entry e = (Map.Entry) it.next(); |
268 | final String key = (String) e.getKey(); |
269 | |
270 | if ( key.startsWith( DefaultPhysicalFileFactory.ATTRIBUTE_SPACE_CHARACTERS_ALLOWED ) ) |
271 | { |
272 | int field = Integer.parseInt( key.substring( key.lastIndexOf( '.' ) + 1 ), 16 ); |
273 | final boolean allowed = |
274 | e.getValue() != null && Boolean.valueOf( e.getValue().toString() ).booleanValue(); |
275 | |
276 | ret.getConfiguration().setSpaceCharacterAllowed( field, allowed ); |
277 | } |
278 | } |
279 | |
280 | ret.addListener( new AbstractLogicalFile.Listener() |
281 | { |
282 | |
283 | public void bytesInserted( final long position, final long bytes ) throws IOException |
284 | { |
285 | final int fileIndex = this.getFileIndex( position ); |
286 | if ( fileIndex >= 0 ) |
287 | { |
288 | // Increment properties headerPosition and checksumPosition for all remaining files. |
289 | for ( int i = fileIndex + 1; i < dtausCount; i++ ) |
290 | { |
291 | index[i].setHeaderPosition( index[i].getHeaderPosition() + bytes ); |
292 | index[i].setChecksumPosition( index[i].getChecksumPosition() + bytes ); |
293 | } |
294 | } |
295 | } |
296 | |
297 | public void bytesDeleted( final long position, final long bytes ) throws IOException |
298 | { |
299 | final int fileIndex = this.getFileIndex( position ); |
300 | if ( fileIndex >= 0 ) |
301 | { |
302 | // Decrement properties headerPosition and checksumPosition for all remaining files. |
303 | for ( int i = fileIndex + 1; i < dtausCount; i++ ) |
304 | { |
305 | index[i].setHeaderPosition( index[i].getHeaderPosition() - bytes ); |
306 | index[i].setChecksumPosition( index[i].getChecksumPosition() - bytes ); |
307 | } |
308 | } |
309 | } |
310 | |
311 | private int getFileIndex( final long position ) |
312 | { |
313 | for ( int i = dtausCount - 1; i >= 0; i-- ) |
314 | { |
315 | if ( position >= index[i].getHeaderPosition() && position <= index[i].getChecksumPosition() ) |
316 | { |
317 | return i; |
318 | } |
319 | } |
320 | |
321 | return -1; |
322 | } |
323 | |
324 | } ); |
325 | |
326 | return ret; |
327 | } |
328 | |
329 | private void resizeIndex( int index ) |
330 | { |
331 | if ( this.index == null ) |
332 | { |
333 | this.index = new AbstractLogicalFile[ index + 1 ]; |
334 | } |
335 | else if ( this.index.length < index + 1 ) |
336 | { |
337 | while ( this.index.length < index + 1 ) |
338 | { |
339 | final int newLength = this.index.length * 2; |
340 | final AbstractLogicalFile[] newIndex = new AbstractLogicalFile[ newLength ]; |
341 | System.arraycopy( this.index, 0, newIndex, 0, this.index.length ); |
342 | this.index = newIndex; |
343 | } |
344 | } |
345 | } |
346 | |
347 | //--Dependencies------------------------------------------------------------ |
348 | |
349 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies |
350 | // This section is managed by jdtaus-container-mojo. |
351 | |
352 | /** |
353 | * Gets the configured <code>TaskMonitor</code> implementation. |
354 | * |
355 | * @return The configured <code>TaskMonitor</code> implementation. |
356 | */ |
357 | private TaskMonitor getTaskMonitor() |
358 | { |
359 | return (TaskMonitor) ContainerFactory.getContainer(). |
360 | getDependency( this, "TaskMonitor" ); |
361 | |
362 | } |
363 | |
364 | /** |
365 | * Gets the configured <code>HeaderValidator</code> implementation. |
366 | * |
367 | * @return The configured <code>HeaderValidator</code> implementation. |
368 | */ |
369 | private HeaderValidator[] getHeaderValidator() |
370 | { |
371 | return (HeaderValidator[]) ContainerFactory.getContainer(). |
372 | getDependency( this, "HeaderValidator" ); |
373 | |
374 | } |
375 | |
376 | // </editor-fold>//GEN-END:jdtausDependencies |
377 | |
378 | //------------------------------------------------------------Dependencies-- |
379 | } |