001/* 002 * jDTAUS Banking RI DTAUS 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.banking.dtaus.ri.zka; 022 023import java.io.IOException; 024import java.util.Iterator; 025import java.util.Map; 026import org.jdtaus.banking.dtaus.Checksum; 027import org.jdtaus.banking.dtaus.Header; 028import org.jdtaus.banking.dtaus.LogicalFile; 029import org.jdtaus.banking.dtaus.PhysicalFile; 030import org.jdtaus.banking.dtaus.PhysicalFileFactory; 031import org.jdtaus.banking.dtaus.spi.HeaderValidator; 032import org.jdtaus.banking.dtaus.spi.IllegalHeaderException; 033import org.jdtaus.banking.messages.AnalysesFileMessage; 034import org.jdtaus.core.container.ContainerFactory; 035import org.jdtaus.core.io.FileOperations; 036import org.jdtaus.core.monitor.spi.Task; 037import org.jdtaus.core.monitor.spi.TaskMonitor; 038 039/** 040 * Default {@code PhysicalFile} implementation. 041 * <p><b>Note:</b><br/> 042 * This implementation is not thread-safe.</p> 043 * 044 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 045 * @version $JDTAUS: DefaultPhysicalFile.java 8661 2012-09-27 11:29:58Z schulte $ 046 */ 047public final class DefaultPhysicalFile implements PhysicalFile 048{ 049 050 /** Index der logischen Dateien. */ 051 private AbstractLogicalFile[] index; 052 053 /** Anzahl vorhandener logischer Dateien. */ 054 private int dtausCount = 0; 055 056 /** Mapping of attribute names to theire values. */ 057 private final java.util.Properties properties; 058 059 /** <code>FileOperations</code> requirement. **/ 060 private FileOperations fileOperations; 061 062 /** Format of this instance. */ 063 private final int format; 064 065 /** 066 * Creates a new {@code DefaultPhysicalFile} instance. 067 * 068 * @param format The format of the new instance. 069 * @param fileOperations The {@code FileOperations} implementation to operate on. 070 * @param properties Configuration properties. 071 * 072 * @throws NullPointerException if either {@code fileOperations} or {@code properties} is {@code null}. 073 * @throws IllegalArgumentException if {@code format} is not equal to {@code FORMAT_DISK} and {@code FORMAT_TAPE}. 074 * @throws IOException wenn nicht gelesen werden kann. 075 * 076 * @see PhysicalFileFactory#FORMAT_DISK 077 * @see PhysicalFileFactory#FORMAT_TAPE 078 */ 079 public DefaultPhysicalFile( 080 final int format, final FileOperations fileOperations, final java.util.Properties properties ) 081 throws IOException 082 { 083 super(); 084 085 if ( fileOperations == null ) 086 { 087 throw new NullPointerException( "fileOperations" ); 088 } 089 if ( properties == null ) 090 { 091 throw new NullPointerException( "properties" ); 092 } 093 if ( format != PhysicalFileFactory.FORMAT_DISK && format != PhysicalFileFactory.FORMAT_TAPE ) 094 { 095 throw new IllegalArgumentException( Integer.toString( format ) ); 096 } 097 098 this.properties = properties; 099 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}