Compare commits
	
		
			2 Commits
		
	
	
		
			2.11.2
			...
			feature/bi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 20c3994507 | |||
| 73c257af03 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -6,6 +6,5 @@
 | 
				
			|||||||
SCViewer initiator_target.launch
 | 
					SCViewer initiator_target.launch
 | 
				
			||||||
SCViewer.xcf
 | 
					SCViewer.xcf
 | 
				
			||||||
SCViewer_1.png
 | 
					SCViewer_1.png
 | 
				
			||||||
 | 
					*.launch
 | 
				
			||||||
copyrightLog.txt
 | 
					copyrightLog.txt
 | 
				
			||||||
/workspace
 | 
					 | 
				
			||||||
?*.launch
 | 
					 | 
				
			||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					 | 
				
			||||||
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_GOALS" value="package"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_OFFLINE" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_PROFILES" value=""/>
 | 
					 | 
				
			||||||
    <listAttribute key="M2_PROPERTIES"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_SKIP_TESTS" value="false"/>
 | 
					 | 
				
			||||||
    <intAttribute key="M2_THREADS" value="1"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_USER_SETTINGS" value=""/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
 | 
					 | 
				
			||||||
</launchConfiguration>
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					 | 
				
			||||||
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_GOALS" value="package"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_OFFLINE" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_PROFILES" value="release-composite"/>
 | 
					 | 
				
			||||||
    <listAttribute key="M2_PROPERTIES"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_SKIP_TESTS" value="false"/>
 | 
					 | 
				
			||||||
    <intAttribute key="M2_THREADS" value="1"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_USER_SETTINGS" value=""/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="true"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/AdoptOpenJDK 8 [1.8.0_232]"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
 | 
					 | 
				
			||||||
</launchConfiguration>
 | 
					 | 
				
			||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					 | 
				
			||||||
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_GOALS" value="tycho-versions:set-version tycho-versions:update-pom"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_OFFLINE" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_PROFILES" value=""/>
 | 
					 | 
				
			||||||
    <listAttribute key="M2_PROPERTIES">
 | 
					 | 
				
			||||||
        <listEntry value="newVersion=${string_prompt:new version number}"/>
 | 
					 | 
				
			||||||
    </listAttribute>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_SKIP_TESTS" value="false"/>
 | 
					 | 
				
			||||||
    <intAttribute key="M2_THREADS" value="1"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="M2_USER_SETTINGS" value=""/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="bad_container_name" value="/com.minres.scviewer.parent/.launch"/>
 | 
					 | 
				
			||||||
    <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
 | 
					 | 
				
			||||||
    <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
 | 
					 | 
				
			||||||
</launchConfiguration>
 | 
					 | 
				
			||||||
							
								
								
									
										37
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								README.md
									
									
									
									
									
								
							@@ -6,18 +6,30 @@ created by the SystemC VCD trace implementation and the SystemC Verification Lib
 | 
				
			|||||||
For further description of the SCV please refer to 
 | 
					For further description of the SCV please refer to 
 | 
				
			||||||
http://www.accellera.org/activities/committees/systemc-verification.
 | 
					http://www.accellera.org/activities/committees/systemc-verification.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> If you encounter issue when running on Linux please try running as `SWT_GTK3=0 scviewer` as there exist issues wiht GTK3.
 | 
					The viewer is in early alpha stage and not yet ready for production use!
 | 
				
			||||||
 | 
					 | 
				
			||||||
The viewer has the following features
 | 
					 | 
				
			||||||
- support of VCD files (compressed and uncompressed)
 | 
					 | 
				
			||||||
 - real numbers
 | 
					 | 
				
			||||||
 - showing vectors and real numbers as analog (step-wise & continuous)
 | 
					 | 
				
			||||||
 - various value representations of bit vectors
 | 
					 | 
				
			||||||
- support of SCV transaction recordings in various formats
 | 
					 | 
				
			||||||
 - text log files (compressed and uncompressed)
 | 
					 | 
				
			||||||
 - sqlite based 
 | 
					 | 
				
			||||||
 - visualization of transaction relations
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The plugins are structured as follows:
 | 
				
			||||||
 | 
					- com.minres.scviewer.database
 | 
				
			||||||
 | 
						the interface defining the API to access the database and the implementation for VCD
 | 
				
			||||||
 | 
					- com.minres.scviewer.database.text
 | 
				
			||||||
 | 
						an implementation of the API to read the text files generated by the SCV
 | 
				
			||||||
 | 
						sc_tr_text database
 | 
				
			||||||
 | 
					- com.minres.scviewer.database.sqlite
 | 
				
			||||||
 | 
						an implementation of the API to read the files generated by implementation in the
 | 
				
			||||||
 | 
						sc_tr_sqlite project using a SQLite based database
 | 
				
			||||||
 | 
					- com.minres.scviewer.database.test
 | 
				
			||||||
 | 
						a some JUnit tests of the 3 back ends
 | 
				
			||||||
 | 
					- com.minres.scviewer.ui
 | 
				
			||||||
 | 
						the viewer it self to diplay the transactions and associated views like the
 | 
				
			||||||
 | 
						outline of the DB and the properties of the transaction
 | 
				
			||||||
 | 
					- com.minres.scviewer.feature
 | 
				
			||||||
 | 
						the feature combining the plugins above into a somhow usable form
 | 
				
			||||||
 | 
					- scv_tr_sqlite
 | 
				
			||||||
 | 
						a C++ project containing the SQLite based SCV database implementation and the scv4tlm 
 | 
				
			||||||
 | 
						socket implementations.
 | 
				
			||||||
 | 
						A simple example (scv_tr_recording_example.cpp) for testig purposes of the database is
 | 
				
			||||||
 | 
						provided. 
 | 
				
			||||||
 | 
						
 | 
				
			||||||
To build the plugins the Eclipse SDK or PDE can be used. In both cases the Groovy
 | 
					To build the plugins the Eclipse SDK or PDE can be used. In both cases the Groovy
 | 
				
			||||||
eclipse plugin (http://groovy.codehaus.org/Eclipse+Plugin or Market) has to be
 | 
					eclipse plugin (http://groovy.codehaus.org/Eclipse+Plugin or Market) has to be
 | 
				
			||||||
installed.
 | 
					installed.
 | 
				
			||||||
@@ -27,5 +39,4 @@ TODO
 | 
				
			|||||||
- add more tests
 | 
					- add more tests
 | 
				
			||||||
- move to feature based product to allow automatic updates
 | 
					- move to feature based product to allow automatic updates
 | 
				
			||||||
- improve graphics
 | 
					- improve graphics
 | 
				
			||||||
- catch-up e3 plugin to functionality of e4 product
 | 
					- catch-up e3 plugin to functionality of e4 product
 | 
				
			||||||
- add calculated traces
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								com.minres.scviewer.database.binary/.classpath
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								com.minres.scviewer.database.binary/.classpath
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<classpath>
 | 
				
			||||||
 | 
						<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 | 
				
			||||||
 | 
						<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 | 
				
			||||||
 | 
						<classpathentry kind="src" path="src"/>
 | 
				
			||||||
 | 
						<classpathentry kind="output" path="bin"/>
 | 
				
			||||||
 | 
					</classpath>
 | 
				
			||||||
							
								
								
									
										33
									
								
								com.minres.scviewer.database.binary/.project
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								com.minres.scviewer.database.binary/.project
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<projectDescription>
 | 
				
			||||||
 | 
						<name>com.minres.scviewer.database.binary</name>
 | 
				
			||||||
 | 
						<comment></comment>
 | 
				
			||||||
 | 
						<projects>
 | 
				
			||||||
 | 
						</projects>
 | 
				
			||||||
 | 
						<buildSpec>
 | 
				
			||||||
 | 
							<buildCommand>
 | 
				
			||||||
 | 
								<name>org.eclipse.jdt.core.javabuilder</name>
 | 
				
			||||||
 | 
								<arguments>
 | 
				
			||||||
 | 
								</arguments>
 | 
				
			||||||
 | 
							</buildCommand>
 | 
				
			||||||
 | 
							<buildCommand>
 | 
				
			||||||
 | 
								<name>org.eclipse.pde.ManifestBuilder</name>
 | 
				
			||||||
 | 
								<arguments>
 | 
				
			||||||
 | 
								</arguments>
 | 
				
			||||||
 | 
							</buildCommand>
 | 
				
			||||||
 | 
							<buildCommand>
 | 
				
			||||||
 | 
								<name>org.eclipse.pde.SchemaBuilder</name>
 | 
				
			||||||
 | 
								<arguments>
 | 
				
			||||||
 | 
								</arguments>
 | 
				
			||||||
 | 
							</buildCommand>
 | 
				
			||||||
 | 
							<buildCommand>
 | 
				
			||||||
 | 
								<name>org.eclipse.pde.ds.core.builder</name>
 | 
				
			||||||
 | 
								<arguments>
 | 
				
			||||||
 | 
								</arguments>
 | 
				
			||||||
 | 
							</buildCommand>
 | 
				
			||||||
 | 
						</buildSpec>
 | 
				
			||||||
 | 
						<natures>
 | 
				
			||||||
 | 
							<nature>org.eclipse.pde.PluginNature</nature>
 | 
				
			||||||
 | 
							<nature>org.eclipse.jdt.core.javanature</nature>
 | 
				
			||||||
 | 
						</natures>
 | 
				
			||||||
 | 
					</projectDescription>
 | 
				
			||||||
@@ -4,5 +4,4 @@ org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 | 
				
			|||||||
org.eclipse.jdt.core.compiler.compliance=1.8
 | 
					org.eclipse.jdt.core.compiler.compliance=1.8
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.release=disabled
 | 
					 | 
				
			||||||
org.eclipse.jdt.core.compiler.source=1.8
 | 
					org.eclipse.jdt.core.compiler.source=1.8
 | 
				
			||||||
@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					eclipse.preferences.version=1
 | 
				
			||||||
 | 
					pluginProject.equinox=false
 | 
				
			||||||
 | 
					pluginProject.extensions=false
 | 
				
			||||||
 | 
					resolve.requirebundle=false
 | 
				
			||||||
@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					eclipse.preferences.version=1
 | 
				
			||||||
 | 
					enabled=true
 | 
				
			||||||
 | 
					path=OSGI-INF
 | 
				
			||||||
 | 
					validationErrorLevel=error
 | 
				
			||||||
 | 
					validationErrorLevel.missingImplicitUnbindMethod=error
 | 
				
			||||||
							
								
								
									
										14
									
								
								com.minres.scviewer.database.binary/META-INF/MANIFEST.MF
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								com.minres.scviewer.database.binary/META-INF/MANIFEST.MF
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					Manifest-Version: 1.0
 | 
				
			||||||
 | 
					Bundle-ManifestVersion: 2
 | 
				
			||||||
 | 
					Bundle-Name: Binary Database
 | 
				
			||||||
 | 
					Bundle-SymbolicName: com.minres.scviewer.database.binary
 | 
				
			||||||
 | 
					Bundle-Version: 1.0.0.qualifier
 | 
				
			||||||
 | 
					Bundle-Vendor: MINRES Technologies GmbH
 | 
				
			||||||
 | 
					Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 | 
				
			||||||
 | 
					Import-Package: org.eclipse.osgi.framework.console;version="1.0.0",
 | 
				
			||||||
 | 
					 org.osgi.framework;version="1.3.0",
 | 
				
			||||||
 | 
					 org.osgi.service.component.annotations;version="1.2.0";resolution:="optional",
 | 
				
			||||||
 | 
					 org.osgi.util.tracker;version="1.3.1"
 | 
				
			||||||
 | 
					Automatic-Module-Name: com.minres.scviewer.database.binary
 | 
				
			||||||
 | 
					Service-Component: OSGI-INF/*.xml
 | 
				
			||||||
 | 
					Require-Bundle: com.minres.scviewer.database
 | 
				
			||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="BinaryDbLoader">
 | 
				
			||||||
 | 
					   <implementation class="com.minres.scviewer.database.binary.BinaryDbLoader"/>
 | 
				
			||||||
 | 
					   <service>
 | 
				
			||||||
 | 
					      <provide interface="com.minres.scviewer.database.IWaveformDbLoader"/>
 | 
				
			||||||
 | 
					   </service>
 | 
				
			||||||
 | 
					</scr:component>
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										5
									
								
								com.minres.scviewer.database.binary/build.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								com.minres.scviewer.database.binary/build.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					source.. = src/
 | 
				
			||||||
 | 
					output.. = bin/
 | 
				
			||||||
 | 
					bin.includes = META-INF/,\
 | 
				
			||||||
 | 
					               .,\
 | 
				
			||||||
 | 
					               OSGI-INF/
 | 
				
			||||||
@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     MINRES Technologies GmbH - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.binary;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveform;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformDb;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformDbLoader;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.RelationType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class BinaryDbLoader implements IWaveformDbLoader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private List<RelationType> usedRelationsList = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private IWaveformDb db;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						public BinaryDbLoader() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Long getMaxTime() {
 | 
				
			||||||
 | 
							return 0L;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public List<IWaveform<? extends IWaveformEvent>> getAllWaves() {
 | 
				
			||||||
 | 
							List<IWaveform<? extends IWaveformEvent>> streams=new ArrayList<IWaveform<? extends IWaveformEvent>>();
 | 
				
			||||||
 | 
							return streams;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public boolean load(IWaveformDb db, File file) throws Exception {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Collection<RelationType> getAllRelationTypes(){
 | 
				
			||||||
 | 
							return usedRelationsList;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,5 +4,4 @@ org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 | 
				
			|||||||
org.eclipse.jdt.core.compiler.compliance=1.8
 | 
					org.eclipse.jdt.core.compiler.compliance=1.8
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.release=disabled
 | 
					 | 
				
			||||||
org.eclipse.jdt.core.compiler.source=1.8
 | 
					org.eclipse.jdt.core.compiler.source=1.8
 | 
				
			||||||
@@ -2,14 +2,16 @@ Manifest-Version: 1.0
 | 
				
			|||||||
Bundle-ManifestVersion: 2
 | 
					Bundle-ManifestVersion: 2
 | 
				
			||||||
Bundle-Name: SQLite transaction database
 | 
					Bundle-Name: SQLite transaction database
 | 
				
			||||||
Bundle-SymbolicName: com.minres.scviewer.database.sqlite
 | 
					Bundle-SymbolicName: com.minres.scviewer.database.sqlite
 | 
				
			||||||
Bundle-Version: 1.1.0.qualifier
 | 
					Bundle-Version: 1.0.0.qualifier
 | 
				
			||||||
Bundle-Vendor: MINRES Technologies GmbH
 | 
					Bundle-Vendor: MINRES Technologies GmbH
 | 
				
			||||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 | 
					Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 | 
				
			||||||
Require-Bundle: com.minres.scviewer.database;bundle-version="1.0.0"
 | 
					Require-Bundle: com.minres.scviewer.database;bundle-version="1.0.0",
 | 
				
			||||||
 | 
					 org.eclipse.equinox.util;bundle-version="1.0.500",
 | 
				
			||||||
 | 
					 org.eclipse.equinox.ds;bundle-version="1.4.200",
 | 
				
			||||||
 | 
					 org.eclipse.osgi.services;bundle-version="3.4.0"
 | 
				
			||||||
Bundle-ClassPath: .,sqlite-jdbc-3.8.7.jar
 | 
					Bundle-ClassPath: .,sqlite-jdbc-3.8.7.jar
 | 
				
			||||||
Service-Component: OSGI-INF/component.xml
 | 
					Service-Component: OSGI-INF/component.xml
 | 
				
			||||||
Bundle-ActivationPolicy: lazy
 | 
					Bundle-ActivationPolicy: lazy
 | 
				
			||||||
Embed-Dependency: sqlite-jdbc
 | 
					Embed-Dependency: sqlite-jdbc
 | 
				
			||||||
Embedded-Artifacts: sqlite-jdbc-3.8.7.jar;g="org.xerial";
 | 
					Embedded-Artifacts: sqlite-jdbc-3.8.7.jar;g="org.xerial";
 | 
				
			||||||
 a="sqlite-jdbc";v="3.8.7"
 | 
					 a="sqlite-jdbc";v="3.8.7"
 | 
				
			||||||
Automatic-Module-Name: com.minres.scviewer.database.sqlite
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
###############################################################################
 | 
					###############################################################################
 | 
				
			||||||
# Copyright (c) 2014, 2015-2021 MINRES Technologies GmbH and others.
 | 
					# Copyright (c) 2014, 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
# All rights reserved. This program and the accompanying materials
 | 
					# All rights reserved. This program and the accompanying materials
 | 
				
			||||||
# are made available under the terms of the Eclipse Public License v1.0
 | 
					# are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
# which accompanies this distribution, and is available at
 | 
					# which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -4,8 +4,8 @@
 | 
				
			|||||||
  <parent>
 | 
					  <parent>
 | 
				
			||||||
  	<groupId>com.minres.scviewer</groupId>
 | 
					  	<groupId>com.minres.scviewer</groupId>
 | 
				
			||||||
  	<artifactId>com.minres.scviewer.parent</artifactId>
 | 
					  	<artifactId>com.minres.scviewer.parent</artifactId>
 | 
				
			||||||
  	<version>2.11.2-SNAPSHOT</version>
 | 
					  	<version>1.0.0-SNAPSHOT</version>
 | 
				
			||||||
	<relativePath>../..</relativePath>
 | 
					  	<relativePath>../com.minres.scviewer.parent</relativePath>
 | 
				
			||||||
  </parent>
 | 
					  </parent>
 | 
				
			||||||
  <packaging>eclipse-plugin</packaging>
 | 
					  <packaging>eclipse-plugin</packaging>
 | 
				
			||||||
  <dependencies>
 | 
					  <dependencies>
 | 
				
			||||||
@@ -15,5 +15,4 @@
 | 
				
			|||||||
  		<version>3.8.7</version>
 | 
					  		<version>3.8.7</version>
 | 
				
			||||||
  	</dependency>
 | 
					  	</dependency>
 | 
				
			||||||
  </dependencies>
 | 
					  </dependencies>
 | 
				
			||||||
  <version>1.1.0-SNAPSHOT</version>
 | 
					  </project>
 | 
				
			||||||
</project>
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -11,11 +11,8 @@
 | 
				
			|||||||
package com.minres.scviewer.database.sqlite;
 | 
					package com.minres.scviewer.database.sqlite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.beans.IntrospectionException;
 | 
					import java.beans.IntrospectionException;
 | 
				
			||||||
import java.beans.PropertyChangeListener;
 | 
					 | 
				
			||||||
import java.beans.PropertyChangeSupport;
 | 
					 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
import java.io.FileInputStream;
 | 
					import java.io.FileInputStream;
 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
import java.lang.reflect.InvocationTargetException;
 | 
					import java.lang.reflect.InvocationTargetException;
 | 
				
			||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
@@ -25,7 +22,7 @@ import java.util.List;
 | 
				
			|||||||
import com.minres.scviewer.database.IWaveform;
 | 
					import com.minres.scviewer.database.IWaveform;
 | 
				
			||||||
import com.minres.scviewer.database.IWaveformDb;
 | 
					import com.minres.scviewer.database.IWaveformDb;
 | 
				
			||||||
import com.minres.scviewer.database.IWaveformDbLoader;
 | 
					import com.minres.scviewer.database.IWaveformDbLoader;
 | 
				
			||||||
import com.minres.scviewer.database.InputFormatException;
 | 
					import com.minres.scviewer.database.IWaveformEvent;
 | 
				
			||||||
import com.minres.scviewer.database.RelationType;
 | 
					import com.minres.scviewer.database.RelationType;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.db.IDatabase;
 | 
					import com.minres.scviewer.database.sqlite.db.IDatabase;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.db.SQLiteDatabase;
 | 
					import com.minres.scviewer.database.sqlite.db.SQLiteDatabase;
 | 
				
			||||||
@@ -39,21 +36,21 @@ public class SQLiteDbLoader implements IWaveformDbLoader {
 | 
				
			|||||||
	protected IDatabase database;
 | 
						protected IDatabase database;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private List<RelationType> usedRelationsList = new ArrayList<>();
 | 
						private List<RelationType> usedRelationsList = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private IWaveformDb db;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private ScvSimProps scvSimProps;
 | 
						private ScvSimProps scvSimProps;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	private static final byte[] x = "SQLite format 3".getBytes();
 | 
						public SQLiteDbLoader() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	/** The pcs. */
 | 
					 | 
				
			||||||
	protected PropertyChangeSupport pcs = new PropertyChangeSupport(this);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Long getMaxTime() {
 | 
						public Long getMaxTime() {
 | 
				
			||||||
		SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<>(ScvTxEvent.class,
 | 
							SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<ScvTxEvent>(ScvTxEvent.class,
 | 
				
			||||||
				database, "time = (SELECT MAX(time) FROM ScvTxEvent)");
 | 
									database, "time = (SELECT MAX(time) FROM ScvTxEvent)");
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			List<ScvTxEvent> event = handler.selectObjects();
 | 
								List<ScvTxEvent> event = handler.selectObjects();
 | 
				
			||||||
			if(!event.isEmpty())
 | 
								if(event.size()>0)
 | 
				
			||||||
				return event.get(0).getTime()*scvSimProps.getTime_resolution();
 | 
									return event.get(0).getTime()*scvSimProps.getTime_resolution();
 | 
				
			||||||
		} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
							} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
				
			||||||
				| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
									| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
				
			||||||
@@ -63,86 +60,54 @@ public class SQLiteDbLoader implements IWaveformDbLoader {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Collection<IWaveform> getAllWaves() {
 | 
						public List<IWaveform<? extends IWaveformEvent>> getAllWaves() {
 | 
				
			||||||
		SQLiteDatabaseSelectHandler<ScvStream> handler = new SQLiteDatabaseSelectHandler<>(ScvStream.class, database);
 | 
							SQLiteDatabaseSelectHandler<ScvStream> handler = new SQLiteDatabaseSelectHandler<ScvStream>(ScvStream.class, database);
 | 
				
			||||||
		List<IWaveform> streams=new ArrayList<>();
 | 
							List<IWaveform<? extends IWaveformEvent>> streams=new ArrayList<IWaveform<? extends IWaveformEvent>>();
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			for(ScvStream scvStream:handler.selectObjects()){
 | 
								for(ScvStream scvStream:handler.selectObjects()){
 | 
				
			||||||
				TxStream stream = new TxStream(database, scvStream);
 | 
									TxStream stream = new TxStream(database, db, scvStream);
 | 
				
			||||||
				stream.setRelationTypeList(usedRelationsList);
 | 
									stream.setRelationTypeList(usedRelationsList);
 | 
				
			||||||
				streams.add(stream);
 | 
									streams.add(stream);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
							} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
				
			||||||
				| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
									| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
				
			||||||
 | 
					//			e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return streams;
 | 
							return streams;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						private byte[] x = "SQLite format 3".getBytes();
 | 
				
			||||||
	public boolean canLoad(File inputFile) {
 | 
					 | 
				
			||||||
		if (!inputFile.isDirectory() && inputFile.exists()) {
 | 
					 | 
				
			||||||
			try(InputStream stream = new FileInputStream(inputFile)){
 | 
					 | 
				
			||||||
				byte[] buffer = new byte[x.length];
 | 
					 | 
				
			||||||
				int readCnt = stream.read(buffer, 0, x.length);
 | 
					 | 
				
			||||||
				if (readCnt == x.length) {
 | 
					 | 
				
			||||||
					for (int i = 0; i < x.length; i++)
 | 
					 | 
				
			||||||
						if (buffer[i] != x[i])
 | 
					 | 
				
			||||||
							return false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			} catch (Exception e) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void load(IWaveformDb db, File file) throws InputFormatException {
 | 
						public boolean load(IWaveformDb db, File file) throws Exception {
 | 
				
			||||||
		dispose();
 | 
							this.db=db;
 | 
				
			||||||
		database=new SQLiteDatabase(file.getAbsolutePath(), db);
 | 
							FileInputStream fis = new FileInputStream(file);
 | 
				
			||||||
 | 
							byte[] buffer = new byte[x.length];
 | 
				
			||||||
 | 
							int read = fis.read(buffer, 0, x.length);
 | 
				
			||||||
 | 
							fis.close();
 | 
				
			||||||
 | 
							if (read == x.length)
 | 
				
			||||||
 | 
								for (int i = 0; i < x.length; i++)
 | 
				
			||||||
 | 
									if (buffer[i] != x[i])	return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							database=new SQLiteDatabase(file.getAbsolutePath());
 | 
				
			||||||
		database.setData("TIMERESOLUTION", 1L);
 | 
							database.setData("TIMERESOLUTION", 1L);
 | 
				
			||||||
		SQLiteDatabaseSelectHandler<ScvSimProps> handler = new SQLiteDatabaseSelectHandler<>(ScvSimProps.class, database);
 | 
							SQLiteDatabaseSelectHandler<ScvSimProps> handler = new SQLiteDatabaseSelectHandler<ScvSimProps>(ScvSimProps.class, database);
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			for(ScvSimProps simProps:handler.selectObjects()){
 | 
								for(ScvSimProps simProps:handler.selectObjects()){
 | 
				
			||||||
				scvSimProps=simProps;
 | 
									scvSimProps=simProps;
 | 
				
			||||||
				database.setData("TIMERESOLUTION", scvSimProps.getTime_resolution());
 | 
									database.setData("TIMERESOLUTION", scvSimProps.getTime_resolution());
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			pcs.firePropertyChange(IWaveformDbLoader.LOADING_FINISHED, null, null);
 | 
								return true;
 | 
				
			||||||
		} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
							} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
				
			||||||
				| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
									| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
				
			||||||
			throw new InputFormatException(e.toString());
 | 
								e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	public void dispose() {
 | 
					 | 
				
			||||||
		database=null;
 | 
					 | 
				
			||||||
		usedRelationsList=null;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Collection<RelationType> getAllRelationTypes(){
 | 
						public Collection<RelationType> getAllRelationTypes(){
 | 
				
			||||||
		return usedRelationsList;
 | 
							return usedRelationsList;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Adds the property change listener.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param l the l
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void addPropertyChangeListener(PropertyChangeListener l) {
 | 
					 | 
				
			||||||
		pcs.addPropertyChangeListener(l);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Removes the property change listener.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param l the l
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void removePropertyChangeListener(PropertyChangeListener l) {
 | 
					 | 
				
			||||||
		pcs.removePropertyChangeListener(l);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -18,7 +18,12 @@ import java.util.Collection;
 | 
				
			|||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.minres.scviewer.database.AssociationType;
 | 
					import com.minres.scviewer.database.AssociationType;
 | 
				
			||||||
import com.minres.scviewer.database.IWaveform;
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttribute;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxGenerator;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxRelation;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxStream;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.db.IDatabase;
 | 
					import com.minres.scviewer.database.sqlite.db.IDatabase;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.db.SQLiteDatabaseSelectHandler;
 | 
					import com.minres.scviewer.database.sqlite.db.SQLiteDatabaseSelectHandler;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.tables.ScvStream;
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvStream;
 | 
				
			||||||
@@ -26,10 +31,6 @@ import com.minres.scviewer.database.sqlite.tables.ScvTx;
 | 
				
			|||||||
import com.minres.scviewer.database.sqlite.tables.ScvTxAttribute;
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvTxAttribute;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.tables.ScvTxEvent;
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvTxEvent;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.tables.ScvTxRelation;
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvTxRelation;
 | 
				
			||||||
import com.minres.scviewer.database.tx.ITx;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.tx.ITxAttribute;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.tx.ITxGenerator;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.tx.ITxRelation;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class Tx implements ITx {
 | 
					public class Tx implements ITx {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,10 +39,8 @@ public class Tx implements ITx {
 | 
				
			|||||||
	private TxGenerator trGenerator;
 | 
						private TxGenerator trGenerator;
 | 
				
			||||||
	private ScvTx scvTx;
 | 
						private ScvTx scvTx;
 | 
				
			||||||
	private List<ITxAttribute> attributes;
 | 
						private List<ITxAttribute> attributes;
 | 
				
			||||||
	private Long begin;
 | 
						private Long begin, end;
 | 
				
			||||||
	private Long end;
 | 
						private List<ITxRelation> incoming, outgoing;
 | 
				
			||||||
	private List<ITxRelation> incoming;
 | 
					 | 
				
			||||||
	private List<ITxRelation> outgoing;
 | 
					 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public Tx(IDatabase database, TxStream trStream, TxGenerator trGenerator, ScvTx scvTx) {
 | 
						public Tx(IDatabase database, TxStream trStream, TxGenerator trGenerator, ScvTx scvTx) {
 | 
				
			||||||
		this.database=database;
 | 
							this.database=database;
 | 
				
			||||||
@@ -56,7 +55,7 @@ public class Tx implements ITx {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public IWaveform getStream() {
 | 
						public ITxStream<ITxEvent> getStream() {
 | 
				
			||||||
		return trStream;
 | 
							return trStream;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,7 +72,7 @@ public class Tx implements ITx {
 | 
				
			|||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Long getBeginTime() {
 | 
						public Long getBeginTime() {
 | 
				
			||||||
		if(begin==null){
 | 
							if(begin==null){
 | 
				
			||||||
		SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<>(ScvTxEvent.class,
 | 
							SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<ScvTxEvent>(ScvTxEvent.class,
 | 
				
			||||||
				database, "tx="+scvTx.getId()+" AND type="+ AssociationType.BEGIN.ordinal());
 | 
									database, "tx="+scvTx.getId()+" AND type="+ AssociationType.BEGIN.ordinal());
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			for(ScvTxEvent scvEvent:handler.selectObjects()){
 | 
								for(ScvTxEvent scvEvent:handler.selectObjects()){
 | 
				
			||||||
@@ -89,7 +88,7 @@ public class Tx implements ITx {
 | 
				
			|||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Long getEndTime() {
 | 
						public Long getEndTime() {
 | 
				
			||||||
		if(end==null){
 | 
							if(end==null){
 | 
				
			||||||
		SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<>(ScvTxEvent.class,
 | 
							SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<ScvTxEvent>(ScvTxEvent.class,
 | 
				
			||||||
				database, "tx="+scvTx.getId()+" AND type="+ AssociationType.END.ordinal());
 | 
									database, "tx="+scvTx.getId()+" AND type="+ AssociationType.END.ordinal());
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			for(ScvTxEvent scvEvent:handler.selectObjects()){
 | 
								for(ScvTxEvent scvEvent:handler.selectObjects()){
 | 
				
			||||||
@@ -105,10 +104,10 @@ public class Tx implements ITx {
 | 
				
			|||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public List<ITxAttribute> getAttributes() {
 | 
						public List<ITxAttribute> getAttributes() {
 | 
				
			||||||
		if(attributes==null){
 | 
							if(attributes==null){
 | 
				
			||||||
			SQLiteDatabaseSelectHandler<ScvTxAttribute> handler = new SQLiteDatabaseSelectHandler<>(
 | 
								SQLiteDatabaseSelectHandler<ScvTxAttribute> handler = new SQLiteDatabaseSelectHandler<ScvTxAttribute>(
 | 
				
			||||||
					ScvTxAttribute.class, database, "tx="+scvTx.getId());
 | 
										ScvTxAttribute.class, database, "tx="+scvTx.getId());
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				attributes = new ArrayList<>();
 | 
									attributes = new ArrayList<ITxAttribute>();
 | 
				
			||||||
				for(ScvTxAttribute scvAttribute:handler.selectObjects()){
 | 
									for(ScvTxAttribute scvAttribute:handler.selectObjects()){
 | 
				
			||||||
					attributes.add(new TxAttribute(this, scvAttribute));
 | 
										attributes.add(new TxAttribute(this, scvAttribute));
 | 
				
			||||||
					
 | 
										
 | 
				
			||||||
@@ -123,10 +122,10 @@ public class Tx implements ITx {
 | 
				
			|||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Collection<ITxRelation> getIncomingRelations() {
 | 
						public Collection<ITxRelation> getIncomingRelations() {
 | 
				
			||||||
		if(incoming==null){
 | 
							if(incoming==null){
 | 
				
			||||||
			SQLiteDatabaseSelectHandler<ScvTxRelation> handler = new SQLiteDatabaseSelectHandler<>(
 | 
								SQLiteDatabaseSelectHandler<ScvTxRelation> handler = new SQLiteDatabaseSelectHandler<ScvTxRelation>(
 | 
				
			||||||
					ScvTxRelation.class, database, "sink="+scvTx.getId());
 | 
										ScvTxRelation.class, database, "sink="+scvTx.getId());
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				incoming = new ArrayList<>();
 | 
									incoming = new ArrayList<ITxRelation>();
 | 
				
			||||||
				for(ScvTxRelation scvRelation:handler.selectObjects()){
 | 
									for(ScvTxRelation scvRelation:handler.selectObjects()){
 | 
				
			||||||
					incoming.add(createRelation(scvRelation, false));
 | 
										incoming.add(createRelation(scvRelation, false));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -140,10 +139,10 @@ public class Tx implements ITx {
 | 
				
			|||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Collection<ITxRelation> getOutgoingRelations() {
 | 
						public Collection<ITxRelation> getOutgoingRelations() {
 | 
				
			||||||
		if(outgoing==null){
 | 
							if(outgoing==null){
 | 
				
			||||||
			SQLiteDatabaseSelectHandler<ScvTxRelation> handler = new SQLiteDatabaseSelectHandler<>(
 | 
								SQLiteDatabaseSelectHandler<ScvTxRelation> handler = new SQLiteDatabaseSelectHandler<ScvTxRelation>(
 | 
				
			||||||
					ScvTxRelation.class, database, "src="+scvTx.getId());
 | 
										ScvTxRelation.class, database, "src="+scvTx.getId());
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				outgoing = new ArrayList<>();
 | 
									outgoing = new ArrayList<ITxRelation>();
 | 
				
			||||||
				for(ScvTxRelation scvRelation:handler.selectObjects()){
 | 
									for(ScvTxRelation scvRelation:handler.selectObjects()){
 | 
				
			||||||
					outgoing.add(createRelation(scvRelation, true));
 | 
										outgoing.add(createRelation(scvRelation, true));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -156,7 +155,7 @@ public class Tx implements ITx {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	private ITxRelation createRelation(ScvTxRelation rel, boolean outgoing) {
 | 
						private ITxRelation createRelation(ScvTxRelation rel, boolean outgoing) {
 | 
				
			||||||
		int otherId = outgoing?rel.getSink():rel.getSrc();
 | 
							int otherId = outgoing?rel.getSink():rel.getSrc();
 | 
				
			||||||
		SQLiteDatabaseSelectHandler<ScvTx> handler = new SQLiteDatabaseSelectHandler<>(ScvTx.class, database,
 | 
							SQLiteDatabaseSelectHandler<ScvTx> handler = new SQLiteDatabaseSelectHandler<ScvTx>(ScvTx.class, database,
 | 
				
			||||||
				"id="+otherId);
 | 
									"id="+otherId);
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			List<ScvTx> res = handler.selectObjects();
 | 
								List<ScvTx> res = handler.selectObjects();
 | 
				
			||||||
@@ -164,7 +163,7 @@ public class Tx implements ITx {
 | 
				
			|||||||
			List<ScvStream> streams = new SQLiteDatabaseSelectHandler<ScvStream>(ScvStream.class, database,
 | 
								List<ScvStream> streams = new SQLiteDatabaseSelectHandler<ScvStream>(ScvStream.class, database,
 | 
				
			||||||
						"id="+res.get(0).getStream()).selectObjects();
 | 
											"id="+res.get(0).getStream()).selectObjects();
 | 
				
			||||||
			if(streams.size()!=1) return null;
 | 
								if(streams.size()!=1) return null;
 | 
				
			||||||
			TxStream tgtStream = (TxStream) database.getWaveformDb().getStreamByName(streams.get(0).getName());
 | 
								TxStream tgtStream = (TxStream) trStream.getDb().getStreamByName(streams.get(0).getName());
 | 
				
			||||||
			Tx that = (Tx) tgtStream.getTransactions().get(otherId);
 | 
								Tx that = (Tx) tgtStream.getTransactions().get(otherId);
 | 
				
			||||||
			if(outgoing)
 | 
								if(outgoing)
 | 
				
			||||||
				return new TxRelation(trStream.getRelationType(rel.getName()), this, that);
 | 
									return new TxRelation(trStream.getRelationType(rel.getName()), this, that);
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -12,8 +12,8 @@ package com.minres.scviewer.database.sqlite;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.minres.scviewer.database.AssociationType;
 | 
					import com.minres.scviewer.database.AssociationType;
 | 
				
			||||||
import com.minres.scviewer.database.DataType;
 | 
					import com.minres.scviewer.database.DataType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttribute;
 | 
				
			||||||
import com.minres.scviewer.database.sqlite.tables.ScvTxAttribute;
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvTxAttribute;
 | 
				
			||||||
import com.minres.scviewer.database.tx.ITxAttribute;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class TxAttribute implements ITxAttribute{
 | 
					public class TxAttribute implements ITxAttribute{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -10,18 +10,16 @@
 | 
				
			|||||||
 *******************************************************************************/
 | 
					 *******************************************************************************/
 | 
				
			||||||
package com.minres.scviewer.database.sqlite;
 | 
					package com.minres.scviewer.database.sqlite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.minres.scviewer.database.EventKind;
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
import com.minres.scviewer.database.IEvent;
 | 
					import com.minres.scviewer.database.ITxEvent;
 | 
				
			||||||
import com.minres.scviewer.database.WaveformType;
 | 
					import com.minres.scviewer.database.IWaveformEvent;
 | 
				
			||||||
import com.minres.scviewer.database.tx.ITx;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.tx.ITxEvent;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class TxEvent implements ITxEvent {
 | 
					public class TxEvent implements ITxEvent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final EventKind type;
 | 
						private final Type type;
 | 
				
			||||||
	private ITx tx;
 | 
						private ITx tx;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public TxEvent(EventKind type, ITx tx) {
 | 
						public TxEvent(Type type, ITx tx) {
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
		this.type = type;
 | 
							this.type = type;
 | 
				
			||||||
		this.tx = tx;
 | 
							this.tx = tx;
 | 
				
			||||||
@@ -29,21 +27,26 @@ public class TxEvent implements ITxEvent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Long getTime() {
 | 
						public Long getTime() {
 | 
				
			||||||
		return type==EventKind.BEGIN?tx.getBeginTime():tx.getEndTime();
 | 
							return type==Type.BEGIN?tx.getBeginTime():tx.getEndTime();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public IEvent duplicate() throws CloneNotSupportedException {
 | 
						public IWaveformEvent duplicate() throws CloneNotSupportedException {
 | 
				
			||||||
		return new TxEvent(type, tx);
 | 
							return new TxEvent(type, tx);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int compareTo(IWaveformEvent o) {
 | 
				
			||||||
 | 
							return getTime().compareTo(o.getTime());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public ITx getTransaction() {
 | 
						public ITx getTransaction() {
 | 
				
			||||||
		return tx;
 | 
							return tx;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public EventKind getKind() {
 | 
						public Type getType() {
 | 
				
			||||||
		return type;
 | 
							return type;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,9 +54,4 @@ public class TxEvent implements ITxEvent {
 | 
				
			|||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		return type.toString()+"@"+getTime()+" of tx #"+tx.getId();
 | 
							return type.toString()+"@"+getTime()+" of tx #"+tx.getId();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public WaveformType getType() {
 | 
					 | 
				
			||||||
		return WaveformType.TRANSACTION;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     MINRES Technologies GmbH - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.sqlite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxGenerator;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxStream;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvGenerator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TxGenerator implements ITxGenerator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private ITxStream<ITxEvent>  stream;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private ScvGenerator scvGenerator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public TxGenerator(ITxStream<ITxEvent>  stream, ScvGenerator scvGenerator) {
 | 
				
			||||||
 | 
							this.stream=stream;
 | 
				
			||||||
 | 
							this.scvGenerator=scvGenerator;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Long getId() {
 | 
				
			||||||
 | 
							return (long) scvGenerator.getId();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public ITxStream<ITxEvent> getStream() {
 | 
				
			||||||
 | 
							return stream;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String getName() {
 | 
				
			||||||
 | 
							return scvGenerator.getName();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public List<ITx> getTransactions() {
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -10,15 +10,14 @@
 | 
				
			|||||||
 *******************************************************************************/
 | 
					 *******************************************************************************/
 | 
				
			||||||
package com.minres.scviewer.database.sqlite;
 | 
					package com.minres.scviewer.database.sqlite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxRelation;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
import com.minres.scviewer.database.RelationType;
 | 
					import com.minres.scviewer.database.RelationType;
 | 
				
			||||||
import com.minres.scviewer.database.tx.ITx;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.tx.ITxRelation;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class TxRelation implements ITxRelation {
 | 
					public class TxRelation implements ITxRelation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RelationType relationType;
 | 
						RelationType relationType;
 | 
				
			||||||
	Tx source;
 | 
						Tx source, target;
 | 
				
			||||||
	Tx target;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public TxRelation(RelationType relationType, Tx source, Tx target) {
 | 
						public TxRelation(RelationType relationType, Tx source, Tx target) {
 | 
				
			||||||
		this.source = source;
 | 
							this.source = source;
 | 
				
			||||||
@@ -0,0 +1,199 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     MINRES Technologies GmbH - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.sqlite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.beans.IntrospectionException;
 | 
				
			||||||
 | 
					import java.lang.reflect.InvocationTargetException;
 | 
				
			||||||
 | 
					import java.sql.SQLException;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Map.Entry;
 | 
				
			||||||
 | 
					import java.util.NavigableMap;
 | 
				
			||||||
 | 
					import java.util.TreeMap;
 | 
				
			||||||
 | 
					import java.util.Vector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.HierNode;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxGenerator;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxStream;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveform;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformDb;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.RelationType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.sqlite.db.IDatabase;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.sqlite.db.SQLiteDatabaseSelectHandler;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvGenerator;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvStream;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.sqlite.tables.ScvTx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TxStream extends HierNode implements ITxStream<ITxEvent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private IDatabase database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String fullName;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private IWaveformDb db;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private ScvStream scvStream;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private TreeMap<Integer, TxGenerator> generators;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private TreeMap<Integer, ITx> transactions;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private Integer maxConcurrency;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private TreeMap<Long, List<ITxEvent>> events;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private List<RelationType> usedRelationsList;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public TxStream(IDatabase database, IWaveformDb waveformDb, ScvStream scvStream) {
 | 
				
			||||||
 | 
							super(scvStream.getName());
 | 
				
			||||||
 | 
							this.database=database;
 | 
				
			||||||
 | 
							fullName=scvStream.getName();
 | 
				
			||||||
 | 
							this.scvStream=scvStream;
 | 
				
			||||||
 | 
							db=waveformDb;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public IWaveformDb getDb() {
 | 
				
			||||||
 | 
							return db;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String getFullName() {
 | 
				
			||||||
 | 
							return fullName;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Long getId() {
 | 
				
			||||||
 | 
							return (long) scvStream.getId();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String getKind() {
 | 
				
			||||||
 | 
							return scvStream.getKind();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public List<ITxGenerator> getGenerators() {
 | 
				
			||||||
 | 
							if(generators==null){
 | 
				
			||||||
 | 
								SQLiteDatabaseSelectHandler<ScvGenerator> handler = new SQLiteDatabaseSelectHandler<ScvGenerator>(
 | 
				
			||||||
 | 
										ScvGenerator.class, database, "stream="+scvStream.getId());
 | 
				
			||||||
 | 
								generators=new TreeMap<Integer, TxGenerator>();
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									for(ScvGenerator scvGenerator:handler.selectObjects()){
 | 
				
			||||||
 | 
										generators.put(scvGenerator.getId(), new TxGenerator(this, scvGenerator));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
				
			||||||
 | 
										| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
				
			||||||
 | 
									e.printStackTrace();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return new ArrayList<ITxGenerator>(generators.values());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int getMaxConcurrency() {
 | 
				
			||||||
 | 
							if(maxConcurrency==null){
 | 
				
			||||||
 | 
								java.sql.Connection connection=null;
 | 
				
			||||||
 | 
								java.sql.Statement statement=null;
 | 
				
			||||||
 | 
								java.sql.ResultSet resultSet=null;
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									connection = database.createConnection();
 | 
				
			||||||
 | 
									statement = connection.createStatement();
 | 
				
			||||||
 | 
									StringBuilder sb = new StringBuilder();
 | 
				
			||||||
 | 
									sb.append("SELECT MAX(concurrencyLevel) as concurrencyLevel FROM ScvTx where stream=");
 | 
				
			||||||
 | 
									sb.append(scvStream.getId());
 | 
				
			||||||
 | 
									resultSet = statement.executeQuery(sb.toString());
 | 
				
			||||||
 | 
									while (resultSet.next()) {
 | 
				
			||||||
 | 
										Object value = resultSet.getObject("concurrencyLevel");
 | 
				
			||||||
 | 
										if(value!=null)
 | 
				
			||||||
 | 
											maxConcurrency=(Integer) value;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} catch (SQLException e) {
 | 
				
			||||||
 | 
									if(maxConcurrency==null) maxConcurrency=0;
 | 
				
			||||||
 | 
								} finally {
 | 
				
			||||||
 | 
									try{
 | 
				
			||||||
 | 
									if(resultSet!=null) resultSet.close();
 | 
				
			||||||
 | 
									if(statement!=null) statement.close();
 | 
				
			||||||
 | 
									if(connection!=null) connection.close();
 | 
				
			||||||
 | 
									} catch (SQLException e) {	}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								maxConcurrency+=1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return maxConcurrency;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public  NavigableMap<Long, List<ITxEvent>> getEvents(){
 | 
				
			||||||
 | 
							if(events==null){
 | 
				
			||||||
 | 
								events=new TreeMap<Long, List<ITxEvent>>();
 | 
				
			||||||
 | 
								for(Entry<Integer, ITx> entry:getTransactions().entrySet()){
 | 
				
			||||||
 | 
									putEvent(new TxEvent(TxEvent.Type.BEGIN, entry.getValue()));
 | 
				
			||||||
 | 
									putEvent(new TxEvent(TxEvent.Type.END, entry.getValue()));
 | 
				
			||||||
 | 
								}	
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return events;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void putEvent(TxEvent ev){
 | 
				
			||||||
 | 
							Long time = ev.getTime();
 | 
				
			||||||
 | 
							if(!events.containsKey(time)){
 | 
				
			||||||
 | 
								Vector<ITxEvent> vector=new Vector<ITxEvent>();
 | 
				
			||||||
 | 
								vector.add(ev);
 | 
				
			||||||
 | 
								events.put(time,  vector);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								events.get(time).add(ev);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected Map<Integer, ITx> getTransactions() {
 | 
				
			||||||
 | 
							if(transactions==null){
 | 
				
			||||||
 | 
								if(generators==null) getGenerators();
 | 
				
			||||||
 | 
								transactions = new TreeMap<Integer, ITx>();
 | 
				
			||||||
 | 
								SQLiteDatabaseSelectHandler<ScvTx> handler = new SQLiteDatabaseSelectHandler<ScvTx>(ScvTx.class, database,
 | 
				
			||||||
 | 
										"stream="+scvStream.getId());
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									for(ScvTx scvTx:handler.selectObjects()){
 | 
				
			||||||
 | 
										transactions.put(scvTx.getId(), new Tx(database, this, generators.get(scvTx.getGenerator()), scvTx));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
 | 
				
			||||||
 | 
										| InvocationTargetException | SQLException | IntrospectionException e) {
 | 
				
			||||||
 | 
									e.printStackTrace();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return transactions;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Collection<ITxEvent> getWaveformEventsAtTime(Long time) {
 | 
				
			||||||
 | 
							return getEvents().get(time);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setRelationTypeList(List<RelationType> usedRelationsList){
 | 
				
			||||||
 | 
							this.usedRelationsList=usedRelationsList;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public RelationType getRelationType(String name) {
 | 
				
			||||||
 | 
							RelationType relType=RelationType.create(name);
 | 
				
			||||||
 | 
							if(!usedRelationsList.contains(relType)) usedRelationsList.add(relType);
 | 
				
			||||||
 | 
							return relType;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Boolean equals(IWaveform<? extends IWaveformEvent> other) {
 | 
				
			||||||
 | 
							return(other instanceof TxStream && this.getId()==other.getId());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -16,8 +16,6 @@ import java.sql.ResultSet;
 | 
				
			|||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
import java.sql.Statement;
 | 
					import java.sql.Statement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.minres.scviewer.database.IWaveformDb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Creates a connection to a database.
 | 
					 * Creates a connection to a database.
 | 
				
			||||||
@@ -60,6 +58,4 @@ public interface IDatabase {
 | 
				
			|||||||
	public void setData(String name, Object value);
 | 
						public void setData(String name, Object value);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public Object getData(String name); 
 | 
						public Object getData(String name); 
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public IWaveformDb getWaveformDb();
 | 
					 | 
				
			||||||
} 
 | 
					} 
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -20,14 +20,10 @@ import java.sql.SQLException;
 | 
				
			|||||||
import java.sql.Statement;
 | 
					import java.sql.Statement;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.minres.scviewer.database.IWaveformDb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class SQLiteDatabase implements IDatabase {
 | 
					public class SQLiteDatabase implements IDatabase {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected String dbFileName;
 | 
						protected String dbFileName;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	protected IWaveformDb waveformDb;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	protected HashMap<String, Object> props;
 | 
						protected HashMap<String, Object> props;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
    static {
 | 
					    static {
 | 
				
			||||||
@@ -43,10 +39,9 @@ public class SQLiteDatabase implements IDatabase {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
	public SQLiteDatabase(String dbFileName, IWaveformDb waveformDb) {
 | 
						public SQLiteDatabase(String dbFileName) {
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
		this.dbFileName = dbFileName;
 | 
							this.dbFileName = dbFileName;
 | 
				
			||||||
		this.waveformDb = waveformDb;
 | 
					 | 
				
			||||||
		props = new HashMap<String, Object>();
 | 
							props = new HashMap<String, Object>();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,9 +89,4 @@ public class SQLiteDatabase implements IDatabase {
 | 
				
			|||||||
		return props.get(name);
 | 
							return props.get(name);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public IWaveformDb getWaveformDb() {
 | 
					 | 
				
			||||||
		return waveformDb;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -77,6 +77,7 @@ public class SQLiteDatabaseSelectHandler<T> extends AbstractDatabaseHandler<T> {
 | 
				
			|||||||
	 * @throws InvocationTargetException
 | 
						 * @throws InvocationTargetException
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public synchronized List<T> selectObjects() throws SQLException,
 | 
						public synchronized List<T> selectObjects() throws SQLException,
 | 
				
			||||||
 | 
								SecurityException, IllegalArgumentException,
 | 
				
			||||||
			InstantiationException, IllegalAccessException,
 | 
								InstantiationException, IllegalAccessException,
 | 
				
			||||||
			IntrospectionException, InvocationTargetException {
 | 
								IntrospectionException, InvocationTargetException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -115,11 +116,12 @@ public class SQLiteDatabaseSelectHandler<T> extends AbstractDatabaseHandler<T> {
 | 
				
			|||||||
	 * @throws InvocationTargetException
 | 
						 * @throws InvocationTargetException
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private List<T> createObjects(ResultSet resultSet)
 | 
						private List<T> createObjects(ResultSet resultSet)
 | 
				
			||||||
			throws SQLException, InstantiationException,
 | 
								throws SecurityException, IllegalArgumentException,
 | 
				
			||||||
 | 
								SQLException, InstantiationException,
 | 
				
			||||||
			IllegalAccessException, IntrospectionException,
 | 
								IllegalAccessException, IntrospectionException,
 | 
				
			||||||
			InvocationTargetException {
 | 
								InvocationTargetException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		List<T> list = new ArrayList<>();
 | 
							List<T> list = new ArrayList<T>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		while (resultSet.next()) {
 | 
							while (resultSet.next()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					eclipse.preferences.version=1
 | 
				
			||||||
 | 
					encoding//src/com/minres/scviewer/database/test/DatabaseServicesTest.java=UTF-8
 | 
				
			||||||
 | 
					encoding/<project>=UTF-8
 | 
				
			||||||
@@ -5,5 +5,4 @@ org.eclipse.jdt.core.compiler.compliance=1.8
 | 
				
			|||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
 | 
					org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
 | 
				
			||||||
org.eclipse.jdt.core.compiler.release=disabled
 | 
					 | 
				
			||||||
org.eclipse.jdt.core.compiler.source=1.8
 | 
					org.eclipse.jdt.core.compiler.source=1.8
 | 
				
			||||||
@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
				
			||||||
 | 
					<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
 | 
				
			||||||
 | 
					<booleanAttribute key="append.args" value="true"/>
 | 
				
			||||||
 | 
					<stringAttribute key="application" value="org.eclipse.pde.junit.runtime.coretestapplication"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="askclear" value="false"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="automaticAdd" value="true"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="automaticValidate" value="false"/>
 | 
				
			||||||
 | 
					<stringAttribute key="bootstrap" value=""/>
 | 
				
			||||||
 | 
					<stringAttribute key="checked" value="[NONE]"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="clearConfig" value="true"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="clearws" value="true"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="clearwslog" value="false"/>
 | 
				
			||||||
 | 
					<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/pde-junit"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="default" value="false"/>
 | 
				
			||||||
 | 
					<stringAttribute key="deselected_workspace_plugins" value="com.minres.scviewer.e4.application,com.minres.scviewer.ui"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="includeOptional" value="true"/>
 | 
				
			||||||
 | 
					<stringAttribute key="location" value="${workspace_loc}/../junit-workspace"/>
 | 
				
			||||||
 | 
					<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
 | 
				
			||||||
 | 
					<listEntry value="/com.minres.scviewer.database.test"/>
 | 
				
			||||||
 | 
					</listAttribute>
 | 
				
			||||||
 | 
					<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
 | 
				
			||||||
 | 
					<listEntry value="4"/>
 | 
				
			||||||
 | 
					</listAttribute>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.minres.scviewer.database.test"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.minres.scviewer.database.test"/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
 | 
				
			||||||
 | 
					<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx512m"/>
 | 
				
			||||||
 | 
					<stringAttribute key="pde.version" value="3.3"/>
 | 
				
			||||||
 | 
					<stringAttribute key="product" value="com.minres.scviewer.e4.product"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="run_in_ui_thread" value="true"/>
 | 
				
			||||||
 | 
					<stringAttribute key="selected_target_plugins" value="com.google.guava@default:default,javax.annotation@default:default,javax.inject@default:default,javax.servlet@default:default,javax.xml@default:default,org.apache.ant@default:default,org.apache.commons.jxpath@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.codehaus.groovy@default:default,org.eclipse.ant.core@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.e4.core.contexts@default:default,org.eclipse.e4.core.di.annotations@default:default,org.eclipse.e4.core.di.extensions@default:default,org.eclipse.e4.core.di@default:default,org.eclipse.e4.core.services@default:default,org.eclipse.e4.emf.xpath@default:default,org.eclipse.e4.ui.di@default:default,org.eclipse.e4.ui.model.workbench@default:default,org.eclipse.e4.ui.services@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.bidi@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.ds@1:true,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.util@default:default,org.eclipse.jface@default:default,org.eclipse.osgi.compatibility.state@default:false,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.eclipse.swt@default:default,org.hamcrest.core@default:default,org.junit@default:default"/>
 | 
				
			||||||
 | 
					<stringAttribute key="selected_workspace_plugins" value="com.minres.scviewer.database.binary@default:default,com.minres.scviewer.database.sqlite@default:true,com.minres.scviewer.database.test@default:default,com.minres.scviewer.database.text@default:true,com.minres.scviewer.database.ui.swt@default:default,com.minres.scviewer.database.ui@default:default,com.minres.scviewer.database.vcd@default:default,com.minres.scviewer.database@default:true,com.opcoach.e4.preferences@default:default"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="show_selected_only" value="false"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="tracing" value="false"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="useCustomFeatures" value="false"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="useDefaultConfig" value="true"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="useDefaultConfigArea" value="false"/>
 | 
				
			||||||
 | 
					<booleanAttribute key="useProduct" value="false"/>
 | 
				
			||||||
 | 
					</launchConfiguration>
 | 
				
			||||||
@@ -2,15 +2,13 @@ Manifest-Version: 1.0
 | 
				
			|||||||
Bundle-ManifestVersion: 2
 | 
					Bundle-ManifestVersion: 2
 | 
				
			||||||
Bundle-Name: SCViewer database tests
 | 
					Bundle-Name: SCViewer database tests
 | 
				
			||||||
Bundle-SymbolicName: com.minres.scviewer.database.test
 | 
					Bundle-SymbolicName: com.minres.scviewer.database.test
 | 
				
			||||||
Bundle-Version: 1.0.1.qualifier
 | 
					Bundle-Version: 1.0.0.qualifier
 | 
				
			||||||
Bundle-Vendor: MINRES Technologies GmbH
 | 
					Bundle-Vendor: MINRES Technologies GnbH
 | 
				
			||||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 | 
					Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 | 
				
			||||||
Require-Bundle: com.minres.scviewer.database,
 | 
					Require-Bundle: org.junit,
 | 
				
			||||||
 | 
					 com.minres.scviewer.database,
 | 
				
			||||||
 com.minres.scviewer.database.sqlite;bundle-version="1.0.0",
 | 
					 com.minres.scviewer.database.sqlite;bundle-version="1.0.0",
 | 
				
			||||||
 com.minres.scviewer.database.text;bundle-version="1.0.0",
 | 
					 com.minres.scviewer.database.text;bundle-version="1.0.0",
 | 
				
			||||||
 com.minres.scviewer.database.vcd;bundle-version="1.0.0",
 | 
					 com.minres.scviewer.database.vcd;bundle-version="1.0.0"
 | 
				
			||||||
 org.junit,
 | 
					 | 
				
			||||||
 org.junit.jupiter.api
 | 
					 | 
				
			||||||
Bundle-ActivationPolicy: lazy
 | 
					Bundle-ActivationPolicy: lazy
 | 
				
			||||||
Service-Component: OSGI-INF/component.xml
 | 
					Service-Component: OSGI-INF/component.xml
 | 
				
			||||||
Automatic-Module-Name: com.minres.scviewer.database.test
 | 
					 | 
				
			||||||
							
								
								
									
										2
									
								
								com.minres.scviewer.database.test/inputs/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								com.minres.scviewer.database.test/inputs/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					/.scviewer.*
 | 
				
			||||||
 | 
					/.my_db.txlog*
 | 
				
			||||||
@@ -2,12 +2,11 @@
 | 
				
			|||||||
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
						xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
				
			||||||
	<modelVersion>4.0.0</modelVersion>
 | 
						<modelVersion>4.0.0</modelVersion>
 | 
				
			||||||
	<artifactId>com.minres.scviewer.database.test</artifactId>
 | 
						<artifactId>com.minres.scviewer.database.test</artifactId>
 | 
				
			||||||
	<version>1.0.1-SNAPSHOT</version>
 | 
					 | 
				
			||||||
	<parent>
 | 
						<parent>
 | 
				
			||||||
		<groupId>com.minres.scviewer</groupId>
 | 
							<groupId>com.minres.scviewer</groupId>
 | 
				
			||||||
		<artifactId>com.minres.scviewer.parent</artifactId>
 | 
							<artifactId>com.minres.scviewer.parent</artifactId>
 | 
				
			||||||
		<version>2.11.2-SNAPSHOT</version>
 | 
							<version>1.0.0-SNAPSHOT</version>
 | 
				
			||||||
		<relativePath>../..</relativePath>
 | 
							<relativePath>../com.minres.scviewer.parent</relativePath>
 | 
				
			||||||
	</parent>
 | 
						</parent>
 | 
				
			||||||
	<packaging>eclipse-test-plugin</packaging>
 | 
						<packaging>eclipse-test-plugin</packaging>
 | 
				
			||||||
	<build>
 | 
						<build>
 | 
				
			||||||
@@ -15,8 +14,17 @@
 | 
				
			|||||||
			<plugin>
 | 
								<plugin>
 | 
				
			||||||
				<groupId>org.eclipse.tycho</groupId>
 | 
									<groupId>org.eclipse.tycho</groupId>
 | 
				
			||||||
				<artifactId>tycho-surefire-plugin</artifactId>
 | 
									<artifactId>tycho-surefire-plugin</artifactId>
 | 
				
			||||||
				<version>${tycho-version}</version>
 | 
									<version>0.23.1</version>
 | 
				
			||||||
 | 
									<configuration>
 | 
				
			||||||
 | 
					<!-- 					<bundleStartLevel /> -->
 | 
				
			||||||
 | 
										<dependencies>
 | 
				
			||||||
 | 
											<dependency>
 | 
				
			||||||
 | 
												<type>p2-installable-unit</type>
 | 
				
			||||||
 | 
												<artifactId>org.eclipse.equinox.ds</artifactId>
 | 
				
			||||||
 | 
											</dependency>
 | 
				
			||||||
 | 
										</dependencies>
 | 
				
			||||||
 | 
									</configuration>
 | 
				
			||||||
			</plugin>
 | 
								</plugin>
 | 
				
			||||||
		</plugins>
 | 
							</plugins>
 | 
				
			||||||
	</build>
 | 
						</build>
 | 
				
			||||||
</project>
 | 
						</project>
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*******************************************************************************
 | 
					/*******************************************************************************
 | 
				
			||||||
 * Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
 | 
					 * Copyright (c) 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 * All rights reserved. This program and the accompanying materials
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 * which accompanies this distribution, and is available at
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
@@ -15,15 +15,11 @@ import static org.junit.Assert.assertNotNull;
 | 
				
			|||||||
import static org.junit.Assert.assertTrue;
 | 
					import static org.junit.Assert.assertTrue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Map.Entry;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.After;
 | 
					import org.junit.After;
 | 
				
			||||||
import org.junit.Before;
 | 
					import org.junit.Before;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.minres.scviewer.database.IEvent;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.IWaveform;
 | 
					 | 
				
			||||||
import com.minres.scviewer.database.IWaveformDb;
 | 
					import com.minres.scviewer.database.IWaveformDb;
 | 
				
			||||||
import com.minres.scviewer.database.IWaveformDbFactory;
 | 
					import com.minres.scviewer.database.IWaveformDbFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,6 +43,13 @@ public class DatabaseServicesTest {
 | 
				
			|||||||
	@Before
 | 
						@Before
 | 
				
			||||||
	public void setUp() throws Exception {
 | 
						public void setUp() throws Exception {
 | 
				
			||||||
		waveformDb=waveformDbFactory.getDatabase();
 | 
							waveformDb=waveformDbFactory.getDatabase();
 | 
				
			||||||
 | 
							// Wait for OSGi dependencies
 | 
				
			||||||
 | 
					//		for (int i = 0; i < 10; i++) {
 | 
				
			||||||
 | 
					//			if (waveformDb.size() == 3) // Dependencies fulfilled
 | 
				
			||||||
 | 
					//				return;
 | 
				
			||||||
 | 
					//			Thread.sleep(1000);
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//		assertEquals("OSGi dependencies unfulfilled", 3, WaveformDb.getLoaders().size());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@After
 | 
						@After
 | 
				
			||||||
@@ -59,20 +62,13 @@ public class DatabaseServicesTest {
 | 
				
			|||||||
		assertTrue(f.exists());
 | 
							assertTrue(f.exists());
 | 
				
			||||||
		waveformDb.load(f);
 | 
							waveformDb.load(f);
 | 
				
			||||||
		assertNotNull(waveformDb);
 | 
							assertNotNull(waveformDb);
 | 
				
			||||||
		List<IWaveform> waves= waveformDb.getAllWaves();
 | 
							assertEquals(14,  waveformDb.getAllWaves().size());
 | 
				
			||||||
		assertEquals(14,  waves.size());
 | 
					 | 
				
			||||||
		assertEquals(2,  waveformDb.getChildNodes().size());
 | 
							assertEquals(2,  waveformDb.getChildNodes().size());
 | 
				
			||||||
		IWaveform bus_data_wave = waves.get(0);
 | 
					 | 
				
			||||||
		Entry<Long, IEvent[]> bus_data_entry = bus_data_wave.getEvents().floorEntry(1400000000L);
 | 
					 | 
				
			||||||
		assertEquals("01111000", bus_data_entry.getValue()[0].toString());
 | 
					 | 
				
			||||||
		IWaveform rw_wave = waves.get(2);
 | 
					 | 
				
			||||||
		Entry<Long, IEvent[]> rw_entry = rw_wave.getEvents().floorEntry(2360000000L);
 | 
					 | 
				
			||||||
		assertEquals("1", rw_entry.getValue()[0].toString());
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testTxSQLite() throws Exception {
 | 
						public void testTxSQLite() throws Exception {
 | 
				
			||||||
		File f = new File("inputs/my_sqldb.txdb").getAbsoluteFile();
 | 
							File f = new File("inputs/my_db.txdb").getAbsoluteFile();
 | 
				
			||||||
		assertTrue(f.exists());
 | 
							assertTrue(f.exists());
 | 
				
			||||||
		waveformDb.load(f);
 | 
							waveformDb.load(f);
 | 
				
			||||||
		assertNotNull(waveformDb);
 | 
							assertNotNull(waveformDb);
 | 
				
			||||||
@@ -86,38 +82,9 @@ public class DatabaseServicesTest {
 | 
				
			|||||||
		assertTrue(f.exists());
 | 
							assertTrue(f.exists());
 | 
				
			||||||
		waveformDb.load(f);
 | 
							waveformDb.load(f);
 | 
				
			||||||
		assertNotNull(waveformDb);
 | 
							assertNotNull(waveformDb);
 | 
				
			||||||
		List<IWaveform> waveforms =  waveformDb.getAllWaves();
 | 
					 | 
				
			||||||
		assertEquals(3,  waveforms.size());
 | 
					 | 
				
			||||||
		assertEquals(1,  waveformDb.getChildNodes().size());
 | 
					 | 
				
			||||||
		for(IWaveform w:waveforms) {
 | 
					 | 
				
			||||||
			if(w.getId().equals(1l)) {
 | 
					 | 
				
			||||||
				assertEquals(2, w.getWidth());
 | 
					 | 
				
			||||||
			} else if(w.getId().equals(2l)) {
 | 
					 | 
				
			||||||
				assertEquals(1, w.getWidth());
 | 
					 | 
				
			||||||
			} else if(w.getId().equals(3l)) {
 | 
					 | 
				
			||||||
				assertEquals(1, w.getWidth());
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testTxTextTruncated() throws Exception {
 | 
					 | 
				
			||||||
		File f = new File("inputs/my_db_truncated.txlog").getAbsoluteFile();
 | 
					 | 
				
			||||||
		assertTrue(f.exists());
 | 
					 | 
				
			||||||
		waveformDb.load(f);
 | 
					 | 
				
			||||||
		assertNotNull(waveformDb);
 | 
					 | 
				
			||||||
		assertEquals(3,  waveformDb.getAllWaves().size());
 | 
							assertEquals(3,  waveformDb.getAllWaves().size());
 | 
				
			||||||
		assertEquals(1,  waveformDb.getChildNodes().size());
 | 
							assertEquals(1,  waveformDb.getChildNodes().size());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testHierarchicalVCD() throws Exception {
 | 
					 | 
				
			||||||
		File f = new File("inputs/simple_system.vcd").getAbsoluteFile();
 | 
					 | 
				
			||||||
		assertTrue(f.exists());
 | 
					 | 
				
			||||||
		waveformDb.load(f);
 | 
					 | 
				
			||||||
		assertNotNull(waveformDb);
 | 
					 | 
				
			||||||
		assertEquals(779,  waveformDb.getAllWaves().size());
 | 
					 | 
				
			||||||
		assertEquals(1,  waveformDb.getChildNodes().size());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
<classpath>
 | 
					<classpath>
 | 
				
			||||||
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 | 
						<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 | 
				
			||||||
	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 | 
						<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 | 
				
			||||||
	<classpathentry kind="src" path="src/"/>
 | 
						<classpathentry kind="src" path="src"/>
 | 
				
			||||||
 | 
						<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
 | 
				
			||||||
	<classpathentry kind="output" path="target/classes"/>
 | 
						<classpathentry kind="output" path="target/classes"/>
 | 
				
			||||||
</classpath>
 | 
					</classpath>
 | 
				
			||||||
@@ -33,6 +33,7 @@
 | 
				
			|||||||
	</buildSpec>
 | 
						</buildSpec>
 | 
				
			||||||
	<natures>
 | 
						<natures>
 | 
				
			||||||
		<nature>org.eclipse.m2e.core.maven2Nature</nature>
 | 
							<nature>org.eclipse.m2e.core.maven2Nature</nature>
 | 
				
			||||||
 | 
							<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
 | 
				
			||||||
		<nature>org.eclipse.pde.PluginNature</nature>
 | 
							<nature>org.eclipse.pde.PluginNature</nature>
 | 
				
			||||||
		<nature>org.eclipse.jdt.core.javanature</nature>
 | 
							<nature>org.eclipse.jdt.core.javanature</nature>
 | 
				
			||||||
	</natures>
 | 
						</natures>
 | 
				
			||||||
@@ -5,5 +5,4 @@ org.eclipse.jdt.core.compiler.compliance=1.8
 | 
				
			|||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
					org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 | 
				
			||||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
 | 
					org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
 | 
				
			||||||
org.eclipse.jdt.core.compiler.release=disabled
 | 
					 | 
				
			||||||
org.eclipse.jdt.core.compiler.source=1.8
 | 
					org.eclipse.jdt.core.compiler.source=1.8
 | 
				
			||||||
@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					eclipse.preferences.version=1
 | 
				
			||||||
 | 
					groovy.compiler.level=23
 | 
				
			||||||
							
								
								
									
										17
									
								
								com.minres.scviewer.database.text/META-INF/MANIFEST.MF
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								com.minres.scviewer.database.text/META-INF/MANIFEST.MF
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					Manifest-Version: 1.0
 | 
				
			||||||
 | 
					Bundle-ManifestVersion: 2
 | 
				
			||||||
 | 
					Bundle-Name: Textual transaction database
 | 
				
			||||||
 | 
					Bundle-SymbolicName: com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					Bundle-Version: 1.0.0.qualifier
 | 
				
			||||||
 | 
					Bundle-Vendor: MINRES Technologies GmbH
 | 
				
			||||||
 | 
					Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 | 
				
			||||||
 | 
					Import-Package: com.minres.scviewer.database,
 | 
				
			||||||
 | 
					 org.osgi.framework;version="1.3.0"
 | 
				
			||||||
 | 
					Require-Bundle: com.minres.scviewer.database;bundle-version="1.0.0",
 | 
				
			||||||
 | 
					 org.codehaus.groovy;bundle-version="1.8.6",
 | 
				
			||||||
 | 
					 org.eclipse.equinox.util;bundle-version="1.0.500",
 | 
				
			||||||
 | 
					 org.eclipse.equinox.ds;bundle-version="1.4.200",
 | 
				
			||||||
 | 
					 org.eclipse.osgi.services;bundle-version="3.4.0",
 | 
				
			||||||
 | 
					 com.google.guava;bundle-version="15.0.0"
 | 
				
			||||||
 | 
					Service-Component: OSGI-INF/component.xml
 | 
				
			||||||
 | 
					Bundle-ActivationPolicy: lazy
 | 
				
			||||||
							
								
								
									
										15
									
								
								com.minres.scviewer.database.text/build.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								com.minres.scviewer.database.text/build.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					# Copyright (c) 2014, 2015 MINRES Technologies GmbH and others.
 | 
				
			||||||
 | 
					# All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					# are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					# which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					# http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Contributors:
 | 
				
			||||||
 | 
					#     MINRES Technologies GmbH - initial API and implementation
 | 
				
			||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					bin.includes = META-INF/,\
 | 
				
			||||||
 | 
					               .,\
 | 
				
			||||||
 | 
					               OSGI-INF/
 | 
				
			||||||
 | 
					bin.excludes = **/*.groovy
 | 
				
			||||||
 | 
					source.. = src/
 | 
				
			||||||
							
								
								
									
										42
									
								
								com.minres.scviewer.database.text/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								com.minres.scviewer.database.text/pom.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
				
			||||||
 | 
						xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
				
			||||||
 | 
						<modelVersion>4.0.0</modelVersion>
 | 
				
			||||||
 | 
						<artifactId>com.minres.scviewer.database.text</artifactId>
 | 
				
			||||||
 | 
						<parent>
 | 
				
			||||||
 | 
							<groupId>com.minres.scviewer</groupId>
 | 
				
			||||||
 | 
							<artifactId>com.minres.scviewer.parent</artifactId>
 | 
				
			||||||
 | 
							<version>1.0.0-SNAPSHOT</version>
 | 
				
			||||||
 | 
							<relativePath>../com.minres.scviewer.parent</relativePath>
 | 
				
			||||||
 | 
						</parent>
 | 
				
			||||||
 | 
						<packaging>eclipse-plugin</packaging>
 | 
				
			||||||
 | 
						<build>
 | 
				
			||||||
 | 
						<plugins>
 | 
				
			||||||
 | 
									<plugin>
 | 
				
			||||||
 | 
									<groupId>org.eclipse.tycho</groupId>
 | 
				
			||||||
 | 
									<artifactId>tycho-compiler-plugin</artifactId>
 | 
				
			||||||
 | 
					        		<version>0.23.1</version>
 | 
				
			||||||
 | 
									<configuration>
 | 
				
			||||||
 | 
										<compilerId>groovy-eclipse-compiler</compilerId>
 | 
				
			||||||
 | 
										<!-- set verbose to be true if you want lots of uninteresting messages -->
 | 
				
			||||||
 | 
										<!-- <verbose>true</verbose> -->
 | 
				
			||||||
 | 
										<source>1.7</source>
 | 
				
			||||||
 | 
										<target>1.7</target>
 | 
				
			||||||
 | 
									</configuration>
 | 
				
			||||||
 | 
									<dependencies>
 | 
				
			||||||
 | 
										<dependency>
 | 
				
			||||||
 | 
											<groupId>org.codehaus.groovy</groupId>
 | 
				
			||||||
 | 
											<artifactId>groovy-eclipse-compiler</artifactId>
 | 
				
			||||||
 | 
											<version>${groovy-eclipse-compiler-version}</version>
 | 
				
			||||||
 | 
										</dependency>
 | 
				
			||||||
 | 
										<dependency>
 | 
				
			||||||
 | 
											<groupId>org.codehaus.groovy</groupId>
 | 
				
			||||||
 | 
											<artifactId>groovy-eclipse-batch</artifactId>
 | 
				
			||||||
 | 
											<version>${groovy-eclipse-batch-version}</version>
 | 
				
			||||||
 | 
											<!-- or choose a different compiler version -->
 | 
				
			||||||
 | 
											<!-- <version>2.1.8-01</version> -->
 | 
				
			||||||
 | 
										</dependency>
 | 
				
			||||||
 | 
									</dependencies>
 | 
				
			||||||
 | 
								</plugin>
 | 
				
			||||||
 | 
						</plugins>
 | 
				
			||||||
 | 
						</build>
 | 
				
			||||||
 | 
						</project>
 | 
				
			||||||
@@ -0,0 +1,221 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.nio.charset.CharsetDecoder;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.zip.GZIPInputStream
 | 
				
			||||||
 | 
					import org.apache.jdbm.DB
 | 
				
			||||||
 | 
					import org.apache.jdbm.DBMaker
 | 
				
			||||||
 | 
					import groovy.io.FileType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.AssociationType
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.DataType
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxGenerator
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxStream
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveform
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformDb
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformDbLoader
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.RelationType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TextDbLoader implements IWaveformDbLoader{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Long maxTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						IWaveformDb db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DB backingDb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def streams = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def relationTypes=[:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public TextDbLoader() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Long getMaxTime() {
 | 
				
			||||||
 | 
							return maxTime;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public List<IWaveform> getAllWaves() {
 | 
				
			||||||
 | 
							return new LinkedList<IWaveform>(streams);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Map<Long, ITxGenerator> getGeneratorsById() {
 | 
				
			||||||
 | 
							TreeMap<Long, ITxGenerator> res = new TreeMap<Long, ITxGenerator>();
 | 
				
			||||||
 | 
							streams.each{TxStream stream -> stream.generators.each{res.put(it.id, id)} }
 | 
				
			||||||
 | 
							return res;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static final byte[] x = "scv_tr_stream".bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						boolean load(IWaveformDb db, File file) throws Exception {
 | 
				
			||||||
 | 
							this.db=db
 | 
				
			||||||
 | 
							this.streams=[]
 | 
				
			||||||
 | 
							def gzipped = isGzipped(file)
 | 
				
			||||||
 | 
							if(isTxfile(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file))){
 | 
				
			||||||
 | 
								if(true) {
 | 
				
			||||||
 | 
									def parentDir=file.absoluteFile.parent
 | 
				
			||||||
 | 
									def filename=file.name
 | 
				
			||||||
 | 
									new File(parentDir).eachFileRecurse (FileType.FILES) { f -> if(f.name=~/^\.${filename}/) f.delete() }
 | 
				
			||||||
 | 
									this.backingDb = DBMaker.openFile(parentDir+File.separator+"."+filename+"_bdb")
 | 
				
			||||||
 | 
											.deleteFilesAfterClose()
 | 
				
			||||||
 | 
											.useRandomAccessFile()
 | 
				
			||||||
 | 
											.setMRUCacheSize(4096) 
 | 
				
			||||||
 | 
											//.disableTransactions()
 | 
				
			||||||
 | 
											.disableLocking()
 | 
				
			||||||
 | 
											.make();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									this.backingDb = DBMaker.openMemory().disableLocking().make()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								parseInput(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file))
 | 
				
			||||||
 | 
								calculateConcurrencyIndicees()
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static boolean isTxfile(InputStream istream) {
 | 
				
			||||||
 | 
							byte[] buffer = new byte[x.size()]
 | 
				
			||||||
 | 
							def readCnt = istream.read(buffer, 0, x.size())
 | 
				
			||||||
 | 
							istream.close()
 | 
				
			||||||
 | 
							if(readCnt==x.size()){
 | 
				
			||||||
 | 
								for(int i=0; i<x.size(); i++)
 | 
				
			||||||
 | 
									if(buffer[i]!=x[i]) return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static boolean isGzipped(File f) {
 | 
				
			||||||
 | 
							InputStream is = null;
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								is = new FileInputStream(f);
 | 
				
			||||||
 | 
								byte [] signature = new byte[2];
 | 
				
			||||||
 | 
								int nread = is.read( signature ); //read the gzip signature
 | 
				
			||||||
 | 
								return nread == 2 && signature[ 0 ] == (byte) 0x1f && signature[ 1 ] == (byte) 0x8b;
 | 
				
			||||||
 | 
							} catch (IOException e) {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							} finally {
 | 
				
			||||||
 | 
								is.close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private stringToScale(String scale){
 | 
				
			||||||
 | 
							switch(scale.trim()){
 | 
				
			||||||
 | 
								case "fs":return 1L
 | 
				
			||||||
 | 
								case "ps":return 1000L
 | 
				
			||||||
 | 
								case "ns":return 1000000L
 | 
				
			||||||
 | 
								case "us":return 1000000000L
 | 
				
			||||||
 | 
								case "ms":return 1000000000000L
 | 
				
			||||||
 | 
								case "s": return 1000000000000000L
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						private def parseInput(InputStream inputStream){
 | 
				
			||||||
 | 
							def streamsById = [:]
 | 
				
			||||||
 | 
							def generatorsById = [:]
 | 
				
			||||||
 | 
							def transactionsById = [:]
 | 
				
			||||||
 | 
							TxGenerator generator
 | 
				
			||||||
 | 
							Tx transaction
 | 
				
			||||||
 | 
							boolean endTransaction=false
 | 
				
			||||||
 | 
							def matcher
 | 
				
			||||||
 | 
							BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
 | 
				
			||||||
 | 
							long lineCnt=0;
 | 
				
			||||||
 | 
							reader.eachLine { line ->
 | 
				
			||||||
 | 
								def tokens = line.split(/\s+/)
 | 
				
			||||||
 | 
								switch(tokens[0]){
 | 
				
			||||||
 | 
									case "scv_tr_stream":
 | 
				
			||||||
 | 
									case "scv_tr_generator":
 | 
				
			||||||
 | 
									case "begin_attribute":
 | 
				
			||||||
 | 
									case "end_attribute":
 | 
				
			||||||
 | 
										if ((matcher = line =~ /^scv_tr_stream\s+\(ID (\d+),\s+name\s+"([^"]+)",\s+kind\s+"([^"]+)"\)$/)) {
 | 
				
			||||||
 | 
											def id = Integer.parseInt(matcher[0][1])
 | 
				
			||||||
 | 
											def stream = new TxStream(db, id, matcher[0][2], matcher[0][3], backingDb)
 | 
				
			||||||
 | 
											streams<<stream
 | 
				
			||||||
 | 
											streamsById[id]=stream
 | 
				
			||||||
 | 
										} else if ((matcher = line =~ /^scv_tr_generator\s+\(ID\s+(\d+),\s+name\s+"([^"]+)",\s+scv_tr_stream\s+(\d+),$/)) {
 | 
				
			||||||
 | 
											def id = Integer.parseInt(matcher[0][1])
 | 
				
			||||||
 | 
											ITxStream stream=streamsById[Integer.parseInt(matcher[0][3])]
 | 
				
			||||||
 | 
											generator=new TxGenerator(id, stream, matcher[0][2])
 | 
				
			||||||
 | 
											stream.generators<<generator
 | 
				
			||||||
 | 
											generatorsById[id]=generator
 | 
				
			||||||
 | 
										} else if ((matcher = line =~ /^begin_attribute \(ID (\d+), name "([^"]+)", type "([^"]+)"\)$/)) {
 | 
				
			||||||
 | 
											generator.begin_attrs << TxAttributeType.getAttrType(matcher[0][2], DataType.valueOf(matcher[0][3]), AssociationType.BEGIN)
 | 
				
			||||||
 | 
										} else if ((matcher = line =~ /^end_attribute \(ID (\d+), name "([^"]+)", type "([^"]+)"\)$/)) {
 | 
				
			||||||
 | 
											generator.end_attrs << TxAttributeType.getAttrType(matcher[0][2], DataType.valueOf(matcher[0][3]), AssociationType.END)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case ")":
 | 
				
			||||||
 | 
										generator=null
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case "tx_begin"://matcher = line =~ /^tx_begin\s+(\d+)\s+(\d+)\s+(\d+)\s+([munpf]?s)/
 | 
				
			||||||
 | 
										def id = Integer.parseInt(tokens[1])
 | 
				
			||||||
 | 
										TxGenerator gen=generatorsById[Integer.parseInt(tokens[2])]
 | 
				
			||||||
 | 
										transaction = new Tx(id, gen.stream, gen, Long.parseLong(tokens[3])*stringToScale(tokens[4]))
 | 
				
			||||||
 | 
										gen.transactions << transaction
 | 
				
			||||||
 | 
										transactionsById[id]= transaction
 | 
				
			||||||
 | 
										gen.begin_attrs_idx=0;
 | 
				
			||||||
 | 
										maxTime = maxTime>transaction.beginTime?maxTime:transaction.beginTime
 | 
				
			||||||
 | 
										endTransaction=false
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case "tx_end"://matcher = line =~ /^tx_end\s+(\d+)\s+(\d+)\s+(\d+)\s+([munpf]?s)/
 | 
				
			||||||
 | 
										def id = Integer.parseInt(tokens[1])
 | 
				
			||||||
 | 
										transaction = transactionsById[id]
 | 
				
			||||||
 | 
										assert Integer.parseInt(tokens[2])==transaction.generator.id
 | 
				
			||||||
 | 
										transaction.endTime = Long.parseLong(tokens[3])*stringToScale(tokens[4])
 | 
				
			||||||
 | 
										transaction.generator.end_attrs_idx=0;
 | 
				
			||||||
 | 
										maxTime = maxTime>transaction.endTime?maxTime:transaction.endTime
 | 
				
			||||||
 | 
										endTransaction=true
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case "tx_record_attribute"://matcher = line =~ /^tx_record_attribute\s+(\d+)\s+"([^"]+)"\s+(\S+)\s*=\s*(.+)$/
 | 
				
			||||||
 | 
										def id = Integer.parseInt(tokens[1])
 | 
				
			||||||
 | 
										transactionsById[id].attributes<<new TxAttribute(tokens[2][1..-2], DataType.valueOf(tokens[3]), AssociationType.RECORD, tokens[5..-1].join(' '))
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case "a"://matcher = line =~ /^a\s+(.+)$/
 | 
				
			||||||
 | 
										if(endTransaction){
 | 
				
			||||||
 | 
											transaction.attributes << new TxAttribute(transaction.generator.end_attrs[0], tokens[1])
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											transaction.attributes << new TxAttribute(transaction.generator.begin_attrs[0], tokens[1])
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case "tx_relation"://matcher = line =~ /^tx_relation\s+\"(\S+)\"\s+(\d+)\s+(\d+)$/
 | 
				
			||||||
 | 
										Tx tr2= transactionsById[Integer.parseInt(tokens[2])]
 | 
				
			||||||
 | 
										Tx tr1= transactionsById[Integer.parseInt(tokens[3])]
 | 
				
			||||||
 | 
										def relType=tokens[1][1..-2]
 | 
				
			||||||
 | 
										if(!relationTypes.containsKey(relType)) relationTypes[relType]=RelationType.create(relType)
 | 
				
			||||||
 | 
										def rel = new TxRelation(relationTypes[relType], tr1, tr2)
 | 
				
			||||||
 | 
										tr1.outgoingRelations<<rel
 | 
				
			||||||
 | 
										tr2.incomingRelations<<rel
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									default:
 | 
				
			||||||
 | 
										println "Don't know what to do with: '$line'"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								lineCnt++
 | 
				
			||||||
 | 
								if((lineCnt%1000) == 0) {
 | 
				
			||||||
 | 
									backingDb.commit()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private def calculateConcurrencyIndicees(){
 | 
				
			||||||
 | 
							streams.each{ TxStream  stream -> stream.getMaxConcurrency() }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Collection<RelationType> getAllRelationTypes(){
 | 
				
			||||||
 | 
							return relationTypes.values();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tx implements ITx {
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Long id
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxGenerator generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TxStream stream
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						int concurrencyIndex
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Long beginTime
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Long endTime
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						ArrayList<ITxAttribute> attributes = new ArrayList<ITxAttribute>()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def incomingRelations =[]
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def outgoingRelations =[]
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Tx(int id, TxStream stream, TxGenerator generator, Long begin){
 | 
				
			||||||
 | 
							this.id=id
 | 
				
			||||||
 | 
							this.stream=stream
 | 
				
			||||||
 | 
							this.generator=generator
 | 
				
			||||||
 | 
							this.beginTime=begin
 | 
				
			||||||
 | 
					        this.endTime=begin
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Collection<ITxRelation> getIncomingRelations() {
 | 
				
			||||||
 | 
							return incomingRelations;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Collection<ITxRelation> getOutgoingRelations() {
 | 
				
			||||||
 | 
							return outgoingRelations;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int compareTo(ITx o) {
 | 
				
			||||||
 | 
							def res =beginTime.compareTo(o.beginTime)
 | 
				
			||||||
 | 
							if(res!=0) 
 | 
				
			||||||
 | 
								return res
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return id.compareTo(o.id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String toString() {
 | 
				
			||||||
 | 
							return "tx#"+getId()+"["+getBeginTime()/1000000+"ns - "+getEndTime()/1000000+"ns]";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.AssociationType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.DataType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttributeType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttribute
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxAttribute  implements ITxAttribute{
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxAttributeType attributeType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def value
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxAttribute(String name, DataType dataType, AssociationType type, value){
 | 
				
			||||||
 | 
							attributeType = TxAttributeTypeFactory.instance.getAttrType(name, dataType, type)
 | 
				
			||||||
 | 
							switch(dataType){
 | 
				
			||||||
 | 
								case DataType.STRING:
 | 
				
			||||||
 | 
								case DataType.ENUMERATION:
 | 
				
			||||||
 | 
									this.value=value[1..-2]
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									this.value=value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TxAttribute(TxAttributeType other){
 | 
				
			||||||
 | 
							attributeType=other
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxAttribute(TxAttributeType other, value){
 | 
				
			||||||
 | 
							this(other.name, other.dataType, other.type, value)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String getName() {
 | 
				
			||||||
 | 
							return attributeType.getName();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public AssociationType getType() {
 | 
				
			||||||
 | 
							attributeType.type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public DataType getDataType() {
 | 
				
			||||||
 | 
							attributeType.dataType;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.AssociationType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.DataType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttributeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxAttributeType implements ITxAttributeType {
 | 
				
			||||||
 | 
						String name
 | 
				
			||||||
 | 
						DataType dataType
 | 
				
			||||||
 | 
						AssociationType type
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						static TxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
 | 
				
			||||||
 | 
							TxAttributeTypeFactory.instance.getAttrType(name, dataType, type)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxAttributeType(String name, DataType dataType, AssociationType type){
 | 
				
			||||||
 | 
							this.name=name
 | 
				
			||||||
 | 
							this.dataType=dataType
 | 
				
			||||||
 | 
							this.type=type
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.AssociationType;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.DataType
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttributeType
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttribute
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxAttributeTypeFactory {
 | 
				
			||||||
 | 
						private static final instance = new TxAttributeTypeFactory()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def attributes = [:]
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private TxAttributeTypeFactory() {
 | 
				
			||||||
 | 
							TxAttributeTypeFactory.metaClass.constructor = {-> instance }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						ITxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
 | 
				
			||||||
 | 
							def key = name+":"+dataType.toString()
 | 
				
			||||||
 | 
							ITxAttributeType res
 | 
				
			||||||
 | 
							if(attributes.containsKey(key)){
 | 
				
			||||||
 | 
								res=attributes[key]
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								res=new TxAttributeType(name, dataType, type)
 | 
				
			||||||
 | 
								attributes[key]=res
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package com.minres.scviewer.database.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxEvent
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformEvent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxEvent implements ITxEvent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						final ITxEvent.Type type;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						final Tx transaction;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						final Long time
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxEvent(ITxEvent.Type type, ITx transaction) {
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
							this.type = type;
 | 
				
			||||||
 | 
							this.transaction = transaction;
 | 
				
			||||||
 | 
							this.time = type==ITxEvent.Type.BEGIN?transaction.beginTime:transaction.endTime
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						IWaveformEvent duplicate() throws CloneNotSupportedException {
 | 
				
			||||||
 | 
							new TxEvent(type, transaction, time)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						int compareTo(IWaveformEvent o) {
 | 
				
			||||||
 | 
							time.compareTo(o.time)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						String toString() {
 | 
				
			||||||
 | 
							type.toString()+"@"+time+" of tx #"+transaction.id;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttributeType
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxAttribute;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxGenerator;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxStream;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxGenerator implements ITxGenerator{
 | 
				
			||||||
 | 
						Long id
 | 
				
			||||||
 | 
						TxStream stream
 | 
				
			||||||
 | 
						String name
 | 
				
			||||||
 | 
						Boolean active = false
 | 
				
			||||||
 | 
						ArrayList<ITx> transactions=[]
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						ArrayList<ITxAttributeType> begin_attrs = []
 | 
				
			||||||
 | 
						int begin_attrs_idx = 0
 | 
				
			||||||
 | 
						ArrayList<ITxAttributeType> end_attrs= []
 | 
				
			||||||
 | 
						int end_attrs_idx = 0
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxGenerator(int id, TxStream stream, name){
 | 
				
			||||||
 | 
							this.id=id
 | 
				
			||||||
 | 
							this.stream=stream
 | 
				
			||||||
 | 
							this.name=name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						ITxStream<? extends ITxEvent> getStream(){
 | 
				
			||||||
 | 
							return stream;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						List<ITx> getTransactions(){
 | 
				
			||||||
 | 
							return transactions
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Boolean isActive() {return active};
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxRelation
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.RelationType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxRelation implements ITxRelation {
 | 
				
			||||||
 | 
						Tx source
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Tx target
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						RelationType relationType
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public TxRelation(RelationType relationType, Tx source, Tx target) {
 | 
				
			||||||
 | 
							this.source = source;
 | 
				
			||||||
 | 
							this.target = target;
 | 
				
			||||||
 | 
							this.relationType = relationType;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public RelationType getRelationType() {
 | 
				
			||||||
 | 
							return relationType;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public ITx getSource() {
 | 
				
			||||||
 | 
							return source;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public ITx getTarget() {
 | 
				
			||||||
 | 
							return target;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright (c) 2012 IT Just working.
 | 
				
			||||||
 | 
					 * All rights reserved. This program and the accompanying materials
 | 
				
			||||||
 | 
					 * are made available under the terms of the Eclipse Public License v1.0
 | 
				
			||||||
 | 
					 * which accompanies this distribution, and is available at
 | 
				
			||||||
 | 
					 * http://www.eclipse.org/legal/epl-v10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Contributors:
 | 
				
			||||||
 | 
					 *     IT Just working - initial API and implementation
 | 
				
			||||||
 | 
					 *******************************************************************************/
 | 
				
			||||||
 | 
					package com.minres.scviewer.database.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeListener;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.Comparator;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.NavigableMap;
 | 
				
			||||||
 | 
					import org.apache.jdbm.DB
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxEvent;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveform;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformDb
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IWaveformEvent
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxGenerator
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.HierNode;
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.IHierNode
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITxStream
 | 
				
			||||||
 | 
					import com.minres.scviewer.database.ITx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TxStream extends HierNode implements ITxStream {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Long id
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						IWaveformDb database
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						String fullName
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						String kind
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def generators = []
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						int maxConcurrency
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private TreeMap<Long, List<ITxEvent>> events
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						TxStream(IWaveformDb db, int id, String name, String kind, DB backingStore){
 | 
				
			||||||
 | 
							super(name)
 | 
				
			||||||
 | 
							this.id=id
 | 
				
			||||||
 | 
							this.database=db
 | 
				
			||||||
 | 
							this.fullName=name
 | 
				
			||||||
 | 
							this.kind=kind
 | 
				
			||||||
 | 
							this.maxConcurrency=0
 | 
				
			||||||
 | 
							//events = new TreeMap<Long, List<ITxEvent>>()
 | 
				
			||||||
 | 
							events=backingStore.createTreeMap("stream-"+name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						List<ITxGenerator> getGenerators(){
 | 
				
			||||||
 | 
							return generators as List<ITxGenerator>
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public IWaveformDb getDb() {
 | 
				
			||||||
 | 
							return database
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int getMaxConcurrency() {
 | 
				
			||||||
 | 
							if(!maxConcurrency){
 | 
				
			||||||
 | 
								generators.each {TxGenerator generator ->
 | 
				
			||||||
 | 
									generator.transactions.each{ Tx tx ->
 | 
				
			||||||
 | 
										putEvent(new TxEvent(ITxEvent.Type.BEGIN, tx))
 | 
				
			||||||
 | 
										putEvent(new TxEvent(ITxEvent.Type.END, tx))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								def rowendtime = [0]
 | 
				
			||||||
 | 
								events.keySet().each{long time ->
 | 
				
			||||||
 | 
									def value=events.get(time)
 | 
				
			||||||
 | 
									def starts=value.findAll{ITxEvent event ->event.type==ITxEvent.Type.BEGIN}
 | 
				
			||||||
 | 
									starts.each {ITxEvent event ->
 | 
				
			||||||
 | 
										Tx tx = event.transaction
 | 
				
			||||||
 | 
										def rowIdx = 0
 | 
				
			||||||
 | 
										for(rowIdx=0; rowIdx<rowendtime.size() && rowendtime[rowIdx]>tx.beginTime; rowIdx++);
 | 
				
			||||||
 | 
										if(rowendtime.size<=rowIdx)
 | 
				
			||||||
 | 
											rowendtime<<tx.endTime
 | 
				
			||||||
 | 
										else
 | 
				
			||||||
 | 
											rowendtime[rowIdx]=tx.endTime
 | 
				
			||||||
 | 
										tx.concurrencyIndex=rowIdx
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								maxConcurrency=rowendtime.size()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return maxConcurrency
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private putEvent(ITxEvent event){
 | 
				
			||||||
 | 
							if(!events.containsKey(event.time)) 
 | 
				
			||||||
 | 
								events.put(event.time, [event])
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								events[event.time]<<event
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public NavigableMap getEvents() {
 | 
				
			||||||
 | 
							return events;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Collection getWaveformEventsAtTime(Long time) {
 | 
				
			||||||
 | 
							return events.get(time);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Boolean equals(IWaveform<? extends IWaveformEvent> other) {
 | 
				
			||||||
 | 
							return(other instanceof TxStream && this.getId()==other.getId());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										706
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/BTree.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										706
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/BTree.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,706 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.Comparator;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.concurrent.locks.ReadWriteLock;
 | 
				
			||||||
 | 
					import java.util.concurrent.locks.ReentrantReadWriteLock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * B+Tree persistent indexing data structure.  B+Trees are optimized for
 | 
				
			||||||
 | 
					 * block-based, random I/O storage because they store multiple keys on
 | 
				
			||||||
 | 
					 * one tree node (called <code>BTreeNode</code>).  In addition, the leaf nodes
 | 
				
			||||||
 | 
					 * directly contain (inline) small values associated with the keys, allowing a
 | 
				
			||||||
 | 
					 * single (or sequential) disk read of all the values on the node.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * B+Trees are n-airy, yeilding log(N) search cost.  They are self-balancing,
 | 
				
			||||||
 | 
					 * preventing search performance degradation when the size of the tree grows.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * BTree stores its keys sorted. By default JDBM expects key to implement
 | 
				
			||||||
 | 
					 * <code>Comparable</code> interface but user may supply its own <code>Comparator</code>
 | 
				
			||||||
 | 
					 * at BTree creation time. Comparator is serialized and stored as part of BTree.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * The B+Tree allows traversing the keys in forward and reverse order using a
 | 
				
			||||||
 | 
					 * TupleBrowser obtained from the browse() methods. But it is better to use
 | 
				
			||||||
 | 
					 * <code>BTreeMap</code> wrapper which implements <code>SortedMap</code> interface
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * This implementation does not directly support duplicate keys. It is
 | 
				
			||||||
 | 
					 * possible to handle duplicates by grouping values using an ArrayList as value.
 | 
				
			||||||
 | 
					 * This scenario is supported by JDBM serialization so there is no big performance penalty.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * There is no limit on key size or value size, but it is recommended to keep
 | 
				
			||||||
 | 
					 * keys as small as possible to reduce disk I/O. If serialized value exceeds 32 bytes,
 | 
				
			||||||
 | 
					 * it is stored in separate record and tree contains only recid reference to it.
 | 
				
			||||||
 | 
					 * BTree uses delta compression for its keys.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class BTree<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final boolean DEBUG = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Default node size (number of entries per node)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final int DEFAULT_SIZE = 32; //TODO test optimal size, it has serious impact on sequencial write and read
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Record manager used to persist changes in BTreeNodes
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected transient DBAbstract _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This BTree's record ID in the DB.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private transient long _recid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Comparator used to index entries (optional)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected Comparator<K> _comparator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serializer used to serialize index keys (optional)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected Serializer<K> keySerializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serializer used to serialize index values (optional)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected Serializer<V> valueSerializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * indicates if values should be loaded during deserialization, set to false during defragmentation
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean loadValues = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** if false map contains only keys, used for set*/
 | 
				
			||||||
 | 
					    boolean hasValues = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The number of structural modifications to the tree for fail fast iterators. This value is just for runtime, it is not persisted
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    transient int modCount = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * cached instance of an insert result, so we do not have to allocate new object on each insert
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected BTreeNode.InsertResult<K, V> insertResultReuse; //TODO investigate performance impact of removing this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Serializer<K> getKeySerializer() {
 | 
				
			||||||
 | 
					        return keySerializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Serializer<V> getValueSerializer() {
 | 
				
			||||||
 | 
					        return valueSerializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Height of the B+Tree.  This is the number of BTreeNodes you have to traverse
 | 
				
			||||||
 | 
					     * to get to a leaf BTreeNode, starting from the root.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private int _height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Recid of the root BTreeNode
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private transient long _root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Total number of entries in the BTree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected volatile long _entries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serializer used for BTreeNodes of this tree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private transient BTreeNode<K, V> _nodeSerializer = new BTreeNode();
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _nodeSerializer._btree = this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Listeners which are notified about changes in records
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected RecordListener[] recordListeners = new RecordListener[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final protected ReadWriteLock lock = new ReentrantReadWriteLock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * No-argument constructor used by serialization.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BTree() {
 | 
				
			||||||
 | 
					        // empty
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new persistent BTree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    public static <K extends Comparable, V> BTree<K, V> createInstance(DBAbstract db)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        return createInstance(db, null, null, null,true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new persistent BTree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <K, V> BTree<K, V> createInstance(DBAbstract db,
 | 
				
			||||||
 | 
					                                                    Comparator<K> comparator,
 | 
				
			||||||
 | 
					                                                    Serializer<K> keySerializer,
 | 
				
			||||||
 | 
					                                                    Serializer<V> valueSerializer,
 | 
				
			||||||
 | 
					                                                    boolean hasValues)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        BTree<K, V> btree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (db == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'db' is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        btree = new BTree<K, V>();
 | 
				
			||||||
 | 
					        btree._db = db;
 | 
				
			||||||
 | 
					        btree._comparator = comparator;
 | 
				
			||||||
 | 
					        btree.keySerializer = keySerializer;
 | 
				
			||||||
 | 
					        btree.valueSerializer = valueSerializer;
 | 
				
			||||||
 | 
					        btree.hasValues = hasValues;
 | 
				
			||||||
 | 
					        btree._recid = db.insert(btree, btree.getRecordManager().defaultSerializer(),false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return btree;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load a persistent BTree.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param db    DB used to store the persistent btree
 | 
				
			||||||
 | 
					     * @param recid Record id of the BTree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    public static <K, V> BTree<K, V> load(DBAbstract db, long recid)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        BTree<K, V> btree = (BTree<K, V>) db.fetch(recid);
 | 
				
			||||||
 | 
					        btree._recid = recid;
 | 
				
			||||||
 | 
					        btree._db = db;
 | 
				
			||||||
 | 
					        btree._nodeSerializer = new BTreeNode<K, V>();
 | 
				
			||||||
 | 
					        btree._nodeSerializer._btree = btree;
 | 
				
			||||||
 | 
					        return btree;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the {@link ReadWriteLock} associated with this BTree.
 | 
				
			||||||
 | 
					     * This should be used with browsing operations to ensure
 | 
				
			||||||
 | 
					     * consistency.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteLock getLock() {
 | 
				
			||||||
 | 
					        return lock;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Insert an entry in the BTree.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * The BTree cannot store duplicate entries.  An existing entry can be
 | 
				
			||||||
 | 
					     * replaced using the <code>replace</code> flag.   If an entry with the
 | 
				
			||||||
 | 
					     * same key already exists in the BTree, its value is returned.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key     Insert key
 | 
				
			||||||
 | 
					     * @param value   Insert value
 | 
				
			||||||
 | 
					     * @param replace Set to true to replace an existing key-value pair.
 | 
				
			||||||
 | 
					     * @return Existing value, if any.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public V insert(final K key, final V value,
 | 
				
			||||||
 | 
					                    final boolean replace)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        if (key == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'key' is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (value == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'value' is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            lock.writeLock().lock();
 | 
				
			||||||
 | 
					            BTreeNode<K, V> rootNode = getRoot();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (rootNode == null) {
 | 
				
			||||||
 | 
					                // BTree is currently empty, create a new root BTreeNode
 | 
				
			||||||
 | 
					                if (DEBUG) {
 | 
				
			||||||
 | 
					                    System.out.println("BTree.insert() new root BTreeNode");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                rootNode = new BTreeNode<K, V>(this, key, value);
 | 
				
			||||||
 | 
					                _root = rootNode._recid;
 | 
				
			||||||
 | 
					                _height = 1;
 | 
				
			||||||
 | 
					                _entries = 1;
 | 
				
			||||||
 | 
					                _db.update(_recid, this);
 | 
				
			||||||
 | 
					                modCount++;
 | 
				
			||||||
 | 
					                //notifi listeners
 | 
				
			||||||
 | 
					                for (RecordListener<K, V> l : recordListeners) {
 | 
				
			||||||
 | 
					                    l.recordInserted(key, value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                BTreeNode.InsertResult<K, V> insert = rootNode.insert(_height, key, value, replace);
 | 
				
			||||||
 | 
					                boolean dirty = false;
 | 
				
			||||||
 | 
					                if (insert._overflow != null) {
 | 
				
			||||||
 | 
					                    // current root node overflowed, we replace with a new root node
 | 
				
			||||||
 | 
					                    if (DEBUG) {
 | 
				
			||||||
 | 
					                        System.out.println("BTreeNode.insert() replace root BTreeNode due to overflow");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    rootNode = new BTreeNode<K, V>(this, rootNode, insert._overflow);
 | 
				
			||||||
 | 
					                    _root = rootNode._recid;
 | 
				
			||||||
 | 
					                    _height += 1;
 | 
				
			||||||
 | 
					                    dirty = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (insert._existing == null) {
 | 
				
			||||||
 | 
					                    _entries++;
 | 
				
			||||||
 | 
					                    modCount++;
 | 
				
			||||||
 | 
					                    dirty = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (dirty) {
 | 
				
			||||||
 | 
					                    _db.update(_recid, this);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                //notify listeners
 | 
				
			||||||
 | 
					                for (RecordListener<K, V> l : recordListeners) {
 | 
				
			||||||
 | 
					                    if (insert._existing == null)
 | 
				
			||||||
 | 
					                        l.recordInserted(key, value);
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        l.recordUpdated(key, insert._existing, value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // insert might have returned an existing value
 | 
				
			||||||
 | 
					                V ret = insert._existing;
 | 
				
			||||||
 | 
					                //zero out tuple and put it for reuse
 | 
				
			||||||
 | 
					                insert._existing = null;
 | 
				
			||||||
 | 
					                insert._overflow = null;
 | 
				
			||||||
 | 
					                this.insertResultReuse = insert;
 | 
				
			||||||
 | 
					                return ret;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Remove an entry with the given key from the BTree.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Removal key
 | 
				
			||||||
 | 
					     * @return Value associated with the key, or null if no entry with given
 | 
				
			||||||
 | 
					     *         key existed in the BTree.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public V remove(K key)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        if (key == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'key' is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            lock.writeLock().lock();
 | 
				
			||||||
 | 
					            BTreeNode<K, V> rootNode = getRoot();
 | 
				
			||||||
 | 
					            if (rootNode == null) {
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            boolean dirty = false;
 | 
				
			||||||
 | 
					            BTreeNode.RemoveResult<K, V> remove = rootNode.remove(_height, key);
 | 
				
			||||||
 | 
					            if (remove._underflow && rootNode.isEmpty()) {
 | 
				
			||||||
 | 
					                _height -= 1;
 | 
				
			||||||
 | 
					                dirty = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _db.delete(_root);
 | 
				
			||||||
 | 
					                if (_height == 0) {
 | 
				
			||||||
 | 
					                    _root = 0;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    _root = rootNode.loadLastChildNode()._recid;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (remove._value != null) {
 | 
				
			||||||
 | 
					                _entries--;
 | 
				
			||||||
 | 
					                modCount++;
 | 
				
			||||||
 | 
					                dirty = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (dirty) {
 | 
				
			||||||
 | 
					                _db.update(_recid, this);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (remove._value != null)
 | 
				
			||||||
 | 
					                for (RecordListener<K, V> l : recordListeners)
 | 
				
			||||||
 | 
					                    l.recordRemoved(key, remove._value);
 | 
				
			||||||
 | 
					            return remove._value;
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Find the value associated with the given key.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Lookup key.
 | 
				
			||||||
 | 
					     * @return Value associated with the key, or null if not found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public V get(K key)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        if (key == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'key' is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            lock.readLock().lock();
 | 
				
			||||||
 | 
					            BTreeNode<K, V> rootNode = getRoot();
 | 
				
			||||||
 | 
					            if (rootNode == null) {
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return rootNode.findValue(_height, key);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            lock.readLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Find the value associated with the given key, or the entry immediately
 | 
				
			||||||
 | 
					     * following this key in the ordered BTree.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Lookup key.
 | 
				
			||||||
 | 
					     * @return Value associated with the key, or a greater entry, or null if no
 | 
				
			||||||
 | 
					     *         greater entry was found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BTreeTuple<K, V> findGreaterOrEqual(K key)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        BTreeTuple<K, V> tuple;
 | 
				
			||||||
 | 
					        BTreeTupleBrowser<K, V> browser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (key == null) {
 | 
				
			||||||
 | 
					            // there can't be a key greater than or equal to "null"
 | 
				
			||||||
 | 
					            // because null is considered an infinite key.
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tuple = new BTreeTuple<K, V>(null, null);
 | 
				
			||||||
 | 
					        browser = browse(key,true);
 | 
				
			||||||
 | 
					        if (browser.getNext(tuple)) {
 | 
				
			||||||
 | 
					            return tuple;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a browser initially positioned at the beginning of the BTree.
 | 
				
			||||||
 | 
					     * <p><b>
 | 
				
			||||||
 | 
					     * WARNING: If you make structural modifications to the BTree during
 | 
				
			||||||
 | 
					     * browsing, you will get inconsistent browing results.
 | 
				
			||||||
 | 
					     * </b>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Browser positionned at the beginning of the BTree.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    public BTreeTupleBrowser<K, V> browse()
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            lock.readLock().lock();
 | 
				
			||||||
 | 
					            BTreeNode<K, V> rootNode = getRoot();
 | 
				
			||||||
 | 
					            if (rootNode == null) {
 | 
				
			||||||
 | 
					                return EMPTY_BROWSER;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return rootNode.findFirst();
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            lock.readLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a browser initially positioned just before the given key.
 | 
				
			||||||
 | 
					     * <p><b>
 | 
				
			||||||
 | 
					     * WARNING: <20>If you make structural modifications to the BTree during
 | 
				
			||||||
 | 
					     * browsing, you will get inconsistent browing results.
 | 
				
			||||||
 | 
					     * </b>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Key used to position the browser.  If null, the browser
 | 
				
			||||||
 | 
					     *            will be positionned after the last entry of the BTree.
 | 
				
			||||||
 | 
					     *            (Null is considered to be an "infinite" key)
 | 
				
			||||||
 | 
					     * @return Browser positionned just before the given key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    public BTreeTupleBrowser<K, V> browse(final K key, final boolean inclusive)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            lock.readLock().lock();
 | 
				
			||||||
 | 
					            BTreeNode<K, V> rootNode = getRoot();
 | 
				
			||||||
 | 
					            if (rootNode == null) {
 | 
				
			||||||
 | 
					                return EMPTY_BROWSER;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            BTreeTupleBrowser<K, V> browser = rootNode.find(_height, key, inclusive);
 | 
				
			||||||
 | 
					            return browser;
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            lock.readLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the persistent record identifier of the BTree.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getRecid() {
 | 
				
			||||||
 | 
					        return _recid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the root BTreeNode, or null if it doesn't exist.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    BTreeNode<K, V> getRoot()
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        if (_root == 0) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        BTreeNode<K, V> root = _db.fetch(_root, _nodeSerializer);
 | 
				
			||||||
 | 
					        if (root != null) {
 | 
				
			||||||
 | 
					            root._recid = _root;
 | 
				
			||||||
 | 
					            root._btree = this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return root;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static BTree readExternal(DataInput in, Serialization ser)
 | 
				
			||||||
 | 
					            throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					        BTree tree = new BTree();
 | 
				
			||||||
 | 
					        tree._db = ser.db;
 | 
				
			||||||
 | 
					        tree._height = in.readInt();
 | 
				
			||||||
 | 
					        tree._recid = in.readLong();
 | 
				
			||||||
 | 
					        tree._root = in.readLong();
 | 
				
			||||||
 | 
					        tree._entries = in.readLong();
 | 
				
			||||||
 | 
					        tree.hasValues = in.readBoolean();
 | 
				
			||||||
 | 
					        tree._comparator = (Comparator) ser.deserialize(in);
 | 
				
			||||||
 | 
					        tree.keySerializer = (Serializer) ser.deserialize(in);
 | 
				
			||||||
 | 
					        tree.valueSerializer = (Serializer) ser.deserialize(in);
 | 
				
			||||||
 | 
					        return tree;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeExternal(DataOutput out)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        out.writeInt(_height);
 | 
				
			||||||
 | 
					        out.writeLong(_recid);
 | 
				
			||||||
 | 
					        out.writeLong(_root);
 | 
				
			||||||
 | 
					        out.writeLong(_entries);
 | 
				
			||||||
 | 
					        out.writeBoolean(hasValues);
 | 
				
			||||||
 | 
					        _db.defaultSerializer().serialize(out, _comparator);
 | 
				
			||||||
 | 
					        _db.defaultSerializer().serialize(out, keySerializer);
 | 
				
			||||||
 | 
					        _db.defaultSerializer().serialize(out, valueSerializer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Copyes tree from one db to other, defragmenting it allong the way
 | 
				
			||||||
 | 
					     * @param recid
 | 
				
			||||||
 | 
					     * @param r1
 | 
				
			||||||
 | 
					     * @param r2
 | 
				
			||||||
 | 
					     * @throws IOException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void defrag(long recid, DBStore r1, DBStore r2) throws IOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            byte[] data = r1.fetchRaw(recid);
 | 
				
			||||||
 | 
					            r2.forceInsert(recid, data);
 | 
				
			||||||
 | 
					            DataInput in = new DataInputOutput(data);
 | 
				
			||||||
 | 
					            BTree t = (BTree) r1.defaultSerializer().deserialize(in);
 | 
				
			||||||
 | 
					            t.loadValues = false;
 | 
				
			||||||
 | 
					            t._db = r1;
 | 
				
			||||||
 | 
					            t._nodeSerializer = new BTreeNode(t, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            BTreeNode p = t.getRoot();
 | 
				
			||||||
 | 
					            if (p != null) {
 | 
				
			||||||
 | 
					                r2.forceInsert(t._root, r1.fetchRaw(t._root));
 | 
				
			||||||
 | 
					                p.defrag(r1, r2);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (ClassNotFoundException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Browser returning no element.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final BTreeTupleBrowser EMPTY_BROWSER = new BTreeTupleBrowser() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean getNext(BTreeTuple tuple) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean getPrevious(BTreeTuple tuple) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void remove(Object key) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * add RecordListener which is notified about record changes
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addRecordListener(RecordListener<K, V> listener) {
 | 
				
			||||||
 | 
					        recordListeners = Arrays.copyOf(recordListeners, recordListeners.length + 1);
 | 
				
			||||||
 | 
					        recordListeners[recordListeners.length - 1] = listener;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * remove RecordListener which is notified about record changes
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void removeRecordListener(RecordListener<K, V> listener) {
 | 
				
			||||||
 | 
					        List l = Arrays.asList(recordListeners);
 | 
				
			||||||
 | 
					        l.remove(listener);
 | 
				
			||||||
 | 
					        recordListeners = (RecordListener[]) l.toArray(new RecordListener[1]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DBAbstract getRecordManager() {
 | 
				
			||||||
 | 
					        return _db;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Comparator<K> getComparator() {
 | 
				
			||||||
 | 
					        return _comparator;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Deletes all BTreeNodes in this BTree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void clear()
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            lock.writeLock().lock();
 | 
				
			||||||
 | 
					            BTreeNode<K, V> rootNode = getRoot();
 | 
				
			||||||
 | 
					            if (rootNode != null)
 | 
				
			||||||
 | 
					                rootNode.delete();
 | 
				
			||||||
 | 
					            _entries = 0;
 | 
				
			||||||
 | 
					            modCount++;
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Used for debugging and testing only.  Populates the 'out' list with
 | 
				
			||||||
 | 
					     * the recids of all child nodes in the BTree.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param out
 | 
				
			||||||
 | 
					     * @throws IOException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void dumpChildNodeRecIDs(List<Long> out) throws IOException {
 | 
				
			||||||
 | 
					        BTreeNode<K, V> root = getRoot();
 | 
				
			||||||
 | 
					        if (root != null) {
 | 
				
			||||||
 | 
					            out.add(root._recid);
 | 
				
			||||||
 | 
					            root.dumpChildNodeRecIDs(out, _height);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean hasValues() {
 | 
				
			||||||
 | 
					        return hasValues;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Browser to traverse a collection of tuples.  The browser allows for
 | 
				
			||||||
 | 
					     * forward and reverse order traversal.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static interface BTreeTupleBrowser<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Get the next tuple.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param tuple Tuple into which values are copied.
 | 
				
			||||||
 | 
					         * @return True if values have been copied in tuple, or false if there is no next tuple.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        boolean getNext(BTree.BTreeTuple<K, V> tuple) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Get the previous tuple.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param tuple Tuple into which values are copied.
 | 
				
			||||||
 | 
					         * @return True if values have been copied in tuple, or false if there is no previous tuple.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        boolean getPrevious(BTree.BTreeTuple<K, V> tuple) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Remove an entry with given key, and increases browsers expectedModCount
 | 
				
			||||||
 | 
					         * This method is here to support 'ConcurrentModificationException' on Map interface.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param key
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        void remove(K key) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tuple consisting of a key-value pair.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final class BTreeTuple<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        K key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        V value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BTreeTuple() {
 | 
				
			||||||
 | 
					            // empty
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BTreeTuple(K key, V value) {
 | 
				
			||||||
 | 
					            this.key = key;
 | 
				
			||||||
 | 
					            this.value = value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An record lazily loaded from store.
 | 
				
			||||||
 | 
					 * This is used in BTree/HTree to store big records outside of index tree
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class BTreeLazyRecord<E> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private E value = null;
 | 
				
			||||||
 | 
					    private DBAbstract db;
 | 
				
			||||||
 | 
					    private Serializer<E> serializer;
 | 
				
			||||||
 | 
					    final long recid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BTreeLazyRecord(DBAbstract db, long recid, Serializer<E> serializer) {
 | 
				
			||||||
 | 
					        this.db = db;
 | 
				
			||||||
 | 
					        this.recid = recid;
 | 
				
			||||||
 | 
					        this.serializer = serializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    E get() {
 | 
				
			||||||
 | 
					        if (value != null) return value;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            value = db.fetch(recid, serializer);
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void delete() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            db.delete(recid);
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        value = null;
 | 
				
			||||||
 | 
					        serializer = null;
 | 
				
			||||||
 | 
					        db = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serialier used to insert already serialized data into store
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final Serializer FAKE_SERIALIZER = new Serializer() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void serialize(DataOutput out, Object obj) throws IOException {
 | 
				
			||||||
 | 
					            byte[] data = (byte[]) obj;
 | 
				
			||||||
 | 
					            out.write(data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Object deserialize(DataInput in) throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					            throw new UnsupportedOperationException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static Object fastDeser(DataInputOutput in, Serializer serializer, int expectedSize) throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					        //we should propably copy data for deserialization into separate buffer and pass it to Serializer
 | 
				
			||||||
 | 
					        //but to make it faster, Serializer will operate directly on top of buffer.
 | 
				
			||||||
 | 
					        //and we check that it readed correct number of bytes.
 | 
				
			||||||
 | 
					        int origAvail = in.available();
 | 
				
			||||||
 | 
					        if (origAvail == 0)
 | 
				
			||||||
 | 
					            throw new InternalError(); //is backed up by byte[] buffer, so there should be always avail bytes
 | 
				
			||||||
 | 
					        Object ret = serializer.deserialize(in);
 | 
				
			||||||
 | 
					        //check than valueSerializer did not read more bytes, if yes it readed bytes from next record
 | 
				
			||||||
 | 
					        int readed = origAvail - in.available();
 | 
				
			||||||
 | 
					        if (readed > expectedSize)
 | 
				
			||||||
 | 
					            throw new IOException("Serializer readed more bytes than is record size.");
 | 
				
			||||||
 | 
					        else if (readed != expectedSize) {
 | 
				
			||||||
 | 
					            //deserializer did not readed all bytes, unussual but valid.
 | 
				
			||||||
 | 
					            //Skip some to get into correct position
 | 
				
			||||||
 | 
					            for (int ii = 0; ii < expectedSize - readed; ii++)
 | 
				
			||||||
 | 
					                in.readUnsignedByte();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * if value in tree is serialized in more bytes, it is stored as separate record outside of tree
 | 
				
			||||||
 | 
					     * This value must be always smaller than 250
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final int MAX_INTREE_RECORD_SIZE = 32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static {
 | 
				
			||||||
 | 
					        if (MAX_INTREE_RECORD_SIZE > 250) throw new Error();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static final int NULL = 255;
 | 
				
			||||||
 | 
					    static final int LAZY_RECORD = 254;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,611 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOError;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentNavigableMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Wrapper for <code>BTree</code> which implements <code>ConcurrentNavigableMap</code> interface
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param <K> key type
 | 
				
			||||||
 | 
					 * @param <V> value type
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class BTreeMap<K, V> extends AbstractMap<K, V> implements ConcurrentNavigableMap<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected BTree<K, V> tree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected final K fromKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected final K toKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected final boolean readonly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected NavigableSet<K> keySet2;
 | 
				
			||||||
 | 
					    private final boolean toInclusive;
 | 
				
			||||||
 | 
					    private final boolean fromInclusive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BTreeMap(BTree<K, V> tree, boolean readonly) {
 | 
				
			||||||
 | 
					        this(tree, readonly, null, false, null, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected BTreeMap(BTree<K, V> tree, boolean readonly, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
 | 
				
			||||||
 | 
					        this.tree = tree;
 | 
				
			||||||
 | 
					        this.fromKey = fromKey;
 | 
				
			||||||
 | 
					        this.fromInclusive = fromInclusive;
 | 
				
			||||||
 | 
					        this.toKey = toKey;
 | 
				
			||||||
 | 
					        this.toInclusive = toInclusive;
 | 
				
			||||||
 | 
					        this.readonly = readonly;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Set<Entry<K, V>> entrySet() {
 | 
				
			||||||
 | 
					        return _entrySet;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Set<java.util.Map.Entry<K, V>> _entrySet = new AbstractSet<Entry<K, V>>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected Entry<K, V> newEntry(K k, V v) {
 | 
				
			||||||
 | 
					            return new SimpleEntry<K, V>(k, v) {
 | 
				
			||||||
 | 
					                private static final long serialVersionUID = 978651696969194154L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                public V setValue(V arg0) {
 | 
				
			||||||
 | 
					                    BTreeMap.this.put(getKey(), arg0);
 | 
				
			||||||
 | 
					                    return super.setValue(arg0);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean add(java.util.Map.Entry<K, V> e) {
 | 
				
			||||||
 | 
					            if (readonly)
 | 
				
			||||||
 | 
					                throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (e.getKey() == null)
 | 
				
			||||||
 | 
					                    throw new NullPointerException("Can not add null key");
 | 
				
			||||||
 | 
					                if (!inBounds(e.getKey()))
 | 
				
			||||||
 | 
					                    throw new IllegalArgumentException("key outside of bounds");
 | 
				
			||||||
 | 
					                return tree.insert(e.getKey(), e.getValue(), true) == null;
 | 
				
			||||||
 | 
					            } catch (IOException e1) {
 | 
				
			||||||
 | 
					                throw new IOError(e1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					        public boolean contains(Object o) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (o instanceof Entry) {
 | 
				
			||||||
 | 
					                Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    if (!inBounds(e.getKey()))
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    if (e.getKey() != null && tree.get(e.getKey()) != null)
 | 
				
			||||||
 | 
					                        return true;
 | 
				
			||||||
 | 
					                } catch (IOException e1) {
 | 
				
			||||||
 | 
					                    throw new IOError(e1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Iterator<java.util.Map.Entry<K, V>> iterator() {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                final BTree.BTreeTupleBrowser<K, V> br = fromKey == null ?
 | 
				
			||||||
 | 
					                        tree.browse() : tree.browse(fromKey, fromInclusive);
 | 
				
			||||||
 | 
					                return new Iterator<Entry<K, V>>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    private Entry<K, V> next;
 | 
				
			||||||
 | 
					                    private K lastKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    void ensureNext() {
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
 | 
				
			||||||
 | 
					                            if (br.getNext(t) && inBounds(t.key))
 | 
				
			||||||
 | 
					                                next = newEntry(t.key, t.value);
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                next = null;
 | 
				
			||||||
 | 
					                        } catch (IOException e1) {
 | 
				
			||||||
 | 
					                            throw new IOError(e1);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ensureNext();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    public boolean hasNext() {
 | 
				
			||||||
 | 
					                        return next != null;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    public java.util.Map.Entry<K, V> next() {
 | 
				
			||||||
 | 
					                        if (next == null)
 | 
				
			||||||
 | 
					                            throw new NoSuchElementException();
 | 
				
			||||||
 | 
					                        Entry<K, V> ret = next;
 | 
				
			||||||
 | 
					                        lastKey = ret.getKey();
 | 
				
			||||||
 | 
					                        //move to next position
 | 
				
			||||||
 | 
					                        ensureNext();
 | 
				
			||||||
 | 
					                        return ret;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    public void remove() {
 | 
				
			||||||
 | 
					                        if (readonly)
 | 
				
			||||||
 | 
					                            throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (lastKey == null)
 | 
				
			||||||
 | 
					                            throw new IllegalStateException();
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            br.remove(lastKey);
 | 
				
			||||||
 | 
					                            lastKey = null;
 | 
				
			||||||
 | 
					                        } catch (IOException e1) {
 | 
				
			||||||
 | 
					                            throw new IOError(e1);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } catch (IOException e) {
 | 
				
			||||||
 | 
					                throw new IOError(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					        public boolean remove(Object o) {
 | 
				
			||||||
 | 
					            if (readonly)
 | 
				
			||||||
 | 
					                throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (o instanceof Entry) {
 | 
				
			||||||
 | 
					                Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    //check for nulls
 | 
				
			||||||
 | 
					                    if (e.getKey() == null || e.getValue() == null)
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    if (!inBounds(e.getKey()))
 | 
				
			||||||
 | 
					                        throw new IllegalArgumentException("out of bounds");
 | 
				
			||||||
 | 
					                    //get old value, must be same as item in entry
 | 
				
			||||||
 | 
					                    V v = get(e.getKey());
 | 
				
			||||||
 | 
					                    if (v == null || !e.getValue().equals(v))
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    V v2 = tree.remove(e.getKey());
 | 
				
			||||||
 | 
					                    return v2 != null;
 | 
				
			||||||
 | 
					                } catch (IOException e1) {
 | 
				
			||||||
 | 
					                    throw new IOError(e1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int size() {
 | 
				
			||||||
 | 
					            return BTreeMap.this.size();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void clear(){
 | 
				
			||||||
 | 
					            if(fromKey!=null || toKey!=null)
 | 
				
			||||||
 | 
					                super.clear();
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    tree.clear();
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    throw new IOError(e);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean inBounds(K e) {
 | 
				
			||||||
 | 
					        if(fromKey == null && toKey == null)
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Comparator comp = comparator();
 | 
				
			||||||
 | 
					        if (comp == null) comp = Utils.COMPARABLE_COMPARATOR;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(fromKey!=null){
 | 
				
			||||||
 | 
					            final int compare = comp.compare(e, fromKey);
 | 
				
			||||||
 | 
					            if(compare<0) return false;
 | 
				
			||||||
 | 
					            if(!fromInclusive && compare == 0) return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(toKey!=null){
 | 
				
			||||||
 | 
					            final int compare = comp.compare(e, toKey);
 | 
				
			||||||
 | 
					            if(compare>0)return false;
 | 
				
			||||||
 | 
					            if(!toInclusive && compare == 0) return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public V get(Object key) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (key == null)
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            if (!inBounds((K) key))
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            return tree.get((K) key);
 | 
				
			||||||
 | 
					        } catch (ClassCastException e) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public V remove(Object key) {
 | 
				
			||||||
 | 
					        if (readonly)
 | 
				
			||||||
 | 
					            throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (key == null || tree.get((K) key) == null)
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            if (!inBounds((K) key))
 | 
				
			||||||
 | 
					                throw new IllegalArgumentException("out of bounds");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return tree.remove((K) key);
 | 
				
			||||||
 | 
					        } catch (ClassCastException e) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V put(K key, V value) {
 | 
				
			||||||
 | 
					        if (readonly)
 | 
				
			||||||
 | 
					            throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (key == null || value == null)
 | 
				
			||||||
 | 
					                throw new NullPointerException("Null key or value");
 | 
				
			||||||
 | 
					            if (!inBounds(key))
 | 
				
			||||||
 | 
					                throw new IllegalArgumentException("out of bounds");
 | 
				
			||||||
 | 
					            return tree.insert(key, value, true);
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clear(){
 | 
				
			||||||
 | 
					        entrySet().clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean containsKey(Object key) {
 | 
				
			||||||
 | 
					        if (key == null)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!inBounds((K) key))
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            V v = tree.get((K) key);
 | 
				
			||||||
 | 
					            return v != null;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        } catch (ClassCastException e) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Comparator<? super K> comparator() {
 | 
				
			||||||
 | 
					        return tree._comparator;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public K firstKey() {
 | 
				
			||||||
 | 
					        if (isEmpty())
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            BTree.BTreeTupleBrowser<K, V> b = fromKey == null ? tree.browse() : tree.browse(fromKey,fromInclusive);
 | 
				
			||||||
 | 
					            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
 | 
				
			||||||
 | 
					            b.getNext(t);
 | 
				
			||||||
 | 
					            return t.key;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public K lastKey() {
 | 
				
			||||||
 | 
					        if (isEmpty())
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            BTree.BTreeTupleBrowser<K, V> b = toKey == null ? tree.browse(null,true) : tree.browse(toKey,false);
 | 
				
			||||||
 | 
					            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();            
 | 
				
			||||||
 | 
					            b.getPrevious(t);            
 | 
				
			||||||
 | 
					            if(!toInclusive && toKey!=null){
 | 
				
			||||||
 | 
					                //make sure we wont return last key
 | 
				
			||||||
 | 
					                Comparator c = comparator();
 | 
				
			||||||
 | 
					                if(c==null) c=Utils.COMPARABLE_COMPARATOR;
 | 
				
			||||||
 | 
					                if(c.compare(t.key,toKey)==0)
 | 
				
			||||||
 | 
					                    b.getPrevious(t);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return t.key;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> headMap(K toKey2, boolean inclusive) {
 | 
				
			||||||
 | 
					        K toKey3 = Utils.min(this.toKey,toKey2,comparator());
 | 
				
			||||||
 | 
					        boolean inclusive2 = toKey3 == toKey? toInclusive : inclusive;
 | 
				
			||||||
 | 
					        return new BTreeMap<K, V>(tree, readonly, this.fromKey, this.fromInclusive, toKey3, inclusive2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> headMap(K toKey) {
 | 
				
			||||||
 | 
					        return headMap(toKey,false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> lowerEntry(K key) {
 | 
				
			||||||
 | 
					        K k = lowerKey(key);
 | 
				
			||||||
 | 
					        return k==null? null : new SimpleEntry<K, V>(k,get(k));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public K lowerKey(K key) {
 | 
				
			||||||
 | 
					        if (isEmpty())
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        K key2 = Utils.min(key,toKey,comparator());
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            BTree.BTreeTupleBrowser<K, V> b  = tree.browse(key2,true) ;
 | 
				
			||||||
 | 
					            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
 | 
				
			||||||
 | 
					            b.getPrevious(t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return t.key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> floorEntry(K key) {
 | 
				
			||||||
 | 
					        K k = floorKey(key);
 | 
				
			||||||
 | 
					        return k==null? null : new SimpleEntry<K, V>(k,get(k));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public K floorKey(K key) {
 | 
				
			||||||
 | 
					        if (isEmpty())
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        K key2 = Utils.max(key,fromKey,comparator());
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            BTree.BTreeTupleBrowser<K, V> b  = tree.browse(key2,true) ;
 | 
				
			||||||
 | 
					            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
 | 
				
			||||||
 | 
					            b.getNext(t);
 | 
				
			||||||
 | 
					            Comparator comp = comparator();
 | 
				
			||||||
 | 
					            if (comp == null) comp = Utils.COMPARABLE_COMPARATOR;
 | 
				
			||||||
 | 
					            if(comp.compare(t.key,key2) == 0)
 | 
				
			||||||
 | 
					                return t.key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            b.getPrevious(t);
 | 
				
			||||||
 | 
					            b.getPrevious(t);
 | 
				
			||||||
 | 
					            return t.key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> ceilingEntry(K key) {
 | 
				
			||||||
 | 
					        K k = ceilingKey(key);
 | 
				
			||||||
 | 
					        return k==null? null : new SimpleEntry<K, V>(k,get(k));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public K ceilingKey(K key) {
 | 
				
			||||||
 | 
					        if (isEmpty())
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        K key2 = Utils.min(key,toKey,comparator());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            BTree.BTreeTupleBrowser<K, V> b  = tree.browse(key2,true) ;
 | 
				
			||||||
 | 
					            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
 | 
				
			||||||
 | 
					            b.getNext(t);
 | 
				
			||||||
 | 
					            return t.key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> higherEntry(K key) {
 | 
				
			||||||
 | 
					        K k = higherKey(key);
 | 
				
			||||||
 | 
					        return k==null? null : new SimpleEntry<K, V>(k,get(k));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public K higherKey(K key) {
 | 
				
			||||||
 | 
					        if (isEmpty())
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        K key2 = Utils.max(key,fromKey,comparator());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            BTree.BTreeTupleBrowser<K, V> b  = tree.browse(key2,false) ;
 | 
				
			||||||
 | 
					            BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
 | 
				
			||||||
 | 
					            b.getNext(t);
 | 
				
			||||||
 | 
					            return t.key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> firstEntry() {
 | 
				
			||||||
 | 
					        K k = firstKey();
 | 
				
			||||||
 | 
					        return k==null? null : new SimpleEntry<K, V>(k,get(k));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> lastEntry() {
 | 
				
			||||||
 | 
					        K k = lastKey();
 | 
				
			||||||
 | 
					        return k==null? null : new SimpleEntry<K, V>(k,get(k));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> pollFirstEntry() {
 | 
				
			||||||
 | 
					        Entry<K,V> first = firstEntry();
 | 
				
			||||||
 | 
					        if(first!=null)
 | 
				
			||||||
 | 
					            remove(first.getKey());
 | 
				
			||||||
 | 
					        return first;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Entry<K, V> pollLastEntry() {
 | 
				
			||||||
 | 
					        Entry<K,V> last = lastEntry();
 | 
				
			||||||
 | 
					        if(last!=null)
 | 
				
			||||||
 | 
					            remove(last.getKey());
 | 
				
			||||||
 | 
					        return last;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> descendingMap() {
 | 
				
			||||||
 | 
					        throw new  UnsupportedOperationException("not implemented yet");
 | 
				
			||||||
 | 
					        //TODO implement descending (reverse order) map
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<K> keySet() {
 | 
				
			||||||
 | 
					        return navigableKeySet();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<K> navigableKeySet() {
 | 
				
			||||||
 | 
					        if(keySet2 == null)
 | 
				
			||||||
 | 
					            keySet2 = new BTreeSet<K>((BTreeMap<K,Object>) this);
 | 
				
			||||||
 | 
					        return keySet2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<K> descendingKeySet() {
 | 
				
			||||||
 | 
					        return descendingMap().navigableKeySet();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> tailMap(K fromKey) {
 | 
				
			||||||
 | 
					        return tailMap(fromKey,true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> tailMap(K fromKey2, boolean inclusive) {
 | 
				
			||||||
 | 
					        K fromKey3 = Utils.max(this.fromKey,fromKey2,comparator());
 | 
				
			||||||
 | 
					        boolean inclusive2 = fromKey3 == toKey? toInclusive : inclusive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new BTreeMap<K, V>(tree, readonly, fromKey3, inclusive2, toKey, toInclusive);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
 | 
				
			||||||
 | 
					        Comparator comp = comparator();
 | 
				
			||||||
 | 
					        if (comp == null) comp = Utils.COMPARABLE_COMPARATOR;
 | 
				
			||||||
 | 
					        if (comp.compare(fromKey, toKey) > 0)
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("fromKey is bigger then toKey");
 | 
				
			||||||
 | 
					        return new BTreeMap<K, V>(tree, readonly, fromKey, fromInclusive, toKey, toInclusive);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConcurrentNavigableMap<K, V> subMap(K fromKey, K toKey) {
 | 
				
			||||||
 | 
					        return subMap(fromKey,true,toKey,false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BTree<K, V> getTree() {
 | 
				
			||||||
 | 
					        return tree;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addRecordListener(RecordListener<K, V> listener) {
 | 
				
			||||||
 | 
					        tree.addRecordListener(listener);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DBAbstract getRecordManager() {
 | 
				
			||||||
 | 
					        return tree.getRecordManager();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void removeRecordListener(RecordListener<K, V> listener) {
 | 
				
			||||||
 | 
					        tree.removeRecordListener(listener);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int size() {
 | 
				
			||||||
 | 
					        if (fromKey == null && toKey == null)
 | 
				
			||||||
 | 
					            return (int) tree._entries; //use fast counter on tree if Map has no bounds
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            //had to count items in iterator
 | 
				
			||||||
 | 
					            Iterator iter = keySet().iterator();
 | 
				
			||||||
 | 
					            int counter = 0;
 | 
				
			||||||
 | 
					            while (iter.hasNext()) {
 | 
				
			||||||
 | 
					                iter.next();
 | 
				
			||||||
 | 
					                counter++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return counter;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V putIfAbsent(K key, V value) {
 | 
				
			||||||
 | 
					        tree.lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (!containsKey(key))
 | 
				
			||||||
 | 
					                 return put(key, value);
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                 return get(key);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            tree.lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean remove(Object key, Object value) {
 | 
				
			||||||
 | 
					        tree.lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (containsKey(key) && get(key).equals(value)) {
 | 
				
			||||||
 | 
					                remove(key);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else return false;
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            tree.lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean replace(K key, V oldValue, V newValue) {
 | 
				
			||||||
 | 
					        tree.lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (containsKey(key) && get(key).equals(oldValue)) {
 | 
				
			||||||
 | 
					                put(key, newValue);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else return false;
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            tree.lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V replace(K key, V value) {
 | 
				
			||||||
 | 
					        tree.lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (containsKey(key)) {
 | 
				
			||||||
 | 
					                return put(key, value);
 | 
				
			||||||
 | 
					             } else return null;
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            tree.lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1550
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/BTreeNode.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1550
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/BTreeNode.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  Licensed to the Apache Software Foundation (ASF) under one or more
 | 
				
			||||||
 | 
					 *  contributor license agreements.  See the NOTICE file distributed with
 | 
				
			||||||
 | 
					 *  this work for additional information regarding copyright ownership.
 | 
				
			||||||
 | 
					 *  The ASF licenses this file to You under the Apache License, Version 2.0
 | 
				
			||||||
 | 
					 *  (the "License"); you may not use this file except in compliance with
 | 
				
			||||||
 | 
					 *  the License.  You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 *  distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 *  See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 *  limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Wrapper class for <code>>SortedMap</code> to implement <code>>NavigableSet</code>
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * This code originally comes from Apache Harmony, was adapted by Jan Kotek for JDBM
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class BTreeSet<E> extends AbstractSet<E> implements NavigableSet<E> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * use keyset from this map
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    final BTreeMap<E, Object> map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BTreeSet(BTreeMap<E, Object> map) {
 | 
				
			||||||
 | 
					        this.map = map;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean add(E object) {
 | 
				
			||||||
 | 
					        return map.put(object, Utils.EMPTY_STRING) == null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean addAll(Collection<? extends E> collection) {
 | 
				
			||||||
 | 
					        return super.addAll(collection);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clear() {
 | 
				
			||||||
 | 
					        map.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Comparator<? super E> comparator() {
 | 
				
			||||||
 | 
					        return map.comparator();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean contains(Object object) {
 | 
				
			||||||
 | 
					        return map.containsKey(object);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isEmpty() {
 | 
				
			||||||
 | 
					        return map.isEmpty();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E lower(E e) {
 | 
				
			||||||
 | 
					        return map.lowerKey(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E floor(E e) {
 | 
				
			||||||
 | 
					        return map.floorKey(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E ceiling(E e) {
 | 
				
			||||||
 | 
					        return map.ceilingKey(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E higher(E e) {
 | 
				
			||||||
 | 
					        return map.higherKey(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E pollFirst() {
 | 
				
			||||||
 | 
					        Map.Entry<E,Object> e = map.pollFirstEntry();
 | 
				
			||||||
 | 
					        return e!=null? e.getKey():null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E pollLast() {
 | 
				
			||||||
 | 
					        Map.Entry<E,Object> e = map.pollLastEntry();
 | 
				
			||||||
 | 
					        return e!=null? e.getKey():null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Iterator<E> iterator() {
 | 
				
			||||||
 | 
					        final Iterator<Map.Entry<E,Object>> iter = map.entrySet().iterator();
 | 
				
			||||||
 | 
					        return new Iterator<E>() {
 | 
				
			||||||
 | 
					            public boolean hasNext() {
 | 
				
			||||||
 | 
					                return iter.hasNext();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public E next() {
 | 
				
			||||||
 | 
					                Map.Entry<E,Object> e = iter.next();
 | 
				
			||||||
 | 
					                return e!=null?e.getKey():null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public void remove() {
 | 
				
			||||||
 | 
					                iter.remove();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<E> descendingSet() {
 | 
				
			||||||
 | 
					        return map.descendingKeySet();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Iterator<E> descendingIterator() {
 | 
				
			||||||
 | 
					        return map.descendingKeySet().iterator();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
 | 
				
			||||||
 | 
					        return map.subMap(fromElement,fromInclusive,toElement,toInclusive).navigableKeySet();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
 | 
				
			||||||
 | 
					        return map.headMap(toElement,inclusive).navigableKeySet();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
 | 
				
			||||||
 | 
					        return map.tailMap(fromElement,inclusive).navigableKeySet();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean remove(Object object) {
 | 
				
			||||||
 | 
					        return map.remove(object) != null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int size() {
 | 
				
			||||||
 | 
					        return map.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E first() {
 | 
				
			||||||
 | 
					        return map.firstKey();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public E last() {
 | 
				
			||||||
 | 
					        return map.lastKey();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SortedSet<E> subSet(E start, E end) {
 | 
				
			||||||
 | 
					        Comparator<? super E> c = map.comparator();
 | 
				
			||||||
 | 
					        int compare = (c == null) ? ((Comparable<E>) start).compareTo(end) : c
 | 
				
			||||||
 | 
					                .compare(start, end);
 | 
				
			||||||
 | 
					        if (compare <= 0) {
 | 
				
			||||||
 | 
					            return new BTreeSet<E>((BTreeMap<E,Object>) map.subMap(start, true,end,false));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SortedSet<E> headSet(E end) {
 | 
				
			||||||
 | 
					        // Check for errors
 | 
				
			||||||
 | 
					        Comparator<? super E> c = map.comparator();
 | 
				
			||||||
 | 
					        if (c == null) {
 | 
				
			||||||
 | 
					            ((Comparable<E>) end).compareTo(end);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            c.compare(end, end);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new BTreeSet<E>((BTreeMap<E,Object>) map.headMap(end,false));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SortedSet<E> tailSet(E start) {
 | 
				
			||||||
 | 
					        // Check for errors
 | 
				
			||||||
 | 
					        Comparator<? super E> c = map.comparator();
 | 
				
			||||||
 | 
					        if (c == null) {
 | 
				
			||||||
 | 
					            ((Comparable<E>) start).compareTo(start);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            c.compare(start, start);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new BTreeSet<E>((BTreeMap<E,Object>) map.tailMap(start,true));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										173
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/DB.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/DB.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
				
			|||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentNavigableMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Database is root class for creating and loading persistent collections. It also contains
 | 
				
			||||||
 | 
					 * transaction operations.
 | 
				
			||||||
 | 
					 * //TODO just write some readme
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 * @author Cees de Groot
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface DB {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes the DB and release resources.
 | 
				
			||||||
 | 
					     * DB can not be used after it was closed
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** @return true if db was already closed */
 | 
				
			||||||
 | 
					    boolean isClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Clear cache and remove all entries it contains.
 | 
				
			||||||
 | 
					     * This may be useful for some Garbage Collection when reference cache is used.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void clearCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defragments storage so it consumes less space.
 | 
				
			||||||
 | 
					     * It basically copyes all records into different store and then renames it, replacing original store.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * Defrag has two steps: In first collections are rearranged, so records in collection are close to each other,
 | 
				
			||||||
 | 
					     * and read speed is improved. In second step all records are sequentially transferred, reclaiming all unused space.
 | 
				
			||||||
 | 
					     * First step is optinal and may slow down defragmentation significantly as ut requires many random-access reads.
 | 
				
			||||||
 | 
					     * Second step reads and writes data sequentially and is very fast, comparable to copying files to new location.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * This commits any uncommited data. Defrag also requires free space, as store is basically recreated at new location.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sortCollections if collection records should be rearranged during defragment, this takes some extra time
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void defrag(boolean sortCollections);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Commit (make persistent) all changes since beginning of transaction.
 | 
				
			||||||
 | 
					     * JDBM supports only single transaction.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Rollback (cancel) all changes since beginning of transaction.
 | 
				
			||||||
 | 
					     * JDBM supports only single transaction.
 | 
				
			||||||
 | 
					     * This operations affects all maps created or loaded by this DB.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void rollback();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This calculates some database statistics such as collection sizes and record distributions.
 | 
				
			||||||
 | 
					     * Can be useful for performance optimalisations and trouble shuting.
 | 
				
			||||||
 | 
					     * This method can run for very long time.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return statistics contained in string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String calculateStatistics();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Copy database content into ZIP file
 | 
				
			||||||
 | 
					     * @param zipFile
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void copyToZip(String zipFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a <code>Map</code> which was already created and saved in DB.
 | 
				
			||||||
 | 
					     * This map uses disk based H*Tree and should have similar performance
 | 
				
			||||||
 | 
					     * as <code>HashMap</code>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name of hash map
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return map
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    <K, V> ConcurrentMap<K, V> getHashMap(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates Map which persists data into DB.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name record name
 | 
				
			||||||
 | 
					     * @return
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    <K, V> ConcurrentMap<K, V> createHashMap(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates  Hash Map which persists data into DB.
 | 
				
			||||||
 | 
					     * Map will use custom serializers for Keys and Values.
 | 
				
			||||||
 | 
					     * Leave keySerializer null to use default serializer for keys
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <K>             Key type
 | 
				
			||||||
 | 
					     * @param <V>             Value type
 | 
				
			||||||
 | 
					     * @param name            record name
 | 
				
			||||||
 | 
					     * @param keySerializer   serializer to be used for Keys, leave null to use default serializer
 | 
				
			||||||
 | 
					     * @param valueSerializer serializer to be used for Values
 | 
				
			||||||
 | 
					     * @return
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    <K, V> ConcurrentMap<K, V> createHashMap(String name, Serializer<K> keySerializer, Serializer<V> valueSerializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> Set<K> createHashSet(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> Set<K> getHashSet(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> Set<K> createHashSet(String name, Serializer<K> keySerializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K, V> ConcurrentNavigableMap<K, V> getTreeMap(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create  TreeMap which persists data into DB.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <K>  Key type
 | 
				
			||||||
 | 
					     * @param <V>  Value type
 | 
				
			||||||
 | 
					     * @param name record name
 | 
				
			||||||
 | 
					     * @return
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    <K extends Comparable, V> NavigableMap<K, V> createTreeMap(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates TreeMap which persists data into DB.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <K>             Key type
 | 
				
			||||||
 | 
					     * @param <V>             Value type
 | 
				
			||||||
 | 
					     * @param name            record name
 | 
				
			||||||
 | 
					     * @param keyComparator   Comparator used to sort keys
 | 
				
			||||||
 | 
					     * @param keySerializer   Serializer used for keys. This may reduce disk space usage     *
 | 
				
			||||||
 | 
					     * @param valueSerializer Serializer used for values. This may reduce disk space usage
 | 
				
			||||||
 | 
					     * @return
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    <K, V> ConcurrentNavigableMap<K, V> createTreeMap(String name,
 | 
				
			||||||
 | 
					                                         Comparator<K> keyComparator, Serializer<K> keySerializer, Serializer<V> valueSerializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> NavigableSet<K> getTreeSet(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> NavigableSet<K> createTreeSet(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> NavigableSet<K> createTreeSet(String name, Comparator<K> keyComparator, Serializer<K> keySerializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> List<K> createLinkedList(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> List<K> createLinkedList(String name, Serializer<K> serializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <K> List<K> getLinkedList(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** returns unmodifiable map which contains all collection names and collections thenselfs*/
 | 
				
			||||||
 | 
					    Map<String,Object> getCollections();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** completely remove collection from store*/
 | 
				
			||||||
 | 
					    void deleteCollection(String name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Java Collections returns their size as int. This may not be enought for JDBM collections.
 | 
				
			||||||
 | 
					     * This method returns number of elements in JDBM collection as long.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param collection created by JDBM
 | 
				
			||||||
 | 
					     * @return number of elements in collection as long
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    long collectionSize(Object collection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,590 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOError;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.lang.ref.WeakReference;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentNavigableMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An abstract class implementing most of DB.
 | 
				
			||||||
 | 
					 * It also has some JDBM package protected stuff (getNamedRecord)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					abstract class DBAbstract implements DB {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reserved slot for name directory recid.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final byte NAME_DIRECTORY_ROOT = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reserved slot for version number
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final byte STORE_VERSION_NUMBER_ROOT = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reserved slot for recid where Serial class info is stored
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * NOTE when introducing more roots, do not forget to update defrag
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final byte SERIAL_CLASS_INFO_RECID_ROOT = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** to prevent double instances of the same collection, we use weak value map
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * //TODO what to do when there is rollback?
 | 
				
			||||||
 | 
					     * //TODO clear on close
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    final private Map<String,WeakReference<Object>> collections = new HashMap<String,WeakReference<Object>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Inserts a new record using a custom serializer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param obj        the object for the new record.
 | 
				
			||||||
 | 
					     * @param serializer a custom serializer
 | 
				
			||||||
 | 
					     * @return the rowid for the new record.
 | 
				
			||||||
 | 
					     * @throws java.io.IOException when one of the underlying I/O operations fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract <A> long insert(A obj, Serializer<A> serializer,boolean disableCache) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Deletes a record.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param recid the rowid for the record that should be deleted.
 | 
				
			||||||
 | 
					     * @throws java.io.IOException when one of the underlying I/O operations fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract void delete(long recid) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates a record using a custom serializer.
 | 
				
			||||||
 | 
					     * If given recid does not exist, IOException will be thrown before/during commit (cache).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param recid      the recid for the record that is to be updated.
 | 
				
			||||||
 | 
					     * @param obj        the new object for the record.
 | 
				
			||||||
 | 
					     * @param serializer a custom serializer
 | 
				
			||||||
 | 
					     * @throws java.io.IOException when one of the underlying I/O operations fails
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract <A> void update(long recid, A obj, Serializer<A> serializer)
 | 
				
			||||||
 | 
					            throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetches a record using a custom serializer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param recid      the recid for the record that must be fetched.
 | 
				
			||||||
 | 
					     * @param serializer a custom serializer
 | 
				
			||||||
 | 
					     * @return the object contained in the record, null if given recid does not exist
 | 
				
			||||||
 | 
					     * @throws java.io.IOException when one of the underlying I/O operations fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract <A> A fetch(long recid, Serializer<A> serializer)
 | 
				
			||||||
 | 
					            throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetches a record using a custom serializer and optionaly disabled cache
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param recid        the recid for the record that must be fetched.
 | 
				
			||||||
 | 
					     * @param serializer   a custom serializer
 | 
				
			||||||
 | 
					     * @param disableCache true to disable any caching mechanism
 | 
				
			||||||
 | 
					     * @return the object contained in the record, null if given recid does not exist
 | 
				
			||||||
 | 
					     * @throws java.io.IOException when one of the underlying I/O operations fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache)
 | 
				
			||||||
 | 
					            throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long insert(Object obj) throws IOException {
 | 
				
			||||||
 | 
					        return insert(obj, defaultSerializer(),false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void update(long recid, Object obj) throws IOException {
 | 
				
			||||||
 | 
					        update(recid, obj, defaultSerializer());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <A> A fetch(long recid) throws IOException {
 | 
				
			||||||
 | 
					        return (A) fetch(recid, defaultSerializer());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K, V> ConcurrentMap<K, V> getHashMap(String name) {
 | 
				
			||||||
 | 
					        Object o = getCollectionInstance(name);
 | 
				
			||||||
 | 
					        if(o!=null)
 | 
				
			||||||
 | 
					            return (ConcurrentMap<K, V>) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            long recid = getNamedObject(name);
 | 
				
			||||||
 | 
					            if(recid == 0) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HTree tree = fetch(recid);
 | 
				
			||||||
 | 
					            tree.setPersistenceContext(this);
 | 
				
			||||||
 | 
					            if(!tree.hasValues()){
 | 
				
			||||||
 | 
					                throw new ClassCastException("HashSet is not HashMap");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(tree));
 | 
				
			||||||
 | 
					            return tree;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K, V> ConcurrentMap<K, V> createHashMap(String name) {
 | 
				
			||||||
 | 
					        return createHashMap(name, null, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K, V> ConcurrentMap<K, V> createHashMap(String name, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            assertNameNotExist(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HTree<K, V> tree = new HTree(this, keySerializer, valueSerializer,true);
 | 
				
			||||||
 | 
					            long recid = insert(tree);
 | 
				
			||||||
 | 
					            setNamedObject(name, recid);
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(tree));
 | 
				
			||||||
 | 
					            return tree;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K> Set<K> getHashSet(String name) {
 | 
				
			||||||
 | 
					        Object o = getCollectionInstance(name);
 | 
				
			||||||
 | 
					        if(o!=null)
 | 
				
			||||||
 | 
					            return (Set<K>) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            long recid = getNamedObject(name);
 | 
				
			||||||
 | 
					            if(recid == 0) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HTree tree = fetch(recid);
 | 
				
			||||||
 | 
					            tree.setPersistenceContext(this);
 | 
				
			||||||
 | 
					            if(tree.hasValues()){
 | 
				
			||||||
 | 
					                throw new ClassCastException("HashMap is not HashSet");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Set<K> ret  =  new HTreeSet(tree);
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(ret));
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K> Set<K> createHashSet(String name) {
 | 
				
			||||||
 | 
					        return createHashSet(name, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K> Set<K> createHashSet(String name, Serializer<K> keySerializer) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            assertNameNotExist(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HTree<K, Object> tree = new HTree(this, keySerializer, null,false);
 | 
				
			||||||
 | 
					            long recid = insert(tree);
 | 
				
			||||||
 | 
					            setNamedObject(name, recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Set<K> ret =  new HTreeSet<K>(tree);
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(ret));
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K, V> ConcurrentNavigableMap<K, V> getTreeMap(String name) {
 | 
				
			||||||
 | 
					        Object o = getCollectionInstance(name);
 | 
				
			||||||
 | 
					        if(o!=null)
 | 
				
			||||||
 | 
					            return (ConcurrentNavigableMap<K, V> ) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            long recid = getNamedObject(name);
 | 
				
			||||||
 | 
					            if(recid == 0) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            BTree t =  BTree.<K, V>load(this, recid);
 | 
				
			||||||
 | 
					            if(!t.hasValues())
 | 
				
			||||||
 | 
					                throw new ClassCastException("TreeSet is not TreeMap");
 | 
				
			||||||
 | 
					            ConcurrentNavigableMap<K,V> ret = new BTreeMap<K, V>(t,false); //TODO put readonly flag here
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(ret));
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K extends Comparable, V> ConcurrentNavigableMap<K, V> createTreeMap(String name) {
 | 
				
			||||||
 | 
					        return createTreeMap(name, null, null, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K, V> ConcurrentNavigableMap<K, V> createTreeMap(String name,
 | 
				
			||||||
 | 
					                                                                          Comparator<K> keyComparator,
 | 
				
			||||||
 | 
					                                                                          Serializer<K> keySerializer,
 | 
				
			||||||
 | 
					                                                                          Serializer<V> valueSerializer) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            assertNameNotExist(name);
 | 
				
			||||||
 | 
					            BTree<K, V> tree = BTree.createInstance(this, keyComparator, keySerializer, valueSerializer,true);
 | 
				
			||||||
 | 
					            setNamedObject(name, tree.getRecid());
 | 
				
			||||||
 | 
					            ConcurrentNavigableMap<K,V> ret = new BTreeMap<K, V>(tree,false); //TODO put readonly flag here
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(ret));
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K> NavigableSet<K> getTreeSet(String name) {
 | 
				
			||||||
 | 
					        Object o = getCollectionInstance(name);
 | 
				
			||||||
 | 
					        if(o!=null)
 | 
				
			||||||
 | 
					            return (NavigableSet<K> ) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            long recid = getNamedObject(name);
 | 
				
			||||||
 | 
					            if(recid == 0) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            BTree t =  BTree.<K, Object>load(this, recid);
 | 
				
			||||||
 | 
					            if(t.hasValues())
 | 
				
			||||||
 | 
					                throw new ClassCastException("TreeMap is not TreeSet");
 | 
				
			||||||
 | 
					            BTreeSet<K> ret =  new BTreeSet<K>(new BTreeMap(t,false));
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(ret));
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K> NavigableSet<K> createTreeSet(String name) {
 | 
				
			||||||
 | 
					        return createTreeSet(name, null, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <K> NavigableSet<K> createTreeSet(String name, Comparator<K> keyComparator, Serializer<K> keySerializer) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            assertNameNotExist(name);
 | 
				
			||||||
 | 
					            BTree<K, Object> tree = BTree.createInstance(this, keyComparator, keySerializer, null,false);
 | 
				
			||||||
 | 
					            setNamedObject(name, tree.getRecid());
 | 
				
			||||||
 | 
					            BTreeSet<K> ret =  new BTreeSet<K>(new BTreeMap(tree,false));
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(ret));
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K> List<K> createLinkedList(String name) {
 | 
				
			||||||
 | 
					        return createLinkedList(name, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K> List<K> createLinkedList(String name, Serializer<K> serializer) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            assertNameNotExist(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //allocate record and overwrite it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LinkedList2<K> list = new LinkedList2<K>(this, serializer);
 | 
				
			||||||
 | 
					            long recid = insert(list);
 | 
				
			||||||
 | 
					            setNamedObject(name, recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(list));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return list;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public <K> List<K> getLinkedList(String name) {
 | 
				
			||||||
 | 
					        Object o = getCollectionInstance(name);
 | 
				
			||||||
 | 
					        if(o!=null)
 | 
				
			||||||
 | 
					            return (List<K> ) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            long recid = getNamedObject(name);
 | 
				
			||||||
 | 
					            if(recid == 0) return null;
 | 
				
			||||||
 | 
					            LinkedList2<K> list = (LinkedList2<K>) fetch(recid);
 | 
				
			||||||
 | 
					            list.setPersistenceContext(this);
 | 
				
			||||||
 | 
					            collections.put(name,new WeakReference<Object>(list));
 | 
				
			||||||
 | 
					            return list;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private synchronized  Object getCollectionInstance(String name){
 | 
				
			||||||
 | 
					        WeakReference ref = collections.get(name);
 | 
				
			||||||
 | 
					        if(ref==null)return null;
 | 
				
			||||||
 | 
					        Object o = ref.get();
 | 
				
			||||||
 | 
					        if(o != null) return o;
 | 
				
			||||||
 | 
					        //already GCed
 | 
				
			||||||
 | 
					        collections.remove(name);
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void assertNameNotExist(String name) throws IOException {
 | 
				
			||||||
 | 
					        if (getNamedObject(name) != 0)
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Object with name '" + name + "' already exists");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Obtain the record id of a named object. Returns 0 if named object
 | 
				
			||||||
 | 
					     * doesn't exist.
 | 
				
			||||||
 | 
					     * Named objects are used to store Map views and other well known objects.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    synchronized protected long getNamedObject(String name) throws IOException{
 | 
				
			||||||
 | 
					        long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
 | 
				
			||||||
 | 
					        if(nameDirectory_recid == 0){
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        HTree<String,Long> m = fetch(nameDirectory_recid);
 | 
				
			||||||
 | 
					        Long res = m.get(name);
 | 
				
			||||||
 | 
					        if(res == null)
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the record id of a named object.
 | 
				
			||||||
 | 
					     * Named objects are used to store Map views and other well known objects.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    synchronized protected void setNamedObject(String name, long recid) throws IOException{
 | 
				
			||||||
 | 
					        long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
 | 
				
			||||||
 | 
					        HTree<String,Long> m = null;
 | 
				
			||||||
 | 
					        if(nameDirectory_recid == 0){
 | 
				
			||||||
 | 
					            //does not exists, create it
 | 
				
			||||||
 | 
					            m = new HTree<String, Long>(this,null,null,true);
 | 
				
			||||||
 | 
					            nameDirectory_recid = insert(m);
 | 
				
			||||||
 | 
					            setRoot(NAME_DIRECTORY_ROOT,nameDirectory_recid);
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					            //fetch it
 | 
				
			||||||
 | 
					            m = fetch(nameDirectory_recid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        m.put(name,recid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public Map<String,Object> getCollections(){
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            Map<String,Object> ret = new LinkedHashMap<String, Object>();
 | 
				
			||||||
 | 
					            long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
 | 
				
			||||||
 | 
					            if(nameDirectory_recid==0)
 | 
				
			||||||
 | 
					                return ret;
 | 
				
			||||||
 | 
					            HTree<String,Long> m = fetch(nameDirectory_recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for(Map.Entry<String,Long> e:m.entrySet()){
 | 
				
			||||||
 | 
					                Object o = fetch(e.getValue());
 | 
				
			||||||
 | 
					                if(o instanceof BTree){
 | 
				
			||||||
 | 
					                    if(((BTree) o).hasValues)
 | 
				
			||||||
 | 
					                        o = getTreeMap(e.getKey());
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        o = getTreeSet(e.getKey());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if( o instanceof  HTree){
 | 
				
			||||||
 | 
					                    if(((HTree) o).hasValues)
 | 
				
			||||||
 | 
					                        o = getHashMap(e.getKey());
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        o = getHashSet(e.getKey());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ret.put(e.getKey(), o);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return Collections.unmodifiableMap(ret);
 | 
				
			||||||
 | 
					        }catch(IOException e){
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public void deleteCollection(String name){
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
 | 
				
			||||||
 | 
					            if(nameDirectory_recid==0)
 | 
				
			||||||
 | 
					                throw new IOException("Collection not found");
 | 
				
			||||||
 | 
					            HTree<String,Long> dir = fetch(nameDirectory_recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Long recid = dir.get(name);
 | 
				
			||||||
 | 
					            if(recid == null) throw new IOException("Collection not found");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Object o = fetch(recid);
 | 
				
			||||||
 | 
					            //we can not use O instance since it is not correctly initialized
 | 
				
			||||||
 | 
					            if(o instanceof LinkedList2){
 | 
				
			||||||
 | 
					                LinkedList2 l = (LinkedList2) o;
 | 
				
			||||||
 | 
					                l.clear();
 | 
				
			||||||
 | 
					                delete(l.rootRecid);
 | 
				
			||||||
 | 
					            }else if(o instanceof BTree){
 | 
				
			||||||
 | 
					                ((BTree) o).clear();
 | 
				
			||||||
 | 
					            } else if( o instanceof  HTree){
 | 
				
			||||||
 | 
					                HTree t = (HTree) o;
 | 
				
			||||||
 | 
					                t.clear();
 | 
				
			||||||
 | 
					                HTreeDirectory n = (HTreeDirectory) fetch(t.rootRecid,t.SERIALIZER);
 | 
				
			||||||
 | 
					                n.deleteAllChildren();
 | 
				
			||||||
 | 
					                delete(t.rootRecid);
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					                throw new InternalError("unknown collection type: "+(o==null?null:o.getClass()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            delete(recid);
 | 
				
			||||||
 | 
					            collections.remove(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            dir.remove(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }catch(IOException e){
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** we need to set reference to this DB instance, so serializer needs to be here*/
 | 
				
			||||||
 | 
					    final Serializer<Serialization> defaultSerializationSerializer = new Serializer<Serialization>(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void serialize(DataOutput out, Serialization obj) throws IOException {
 | 
				
			||||||
 | 
					            LongPacker.packLong(out,obj.serialClassInfoRecid);
 | 
				
			||||||
 | 
					            SerialClassInfo.serializer.serialize(out,obj.registered);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Serialization deserialize(DataInput in) throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					            final long recid = LongPacker.unpackLong(in);
 | 
				
			||||||
 | 
					            final ArrayList<SerialClassInfo.ClassInfo> classes = SerialClassInfo.serializer.deserialize(in);
 | 
				
			||||||
 | 
					            return new Serialization(DBAbstract.this,recid,classes);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized Serializer defaultSerializer() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            long serialClassInfoRecid = getRoot(SERIAL_CLASS_INFO_RECID_ROOT);
 | 
				
			||||||
 | 
					            if (serialClassInfoRecid == 0) {
 | 
				
			||||||
 | 
					                //allocate new recid 
 | 
				
			||||||
 | 
					                serialClassInfoRecid = insert(null,Utils.NULL_SERIALIZER,false);
 | 
				
			||||||
 | 
					                //and insert new serializer
 | 
				
			||||||
 | 
					                Serialization ser = new Serialization(this,serialClassInfoRecid,new ArrayList<SerialClassInfo.ClassInfo>());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                update(serialClassInfoRecid,ser, defaultSerializationSerializer);
 | 
				
			||||||
 | 
					                setRoot(SERIAL_CLASS_INFO_RECID_ROOT, serialClassInfoRecid);
 | 
				
			||||||
 | 
					                return ser;
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					                return fetch(serialClassInfoRecid,defaultSerializationSerializer);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final protected void checkNotClosed(){
 | 
				
			||||||
 | 
					        if(isClosed()) throw new IllegalStateException("db was closed");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected abstract void setRoot(byte root, long recid);
 | 
				
			||||||
 | 
					    protected abstract long getRoot(byte root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public long collectionSize(Object collection){
 | 
				
			||||||
 | 
					        if(collection instanceof BTreeMap){
 | 
				
			||||||
 | 
					            BTreeMap t = (BTreeMap) collection;
 | 
				
			||||||
 | 
					            if(t.fromKey!=null|| t.toKey!=null) throw new IllegalArgumentException("collectionSize does not work on BTree submap");
 | 
				
			||||||
 | 
					            return t.tree._entries;
 | 
				
			||||||
 | 
					        }else if(collection instanceof  HTree){
 | 
				
			||||||
 | 
					            return ((HTree)collection).getRoot().size;
 | 
				
			||||||
 | 
					        }else if(collection instanceof  HTreeSet){
 | 
				
			||||||
 | 
					            return collectionSize(((HTreeSet) collection).map);
 | 
				
			||||||
 | 
					        }else if(collection instanceof  BTreeSet){
 | 
				
			||||||
 | 
					            return collectionSize(((BTreeSet) collection).map);
 | 
				
			||||||
 | 
					        }else if(collection instanceof LinkedList2){
 | 
				
			||||||
 | 
					            return ((LinkedList2)collection).getRoot().size;
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Not JDBM collection");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void addShutdownHook(){
 | 
				
			||||||
 | 
					        if(shutdownCloseThread!=null){
 | 
				
			||||||
 | 
					            shutdownCloseThread = new ShutdownCloseThread();
 | 
				
			||||||
 | 
					            Runtime.getRuntime().addShutdownHook(shutdownCloseThread);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void close(){
 | 
				
			||||||
 | 
					        if(shutdownCloseThread!=null){
 | 
				
			||||||
 | 
					            Runtime.getRuntime().removeShutdownHook(shutdownCloseThread);
 | 
				
			||||||
 | 
					            shutdownCloseThread.dbToClose = null;
 | 
				
			||||||
 | 
					            shutdownCloseThread = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ShutdownCloseThread shutdownCloseThread = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static class ShutdownCloseThread extends Thread{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DBAbstract dbToClose = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ShutdownCloseThread(){
 | 
				
			||||||
 | 
					            super("JDBM shutdown");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void run(){
 | 
				
			||||||
 | 
					            if(dbToClose!=null && !dbToClose.isClosed()){
 | 
				
			||||||
 | 
					                dbToClose.shutdownCloseThread = null;
 | 
				
			||||||
 | 
					                dbToClose.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public void   rollback() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					        for(WeakReference<Object> o:collections.values()){
 | 
				
			||||||
 | 
					            Object c =  o.get();
 | 
				
			||||||
 | 
					            if(c != null && c instanceof BTreeMap){
 | 
				
			||||||
 | 
					                //reload tree
 | 
				
			||||||
 | 
					                BTreeMap m = (BTreeMap) c;
 | 
				
			||||||
 | 
					                m.tree = fetch(m.tree.getRecid());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(c != null && c instanceof BTreeSet){
 | 
				
			||||||
 | 
					                //reload tree
 | 
				
			||||||
 | 
					                BTreeSet m = (BTreeSet) c;
 | 
				
			||||||
 | 
					                m.map.tree = fetch(m.map.tree.getRecid());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,162 @@
 | 
				
			|||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.crypto.Cipher;
 | 
				
			||||||
 | 
					import java.io.IOError;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Comparator;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract class with common cache functionality
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					abstract class DBCache extends DBStore{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static final int NUM_OF_DIRTY_RECORDS_BEFORE_AUTOCOMIT = 1024;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static final byte NONE = 1;
 | 
				
			||||||
 | 
					    static final byte MRU = 2;
 | 
				
			||||||
 | 
					    static final byte WEAK = 3;
 | 
				
			||||||
 | 
					    static final byte SOFT = 4;
 | 
				
			||||||
 | 
					    static final byte HARD = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static final class DirtyCacheEntry {
 | 
				
			||||||
 | 
					        long _recid; //TODO recid is already part of _hashDirties, so this field could be removed to save memory
 | 
				
			||||||
 | 
					        Object _obj;
 | 
				
			||||||
 | 
					        Serializer _serializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Dirty status of _hash CacheEntry Values
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    final protected LongHashMap<DirtyCacheEntry> _hashDirties = new LongHashMap<DirtyCacheEntry>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Serializer cachedDefaultSerializer = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Construct a CacheRecordManager wrapping another DB and
 | 
				
			||||||
 | 
					     * using a given cache policy.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBCache(String filename, boolean readonly, boolean transactionDisabled,
 | 
				
			||||||
 | 
					                      Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
 | 
				
			||||||
 | 
					                     boolean deleteFilesAfterClose,boolean lockingDisabled){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(filename, readonly, transactionDisabled,
 | 
				
			||||||
 | 
					                cipherIn, cipherOut, useRandomAccessFile,
 | 
				
			||||||
 | 
					                deleteFilesAfterClose,lockingDisabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized  Serializer defaultSerializer(){
 | 
				
			||||||
 | 
					        if(cachedDefaultSerializer==null)
 | 
				
			||||||
 | 
					            cachedDefaultSerializer = super.defaultSerializer();
 | 
				
			||||||
 | 
					        return cachedDefaultSerializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    boolean needsAutoCommit() {
 | 
				
			||||||
 | 
					        return super.needsAutoCommit()||
 | 
				
			||||||
 | 
					                (transactionsDisabled &&  !commitInProgress && _hashDirties.size() > NUM_OF_DIRTY_RECORDS_BEFORE_AUTOCOMIT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> long insert(final A obj, final Serializer<A> serializer, final boolean disableCache)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(super.needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(disableCache)
 | 
				
			||||||
 | 
					            return super.insert(obj, serializer, disableCache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //prealocate recid so we have something to return
 | 
				
			||||||
 | 
					        final long recid = super.insert(PREALOCATE_OBJ, null, disableCache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//        super.update(recid, obj,serializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//        return super.insert(obj,serializer,disableCache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //and create new dirty record for future update
 | 
				
			||||||
 | 
					        final DirtyCacheEntry e = new DirtyCacheEntry();
 | 
				
			||||||
 | 
					        e._recid = recid;
 | 
				
			||||||
 | 
					        e._obj = obj;
 | 
				
			||||||
 | 
					        e._serializer = serializer;
 | 
				
			||||||
 | 
					        _hashDirties.put(recid,e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return recid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void commit() {
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            commitInProgress = true;
 | 
				
			||||||
 | 
					            updateCacheEntries();
 | 
				
			||||||
 | 
					            super.commit();
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            commitInProgress = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized  void rollback(){
 | 
				
			||||||
 | 
					        cachedDefaultSerializer = null;
 | 
				
			||||||
 | 
					        _hashDirties.clear();
 | 
				
			||||||
 | 
					        super.rollback();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static final Comparator<DirtyCacheEntry> DIRTY_COMPARATOR = new Comparator<DirtyCacheEntry>() {
 | 
				
			||||||
 | 
					        final public int compare(DirtyCacheEntry o1, DirtyCacheEntry o2) {
 | 
				
			||||||
 | 
					            return (int) (o1._recid - o2._recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update all dirty cache objects to the underlying DB.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void updateCacheEntries() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            synchronized(_hashDirties){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while(!_hashDirties.isEmpty()){
 | 
				
			||||||
 | 
					                    //make defensive copy of values as _db.update() may trigger changes in db
 | 
				
			||||||
 | 
					                    //and this would modify dirties again
 | 
				
			||||||
 | 
					                    DirtyCacheEntry[] vals = new DirtyCacheEntry[_hashDirties.size()];
 | 
				
			||||||
 | 
					                    Iterator<DirtyCacheEntry> iter = _hashDirties.valuesIterator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for(int i = 0;i<vals.length;i++){
 | 
				
			||||||
 | 
					                        vals[i] = iter.next();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    iter = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    java.util.Arrays.sort(vals,DIRTY_COMPARATOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    for(int i = 0;i<vals.length;i++){
 | 
				
			||||||
 | 
					                        final DirtyCacheEntry entry = vals[i];
 | 
				
			||||||
 | 
					                        vals[i] = null;
 | 
				
			||||||
 | 
					                        super.update(entry._recid, entry._obj, entry._serializer);
 | 
				
			||||||
 | 
					                        _hashDirties.remove(entry._recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //update may have triggered more records to be added into dirties, so repeat until all records are written.
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,350 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.crypto.Cipher;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A DB wrapping and caching another DB.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 * @author Cees de Groot
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * TODO add 'cache miss' statistics
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class DBCacheMRU
 | 
				
			||||||
 | 
					        extends DBCache {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final boolean debug = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Cached object hashtable
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected LongHashMap<CacheEntry> _hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Maximum number of objects in the cache.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected int _max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Beginning of linked-list of cache elements.  First entry is element
 | 
				
			||||||
 | 
					     * which has been used least recently.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected CacheEntry _first;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * End of linked-list of cache elements.  Last entry is element
 | 
				
			||||||
 | 
					     * which has been used most recently.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected CacheEntry _last;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Construct a CacheRecordManager wrapping another DB and
 | 
				
			||||||
 | 
					     * using a given cache policy.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBCacheMRU(String filename, boolean readonly, boolean transactionDisabled,
 | 
				
			||||||
 | 
					                      Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
 | 
				
			||||||
 | 
					                     boolean deleteFilesAfterClose, int cacheMaxRecords, boolean lockingDisabled)  {
 | 
				
			||||||
 | 
					        super(filename, readonly, transactionDisabled,
 | 
				
			||||||
 | 
					            cipherIn, cipherOut, useRandomAccessFile,
 | 
				
			||||||
 | 
					            deleteFilesAfterClose,lockingDisabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _hash = new LongHashMap<CacheEntry>(cacheMaxRecords);
 | 
				
			||||||
 | 
					        _max = cacheMaxRecords;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (disableCache)
 | 
				
			||||||
 | 
					            return super.fetch(recid, serializer, disableCache);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return fetch(recid, serializer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void delete(long recid)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super.delete(recid);
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            CacheEntry entry = _hash.get(recid);
 | 
				
			||||||
 | 
					            if (entry != null) {
 | 
				
			||||||
 | 
					                removeEntry(entry);
 | 
				
			||||||
 | 
					                _hash.remove(entry._recid);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _hashDirties.remove(recid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(super.needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> void update(final long recid, final A obj, final Serializer<A> serializer) throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //remove entry if it already exists
 | 
				
			||||||
 | 
					            CacheEntry entry = cacheGet(recid);
 | 
				
			||||||
 | 
					            if (entry != null) {
 | 
				
			||||||
 | 
					                _hash.remove(recid);
 | 
				
			||||||
 | 
					                removeEntry(entry);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //check if entry is in dirties, in this case just update its object
 | 
				
			||||||
 | 
					            DirtyCacheEntry e = _hashDirties.get(recid);
 | 
				
			||||||
 | 
					            if(e!=null){
 | 
				
			||||||
 | 
					                if(recid!=e._recid) throw new Error();
 | 
				
			||||||
 | 
					                e._obj = obj;
 | 
				
			||||||
 | 
					                e._serializer = serializer;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            //create new dirty entry
 | 
				
			||||||
 | 
					            e = new DirtyCacheEntry();
 | 
				
			||||||
 | 
					            e._recid = recid;
 | 
				
			||||||
 | 
					            e._obj = obj;
 | 
				
			||||||
 | 
					            e._serializer = serializer;
 | 
				
			||||||
 | 
					            _hashDirties.put(recid,e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(super.needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> A fetch(long recid, Serializer<A> serializer)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final CacheEntry entry = cacheGet(recid);
 | 
				
			||||||
 | 
					        if (entry != null) {
 | 
				
			||||||
 | 
					            return (A) entry._obj;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //check dirties
 | 
				
			||||||
 | 
					        final DirtyCacheEntry entry2 = _hashDirties.get(recid);
 | 
				
			||||||
 | 
					        if(entry2!=null){
 | 
				
			||||||
 | 
					            return (A) entry2._obj;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        A value = super.fetch(recid, serializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(super.needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //put record into MRU cache
 | 
				
			||||||
 | 
					        cachePut(recid, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void close() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(isClosed())
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updateCacheEntries();
 | 
				
			||||||
 | 
					        super.close();
 | 
				
			||||||
 | 
					        _hash = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void rollback() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // discard all cache entries since we don't know which entries
 | 
				
			||||||
 | 
					        // where part of the transaction
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            _hash.clear();
 | 
				
			||||||
 | 
					            _first = null;
 | 
				
			||||||
 | 
					            _last = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super.rollback();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Obtain an object in the cache
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected CacheEntry cacheGet(long key) {
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            CacheEntry entry = _hash.get(key);
 | 
				
			||||||
 | 
					            if ( entry != null && _last != entry) {
 | 
				
			||||||
 | 
					                //touch entry
 | 
				
			||||||
 | 
					                removeEntry(entry);
 | 
				
			||||||
 | 
					                addEntry(entry);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return entry;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Place an object in the cache.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void cachePut(final long recid, final Object value) throws IOException {
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            CacheEntry entry = _hash.get(recid);
 | 
				
			||||||
 | 
					            if (entry != null) {
 | 
				
			||||||
 | 
					                entry._obj = value;
 | 
				
			||||||
 | 
					                //touch entry
 | 
				
			||||||
 | 
					                if (_last != entry) {
 | 
				
			||||||
 | 
					                    removeEntry(entry);
 | 
				
			||||||
 | 
					                    addEntry(entry);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (_hash.size() >= _max) {
 | 
				
			||||||
 | 
					                    // purge and recycle entry
 | 
				
			||||||
 | 
					                    entry = purgeEntry();
 | 
				
			||||||
 | 
					                    entry._recid = recid;
 | 
				
			||||||
 | 
					                    entry._obj = value;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    entry = new CacheEntry(recid, value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                addEntry(entry);
 | 
				
			||||||
 | 
					                _hash.put(entry._recid, entry);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Add a CacheEntry.  Entry goes at the end of the list.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void addEntry(CacheEntry entry) {
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            if (_first == null) {
 | 
				
			||||||
 | 
					                _first = entry;
 | 
				
			||||||
 | 
					                _last = entry;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                _last._next = entry;
 | 
				
			||||||
 | 
					                entry._previous = _last;
 | 
				
			||||||
 | 
					                _last = entry;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Remove a CacheEntry from linked list
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void removeEntry(CacheEntry entry) {
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            if (entry == _first) {
 | 
				
			||||||
 | 
					                _first = entry._next;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (_last == entry) {
 | 
				
			||||||
 | 
					                _last = entry._previous;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            CacheEntry previous = entry._previous;
 | 
				
			||||||
 | 
					            CacheEntry next = entry._next;
 | 
				
			||||||
 | 
					            if (previous != null) {
 | 
				
			||||||
 | 
					                previous._next = next;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (next != null) {
 | 
				
			||||||
 | 
					                next._previous = previous;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            entry._previous = null;
 | 
				
			||||||
 | 
					            entry._next = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Purge least recently used object from the cache
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return recyclable CacheEntry
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected CacheEntry purgeEntry() {
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            CacheEntry entry = _first;
 | 
				
			||||||
 | 
					            if (entry == null)
 | 
				
			||||||
 | 
					                return new CacheEntry(-1, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            removeEntry(entry);
 | 
				
			||||||
 | 
					            _hash.remove(entry._recid);
 | 
				
			||||||
 | 
					            entry._obj = null;
 | 
				
			||||||
 | 
					            return entry;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    static final class CacheEntry {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected long _recid;
 | 
				
			||||||
 | 
					        protected Object _obj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected CacheEntry _previous;
 | 
				
			||||||
 | 
					        protected CacheEntry _next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CacheEntry(long recid, Object obj) {
 | 
				
			||||||
 | 
					            _recid = recid;
 | 
				
			||||||
 | 
					            _obj = obj;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clearCache() {
 | 
				
			||||||
 | 
					        if(debug)
 | 
				
			||||||
 | 
					            System.err.println("DBCache: Clear cache");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // discard all cache entries since we don't know which entries
 | 
				
			||||||
 | 
					        // where part of the transaction
 | 
				
			||||||
 | 
					        synchronized (_hash){
 | 
				
			||||||
 | 
					            _hash.clear();
 | 
				
			||||||
 | 
					            _first = null;
 | 
				
			||||||
 | 
					            _last = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //clear dirties
 | 
				
			||||||
 | 
					            updateCacheEntries();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,401 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.crypto.Cipher;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.lang.ref.ReferenceQueue;
 | 
				
			||||||
 | 
					import java.lang.ref.SoftReference;
 | 
				
			||||||
 | 
					import java.lang.ref.WeakReference;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A DB wrapping and caching another DB.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 * @author Cees de Groot
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * TODO add 'cache miss' statistics
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DBCacheRef
 | 
				
			||||||
 | 
					        extends DBCache {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final boolean debug = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * If Soft Cache is enabled, this contains softly referenced clean entries.
 | 
				
			||||||
 | 
					     * If entry became dirty, it is moved to _hash with limited size.
 | 
				
			||||||
 | 
					     * This map is accessed from SoftCache Disposer thread, so all access must be
 | 
				
			||||||
 | 
					     * synchronized
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected LongHashMap _softHash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reference queue used to collect Soft Cache entries
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected ReferenceQueue<ReferenceCacheEntry> _refQueue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Thread in which Soft Cache references are disposed
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected Thread _softRefThread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static AtomicInteger threadCounter = new AtomicInteger(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** counter which counts number of insert since last 'action'*/
 | 
				
			||||||
 | 
					    protected int insertCounter = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final boolean _autoClearReferenceCacheOnLowMem;
 | 
				
			||||||
 | 
					    private final byte _cacheType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Construct a CacheRecordManager wrapping another DB and
 | 
				
			||||||
 | 
					     * using a given cache policy.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBCacheRef(String filename, boolean readonly, boolean transactionDisabled,
 | 
				
			||||||
 | 
					                      Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
 | 
				
			||||||
 | 
					                      boolean deleteFilesAfterClose,
 | 
				
			||||||
 | 
					                      byte cacheType, boolean cacheAutoClearOnLowMem, boolean lockingDisabled) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(filename, readonly, transactionDisabled,
 | 
				
			||||||
 | 
					                cipherIn, cipherOut, useRandomAccessFile,
 | 
				
			||||||
 | 
					                deleteFilesAfterClose, lockingDisabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this._cacheType = cacheType;
 | 
				
			||||||
 | 
					        _autoClearReferenceCacheOnLowMem = cacheAutoClearOnLowMem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _softHash = new LongHashMap<ReferenceCacheEntry>();
 | 
				
			||||||
 | 
					        _refQueue = new ReferenceQueue<ReferenceCacheEntry>();
 | 
				
			||||||
 | 
					        _softRefThread = new Thread(
 | 
				
			||||||
 | 
					                    new SoftRunnable(this, _refQueue),
 | 
				
			||||||
 | 
					                    "JDBM Soft Cache Disposer " + (threadCounter.incrementAndGet()));
 | 
				
			||||||
 | 
					        _softRefThread.setDaemon(true);
 | 
				
			||||||
 | 
					        _softRefThread.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void clearCacheIfLowOnMem() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        insertCounter = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!_autoClearReferenceCacheOnLowMem)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Runtime r = Runtime.getRuntime();
 | 
				
			||||||
 | 
					        long max = r.maxMemory();
 | 
				
			||||||
 | 
					        if(max == Long.MAX_VALUE)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        double free = r.freeMemory();
 | 
				
			||||||
 | 
					        double total = r.totalMemory();
 | 
				
			||||||
 | 
					        //We believe that free refers to total not max.
 | 
				
			||||||
 | 
					        //Increasing heap size to max would increase to max
 | 
				
			||||||
 | 
					        free = free + (max-total);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(debug)
 | 
				
			||||||
 | 
					            System.err.println("DBCache: freemem = " +free + " = "+(free/max)+"%");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(free<1e7 || free*4 <max)
 | 
				
			||||||
 | 
					            clearCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (disableCache)
 | 
				
			||||||
 | 
					            return super.fetch(recid, serializer, disableCache);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return fetch(recid, serializer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void delete(long recid)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super.delete(recid);
 | 
				
			||||||
 | 
					        synchronized (_hashDirties){
 | 
				
			||||||
 | 
					            _hashDirties.remove(recid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        synchronized (_softHash) {
 | 
				
			||||||
 | 
					            Object e = _softHash.remove(recid);
 | 
				
			||||||
 | 
					            if (e != null && e instanceof ReferenceCacheEntry) {
 | 
				
			||||||
 | 
					                ((ReferenceCacheEntry)e).clear();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> void update(final long recid, A obj, Serializer<A> serializer) throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (_softHash) {
 | 
				
			||||||
 | 
					            //soft cache can not contain dirty objects
 | 
				
			||||||
 | 
					            Object e = _softHash.remove(recid);
 | 
				
			||||||
 | 
					            if (e != null && e instanceof ReferenceCacheEntry) {
 | 
				
			||||||
 | 
					                ((ReferenceCacheEntry)e).clear();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        synchronized (_hashDirties){
 | 
				
			||||||
 | 
					            //put into dirty cache
 | 
				
			||||||
 | 
					            final DirtyCacheEntry e = new DirtyCacheEntry();
 | 
				
			||||||
 | 
					            e._recid = recid;
 | 
				
			||||||
 | 
					            e._obj = obj;
 | 
				
			||||||
 | 
					            e._serializer = serializer;
 | 
				
			||||||
 | 
					            _hashDirties.put(recid,e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> A fetch(long recid, Serializer<A> serializer)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (_softHash) {
 | 
				
			||||||
 | 
					            Object e = _softHash.get(recid);
 | 
				
			||||||
 | 
					            if (e != null) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(e instanceof ReferenceCacheEntry)
 | 
				
			||||||
 | 
					                    e = ((ReferenceCacheEntry)e).get();
 | 
				
			||||||
 | 
					                if (e != null) {
 | 
				
			||||||
 | 
					                    return (A) e;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (_hashDirties){
 | 
				
			||||||
 | 
					            DirtyCacheEntry e2 = _hashDirties.get(recid);
 | 
				
			||||||
 | 
					            if(e2!=null){
 | 
				
			||||||
 | 
					                return (A) e2._obj;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        A value = super.fetch(recid, serializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(needsAutoCommit())
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           synchronized (_softHash) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (_cacheType == SOFT)
 | 
				
			||||||
 | 
					                        _softHash.put(recid, new SoftCacheEntry(recid, value, _refQueue));
 | 
				
			||||||
 | 
					                    else if (_cacheType == WEAK)
 | 
				
			||||||
 | 
					                        _softHash.put(recid, new WeakCacheEntry(recid, value, _refQueue));
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        _softHash.put(recid,value);
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void close() {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updateCacheEntries();
 | 
				
			||||||
 | 
					        super.close();
 | 
				
			||||||
 | 
					        _softHash = null;
 | 
				
			||||||
 | 
					        _softRefThread.interrupt();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void rollback() {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // discard all cache entries since we don't know which entries
 | 
				
			||||||
 | 
					        // where part of the transaction
 | 
				
			||||||
 | 
					        synchronized (_softHash) {
 | 
				
			||||||
 | 
					            Iterator<ReferenceCacheEntry> iter = _softHash.valuesIterator();
 | 
				
			||||||
 | 
					            while (iter.hasNext()) {
 | 
				
			||||||
 | 
					                ReferenceCacheEntry e = iter.next();
 | 
				
			||||||
 | 
					                e.clear();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _softHash.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super.rollback();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
						protected boolean isCacheEntryDirty(DirtyCacheEntry entry) {
 | 
				
			||||||
 | 
							return _hashDirties.get(entry._recid) != null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected void setCacheEntryDirty(DirtyCacheEntry entry, boolean dirty) {
 | 
				
			||||||
 | 
							if (dirty) {
 | 
				
			||||||
 | 
								_hashDirties.put(entry._recid, entry);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_hashDirties.remove(entry._recid);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    interface ReferenceCacheEntry {
 | 
				
			||||||
 | 
					        long getRecid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Object get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    static final class SoftCacheEntry extends SoftReference implements ReferenceCacheEntry {
 | 
				
			||||||
 | 
					        protected final long _recid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public long getRecid() {
 | 
				
			||||||
 | 
					            return _recid;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SoftCacheEntry(long recid, Object obj, ReferenceQueue queue) {
 | 
				
			||||||
 | 
					            super(obj, queue);
 | 
				
			||||||
 | 
					            _recid = recid;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    static final class WeakCacheEntry extends WeakReference implements ReferenceCacheEntry {
 | 
				
			||||||
 | 
					        protected final long _recid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public long getRecid() {
 | 
				
			||||||
 | 
					            return _recid;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        WeakCacheEntry(long recid, Object obj, ReferenceQueue queue) {
 | 
				
			||||||
 | 
					            super(obj, queue);
 | 
				
			||||||
 | 
					            _recid = recid;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Runs in separate thread and cleans SoftCache.
 | 
				
			||||||
 | 
					     * Runnable auto exists when CacheRecordManager is GCed
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @author Jan Kotek
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final class SoftRunnable implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private ReferenceQueue<ReferenceCacheEntry> entryQueue;
 | 
				
			||||||
 | 
					        private WeakReference<DBCacheRef> db2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public SoftRunnable(DBCacheRef db,
 | 
				
			||||||
 | 
					                            ReferenceQueue<ReferenceCacheEntry> entryQueue) {
 | 
				
			||||||
 | 
					            this.db2 = new WeakReference<DBCacheRef>(db);
 | 
				
			||||||
 | 
					            this.entryQueue = entryQueue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            while (true) try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //collect next item from cache,
 | 
				
			||||||
 | 
					                //limit 10000 ms is to keep periodically checking if db was GCed
 | 
				
			||||||
 | 
					                ReferenceCacheEntry e = (ReferenceCacheEntry) entryQueue.remove(10000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //check if  db was GCed, cancel in that case
 | 
				
			||||||
 | 
					                DBCacheRef db = db2.get();
 | 
				
			||||||
 | 
					                if (db == null)
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (e != null) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    synchronized (db._softHash) {
 | 
				
			||||||
 | 
					                        int counter = 0;
 | 
				
			||||||
 | 
					                        while (e != null) {
 | 
				
			||||||
 | 
					                            db._softHash.remove(e.getRecid());
 | 
				
			||||||
 | 
					                            e = (SoftCacheEntry) entryQueue.poll();
 | 
				
			||||||
 | 
					                            if(debug)
 | 
				
			||||||
 | 
					                                counter++;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if(debug)
 | 
				
			||||||
 | 
					                            System.err.println("DBCache: "+counter+" objects released from ref cache.");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }else{
 | 
				
			||||||
 | 
					                    //check memory consumption every 10 seconds
 | 
				
			||||||
 | 
					                    db.clearCacheIfLowOnMem();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            } catch (Throwable e) {
 | 
				
			||||||
 | 
					                //this thread must keep spinning,
 | 
				
			||||||
 | 
					                //otherwise SoftCacheEntries would not be disposed
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clearCache() {
 | 
				
			||||||
 | 
					        if(debug)
 | 
				
			||||||
 | 
					            System.err.println("DBCache: Clear cache");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (_softHash) {
 | 
				
			||||||
 | 
					            if(_cacheType!=HARD){
 | 
				
			||||||
 | 
					                Iterator<ReferenceCacheEntry> iter = _softHash.valuesIterator();
 | 
				
			||||||
 | 
					                while (iter.hasNext()) {
 | 
				
			||||||
 | 
					                    ReferenceCacheEntry e = iter.next();
 | 
				
			||||||
 | 
					                    e.clear();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _softHash.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,351 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.crypto.Cipher;
 | 
				
			||||||
 | 
					import javax.crypto.SecretKey;
 | 
				
			||||||
 | 
					import javax.crypto.SecretKeyFactory;
 | 
				
			||||||
 | 
					import javax.crypto.spec.IvParameterSpec;
 | 
				
			||||||
 | 
					import javax.crypto.spec.PBEKeySpec;
 | 
				
			||||||
 | 
					import javax.crypto.spec.SecretKeySpec;
 | 
				
			||||||
 | 
					import java.io.IOError;
 | 
				
			||||||
 | 
					import java.security.spec.KeySpec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class used to configure and create DB. It uses builder pattern.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DBMaker {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private byte cacheType = DBCacheRef.MRU;
 | 
				
			||||||
 | 
					    private int mruCacheSize = 2048;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String location = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean disableTransactions = false;
 | 
				
			||||||
 | 
					    private boolean lockingDisabled = false;
 | 
				
			||||||
 | 
					    private boolean readonly = false;
 | 
				
			||||||
 | 
					    private String password = null;
 | 
				
			||||||
 | 
					    private boolean useAES256Bit = true;
 | 
				
			||||||
 | 
					    private boolean useRandomAccessFile = false;
 | 
				
			||||||
 | 
					    private boolean autoClearRefCacheOnLowMem = true;
 | 
				
			||||||
 | 
					    private  boolean closeOnJVMExit = false;
 | 
				
			||||||
 | 
					    private  boolean deleteFilesAfterCloseFlag = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private DBMaker(){}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates new DBMaker and sets file to load data from.
 | 
				
			||||||
 | 
					     * @param file to load data from
 | 
				
			||||||
 | 
					     * @return new DBMaker
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static DBMaker openFile(String file){
 | 
				
			||||||
 | 
					        DBMaker m = new DBMaker();
 | 
				
			||||||
 | 
					        m.location = file;
 | 
				
			||||||
 | 
					        return m;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates new DBMaker which uses in memory store. Data will be lost after JVM exits.
 | 
				
			||||||
 | 
					     * @return new DBMaker
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static DBMaker openMemory(){
 | 
				
			||||||
 | 
					        return new DBMaker();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Open store in zip file
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param zip file
 | 
				
			||||||
 | 
					     * @return new DBMaker
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static DBMaker openZip(String zip) {
 | 
				
			||||||
 | 
					        DBMaker m = new DBMaker();
 | 
				
			||||||
 | 
					        m.location = "$$ZIP$$://"+zip;
 | 
				
			||||||
 | 
					        return m;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     static String  isZipFileLocation(String location){
 | 
				
			||||||
 | 
					         String match = "$$ZIP$$://";
 | 
				
			||||||
 | 
					         if( location.startsWith(match)){
 | 
				
			||||||
 | 
					             return location.substring(match.length());
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					         return null;
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Use WeakReference for cache.
 | 
				
			||||||
 | 
					     * This cache does not improve performance much,
 | 
				
			||||||
 | 
					     * but prevents JDBM from creating multiple instances of the same object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker enableWeakCache() {
 | 
				
			||||||
 | 
					        cacheType = DBCacheRef.WEAK;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Use SoftReference for cache.
 | 
				
			||||||
 | 
					     * This cache greatly improves performance if you have enoguth memory.
 | 
				
			||||||
 | 
					     * Instances in cache are Garbage Collected when memory gets low
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker enableSoftCache() {
 | 
				
			||||||
 | 
					        cacheType = DBCacheRef.SOFT;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Use hard reference for cache.
 | 
				
			||||||
 | 
					     * This greatly improves performance if there is enought memory
 | 
				
			||||||
 | 
					     * Hard cache has smaller memory overhead then Soft or Weak, because
 | 
				
			||||||
 | 
					     * reference objects and queue does not have to be maintained
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker enableHardCache() {
 | 
				
			||||||
 | 
					        cacheType = DBCacheRef.SOFT;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Use 'Most Recently Used' cache with limited size.
 | 
				
			||||||
 | 
					     * Oldest instances are released from cache when new instances are fetched.
 | 
				
			||||||
 | 
					     * This cache is not cleared by GC. Is good for systems with limited memory.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * Default size for MRU cache is 2048 records.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker enableMRUCache() {
 | 
				
			||||||
 | 
					        cacheType = DBCacheRef.MRU;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Sets 'Most Recently Used' cache size. This cache is activated by default with size 2048
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param cacheSize number of instances which will be kept in cache.
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker setMRUCacheSize(int cacheSize) {
 | 
				
			||||||
 | 
					        if (cacheSize < 0) throw new IllegalArgumentException("Cache size is smaller than zero");
 | 
				
			||||||
 | 
					        cacheType = DBCacheRef.MRU;
 | 
				
			||||||
 | 
					        mruCacheSize = cacheSize;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * If reference (soft,weak or hard) cache is enabled,
 | 
				
			||||||
 | 
					     * GC may not release references fast enough (or not at all in case of hard cache).
 | 
				
			||||||
 | 
					     * So JDBM periodically checks amount of free heap memory.
 | 
				
			||||||
 | 
					     * If free memory is less than 25% or 10MB,
 | 
				
			||||||
 | 
					     * JDBM completely clears its reference cache to prevent possible memory issues.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Calling this method disables auto cache clearing when mem is low.
 | 
				
			||||||
 | 
					     * And of course it can cause some out of memory exceptions.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker disableCacheAutoClear(){
 | 
				
			||||||
 | 
					        this.autoClearRefCacheOnLowMem = false;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Enabled storage encryption using AES cipher. JDBM supports both 128 bit and 256 bit encryption if JRE provides it.
 | 
				
			||||||
 | 
					     * There are some restrictions on AES 256 bit and not all JREs have it  by default.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * Storage can not be read (decrypted), unless the key is provided next time it is opened
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param password used to encrypt store
 | 
				
			||||||
 | 
					     * @param useAES256Bit if true strong AES 256 bit encryption is used. Otherwise more usual AES 128 bit is used.
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker enableEncryption(String password, boolean useAES256Bit) {
 | 
				
			||||||
 | 
					        this.password = password;
 | 
				
			||||||
 | 
					        this.useAES256Bit = useAES256Bit;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Make DB readonly.
 | 
				
			||||||
 | 
					     * Update/delete/insert operation will throw 'UnsupportedOperationException'
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker readonly() {
 | 
				
			||||||
 | 
					        readonly = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disable cache completely
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker disableCache() {
 | 
				
			||||||
 | 
					        cacheType = DBCacheRef.NONE;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Option to disable transaction (to increase performance at the cost of potential data loss).
 | 
				
			||||||
 | 
					     * Transactions are enabled by default
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * Switches off transactioning for the record manager. This means
 | 
				
			||||||
 | 
					     * that a) a transaction log is not kept, and b) writes aren't
 | 
				
			||||||
 | 
					     * synch'ed after every update. Writes are cached in memory and then flushed
 | 
				
			||||||
 | 
					     * to disk every N writes. You may also flush writes manually by calling commit().
 | 
				
			||||||
 | 
					     * This is useful when batch inserting into a new database.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * When using this, database must be properly closed before JVM shutdown.
 | 
				
			||||||
 | 
					     * Failing to do so may and WILL corrupt store.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker disableTransactions() {
 | 
				
			||||||
 | 
					        this.disableTransactions = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disable file system based locking (for file systems that do not support it).
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * Locking is not supported by many remote or distributed file systems; such
 | 
				
			||||||
 | 
					     * as Lustre and NFS. Attempts to perform locks will result in an 
 | 
				
			||||||
 | 
					     * IOException with the message "Function not implemented".
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * Disabling locking will avoid this issue, though of course it comes with
 | 
				
			||||||
 | 
					     * all the issues of uncontrolled file access.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker disableLocking(){
 | 
				
			||||||
 | 
					        this.lockingDisabled = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * By default JDBM uses mapped memory buffers to read from files.
 | 
				
			||||||
 | 
					     * But this may behave strangely on some platforms.
 | 
				
			||||||
 | 
					     * Safe alternative is to use old RandomAccessFile rather then mapped ByteBuffer.
 | 
				
			||||||
 | 
					     * There is typically slower (pages needs to be copyed into memory on every write).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker useRandomAccessFile(){
 | 
				
			||||||
 | 
					        this.useRandomAccessFile = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Registers shutdown hook and close database on JVM exit, if it was not already closed; 
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker closeOnExit(){
 | 
				
			||||||
 | 
					        this.closeOnJVMExit = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete all storage files after DB is closed
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return this builder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBMaker deleteFilesAfterClose(){
 | 
				
			||||||
 | 
					        this.deleteFilesAfterCloseFlag = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Opens database with settings earlier specified in this builder.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return new DB
 | 
				
			||||||
 | 
					     * @throws java.io.IOError if db could not be opened
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DB make() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Cipher cipherIn = null;
 | 
				
			||||||
 | 
					        Cipher cipherOut = null;
 | 
				
			||||||
 | 
					        if (password != null) try {
 | 
				
			||||||
 | 
					            //initialize ciphers
 | 
				
			||||||
 | 
					            //this code comes from stack owerflow
 | 
				
			||||||
 | 
					            //http://stackoverflow.com/questions/992019/java-256bit-aes-encryption/992413#992413
 | 
				
			||||||
 | 
					            byte[] salt = new byte[]{3, -34, 123, 53, 78, 121, -12, -1, 45, -12, -48, 89, 11, 100, 99, 8};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
 | 
				
			||||||
 | 
					            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1024, useAES256Bit?256:128);
 | 
				
			||||||
 | 
					            SecretKey tmp = factory.generateSecret(spec);
 | 
				
			||||||
 | 
					            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            String transform = "AES/CBC/NoPadding";
 | 
				
			||||||
 | 
					            IvParameterSpec params = new IvParameterSpec(salt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cipherIn = Cipher.getInstance(transform);
 | 
				
			||||||
 | 
					            cipherIn.init(Cipher.ENCRYPT_MODE, secret, params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cipherOut = Cipher.getInstance(transform);
 | 
				
			||||||
 | 
					            cipherOut.init(Cipher.DECRYPT_MODE, secret, params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //sanity check, try with page size
 | 
				
			||||||
 | 
					            byte[] data = new byte[Storage.PAGE_SIZE];
 | 
				
			||||||
 | 
					            byte[] encData = cipherIn.doFinal(data);
 | 
				
			||||||
 | 
					            if (encData.length != Storage.PAGE_SIZE)
 | 
				
			||||||
 | 
					                throw new Error("Page size changed after encryption, make sure you use '/NoPadding'");
 | 
				
			||||||
 | 
					            byte[] data2 = cipherOut.doFinal(encData);
 | 
				
			||||||
 | 
					            for (int i = 0; i < data.length; i++) {
 | 
				
			||||||
 | 
					                if (data[i] != data2[i]) throw new Error("Encryption provided by JRE does not work");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DBAbstract db = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (cacheType == DBCacheRef.MRU){
 | 
				
			||||||
 | 
					          db = new DBCacheMRU(location, readonly, disableTransactions, cipherIn, cipherOut,useRandomAccessFile,deleteFilesAfterCloseFlag, mruCacheSize,lockingDisabled);
 | 
				
			||||||
 | 
					        }else if( cacheType == DBCacheRef.SOFT || cacheType == DBCacheRef.HARD || cacheType == DBCacheRef.WEAK) {
 | 
				
			||||||
 | 
					            db = new DBCacheRef(location, readonly, disableTransactions, cipherIn, cipherOut,useRandomAccessFile,deleteFilesAfterCloseFlag, cacheType,autoClearRefCacheOnLowMem,lockingDisabled);
 | 
				
			||||||
 | 
					        } else if (cacheType == DBCacheRef.NONE) {
 | 
				
			||||||
 | 
					            db = new DBStore(location, readonly, disableTransactions, cipherIn, cipherOut,useRandomAccessFile,deleteFilesAfterCloseFlag,lockingDisabled);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Unknown cache type: " + cacheType);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(closeOnJVMExit){
 | 
				
			||||||
 | 
					            db.addShutdownHook();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return db;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,928 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.crypto.Cipher;
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.zip.ZipEntry;
 | 
				
			||||||
 | 
					import java.util.zip.ZipOutputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This class manages records, which are uninterpreted blobs of data. The
 | 
				
			||||||
 | 
					 * set of operations is simple and straightforward: you communicate with
 | 
				
			||||||
 | 
					 * the class using long "rowids" and byte[] data blocks. Rowids are returned
 | 
				
			||||||
 | 
					 * on inserts and you can stash them away someplace safe to be able to get
 | 
				
			||||||
 | 
					 * back to them. Data blocks can be as long as you wish, and may have
 | 
				
			||||||
 | 
					 * lengths different from the original when updating.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * Operations are synchronized, so that only one of them will happen
 | 
				
			||||||
 | 
					 * concurrently even if you hammer away from multiple threads. Operations
 | 
				
			||||||
 | 
					 * are made atomic by keeping a transaction log which is recovered after
 | 
				
			||||||
 | 
					 * a crash, so the operations specified by this interface all have ACID
 | 
				
			||||||
 | 
					 * properties.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * You identify a file by just the name. The package attaches <tt>.db</tt>
 | 
				
			||||||
 | 
					 * for the database file, and <tt>.lg</tt> for the transaction log. The
 | 
				
			||||||
 | 
					 * transaction log is synchronized regularly and then restarted, so don't
 | 
				
			||||||
 | 
					 * worry if you see the size going up and down.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 * @author Cees de Groot
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class DBStore
 | 
				
			||||||
 | 
					        extends DBAbstract {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Version of storage. It should be safe to open lower versions, but engine should throw exception
 | 
				
			||||||
 | 
					     * while opening new versions (as it contains unsupported features or serialization)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final long STORE_FORMAT_VERSION = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Underlying file for store records.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private PageFile _file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Page manager for physical manager.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private PageManager _pageman;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Physical row identifier manager.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private PhysicalRowIdManager _physMgr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Indicated that store is opened for readonly operations
 | 
				
			||||||
 | 
					     * If true, store will throw UnsupportedOperationException when update/insert/delete operation is called
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private final boolean readonly;
 | 
				
			||||||
 | 
					    final boolean transactionsDisabled;
 | 
				
			||||||
 | 
					    private final boolean deleteFilesAfterClose;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int AUTOCOMMIT_AFTER_N_PAGES = 1024 * 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean commitInProgress = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * cipher used for decryption, may be null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Cipher cipherOut;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * cipher used for encryption, may be null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Cipher cipherIn;
 | 
				
			||||||
 | 
					    private boolean useRandomAccessFile;
 | 
				
			||||||
 | 
					    private boolean lockingDisabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void checkCanWrite() {
 | 
				
			||||||
 | 
					        if (readonly)
 | 
				
			||||||
 | 
					            throw new UnsupportedOperationException("Could not write, store is opened as read-only");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Logigal to Physical row identifier manager.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private LogicalRowIdManager _logicMgr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Static debugging flag
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final boolean DEBUG = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    static final long PREALOCATE_PHYS_RECID = Short.MIN_VALUE;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    static final Object PREALOCATE_OBJ = new Object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final DataInputOutput buffer = new DataInputOutput();
 | 
				
			||||||
 | 
					    private boolean bufferInUse = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String _filename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DBStore(String filename, boolean readonly, boolean transactionDisabled, boolean lockingDisabled) throws IOException {
 | 
				
			||||||
 | 
					        this(filename, readonly, transactionDisabled, null, null, false,false,false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a record manager for the indicated file
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException when the file cannot be opened or is not
 | 
				
			||||||
 | 
					     *                     a valid file content-wise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DBStore(String filename, boolean readonly, boolean transactionDisabled,
 | 
				
			||||||
 | 
					                   Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
 | 
				
			||||||
 | 
					                   boolean deleteFilesAfterClose, boolean lockingDisabled){
 | 
				
			||||||
 | 
					        _filename = filename;
 | 
				
			||||||
 | 
					        this.readonly = readonly;
 | 
				
			||||||
 | 
					        this.transactionsDisabled = transactionDisabled;
 | 
				
			||||||
 | 
					        this.cipherIn = cipherIn;
 | 
				
			||||||
 | 
					        this.cipherOut = cipherOut;
 | 
				
			||||||
 | 
					        this.useRandomAccessFile = useRandomAccessFile;
 | 
				
			||||||
 | 
					        this.deleteFilesAfterClose = deleteFilesAfterClose;
 | 
				
			||||||
 | 
					        this.lockingDisabled = lockingDisabled;
 | 
				
			||||||
 | 
					        reopen();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void reopen()  {
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					        _file = new PageFile(_filename, readonly, transactionsDisabled, cipherIn, cipherOut,useRandomAccessFile,lockingDisabled);
 | 
				
			||||||
 | 
					        _pageman = new PageManager(_file);
 | 
				
			||||||
 | 
					        _physMgr = new PhysicalRowIdManager(_file, _pageman);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _logicMgr = new LogicalRowIdManager(_file, _pageman);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long versionNumber = getRoot(STORE_VERSION_NUMBER_ROOT);
 | 
				
			||||||
 | 
					        if (versionNumber > STORE_FORMAT_VERSION)
 | 
				
			||||||
 | 
					            throw new IOException("Unsupported version of store. Please update JDBM. Minimal supported ver:" + STORE_FORMAT_VERSION + ", store ver:" + versionNumber);
 | 
				
			||||||
 | 
					        if (!readonly)
 | 
				
			||||||
 | 
					            setRoot(STORE_VERSION_NUMBER_ROOT, STORE_FORMAT_VERSION);
 | 
				
			||||||
 | 
					        }catch(IOException e){
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes the record manager.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException when one of the underlying I/O operations fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void close() {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            super.close();
 | 
				
			||||||
 | 
					            _pageman.close();
 | 
				
			||||||
 | 
					            _file.close();
 | 
				
			||||||
 | 
					            if(deleteFilesAfterClose)
 | 
				
			||||||
 | 
					                _file.storage.deleteAllFiles();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            _pageman = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            _file = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isClosed() {
 | 
				
			||||||
 | 
					        return _pageman==null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> long insert(final A obj, final Serializer<A> serializer, final boolean disableCache)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					        checkCanWrite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (needsAutoCommit()) {
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (bufferInUse) {
 | 
				
			||||||
 | 
					            //current reusable buffer is in use, have to fallback into creating new instances
 | 
				
			||||||
 | 
					            DataInputOutput buffer2 = new DataInputOutput();
 | 
				
			||||||
 | 
					            return insert2(obj, serializer, buffer2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bufferInUse = true;
 | 
				
			||||||
 | 
					            return insert2(obj, serializer, buffer);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            bufferInUse = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean needsAutoCommit() {
 | 
				
			||||||
 | 
					        return  transactionsDisabled && !commitInProgress &&
 | 
				
			||||||
 | 
					                (_file.getDirtyPageCount() >= AUTOCOMMIT_AFTER_N_PAGES );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private <A> long insert2(A obj, Serializer<A> serializer, DataInputOutput buf)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        buf.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long physRowId;
 | 
				
			||||||
 | 
					        if(obj==PREALOCATE_OBJ){
 | 
				
			||||||
 | 
					            //if inserted record is  PREALOCATE_OBJ , it gets special handling.
 | 
				
			||||||
 | 
					            //it is inserted only into _logicMgr with special value to indicate null
 | 
				
			||||||
 | 
					            //this is used to preallocate recid for lazy inserts in cache
 | 
				
			||||||
 | 
					            physRowId = PREALOCATE_PHYS_RECID;
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					            serializer.serialize(buf, obj);
 | 
				
			||||||
 | 
					            if(buf.getPos()>RecordHeader.MAX_RECORD_SIZE){
 | 
				
			||||||
 | 
					                throw new IllegalArgumentException("Too big record. JDBM only supports record size up to: "+RecordHeader.MAX_RECORD_SIZE+" bytes. Record size was: "+buf.getPos());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            physRowId = _physMgr.insert(buf.getBuf(), 0, buf.getPos());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        final long recid = _logicMgr.insert(physRowId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (DEBUG) {
 | 
				
			||||||
 | 
					            System.out.println("BaseRecordManager.insert() recid " + recid + " length " + buf.getPos());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return compressRecid(recid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void delete(long logRowId)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					        checkCanWrite();
 | 
				
			||||||
 | 
					        if (logRowId <= 0) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'recid' is invalid: "
 | 
				
			||||||
 | 
					                    + logRowId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (needsAutoCommit()) {
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (DEBUG) {
 | 
				
			||||||
 | 
					            System.out.println("BaseRecordManager.delete() recid " + logRowId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logRowId =  decompressRecid(logRowId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long physRowId = _logicMgr.fetch(logRowId);
 | 
				
			||||||
 | 
					        _logicMgr.delete(logRowId);
 | 
				
			||||||
 | 
					        if(physRowId!=PREALOCATE_PHYS_RECID){
 | 
				
			||||||
 | 
					            _physMgr.free(physRowId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> void update(long recid, A obj, Serializer<A> serializer)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					        checkCanWrite();
 | 
				
			||||||
 | 
					        if (recid <= 0) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'recid' is invalid: "
 | 
				
			||||||
 | 
					                    + recid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (needsAutoCommit()) {
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (bufferInUse) {
 | 
				
			||||||
 | 
					            //current reusable buffer is in use, have to create new instances
 | 
				
			||||||
 | 
					            DataInputOutput buffer2 = new DataInputOutput();
 | 
				
			||||||
 | 
					            update2(recid, obj, serializer, buffer2);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            bufferInUse = true;
 | 
				
			||||||
 | 
					            update2(recid, obj, serializer, buffer);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            bufferInUse = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private <A> void update2(long logRecid, final A obj, final Serializer<A> serializer, final DataInputOutput buf)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logRecid =  decompressRecid(logRecid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long physRecid = _logicMgr.fetch(logRecid);
 | 
				
			||||||
 | 
					        if (physRecid == 0)
 | 
				
			||||||
 | 
					            throw new IOException("Can not update, recid does not exist: " + logRecid);
 | 
				
			||||||
 | 
					        buf.reset();
 | 
				
			||||||
 | 
					        serializer.serialize(buf, obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (DEBUG) {
 | 
				
			||||||
 | 
					            System.out.println("BaseRecordManager.update() recid " + logRecid + " length " + buf.getPos());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long newRecid =
 | 
				
			||||||
 | 
					            physRecid!=PREALOCATE_PHYS_RECID?
 | 
				
			||||||
 | 
					            _physMgr.update(physRecid, buf.getBuf(), 0, buf.getPos()):
 | 
				
			||||||
 | 
					            //previous record was only virtual and does not actually exist, so make new insert
 | 
				
			||||||
 | 
					            _physMgr.insert(buf.getBuf(),0,buf.getPos());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _logicMgr.update(logRecid, newRecid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> A fetch(final long recid, final Serializer<A> serializer)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					        if (recid <= 0) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Argument 'recid' is invalid: "  + recid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (bufferInUse) {
 | 
				
			||||||
 | 
					            //current reusable buffer is in use, have to create new instances
 | 
				
			||||||
 | 
					            DataInputOutput buffer2 = new DataInputOutput();
 | 
				
			||||||
 | 
					            return fetch2(recid, serializer, buffer2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            bufferInUse = true;
 | 
				
			||||||
 | 
					            return fetch2(recid, serializer, buffer);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            bufferInUse = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache) throws IOException {
 | 
				
			||||||
 | 
					        //we dont have any cache, so can ignore disableCache parameter
 | 
				
			||||||
 | 
					        return fetch(recid, serializer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private <A> A fetch2(long recid, final Serializer<A> serializer, final DataInputOutput buf)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        recid =  decompressRecid(recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buf.reset();
 | 
				
			||||||
 | 
					        long physLocation = _logicMgr.fetch(recid);
 | 
				
			||||||
 | 
					        if (physLocation == 0) {
 | 
				
			||||||
 | 
					            //throw new IOException("Record not found, recid: "+recid);
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(physLocation == PREALOCATE_PHYS_RECID){
 | 
				
			||||||
 | 
					            throw new InternalError("cache should prevent this!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _physMgr.fetch(buf, physLocation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (DEBUG) {
 | 
				
			||||||
 | 
					            System.out.println("BaseRecordManager.fetch() recid " + recid + " length " + buf.getPos());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        buf.resetForReading();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return serializer.deserialize(buf); //TODO there should be write limit to throw EOFException
 | 
				
			||||||
 | 
					        } catch (ClassNotFoundException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    byte[] fetchRaw(long recid) throws IOException {
 | 
				
			||||||
 | 
					        recid =  decompressRecid(recid);
 | 
				
			||||||
 | 
					        long physLocation = _logicMgr.fetch(recid);
 | 
				
			||||||
 | 
					        if (physLocation == 0) {
 | 
				
			||||||
 | 
					            //throw new IOException("Record not found, recid: "+recid);
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        DataInputOutput i = new DataInputOutput();
 | 
				
			||||||
 | 
					        _physMgr.fetch(i, physLocation);
 | 
				
			||||||
 | 
					        return i.toByteArray();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized long getRoot(final byte id){
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return _pageman.getFileHeader().fileHeaderGetRoot(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void setRoot(final byte id, final long rowid){
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					        checkCanWrite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _pageman.getFileHeader().fileHeaderSetRoot(id, rowid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void commit() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            commitInProgress = true;
 | 
				
			||||||
 | 
					            checkNotClosed();
 | 
				
			||||||
 | 
					            checkCanWrite();
 | 
				
			||||||
 | 
					            /** flush free phys rows into pages*/
 | 
				
			||||||
 | 
					            _physMgr.commit();
 | 
				
			||||||
 | 
					            _logicMgr.commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /**commit pages */
 | 
				
			||||||
 | 
					            _pageman.commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            commitInProgress= false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void rollback() {
 | 
				
			||||||
 | 
					        if (transactionsDisabled)
 | 
				
			||||||
 | 
					            throw new IllegalAccessError("Transactions are disabled, can not rollback");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            checkNotClosed();
 | 
				
			||||||
 | 
					            _physMgr.rollback();
 | 
				
			||||||
 | 
					            _logicMgr.rollback();
 | 
				
			||||||
 | 
					            _pageman.rollback();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            super.rollback();
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void copyToZip(String zipFile) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            String zip = zipFile;
 | 
				
			||||||
 | 
					            String zip2 = "db";
 | 
				
			||||||
 | 
					            ZipOutputStream z = new ZipOutputStream(new FileOutputStream(zip));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //copy zero pages
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                String file = zip2 +  0;
 | 
				
			||||||
 | 
					                z.putNextEntry(new ZipEntry(file));
 | 
				
			||||||
 | 
					                z.write(Utils.encrypt(cipherIn, _pageman.getHeaderBufData()));
 | 
				
			||||||
 | 
					                z.closeEntry();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //iterate over pages and create new file for each
 | 
				
			||||||
 | 
					            for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					                 pageid != 0;
 | 
				
			||||||
 | 
					                 pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                PageIo page = _file.get(pageid);
 | 
				
			||||||
 | 
					                String file = zip2 +  pageid;
 | 
				
			||||||
 | 
					                z.putNextEntry(new ZipEntry(file));
 | 
				
			||||||
 | 
					                z.write(Utils.encrypt(cipherIn, page.getData()));
 | 
				
			||||||
 | 
					                z.closeEntry();
 | 
				
			||||||
 | 
					                _file.release(page);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (long pageid = _pageman.getFirst(Magic.FREELOGIDS_PAGE);
 | 
				
			||||||
 | 
					                 pageid != 0;
 | 
				
			||||||
 | 
					                 pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                PageIo page = _file.get(pageid);
 | 
				
			||||||
 | 
					                String file = zip2 + pageid;
 | 
				
			||||||
 | 
					                z.putNextEntry(new ZipEntry(file));
 | 
				
			||||||
 | 
					                z.write(Utils.encrypt(cipherIn, page.getData()));
 | 
				
			||||||
 | 
					                z.closeEntry();
 | 
				
			||||||
 | 
					                _file.release(page);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (long pageid = _pageman.getFirst(Magic.USED_PAGE);
 | 
				
			||||||
 | 
					                 pageid != 0;
 | 
				
			||||||
 | 
					                 pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                PageIo page = _file.get(pageid);
 | 
				
			||||||
 | 
					                String file = zip2 + pageid;
 | 
				
			||||||
 | 
					                z.putNextEntry(new ZipEntry(file));
 | 
				
			||||||
 | 
					                z.write(Utils.encrypt(cipherIn, page.getData()));
 | 
				
			||||||
 | 
					                z.closeEntry();
 | 
				
			||||||
 | 
					                _file.release(page);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (long pageid = _pageman.getFirst(Magic.FREEPHYSIDS_PAGE);
 | 
				
			||||||
 | 
					                 pageid != 0;
 | 
				
			||||||
 | 
					                 pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                PageIo page = _file.get(pageid);
 | 
				
			||||||
 | 
					                String file = zip2 + pageid;
 | 
				
			||||||
 | 
					                z.putNextEntry(new ZipEntry(file));
 | 
				
			||||||
 | 
					                z.write(Utils.encrypt(cipherIn, page.getData()));
 | 
				
			||||||
 | 
					                z.closeEntry();
 | 
				
			||||||
 | 
					                _file.release(page);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (long pageid = _pageman.getFirst(Magic.FREEPHYSIDS_ROOT_PAGE);
 | 
				
			||||||
 | 
					                 pageid != 0;
 | 
				
			||||||
 | 
					                 pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                PageIo page = _file.get(pageid);
 | 
				
			||||||
 | 
					                String file = zip2 + pageid;
 | 
				
			||||||
 | 
					                z.putNextEntry(new ZipEntry(file));
 | 
				
			||||||
 | 
					                z.write(Utils.encrypt(cipherIn, page.getData()));
 | 
				
			||||||
 | 
					                z.closeEntry();
 | 
				
			||||||
 | 
					                _file.release(page);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            z.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void clearCache() {
 | 
				
			||||||
 | 
					        //no cache
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private long statisticsCountPages(short pageType) throws IOException {
 | 
				
			||||||
 | 
					        long pageCounter = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (long pageid = _pageman.getFirst(pageType);
 | 
				
			||||||
 | 
					             pageid != 0;
 | 
				
			||||||
 | 
					             pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					            pageCounter++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return pageCounter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized String calculateStatistics() {
 | 
				
			||||||
 | 
					        checkNotClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final StringBuilder b = new StringBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //count pages
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                b.append("PAGES:\n");
 | 
				
			||||||
 | 
					                long total = 0;
 | 
				
			||||||
 | 
					                long pages = statisticsCountPages(Magic.USED_PAGE);
 | 
				
			||||||
 | 
					                total += pages;
 | 
				
			||||||
 | 
					                b.append("  " + pages + " used pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
 | 
				
			||||||
 | 
					                pages = statisticsCountPages(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					                total += pages;
 | 
				
			||||||
 | 
					                b.append("  " + pages + " record translation pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
 | 
				
			||||||
 | 
					                pages = statisticsCountPages(Magic.FREE_PAGE);
 | 
				
			||||||
 | 
					                total += pages;
 | 
				
			||||||
 | 
					                b.append("  " + pages + " free (unused) pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
 | 
				
			||||||
 | 
					                pages = statisticsCountPages(Magic.FREEPHYSIDS_PAGE);
 | 
				
			||||||
 | 
					                total += pages;
 | 
				
			||||||
 | 
					                b.append("  " + pages + " free (phys) pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
 | 
				
			||||||
 | 
					                pages = statisticsCountPages(Magic.FREELOGIDS_PAGE);
 | 
				
			||||||
 | 
					                total += pages;
 | 
				
			||||||
 | 
					                b.append("  " + pages + " free (logical) pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
 | 
				
			||||||
 | 
					                b.append("  Total number of pages is " + total + " with size " + Utils.formatSpaceUsage(total * Storage.PAGE_SIZE) + "\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                b.append("RECORDS:\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                long recordCount = 0;
 | 
				
			||||||
 | 
					                long freeRecordCount = 0;
 | 
				
			||||||
 | 
					                long maximalRecordSize = 0;
 | 
				
			||||||
 | 
					                long maximalAvailSizeDiff = 0;
 | 
				
			||||||
 | 
					                long totalRecordSize = 0;
 | 
				
			||||||
 | 
					                long totalAvailDiff = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //count records
 | 
				
			||||||
 | 
					                for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					                     pageid != 0;
 | 
				
			||||||
 | 
					                     pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                    PageIo io = _file.get(pageid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for (int i = 0; i < _logicMgr.ELEMS_PER_PAGE; i += 1) {
 | 
				
			||||||
 | 
					                        final int pos = Magic.PAGE_HEADER_SIZE + i * Magic.PhysicalRowId_SIZE;
 | 
				
			||||||
 | 
					                        final long physLoc = io.pageHeaderGetLocation((short) pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (physLoc == 0) {
 | 
				
			||||||
 | 
					                            freeRecordCount++;
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if(physLoc == PREALOCATE_PHYS_RECID){
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        recordCount++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        //get size
 | 
				
			||||||
 | 
					                        PageIo page = _file.get(physLoc>>> Storage.PAGE_SIZE_SHIFT);
 | 
				
			||||||
 | 
					                        final short physOffset =(short) (physLoc & Storage.OFFSET_MASK);
 | 
				
			||||||
 | 
					                        int availSize = RecordHeader.getAvailableSize(page, physOffset);
 | 
				
			||||||
 | 
					                        int currentSize = RecordHeader.getCurrentSize(page, physOffset);
 | 
				
			||||||
 | 
					                        _file.release(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        maximalAvailSizeDiff = Math.max(maximalAvailSizeDiff, availSize - currentSize);
 | 
				
			||||||
 | 
					                        maximalRecordSize = Math.max(maximalRecordSize, currentSize);
 | 
				
			||||||
 | 
					                        totalAvailDiff += availSize - currentSize;
 | 
				
			||||||
 | 
					                        totalRecordSize += currentSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _file.release(io);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                b.append("  Contains " + recordCount + " records and " + freeRecordCount + " free slots.\n");
 | 
				
			||||||
 | 
					                b.append("  Total space occupied by data is " + Utils.formatSpaceUsage(totalRecordSize) + "\n");
 | 
				
			||||||
 | 
					                b.append("  Average data size in record is " + Utils.formatSpaceUsage(Math.round(1D * totalRecordSize / recordCount)) + "\n");
 | 
				
			||||||
 | 
					                b.append("  Maximal data size in record is " + Utils.formatSpaceUsage(maximalRecordSize) + "\n");
 | 
				
			||||||
 | 
					                b.append("  Space wasted in record fragmentation is " + Utils.formatSpaceUsage(totalAvailDiff) + "\n");
 | 
				
			||||||
 | 
					                b.append("  Maximal space wasted in single record fragmentation is " + Utils.formatSpaceUsage(maximalAvailSizeDiff) + "\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return b.toString();
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void defrag(boolean sortCollections) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            checkNotClosed();
 | 
				
			||||||
 | 
					            checkCanWrite();
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					            final String filename2 = _filename + "_defrag" + System.currentTimeMillis();
 | 
				
			||||||
 | 
					            final String filename1 = _filename;
 | 
				
			||||||
 | 
					            DBStore db2 = new DBStore(filename2, false, true, cipherIn, cipherOut, false,false,false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //recreate logical file with original page layout
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                //find minimal logical pageid (logical pageids are negative)
 | 
				
			||||||
 | 
					                LongHashMap<String> logicalPages = new LongHashMap<String>();
 | 
				
			||||||
 | 
					                long minpageid = 0;
 | 
				
			||||||
 | 
					                for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					                     pageid != 0;
 | 
				
			||||||
 | 
					                     pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                    minpageid = Math.min(minpageid, pageid);
 | 
				
			||||||
 | 
					                    logicalPages.put(pageid, Utils.EMPTY_STRING);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //fill second db with logical pages
 | 
				
			||||||
 | 
					                long pageCounter = 0;
 | 
				
			||||||
 | 
					                for (
 | 
				
			||||||
 | 
					                        long pageid = db2._pageman.allocate(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					                        pageid >= minpageid;
 | 
				
			||||||
 | 
					                        pageid = db2._pageman.allocate(Magic.TRANSLATION_PAGE)
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                    pageCounter++;
 | 
				
			||||||
 | 
					                    if (pageCounter % 1000 == 0)
 | 
				
			||||||
 | 
					                        db2.commit();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                logicalPages = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //reinsert collections so physical records are located near each other
 | 
				
			||||||
 | 
					            //iterate over named object recids, it is sorted with TreeSet
 | 
				
			||||||
 | 
					            if(sortCollections){
 | 
				
			||||||
 | 
					                long nameRecid = getRoot(NAME_DIRECTORY_ROOT);
 | 
				
			||||||
 | 
					                Collection<Long> recids = new TreeSet<Long>();
 | 
				
			||||||
 | 
					                if(nameRecid!=0){
 | 
				
			||||||
 | 
					                    HTree<String,Long> m = fetch(nameRecid);
 | 
				
			||||||
 | 
					                    recids.addAll(m.values());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (Long namedRecid : recids) {
 | 
				
			||||||
 | 
					                    Object obj = fetch(namedRecid);
 | 
				
			||||||
 | 
					                    if (obj instanceof LinkedList) {
 | 
				
			||||||
 | 
					                        LinkedList2.defrag(namedRecid, this, db2);
 | 
				
			||||||
 | 
					                    } else if (obj instanceof HTree) {
 | 
				
			||||||
 | 
					                        HTree.defrag(namedRecid, this, db2);
 | 
				
			||||||
 | 
					                    } else if (obj instanceof BTree) {
 | 
				
			||||||
 | 
					                        BTree.defrag(namedRecid, this, db2);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					                 pageid != 0;
 | 
				
			||||||
 | 
					                 pageid = _pageman.getNext(pageid)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                PageIo io = _file.get(pageid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (int i = 0; i < _logicMgr.ELEMS_PER_PAGE; i += 1) {
 | 
				
			||||||
 | 
					                    final int pos = Magic.PAGE_HEADER_SIZE + i * Magic.PhysicalRowId_SIZE;
 | 
				
			||||||
 | 
					                    if (pos > Short.MAX_VALUE)
 | 
				
			||||||
 | 
					                        throw new Error();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //write to new file
 | 
				
			||||||
 | 
					                    final long logicalRowId =  ((-pageid) << Storage.PAGE_SIZE_SHIFT) + (long) pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //read from logical location in second db,
 | 
				
			||||||
 | 
					                    //check if record was already inserted as part of collections
 | 
				
			||||||
 | 
					                    if (db2._pageman.getLast(Magic.TRANSLATION_PAGE) <= pageid &&
 | 
				
			||||||
 | 
					                            db2._logicMgr.fetch(logicalRowId) != 0) {
 | 
				
			||||||
 | 
					                        //yes, this record already exists in second db
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //get physical location in this db
 | 
				
			||||||
 | 
					                    final long physRowId =  io.pageHeaderGetLocation((short) pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (physRowId == 0)
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (physRowId == PREALOCATE_PHYS_RECID){
 | 
				
			||||||
 | 
					                        db2._logicMgr.forceInsert(logicalRowId, physRowId);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //read from physical location at this db
 | 
				
			||||||
 | 
					                    DataInputOutput b = new DataInputOutput();
 | 
				
			||||||
 | 
					                    _physMgr.fetch(b, physRowId);
 | 
				
			||||||
 | 
					                    byte[] bb = b.toByteArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //force insert into other file, without decompressing logical id to external form
 | 
				
			||||||
 | 
					                    long physLoc = db2._physMgr.insert(bb, 0, bb.length);
 | 
				
			||||||
 | 
					                    db2._logicMgr.forceInsert(logicalRowId, physLoc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _file.release(io);
 | 
				
			||||||
 | 
					                db2.commit();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for(byte b = 0;b<Magic.FILE_HEADER_NROOTS;b++){
 | 
				
			||||||
 | 
					                db2.setRoot(b, getRoot(b));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            db2.close();
 | 
				
			||||||
 | 
					            _pageman.close();
 | 
				
			||||||
 | 
					            _file.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            List<File> filesToDelete = new ArrayList<File>();
 | 
				
			||||||
 | 
					            //now rename old files
 | 
				
			||||||
 | 
					            String[] exts = {StorageDiskMapped.IDR, StorageDiskMapped.DBR};
 | 
				
			||||||
 | 
					            for (String ext : exts) {
 | 
				
			||||||
 | 
					                String f1 = filename1 + ext;
 | 
				
			||||||
 | 
					                String f2 = filename2 + "_OLD" + ext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //first rename transaction log
 | 
				
			||||||
 | 
					                File f1t = new File(f1 + StorageDisk.transaction_log_file_extension);
 | 
				
			||||||
 | 
					                File f2t = new File(f2 + StorageDisk.transaction_log_file_extension);
 | 
				
			||||||
 | 
					                f1t.renameTo(f2t);
 | 
				
			||||||
 | 
					                filesToDelete.add(f2t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //rename data files, iterate until file exist
 | 
				
			||||||
 | 
					                for (int i = 0; ; i++) {
 | 
				
			||||||
 | 
					                    File f1d = new File(f1 + "." + i);
 | 
				
			||||||
 | 
					                    if (!f1d.exists()) break;
 | 
				
			||||||
 | 
					                    File f2d = new File(f2 + "." + i);
 | 
				
			||||||
 | 
					                    f1d.renameTo(f2d);
 | 
				
			||||||
 | 
					                    filesToDelete.add(f2d);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //rename new files
 | 
				
			||||||
 | 
					            for (String ext : exts) {
 | 
				
			||||||
 | 
					                String f1 = filename2 + ext;
 | 
				
			||||||
 | 
					                String f2 = filename1 + ext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //first rename transaction log
 | 
				
			||||||
 | 
					                File f1t = new File(f1 + StorageDisk.transaction_log_file_extension);
 | 
				
			||||||
 | 
					                File f2t = new File(f2 + StorageDisk.transaction_log_file_extension);
 | 
				
			||||||
 | 
					                f1t.renameTo(f2t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //rename data files, iterate until file exist
 | 
				
			||||||
 | 
					                for (int i = 0; ; i++) {
 | 
				
			||||||
 | 
					                    File f1d = new File(f1 + "." + i);
 | 
				
			||||||
 | 
					                    if (!f1d.exists()) break;
 | 
				
			||||||
 | 
					                    File f2d = new File(f2 + "." + i);
 | 
				
			||||||
 | 
					                    f1d.renameTo(f2d);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (File d : filesToDelete) {
 | 
				
			||||||
 | 
					                d.delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            reopen();
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Insert data at forced logicalRowId, use only for defragmentation !!
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param logicalRowId
 | 
				
			||||||
 | 
					     * @param data
 | 
				
			||||||
 | 
					     * @throws IOException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void forceInsert(long logicalRowId, byte[] data) throws IOException {
 | 
				
			||||||
 | 
					        logicalRowId = decompressRecid(logicalRowId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (needsAutoCommit()) {
 | 
				
			||||||
 | 
					            commit();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long physLoc = _physMgr.insert(data, 0, data.length);
 | 
				
			||||||
 | 
					        _logicMgr.forceInsert(logicalRowId, physLoc);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns number of records stored in database.
 | 
				
			||||||
 | 
					     * Is used for unit tests
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    long countRecords() throws IOException {
 | 
				
			||||||
 | 
					        long counter = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long page = _pageman.getFirst(Magic.TRANSLATION_PAGE);
 | 
				
			||||||
 | 
					        while (page != 0) {
 | 
				
			||||||
 | 
					            PageIo io = _file.get(page);
 | 
				
			||||||
 | 
					            for (int i = 0; i < _logicMgr.ELEMS_PER_PAGE; i += 1) {
 | 
				
			||||||
 | 
					                int pos = Magic.PAGE_HEADER_SIZE + i * Magic.PhysicalRowId_SIZE;
 | 
				
			||||||
 | 
					                if (pos > Short.MAX_VALUE)
 | 
				
			||||||
 | 
					                    throw new Error();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //get physical location
 | 
				
			||||||
 | 
					                long physRowId = io.pageHeaderGetLocation((short) pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (physRowId != 0)
 | 
				
			||||||
 | 
					                    counter += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _file.release(io);
 | 
				
			||||||
 | 
					            page = _pageman.getNext(page);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return counter;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private  static int COMPRESS_RECID_PAGE_SHIFT = Integer.MIN_VALUE;
 | 
				
			||||||
 | 
					    static{
 | 
				
			||||||
 | 
					        int shift = 1;
 | 
				
			||||||
 | 
					        while((1<<shift) <LogicalRowIdManager.ELEMS_PER_PAGE )
 | 
				
			||||||
 | 
					        shift++;
 | 
				
			||||||
 | 
					        COMPRESS_RECID_PAGE_SHIFT = shift;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static long COMPRESS_RECID_OFFSET_MASK =  0xFFFFFFFFFFFFFFFFL >>> (64- COMPRESS_RECID_PAGE_SHIFT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Compress recid from physical form (block - offset) to (block - slot).
 | 
				
			||||||
 | 
					     * This way resulting number is smaller and can be easier packed with LongPacker
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static long compressRecid(final long recid) {
 | 
				
			||||||
 | 
					        final long page = recid>>> Storage.PAGE_SIZE_SHIFT;
 | 
				
			||||||
 | 
					        short offset =  (short) (recid & Storage.OFFSET_MASK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offset = (short) (offset - Magic.PAGE_HEADER_SIZE);
 | 
				
			||||||
 | 
					        if (offset % Magic.PhysicalRowId_SIZE != 0)
 | 
				
			||||||
 | 
					            throw new InternalError("recid not dividable "+Magic.PhysicalRowId_SIZE);
 | 
				
			||||||
 | 
					        long slot = offset / Magic.PhysicalRowId_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (page << COMPRESS_RECID_PAGE_SHIFT) + slot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static long decompressRecid(final long recid) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final long page = recid >>> COMPRESS_RECID_PAGE_SHIFT;
 | 
				
			||||||
 | 
					        final short offset = (short) ((recid & COMPRESS_RECID_OFFSET_MASK) * Magic.PhysicalRowId_SIZE + Magic.PAGE_HEADER_SIZE);
 | 
				
			||||||
 | 
					        return  (page << Storage.PAGE_SIZE_SHIFT) + (long) offset;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,297 @@
 | 
				
			|||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Utility class which implements DataInput and DataOutput on top of byte[] buffer
 | 
				
			||||||
 | 
					 * with minimal overhead
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class DataInputOutput implements DataInput, DataOutput, ObjectInput, ObjectOutput {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int pos = 0;
 | 
				
			||||||
 | 
					    private int count = 0;
 | 
				
			||||||
 | 
					    private byte[] buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DataInputOutput() {
 | 
				
			||||||
 | 
					        buf = new byte[8];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DataInputOutput(byte[] data) {
 | 
				
			||||||
 | 
					        buf = data;
 | 
				
			||||||
 | 
					        count = data.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getBuf() {
 | 
				
			||||||
 | 
					        return buf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getPos() {
 | 
				
			||||||
 | 
					        return pos;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void reset() {
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					        count = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void resetForReading() {
 | 
				
			||||||
 | 
					        count = pos;
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void reset(byte[] b) {
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					        buf = b;
 | 
				
			||||||
 | 
					        count = b.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] toByteArray() {
 | 
				
			||||||
 | 
					        byte[] d = new byte[pos];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, d, 0, pos);
 | 
				
			||||||
 | 
					        return d;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int available() {
 | 
				
			||||||
 | 
					        return count - pos;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readFully(byte[] b) throws IOException {
 | 
				
			||||||
 | 
					        readFully(b, 0, b.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readFully(byte[] b, int off, int len) throws IOException {
 | 
				
			||||||
 | 
					        System.arraycopy(buf, pos, b, off, len);
 | 
				
			||||||
 | 
					        pos += len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int skipBytes(int n) throws IOException {
 | 
				
			||||||
 | 
					        pos += n;
 | 
				
			||||||
 | 
					        return n;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean readBoolean() throws IOException {
 | 
				
			||||||
 | 
					        return buf[pos++] == 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte readByte() throws IOException {
 | 
				
			||||||
 | 
					        return buf[pos++];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int readUnsignedByte() throws IOException {
 | 
				
			||||||
 | 
					        return buf[pos++] & 0xff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public short readShort() throws IOException {
 | 
				
			||||||
 | 
					        return (short)
 | 
				
			||||||
 | 
					                (((short) (buf[pos++] & 0xff) << 8) |
 | 
				
			||||||
 | 
					                        ((short) (buf[pos++] & 0xff) << 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int readUnsignedShort() throws IOException {
 | 
				
			||||||
 | 
					        return (((int) (buf[pos++] & 0xff) << 8) |
 | 
				
			||||||
 | 
					                ((int) (buf[pos++] & 0xff) << 0));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public char readChar() throws IOException {
 | 
				
			||||||
 | 
					        return (char) readInt();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int readInt() throws IOException {
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					                (((buf[pos++] & 0xff) << 24) |
 | 
				
			||||||
 | 
					                        ((buf[pos++] & 0xff) << 16) |
 | 
				
			||||||
 | 
					                        ((buf[pos++] & 0xff) << 8) |
 | 
				
			||||||
 | 
					                        ((buf[pos++] & 0xff) << 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long readLong() throws IOException {
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					                (((long) (buf[pos++] & 0xff) << 56) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 48) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 40) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 32) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 24) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 16) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 8) |
 | 
				
			||||||
 | 
					                        ((long) (buf[pos++] & 0xff) << 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float readFloat() throws IOException {
 | 
				
			||||||
 | 
					        return Float.intBitsToFloat(readInt());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public double readDouble() throws IOException {
 | 
				
			||||||
 | 
					        return Double.longBitsToDouble(readLong());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String readLine() throws IOException {
 | 
				
			||||||
 | 
					        return readUTF();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String readUTF() throws IOException {
 | 
				
			||||||
 | 
					        return Serialization.deserializeString(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * make sure there will be enought space in buffer to write N bytes
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void ensureAvail(int n) {
 | 
				
			||||||
 | 
					        if (pos + n >= buf.length) {
 | 
				
			||||||
 | 
					            int newSize = Math.max(pos + n, buf.length * 2);
 | 
				
			||||||
 | 
					            buf = Arrays.copyOf(buf, newSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void write(int b) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(1);
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void write(byte[] b) throws IOException {
 | 
				
			||||||
 | 
					        write(b, 0, b.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void write(byte[] b, int off, int len) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(len);
 | 
				
			||||||
 | 
					        System.arraycopy(b, off, buf, pos, len);
 | 
				
			||||||
 | 
					        pos += len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeBoolean(boolean v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(1);
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (v ? 1 : 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeByte(int v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(1);
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeShort(int v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(2);
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 8));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeChar(int v) throws IOException {
 | 
				
			||||||
 | 
					        writeInt(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeInt(int v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(4);
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 24));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 16));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 8));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeLong(long v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(8);
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 56));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 48));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 40));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 32));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 24));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 16));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 8));
 | 
				
			||||||
 | 
					        buf[pos++] = (byte) (0xff & (v >> 0));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeFloat(float v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(4);
 | 
				
			||||||
 | 
					        writeInt(Float.floatToIntBits(v));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeDouble(double v) throws IOException {
 | 
				
			||||||
 | 
					        ensureAvail(8);
 | 
				
			||||||
 | 
					        writeLong(Double.doubleToLongBits(v));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeBytes(String s) throws IOException {
 | 
				
			||||||
 | 
					        writeUTF(s);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeChars(String s) throws IOException {
 | 
				
			||||||
 | 
					        writeUTF(s);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeUTF(String s) throws IOException {
 | 
				
			||||||
 | 
					        Serialization.serializeString(this, s);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** helper method to write data directly from PageIo*/
 | 
				
			||||||
 | 
					    public void writeFromByteBuffer(ByteBuffer b, int offset, int length) {
 | 
				
			||||||
 | 
					        ensureAvail(length);
 | 
				
			||||||
 | 
					        b.position(offset);
 | 
				
			||||||
 | 
					        b.get(buf,pos,length);
 | 
				
			||||||
 | 
					        pos+=length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //temp var used for Externalizable
 | 
				
			||||||
 | 
					    SerialClassInfo serializer;
 | 
				
			||||||
 | 
					    //temp var used for Externalizable
 | 
				
			||||||
 | 
					    Serialization.FastArrayList objectStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Object readObject() throws ClassNotFoundException, IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectInput
 | 
				
			||||||
 | 
					        //Fake method which reads data from serializer.
 | 
				
			||||||
 | 
					        //We could probably implement separate wrapper for this, but I want to safe class space
 | 
				
			||||||
 | 
					        return serializer.deserialize(this, objectStack);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int read() throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectInput
 | 
				
			||||||
 | 
					        return readUnsignedByte();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int read(byte[] b) throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectInput
 | 
				
			||||||
 | 
					        readFully(b);
 | 
				
			||||||
 | 
					        return b.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int read(byte[] b, int off, int len) throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectInput
 | 
				
			||||||
 | 
					        readFully(b,off,len);
 | 
				
			||||||
 | 
					        return len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long skip(long n) throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectInput
 | 
				
			||||||
 | 
					        pos += n;
 | 
				
			||||||
 | 
					        return n;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void close() throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectInput
 | 
				
			||||||
 | 
					        //do nothing
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeObject(Object obj) throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectOutput
 | 
				
			||||||
 | 
					        serializer.serialize(this,obj,objectStack);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void flush() throws IOException {
 | 
				
			||||||
 | 
					        //is here just to implement ObjectOutput
 | 
				
			||||||
 | 
					        //do nothing
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,215 @@
 | 
				
			|||||||
 | 
					///*
 | 
				
			||||||
 | 
					//package org.apache.jdbm;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//import java.io.DataInput;
 | 
				
			||||||
 | 
					//import java.io.DataOutput;
 | 
				
			||||||
 | 
					//import java.io.IOException;
 | 
				
			||||||
 | 
					//import java.nio.Buffer;
 | 
				
			||||||
 | 
					//import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					//import java.util.Arrays;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//*/
 | 
				
			||||||
 | 
					///**
 | 
				
			||||||
 | 
					// * Utility class which implements DataInput and DataOutput on top of ByteBuffer
 | 
				
			||||||
 | 
					// * with minimal overhead
 | 
				
			||||||
 | 
					// * This class is not used, is left here in case we would ever need it.
 | 
				
			||||||
 | 
					// *
 | 
				
			||||||
 | 
					// * @author Jan Kotek
 | 
				
			||||||
 | 
					// *//*
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//class DataInputOutput2 implements DataInput, DataOutput {
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    private ByteBuffer buf;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public DataInputOutput2() {
 | 
				
			||||||
 | 
					//        buf = ByteBuffer.allocate(8);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public DataInputOutput2(ByteBuffer data) {
 | 
				
			||||||
 | 
					//        buf = data;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public DataInputOutput2(byte[] data) {
 | 
				
			||||||
 | 
					//        buf = ByteBuffer.wrap(data);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public int getPos() {
 | 
				
			||||||
 | 
					//        return buf.position();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void reset() {
 | 
				
			||||||
 | 
					//        buf.rewind();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void reset(byte[] b) {
 | 
				
			||||||
 | 
					//        buf = ByteBuffer.wrap(b);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void resetForReading() {
 | 
				
			||||||
 | 
					//        buf.flip();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public byte[] toByteArray() {
 | 
				
			||||||
 | 
					//        byte[] d = new byte[buf.position()];
 | 
				
			||||||
 | 
					//        buf.position(0);
 | 
				
			||||||
 | 
					//        buf.get(d); //reading N bytes restores to current position
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//        return d;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public int available() {
 | 
				
			||||||
 | 
					//        return buf.remaining();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void readFully(byte[] b) throws IOException {
 | 
				
			||||||
 | 
					//        readFully(b, 0, b.length);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void readFully(byte[] b, int off, int len) throws IOException {
 | 
				
			||||||
 | 
					//        buf.get(b,off,len);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public int skipBytes(int n) throws IOException {
 | 
				
			||||||
 | 
					//        buf.position(buf.position()+n);
 | 
				
			||||||
 | 
					//        return n;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public boolean readBoolean() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.get()==1;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public byte readByte() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.get();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public int readUnsignedByte() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.get() & 0xff;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public short readShort() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.getShort();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public int readUnsignedShort() throws IOException {
 | 
				
			||||||
 | 
					//        return (((int) (buf.get() & 0xff) << 8) |
 | 
				
			||||||
 | 
					//                ((int) (buf.get() & 0xff) << 0));
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public char readChar() throws IOException {
 | 
				
			||||||
 | 
					//        return (char) readInt();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public int readInt() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.getInt();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public long readLong() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.getLong();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public float readFloat() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.getFloat();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public double readDouble() throws IOException {
 | 
				
			||||||
 | 
					//        return buf.getDouble();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public String readLine() throws IOException {
 | 
				
			||||||
 | 
					//        return readUTF();
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public String readUTF() throws IOException {
 | 
				
			||||||
 | 
					//        return Serialization.deserializeString(this);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    */
 | 
				
			||||||
 | 
					///**
 | 
				
			||||||
 | 
					//     * make sure there will be enough space in buffer to write N bytes
 | 
				
			||||||
 | 
					//     *//*
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    private void ensureAvail(int n) {
 | 
				
			||||||
 | 
					//        int pos = buf.position();
 | 
				
			||||||
 | 
					//        if (pos + n >= buf.limit()) {
 | 
				
			||||||
 | 
					//            int newSize = Math.max(pos + n, buf.limit() * 2);
 | 
				
			||||||
 | 
					//            byte[] b = new byte[newSize];
 | 
				
			||||||
 | 
					//            buf.get(b);
 | 
				
			||||||
 | 
					//            buf = ByteBuffer.wrap(b);
 | 
				
			||||||
 | 
					//            buf.position(pos);
 | 
				
			||||||
 | 
					//        }
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void write(final int b) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(1);
 | 
				
			||||||
 | 
					//        buf.put((byte) b);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void write(final byte[] b) throws IOException {
 | 
				
			||||||
 | 
					//        write(b, 0, b.length);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void write(final byte[] b, final int off, final int len) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(len);
 | 
				
			||||||
 | 
					//        buf.put(b,off,len);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeBoolean(final boolean v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(1);
 | 
				
			||||||
 | 
					//        buf.put((byte) (v?1:0));
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeByte(final int v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(1);
 | 
				
			||||||
 | 
					//        buf.put((byte) v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeShort(final short v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(2);
 | 
				
			||||||
 | 
					//        buf.putShort(v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeChar(final int v) throws IOException {
 | 
				
			||||||
 | 
					//        writeInt(v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeInt(final int v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(4);
 | 
				
			||||||
 | 
					//        buf.putInt(v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeLong(final long v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(8);
 | 
				
			||||||
 | 
					//        buf.putLong(v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeFloat(final float v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(4);
 | 
				
			||||||
 | 
					//        buf.putFloat(v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeDouble(final double v) throws IOException {
 | 
				
			||||||
 | 
					//        ensureAvail(8);
 | 
				
			||||||
 | 
					//        buf.putDouble(v);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeBytes(String s) throws IOException {
 | 
				
			||||||
 | 
					//        writeUTF(s);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeChars(String s) throws IOException {
 | 
				
			||||||
 | 
					//        writeUTF(s);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    public void writeUTF(String s) throws IOException {
 | 
				
			||||||
 | 
					//        Serialization.serializeString(this, s);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//*/
 | 
				
			||||||
							
								
								
									
										542
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/HTree.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								com.minres.scviewer.database.text/src/org/apache/jdbm/HTree.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,542 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicReference;
 | 
				
			||||||
 | 
					import java.util.concurrent.locks.ReadWriteLock;
 | 
				
			||||||
 | 
					import java.util.concurrent.locks.ReentrantReadWriteLock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Persistent HashMap implementation for DB.
 | 
				
			||||||
 | 
					 * Implemented as an H*Tree structure.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 * @author Jan Kotek
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class HTree<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final Serializer SERIALIZER = new Serializer<Object>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Object deserialize(DataInput ds2) throws IOException {
 | 
				
			||||||
 | 
					            DataInputOutput ds = (DataInputOutput) ds2;
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                int i = ds.readUnsignedByte();
 | 
				
			||||||
 | 
					                if (i == SerializationHeader.HTREE_BUCKET) { //is HashBucket?
 | 
				
			||||||
 | 
					                    HTreeBucket ret = new HTreeBucket(HTree.this);
 | 
				
			||||||
 | 
					                    if (loadValues)
 | 
				
			||||||
 | 
					                        ret.readExternal(ds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (loadValues && ds.available() != 0)
 | 
				
			||||||
 | 
					                        throw new InternalError("bytes left: " + ds.available());
 | 
				
			||||||
 | 
					                    return ret;
 | 
				
			||||||
 | 
					                } else if (i == SerializationHeader.HTREE_DIRECTORY) {
 | 
				
			||||||
 | 
					                    HTreeDirectory ret = new HTreeDirectory(HTree.this);
 | 
				
			||||||
 | 
					                    ret.readExternal(ds);
 | 
				
			||||||
 | 
					                    if (loadValues && ds.available() != 0)
 | 
				
			||||||
 | 
					                        throw new InternalError("bytes left: " + ds.available());
 | 
				
			||||||
 | 
					                    return ret;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    throw new InternalError("Wrong HTree header: " + i);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (ClassNotFoundException e) {
 | 
				
			||||||
 | 
					                throw new IOException(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void serialize(DataOutput out, Object obj) throws IOException {
 | 
				
			||||||
 | 
					            if (obj instanceof HTreeBucket) {
 | 
				
			||||||
 | 
					                out.write(SerializationHeader.HTREE_BUCKET);
 | 
				
			||||||
 | 
					                HTreeBucket b = (HTreeBucket) obj;
 | 
				
			||||||
 | 
					                b.writeExternal(out);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                out.write(SerializationHeader.HTREE_DIRECTORY);
 | 
				
			||||||
 | 
					                HTreeDirectory n = (HTreeDirectory) obj;
 | 
				
			||||||
 | 
					                n.writeExternal(out);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final protected ReadWriteLock lock = new ReentrantReadWriteLock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Listeners which are notified about changes in records
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected RecordListener[] recordListeners = new RecordListener[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serializer used to serialize index keys (optional)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected Serializer<K> keySerializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serializer used to serialize index values (optional)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected Serializer<V> valueSerializer;
 | 
				
			||||||
 | 
					    protected boolean readonly = false;
 | 
				
			||||||
 | 
					    final long rootRecid;
 | 
				
			||||||
 | 
					    DBAbstract db;
 | 
				
			||||||
 | 
					    /** if false map contains only keys, used for set*/
 | 
				
			||||||
 | 
					    boolean hasValues = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * counts structural changes in tree at runtume. Is here to support fail-fast behaviour.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int modCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * indicates if values should be loaded during deserialization, set to true during defragmentation
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private boolean loadValues = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Serializer<K> getKeySerializer() {
 | 
				
			||||||
 | 
					        return keySerializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Serializer<V> getValueSerializer() {
 | 
				
			||||||
 | 
					        return valueSerializer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * cache writing buffer, so it does not have to be allocated on each write
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    AtomicReference<DataInputOutput> writeBufferCache = new AtomicReference<DataInputOutput>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a persistent hashtable.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public HTree(DBAbstract db, Serializer<K> keySerializer, Serializer<V> valueSerializer, boolean hasValues)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        this.keySerializer = keySerializer;
 | 
				
			||||||
 | 
					        this.valueSerializer = valueSerializer;
 | 
				
			||||||
 | 
					        this.db = db;
 | 
				
			||||||
 | 
					        this.hasValues = hasValues;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HTreeDirectory<K, V> root = new HTreeDirectory<K, V>(this, (byte) 0);
 | 
				
			||||||
 | 
					        root.setPersistenceContext(0);
 | 
				
			||||||
 | 
					        this.rootRecid = db.insert(root, this.SERIALIZER,false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load a persistent hashtable
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public HTree(DBAbstract db,long rootRecid, Serializer<K> keySerializer, Serializer<V> valueSerializer, boolean hasValues)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        this.db = db;
 | 
				
			||||||
 | 
					        this.rootRecid = rootRecid;
 | 
				
			||||||
 | 
					        this.keySerializer = keySerializer;
 | 
				
			||||||
 | 
					        this.valueSerializer = valueSerializer;
 | 
				
			||||||
 | 
					        this.hasValues = hasValues;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setPersistenceContext(DBAbstract db) {
 | 
				
			||||||
 | 
					        this.db = db;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V put(K key, V value) {
 | 
				
			||||||
 | 
					        if (readonly)
 | 
				
			||||||
 | 
					            throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (key == null || value == null)
 | 
				
			||||||
 | 
					                throw new NullPointerException("Null key or value");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            V oldVal = (V) getRoot().put(key, value);
 | 
				
			||||||
 | 
					            if (oldVal == null) {
 | 
				
			||||||
 | 
					                modCount++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //increase size
 | 
				
			||||||
 | 
					                HTreeDirectory root = getRoot();
 | 
				
			||||||
 | 
					                root.size++;
 | 
				
			||||||
 | 
					                db.update(rootRecid,root,SERIALIZER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (RecordListener<K, V> r : recordListeners)
 | 
				
			||||||
 | 
					                    r.recordInserted(key, value);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //notify listeners
 | 
				
			||||||
 | 
					                for (RecordListener<K, V> r : recordListeners)
 | 
				
			||||||
 | 
					                    r.recordUpdated(key, oldVal, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return oldVal;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V get(Object key) {
 | 
				
			||||||
 | 
					        if (key == null)
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        lock.readLock().lock();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return getRoot().get((K) key);
 | 
				
			||||||
 | 
					        } catch (ClassCastException e) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.readLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V remove(Object key) {
 | 
				
			||||||
 | 
					        if (readonly)
 | 
				
			||||||
 | 
					            throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (key == null)
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            V val = (V) getRoot().remove(key);
 | 
				
			||||||
 | 
					             modCount++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (val != null){
 | 
				
			||||||
 | 
					                //decrease size
 | 
				
			||||||
 | 
					                HTreeDirectory root = getRoot();
 | 
				
			||||||
 | 
					                root.size--;
 | 
				
			||||||
 | 
					                db.update(rootRecid,root,SERIALIZER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  for (RecordListener r : recordListeners)
 | 
				
			||||||
 | 
					                        r.recordRemoved(key, val);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return val;
 | 
				
			||||||
 | 
					        } catch (ClassCastException e) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean containsKey(Object key) {
 | 
				
			||||||
 | 
					        if (key == null)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        //no need for locking, get is already locked
 | 
				
			||||||
 | 
					        V v = get((K) key);
 | 
				
			||||||
 | 
					        return v != null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clear() {
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            Iterator<K> keyIter = keys();
 | 
				
			||||||
 | 
					            while (keyIter.hasNext()) {
 | 
				
			||||||
 | 
					                keyIter.next();
 | 
				
			||||||
 | 
					                keyIter.remove();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns an enumeration of the keys contained in this
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Iterator<K> keys()
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        lock.readLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            return getRoot().keys();
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.readLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DBAbstract getRecordManager() {
 | 
				
			||||||
 | 
					        return db;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * add RecordListener which is notified about record changes
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addRecordListener(RecordListener<K, V> listener) {
 | 
				
			||||||
 | 
					        recordListeners = Arrays.copyOf(recordListeners, recordListeners.length + 1);
 | 
				
			||||||
 | 
					        recordListeners[recordListeners.length - 1] = listener;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * remove RecordListener which is notified about record changes
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void removeRecordListener(RecordListener<K, V> listener) {
 | 
				
			||||||
 | 
					        List l = Arrays.asList(recordListeners);
 | 
				
			||||||
 | 
					        l.remove(listener);
 | 
				
			||||||
 | 
					        recordListeners = (RecordListener[]) l.toArray(new RecordListener[1]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Set<Entry<K, V>> entrySet() {
 | 
				
			||||||
 | 
					        return _entrySet;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Set<Entry<K, V>> _entrySet = new AbstractSet<Entry<K, V>>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected Entry<K, V> newEntry(K k, V v) {
 | 
				
			||||||
 | 
					            return new SimpleEntry<K, V>(k, v) {
 | 
				
			||||||
 | 
					                private static final long serialVersionUID = 978651696969194154L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                public V setValue(V arg0) {
 | 
				
			||||||
 | 
					                    //put is already locked
 | 
				
			||||||
 | 
					                    HTree.this.put(getKey(), arg0);
 | 
				
			||||||
 | 
					                    return super.setValue(arg0);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean add(java.util.Map.Entry<K, V> e) {
 | 
				
			||||||
 | 
					            if (readonly)
 | 
				
			||||||
 | 
					                throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					            if (e.getKey() == null)
 | 
				
			||||||
 | 
					                throw new NullPointerException("Can not add null key");
 | 
				
			||||||
 | 
					            lock.writeLock().lock();
 | 
				
			||||||
 | 
					            try{
 | 
				
			||||||
 | 
					                if (e.getValue().equals(get(e.getKey())))
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                HTree.this.put(e.getKey(), e.getValue());
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }finally {
 | 
				
			||||||
 | 
					                lock.writeLock().unlock();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					        public boolean contains(Object o) {
 | 
				
			||||||
 | 
					            if (o instanceof Entry) {
 | 
				
			||||||
 | 
					                Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //get is already locked
 | 
				
			||||||
 | 
					                if (e.getKey() != null && HTree.this.get(e.getKey()) != null)
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Iterator<java.util.Map.Entry<K, V>> iterator() {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                final Iterator<K> br = keys();
 | 
				
			||||||
 | 
					                return new Iterator<Entry<K, V>>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    public boolean hasNext() {
 | 
				
			||||||
 | 
					                        return br.hasNext();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    public java.util.Map.Entry<K, V> next() {
 | 
				
			||||||
 | 
					                        K k = br.next();
 | 
				
			||||||
 | 
					                        return newEntry(k, get(k));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    public void remove() {
 | 
				
			||||||
 | 
					                        if (readonly)
 | 
				
			||||||
 | 
					                            throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					                        br.remove();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } catch (IOException e) {
 | 
				
			||||||
 | 
					                throw new IOError(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					        public boolean remove(Object o) {
 | 
				
			||||||
 | 
					            if (readonly)
 | 
				
			||||||
 | 
					                throw new UnsupportedOperationException("readonly");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (o instanceof Entry) {
 | 
				
			||||||
 | 
					                Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //check for nulls
 | 
				
			||||||
 | 
					                if (e.getKey() == null || e.getValue() == null)
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                lock.writeLock().lock();
 | 
				
			||||||
 | 
					                try{
 | 
				
			||||||
 | 
					                    //get old value, must be same as item in entry
 | 
				
			||||||
 | 
					                    V v = get(e.getKey());
 | 
				
			||||||
 | 
					                    if (v == null || !e.getValue().equals(v))
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    HTree.this.remove(e.getKey());
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					                }finally{
 | 
				
			||||||
 | 
					                    lock.writeLock().unlock();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public int size() {
 | 
				
			||||||
 | 
					            lock.readLock().lock();
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                int counter = 0;
 | 
				
			||||||
 | 
					                Iterator<K> it = keys();
 | 
				
			||||||
 | 
					                while (it.hasNext()) {
 | 
				
			||||||
 | 
					                    it.next();
 | 
				
			||||||
 | 
					                    counter++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return counter;
 | 
				
			||||||
 | 
					            } catch (IOException e) {
 | 
				
			||||||
 | 
					                throw new IOError(e);
 | 
				
			||||||
 | 
					            }finally {
 | 
				
			||||||
 | 
					                lock.readLock().unlock();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HTreeDirectory<K, V> getRoot() {
 | 
				
			||||||
 | 
					        //assumes that caller already holds read or write lock
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            HTreeDirectory<K, V> root = (HTreeDirectory<K, V>) db.fetch(rootRecid, this.SERIALIZER);
 | 
				
			||||||
 | 
					            root.setPersistenceContext(rootRecid);
 | 
				
			||||||
 | 
					            return root;
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static HTree deserialize(DataInput is, Serialization ser) throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					        long rootRecid = LongPacker.unpackLong(is);
 | 
				
			||||||
 | 
					        boolean hasValues = is.readBoolean();
 | 
				
			||||||
 | 
					        Serializer keySerializer = (Serializer) ser.deserialize(is);
 | 
				
			||||||
 | 
					        Serializer valueSerializer = (Serializer)  ser.deserialize(is);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new HTree(ser.db,rootRecid, keySerializer, valueSerializer, hasValues);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void serialize(DataOutput out) throws IOException {
 | 
				
			||||||
 | 
					        LongPacker.packLong(out, rootRecid);
 | 
				
			||||||
 | 
					        out.writeBoolean(hasValues);;
 | 
				
			||||||
 | 
					        db.defaultSerializer().serialize(out, keySerializer);
 | 
				
			||||||
 | 
					        db.defaultSerializer().serialize(out, valueSerializer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static void defrag(Long recid, DBStore r1, DBStore r2) throws IOException {
 | 
				
			||||||
 | 
					        //TODO should modCount be increased after defrag, revert or commit?
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            byte[] data = r1.fetchRaw(recid);
 | 
				
			||||||
 | 
					            r2.forceInsert(recid, data);
 | 
				
			||||||
 | 
					            DataInput in = new DataInputStream(new ByteArrayInputStream(data));
 | 
				
			||||||
 | 
					            HTree t = (HTree) r1.defaultSerializer().deserialize(in);
 | 
				
			||||||
 | 
					            t.db = r1;
 | 
				
			||||||
 | 
					            t.loadValues = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HTreeDirectory d = t.getRoot();
 | 
				
			||||||
 | 
					            if (d != null) {
 | 
				
			||||||
 | 
					                r2.forceInsert(t.rootRecid, r1.fetchRaw(t.rootRecid));
 | 
				
			||||||
 | 
					                d.defrag(r1, r2);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (ClassNotFoundException e) {
 | 
				
			||||||
 | 
					            throw new IOError(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int size(){
 | 
				
			||||||
 | 
					        return (int) getRoot().size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean hasValues() {
 | 
				
			||||||
 | 
					        return hasValues;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V putIfAbsent(K key, V value) {
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (!containsKey(key))
 | 
				
			||||||
 | 
					                return put(key, value);
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                return get(key);
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean remove(Object key, Object value) {
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (containsKey(key) && get(key).equals(value)) {
 | 
				
			||||||
 | 
					                remove(key);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else return false;
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean replace(K key, V oldValue, V newValue) {
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (containsKey(key) && get(key).equals(oldValue)) {
 | 
				
			||||||
 | 
					                put(key, newValue);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else return false;
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public V replace(K key, V value) {
 | 
				
			||||||
 | 
					        lock.writeLock().lock();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            if (containsKey(key)) {
 | 
				
			||||||
 | 
					                return put(key, value);
 | 
				
			||||||
 | 
					            } else return null;
 | 
				
			||||||
 | 
					        }finally {
 | 
				
			||||||
 | 
					            lock.writeLock().unlock();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,352 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A bucket is a placeholder for multiple (key, value) pairs.  Buckets
 | 
				
			||||||
 | 
					 * are used to store collisions (same hash value) at all levels of an
 | 
				
			||||||
 | 
					 * H*tree.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * There are two types of buckets: leaf and non-leaf.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * Non-leaf buckets are buckets which hold collisions which happen
 | 
				
			||||||
 | 
					 * when the H*tree is not fully expanded.   Keys in a non-leaf buckets
 | 
				
			||||||
 | 
					 * can have different hash codes.  Non-leaf buckets are limited to an
 | 
				
			||||||
 | 
					 * arbitrary size.  When this limit is reached, the H*tree should create
 | 
				
			||||||
 | 
					 * a new HTreeDirectory node and distribute keys of the non-leaf buckets into
 | 
				
			||||||
 | 
					 * the newly created HTreeDirectory.
 | 
				
			||||||
 | 
					 * <p/>
 | 
				
			||||||
 | 
					 * A leaf bucket is a bucket which contains keys which all have
 | 
				
			||||||
 | 
					 * the same <code>hashCode()</code>.  Leaf buckets stand at the
 | 
				
			||||||
 | 
					 * bottom of an H*tree because the hashing algorithm cannot further
 | 
				
			||||||
 | 
					 * discriminate between different keys based on their hash code.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class HTreeBucket<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The maximum number of elements (key, value) a non-leaf bucket
 | 
				
			||||||
 | 
					     * can contain.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final int OVERFLOW_SIZE = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Depth of this bucket.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private byte _depth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Keys and values in this bucket.  Keys are followed by values at KEYPOS+OVERFLOW_SIZE
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Object[] _keysAndValues;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private byte size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final HTree<K, V> tree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Public constructor for serialization.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public HTreeBucket(HTree<K, V> tree) {
 | 
				
			||||||
 | 
					        this.tree = tree;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Construct a bucket with a given depth level.  Depth level is the
 | 
				
			||||||
 | 
					     * number of <code>HashDirectory</code> above this bucket.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public HTreeBucket(HTree<K, V> tree, byte level) {
 | 
				
			||||||
 | 
					        this.tree = tree;
 | 
				
			||||||
 | 
					        if (level > HTreeDirectory.MAX_DEPTH + 1) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException(
 | 
				
			||||||
 | 
					                    "Cannot create bucket with depth > MAX_DEPTH+1. "
 | 
				
			||||||
 | 
					                            + "Depth=" + level);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        _depth = level;
 | 
				
			||||||
 | 
					        _keysAndValues = new Object[OVERFLOW_SIZE * 2];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of elements contained in this bucket.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getElementCount() {
 | 
				
			||||||
 | 
					        return size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether or not this bucket is a "leaf bucket".
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isLeaf() {
 | 
				
			||||||
 | 
					        return (_depth > HTreeDirectory.MAX_DEPTH);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns true if bucket can accept at least one more element.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean hasRoom() {
 | 
				
			||||||
 | 
					        if (isLeaf()) {
 | 
				
			||||||
 | 
					            return true;  // leaf buckets are never full
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // non-leaf bucket
 | 
				
			||||||
 | 
					            return (size < OVERFLOW_SIZE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Add an element (key, value) to this bucket.  If an existing element
 | 
				
			||||||
 | 
					     * has the same key, it is replaced silently.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Object which was previously associated with the given key
 | 
				
			||||||
 | 
					     *         or <code>null</code> if no association existed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public V addElement(K key, V value) {
 | 
				
			||||||
 | 
					        //find entry
 | 
				
			||||||
 | 
					        byte existing = -1;
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            if (key.equals(_keysAndValues[i])) {
 | 
				
			||||||
 | 
					                existing = i;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (existing != -1) {
 | 
				
			||||||
 | 
					            // replace existing element
 | 
				
			||||||
 | 
					            Object before = _keysAndValues[existing + OVERFLOW_SIZE];
 | 
				
			||||||
 | 
					            if (before instanceof BTreeLazyRecord) {
 | 
				
			||||||
 | 
					                BTreeLazyRecord<V> rec = (BTreeLazyRecord<V>) before;
 | 
				
			||||||
 | 
					                before = rec.get();
 | 
				
			||||||
 | 
					                rec.delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _keysAndValues[existing + OVERFLOW_SIZE] = value;
 | 
				
			||||||
 | 
					            return (V) before;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // add new (key, value) pair
 | 
				
			||||||
 | 
					            _keysAndValues[size] = key;
 | 
				
			||||||
 | 
					            _keysAndValues[size + OVERFLOW_SIZE] = value;
 | 
				
			||||||
 | 
					            size++;
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Remove an element, given a specific key.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Key of the element to remove
 | 
				
			||||||
 | 
					     * @return Removed element value, or <code>null</code> if not found
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public V removeElement(K key) {
 | 
				
			||||||
 | 
					        //find entry
 | 
				
			||||||
 | 
					        byte existing = -1;
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            if (key.equals(_keysAndValues[i])) {
 | 
				
			||||||
 | 
					                existing = i;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (existing != -1) {
 | 
				
			||||||
 | 
					            Object o = _keysAndValues[existing + OVERFLOW_SIZE];
 | 
				
			||||||
 | 
					            if (o instanceof BTreeLazyRecord) {
 | 
				
			||||||
 | 
					                BTreeLazyRecord<V> rec = (BTreeLazyRecord<V>) o;
 | 
				
			||||||
 | 
					                o = rec.get();
 | 
				
			||||||
 | 
					                rec.delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //move last element to existing
 | 
				
			||||||
 | 
					            size--;
 | 
				
			||||||
 | 
					            _keysAndValues[existing] = _keysAndValues[size];
 | 
				
			||||||
 | 
					            _keysAndValues[existing + OVERFLOW_SIZE] = _keysAndValues[size + OVERFLOW_SIZE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //and unset last element
 | 
				
			||||||
 | 
					            _keysAndValues[size] = null;
 | 
				
			||||||
 | 
					            _keysAndValues[size + OVERFLOW_SIZE] = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return (V) o;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // not found
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the value associated with a given key.  If the given key
 | 
				
			||||||
 | 
					     * is not found in this bucket, returns <code>null</code>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public V getValue(K key) {
 | 
				
			||||||
 | 
					        //find entry
 | 
				
			||||||
 | 
					        byte existing = -1;
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            if (key.equals(_keysAndValues[i])) {
 | 
				
			||||||
 | 
					                existing = i;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (existing != -1) {
 | 
				
			||||||
 | 
					            Object o = _keysAndValues[existing + OVERFLOW_SIZE];
 | 
				
			||||||
 | 
					            if (o instanceof BTreeLazyRecord)
 | 
				
			||||||
 | 
					                return ((BTreeLazyRecord<V>) o).get();
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                return (V) o;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // key not found
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Obtain keys contained in this buckets.  Keys are ordered to match
 | 
				
			||||||
 | 
					     * their values, which be be obtained by calling <code>getValues()</code>.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * As an optimization, the Vector returned is the instance member
 | 
				
			||||||
 | 
					     * of this class.  Please don't modify outside the scope of this class.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ArrayList<K> getKeys() {
 | 
				
			||||||
 | 
					        ArrayList<K> ret = new ArrayList<K>();
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            ret.add((K) _keysAndValues[i]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Obtain values contained in this buckets.  Values are ordered to match
 | 
				
			||||||
 | 
					     * their keys, which be be obtained by calling <code>getKeys()</code>.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * As an optimization, the Vector returned is the instance member
 | 
				
			||||||
 | 
					     * of this class.  Please don't modify outside the scope of this class.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ArrayList<V> getValues() {
 | 
				
			||||||
 | 
					        ArrayList<V> ret = new ArrayList<V>();
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            ret.add((V) _keysAndValues[i + OVERFLOW_SIZE]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeExternal(DataOutput out)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        out.write(_depth);
 | 
				
			||||||
 | 
					        out.write(size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DataInputOutput out3 = tree.writeBufferCache.getAndSet(null);
 | 
				
			||||||
 | 
					        if (out3 == null)
 | 
				
			||||||
 | 
					            out3 = new DataInputOutput();
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            out3.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Serializer keySerializer = tree.keySerializer != null ? tree.keySerializer : tree.getRecordManager().defaultSerializer();
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            out3.reset();
 | 
				
			||||||
 | 
					            keySerializer.serialize(out3, _keysAndValues[i]);
 | 
				
			||||||
 | 
					            LongPacker.packInt(out, out3.getPos());
 | 
				
			||||||
 | 
					            out.write(out3.getBuf(), 0, out3.getPos());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //write values
 | 
				
			||||||
 | 
					        if(tree.hasValues()){
 | 
				
			||||||
 | 
					            Serializer valSerializer = tree.valueSerializer != null ? tree.valueSerializer : tree.getRecordManager().defaultSerializer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					                Object value = _keysAndValues[i + OVERFLOW_SIZE];
 | 
				
			||||||
 | 
					                if (value == null) {
 | 
				
			||||||
 | 
					                    out.write(BTreeLazyRecord.NULL);
 | 
				
			||||||
 | 
					                } else if (value instanceof BTreeLazyRecord) {
 | 
				
			||||||
 | 
					                    out.write(BTreeLazyRecord.LAZY_RECORD);
 | 
				
			||||||
 | 
					                    LongPacker.packLong(out, ((BTreeLazyRecord) value).recid);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    //transform to byte array
 | 
				
			||||||
 | 
					                    out3.reset();
 | 
				
			||||||
 | 
					                    valSerializer.serialize(out3, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (out3.getPos() > BTreeLazyRecord.MAX_INTREE_RECORD_SIZE) {
 | 
				
			||||||
 | 
					                        //store as separate record
 | 
				
			||||||
 | 
					                        long recid = tree.getRecordManager().insert(out3.toByteArray(), BTreeLazyRecord.FAKE_SERIALIZER,true);
 | 
				
			||||||
 | 
					                        out.write(BTreeLazyRecord.LAZY_RECORD);
 | 
				
			||||||
 | 
					                        LongPacker.packLong(out, recid);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        out.write(out3.getPos());
 | 
				
			||||||
 | 
					                        out.write(out3.getBuf(), 0, out3.getPos());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        tree.writeBufferCache.set(out3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readExternal(DataInputOutput in) throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					        _depth = in.readByte();
 | 
				
			||||||
 | 
					        size = in.readByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //read keys
 | 
				
			||||||
 | 
					        Serializer keySerializer = tree.keySerializer != null ? tree.keySerializer : tree.getRecordManager().defaultSerializer();
 | 
				
			||||||
 | 
					        _keysAndValues = (K[]) new Object[OVERFLOW_SIZE * 2];
 | 
				
			||||||
 | 
					        for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					            int expectedSize = LongPacker.unpackInt(in);
 | 
				
			||||||
 | 
					            K key = (K) BTreeLazyRecord.fastDeser(in, keySerializer, expectedSize);
 | 
				
			||||||
 | 
					            _keysAndValues[i] = key;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //read values
 | 
				
			||||||
 | 
					        if(tree.hasValues()){
 | 
				
			||||||
 | 
					            Serializer<V> valSerializer = tree.valueSerializer != null ? tree.valueSerializer : (Serializer<V>) tree.getRecordManager().defaultSerializer();
 | 
				
			||||||
 | 
					            for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					                int header = in.readUnsignedByte();
 | 
				
			||||||
 | 
					                if (header == BTreeLazyRecord.NULL) {
 | 
				
			||||||
 | 
					                    _keysAndValues[i + OVERFLOW_SIZE] = null;
 | 
				
			||||||
 | 
					                } else if (header == BTreeLazyRecord.LAZY_RECORD) {
 | 
				
			||||||
 | 
					                    long recid = LongPacker.unpackLong(in);
 | 
				
			||||||
 | 
					                    _keysAndValues[i + OVERFLOW_SIZE] = (new BTreeLazyRecord(tree.getRecordManager(), recid, valSerializer));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    _keysAndValues[i + OVERFLOW_SIZE] = BTreeLazyRecord.fastDeser(in, valSerializer, header);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					            for (byte i = 0; i < size; i++) {
 | 
				
			||||||
 | 
					                if(_keysAndValues[i]!=null)
 | 
				
			||||||
 | 
					                    _keysAndValues[i+OVERFLOW_SIZE] = Utils.EMPTY_STRING;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,618 @@
 | 
				
			|||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					 * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Hashtable directory page.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Alex Boisvert
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class HTreeDirectory<K, V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Maximum number of children in a directory.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * (Must be a power of 2 -- if you update this value, you must also
 | 
				
			||||||
 | 
					     * update BIT_SIZE and MAX_DEPTH.)
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * !!!! do not change this, it affects storage format, there are also magic numbers which relies on 255 !!!
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final int MAX_CHILDREN = 256;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Number of significant bits per directory level.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final int BIT_SIZE = 8; // log2(256) = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Maximum number of levels (zero-based)
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * (4 * 8 bits = 32 bits, which is the size of an "int", and as
 | 
				
			||||||
 | 
					     * you know, hashcodes in Java are "ints")
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final int MAX_DEPTH = 3; // 4 levels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Record ids of children nodes.
 | 
				
			||||||
 | 
					     * It is saved in matrix to save memory, some subarrays may be null.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private long[][] _children;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Depth of this directory page, zero-based
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private byte _depth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This directory's record ID in the DB.  (transient)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private long _recid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** if this is root (depth=0), it contains size, otherwise -1*/
 | 
				
			||||||
 | 
					    long size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected final HTree<K, V> tree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Public constructor used by serialization
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public HTreeDirectory(HTree<K, V> tree) {
 | 
				
			||||||
 | 
					        this.tree = tree;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Construct a HashDirectory
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param depth Depth of this directory node.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    HTreeDirectory(HTree<K, V> tree, byte depth) {
 | 
				
			||||||
 | 
					        this.tree = tree;
 | 
				
			||||||
 | 
					        _depth = depth;
 | 
				
			||||||
 | 
					        _children = new long[32][];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets persistence context.  This method must be called before any
 | 
				
			||||||
 | 
					     * persistence-related operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param recid Record id of this directory.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void setPersistenceContext(long recid) {
 | 
				
			||||||
 | 
					        this._recid = recid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the record identifier used to load this hashtable.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    long getRecid() {
 | 
				
			||||||
 | 
					        return _recid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether or not this directory is empty.  A directory
 | 
				
			||||||
 | 
					     * is empty when it no longer contains buckets or sub-directories.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean isEmpty() {
 | 
				
			||||||
 | 
					        for (int i = 0; i < _children.length; i++) {
 | 
				
			||||||
 | 
					            long[] sub = _children[i];
 | 
				
			||||||
 | 
					            if (sub!=null){
 | 
				
			||||||
 | 
					                for (int j = 0; j < 8; j++) {
 | 
				
			||||||
 | 
					                    if(sub[j] != 0) {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the value which is associated with the given key. Returns
 | 
				
			||||||
 | 
					     * <code>null</code> if there is not association for this key.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key key whose associated value is to be returned
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    V get(K key)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        int hash = hashCode(key);
 | 
				
			||||||
 | 
					        long child_recid = getRecid(hash);
 | 
				
			||||||
 | 
					        if (child_recid == 0) {
 | 
				
			||||||
 | 
					            // not bucket/node --> not found
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
 | 
				
			||||||
 | 
					            // System.out.println("HashDirectory.get() child is : "+node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (node instanceof HTreeDirectory) {
 | 
				
			||||||
 | 
					                // recurse into next directory level
 | 
				
			||||||
 | 
					                HTreeDirectory<K, V> dir = (HTreeDirectory<K, V>) node;
 | 
				
			||||||
 | 
					                dir.setPersistenceContext(child_recid);
 | 
				
			||||||
 | 
					                return dir.get(key);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // node is a bucket
 | 
				
			||||||
 | 
					                HTreeBucket<K, V> bucket = (HTreeBucket) node;
 | 
				
			||||||
 | 
					                return bucket.getValue(key);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private long getRecid(int hash) {
 | 
				
			||||||
 | 
					        long[] sub = _children[hash>>>3];
 | 
				
			||||||
 | 
					        return sub==null? 0 : sub[hash%8];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void putRecid(int hash, long recid) {
 | 
				
			||||||
 | 
					        long[] sub = _children[hash>>>3];
 | 
				
			||||||
 | 
					        if(sub == null){
 | 
				
			||||||
 | 
					            sub = new long[8];
 | 
				
			||||||
 | 
					            _children[hash>>>3] = sub;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        sub[hash%8] = recid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Associates the specified value with the specified key.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key   key with which the specified value is to be assocated.
 | 
				
			||||||
 | 
					     * @param value value to be associated with the specified key.
 | 
				
			||||||
 | 
					     * @return object which was previously associated with the given key,
 | 
				
			||||||
 | 
					     *         or <code>null</code> if no association existed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Object put(final Object key, final Object value)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        if (value == null) {
 | 
				
			||||||
 | 
					            return remove(key);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        int hash = hashCode(key);
 | 
				
			||||||
 | 
					        long child_recid = getRecid(hash);
 | 
				
			||||||
 | 
					        if (child_recid == 0) {
 | 
				
			||||||
 | 
					            // no bucket/node here yet, let's create a bucket
 | 
				
			||||||
 | 
					            HTreeBucket bucket = new HTreeBucket(tree, (byte) (_depth + 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // insert (key,value) pair in bucket
 | 
				
			||||||
 | 
					            Object existing = bucket.addElement(key, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            long b_recid = tree.db.insert(bucket, tree.SERIALIZER,false);
 | 
				
			||||||
 | 
					            putRecid(hash, b_recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tree.db.update(_recid, this, tree.SERIALIZER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // System.out.println("Added: "+bucket);
 | 
				
			||||||
 | 
					            return existing;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (node instanceof HTreeDirectory) {
 | 
				
			||||||
 | 
					                // recursive insert in next directory level
 | 
				
			||||||
 | 
					                HTreeDirectory dir = (HTreeDirectory) node;
 | 
				
			||||||
 | 
					                dir.setPersistenceContext(child_recid);
 | 
				
			||||||
 | 
					                return dir.put(key, value);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // node is a bucket
 | 
				
			||||||
 | 
					                HTreeBucket bucket = (HTreeBucket) node;
 | 
				
			||||||
 | 
					                if (bucket.hasRoom()) {
 | 
				
			||||||
 | 
					                    Object existing = bucket.addElement(key, value);
 | 
				
			||||||
 | 
					                    tree.db.update(child_recid, bucket, tree.SERIALIZER);
 | 
				
			||||||
 | 
					                    // System.out.println("Added: "+bucket);
 | 
				
			||||||
 | 
					                    return existing;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // overflow, so create a new directory
 | 
				
			||||||
 | 
					                    if (_depth == MAX_DEPTH) {
 | 
				
			||||||
 | 
					                        throw new RuntimeException("Cannot create deeper directory. "
 | 
				
			||||||
 | 
					                                + "Depth=" + _depth);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    HTreeDirectory dir = new HTreeDirectory(tree, (byte) (_depth + 1));
 | 
				
			||||||
 | 
					                    long dir_recid = tree.db.insert(dir, tree.SERIALIZER,false);
 | 
				
			||||||
 | 
					                    dir.setPersistenceContext(dir_recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    putRecid(hash, dir_recid);
 | 
				
			||||||
 | 
					                    tree.db.update(_recid, this, tree.SERIALIZER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // discard overflown bucket
 | 
				
			||||||
 | 
					                    tree.db.delete(child_recid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // migrate existing bucket elements
 | 
				
			||||||
 | 
					                    ArrayList keys = bucket.getKeys();
 | 
				
			||||||
 | 
					                    ArrayList values = bucket.getValues();
 | 
				
			||||||
 | 
					                    int entries = keys.size();
 | 
				
			||||||
 | 
					                    for (int i = 0; i < entries; i++) {
 | 
				
			||||||
 | 
					                        dir.put(keys.get(i), values.get(i));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // (finally!) insert new element
 | 
				
			||||||
 | 
					                    return dir.put(key, value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Remove the value which is associated with the given key.  If the
 | 
				
			||||||
 | 
					     * key does not exist, this method simply ignores the operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key key whose associated value is to be removed
 | 
				
			||||||
 | 
					     * @return object which was associated with the given key, or
 | 
				
			||||||
 | 
					     *         <code>null</code> if no association existed with given key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Object remove(Object key) throws IOException {
 | 
				
			||||||
 | 
					        int hash = hashCode(key);
 | 
				
			||||||
 | 
					        long child_recid = getRecid(hash);
 | 
				
			||||||
 | 
					        if (child_recid == 0) {
 | 
				
			||||||
 | 
					            // not bucket/node --> not found
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
 | 
				
			||||||
 | 
					            // System.out.println("HashDirectory.remove() child is : "+node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (node instanceof HTreeDirectory) {
 | 
				
			||||||
 | 
					                // recurse into next directory level
 | 
				
			||||||
 | 
					                HTreeDirectory dir = (HTreeDirectory) node;
 | 
				
			||||||
 | 
					                dir.setPersistenceContext(child_recid);
 | 
				
			||||||
 | 
					                Object existing = dir.remove(key);
 | 
				
			||||||
 | 
					                if (existing != null) {
 | 
				
			||||||
 | 
					                    if (dir.isEmpty()) {
 | 
				
			||||||
 | 
					                        // delete empty directory
 | 
				
			||||||
 | 
					                        tree.db.delete(child_recid);
 | 
				
			||||||
 | 
					                        putRecid(hash, 0);
 | 
				
			||||||
 | 
					                        tree.db.update(_recid, this, tree.SERIALIZER);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return existing;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // node is a bucket
 | 
				
			||||||
 | 
					                HTreeBucket bucket = (HTreeBucket) node;
 | 
				
			||||||
 | 
					                Object existing = bucket.removeElement(key);
 | 
				
			||||||
 | 
					                if (existing != null) {
 | 
				
			||||||
 | 
					                    if (bucket.getElementCount() >= 1) {
 | 
				
			||||||
 | 
					                        tree.db.update(child_recid, bucket, tree.SERIALIZER);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // delete bucket, it's empty
 | 
				
			||||||
 | 
					                        tree.db.delete(child_recid);
 | 
				
			||||||
 | 
					                        putRecid(hash, 0);
 | 
				
			||||||
 | 
					                        tree.db.update(_recid, this, tree.SERIALIZER);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return existing;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Calculates the hashcode of a key, based on the current directory
 | 
				
			||||||
 | 
					     * depth.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private int hashCode(Object key) {
 | 
				
			||||||
 | 
					        int hashMask = hashMask();
 | 
				
			||||||
 | 
					        int hash = key.hashCode();
 | 
				
			||||||
 | 
					        hash = hash & hashMask;
 | 
				
			||||||
 | 
					        hash = hash >>> ((MAX_DEPTH - _depth) * BIT_SIZE);
 | 
				
			||||||
 | 
					        hash = hash % MAX_CHILDREN;
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					        System.out.println("HashDirectory.hashCode() is: 0x"
 | 
				
			||||||
 | 
					                           +Integer.toHexString(hash)
 | 
				
			||||||
 | 
					                           +" for object hashCode() 0x"
 | 
				
			||||||
 | 
					                           +Integer.toHexString(key.hashCode()));
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
 | 
					        return hash;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Calculates the hashmask of this directory.  The hashmask is the
 | 
				
			||||||
 | 
					     * bit mask applied to a hashcode to retain only bits that are
 | 
				
			||||||
 | 
					     * relevant to this directory level.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int hashMask() {
 | 
				
			||||||
 | 
					        int bits = MAX_CHILDREN - 1;
 | 
				
			||||||
 | 
					        int hashMask = bits << ((MAX_DEPTH - _depth) * BIT_SIZE);
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					        System.out.println("HashDirectory.hashMask() is: 0x"
 | 
				
			||||||
 | 
					                           +Integer.toHexString(hashMask));
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
 | 
					        return hashMask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns an enumeration of the keys contained in this
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Iterator<K> keys()
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        return new HDIterator(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns an enumeration of the values contained in this
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Iterator<V> values()
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        return new HDIterator(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeExternal(DataOutput out)
 | 
				
			||||||
 | 
					            throws IOException {
 | 
				
			||||||
 | 
					        out.writeByte(_depth);
 | 
				
			||||||
 | 
					        if(_depth==0){
 | 
				
			||||||
 | 
					            LongPacker.packLong(out,size);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int zeroStart = 0;
 | 
				
			||||||
 | 
					        for (int i = 0; i < MAX_CHILDREN; i++) {
 | 
				
			||||||
 | 
					            if (getRecid(i) != 0) {
 | 
				
			||||||
 | 
					                zeroStart = i;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        out.write(zeroStart);
 | 
				
			||||||
 | 
					        if (zeroStart == MAX_CHILDREN)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int zeroEnd = 0;
 | 
				
			||||||
 | 
					        for (int i = MAX_CHILDREN - 1; i >= 0; i--) {
 | 
				
			||||||
 | 
					            if (getRecid(i) != 0) {
 | 
				
			||||||
 | 
					                zeroEnd = i;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        out.write(zeroEnd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = zeroStart; i <= zeroEnd; i++) {
 | 
				
			||||||
 | 
					            LongPacker.packLong(out, getRecid(i));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readExternal(DataInputOutput in)
 | 
				
			||||||
 | 
					            throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					        _depth = in.readByte();
 | 
				
			||||||
 | 
					        if(_depth==0)
 | 
				
			||||||
 | 
					            size = LongPacker.unpackLong(in);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					           size = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _children = new long[32][];
 | 
				
			||||||
 | 
					        int zeroStart = in.readUnsignedByte();
 | 
				
			||||||
 | 
					        int zeroEnd = in.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = zeroStart; i <= zeroEnd; i++) {
 | 
				
			||||||
 | 
					            long recid = LongPacker.unpackLong(in);
 | 
				
			||||||
 | 
					            if(recid!=0)
 | 
				
			||||||
 | 
					                putRecid(i,recid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void defrag(DBStore r1, DBStore r2) throws IOException, ClassNotFoundException {
 | 
				
			||||||
 | 
					        for (long[] sub: _children) {
 | 
				
			||||||
 | 
					            if(sub==null) continue;
 | 
				
			||||||
 | 
					            for (long child : sub) {
 | 
				
			||||||
 | 
					                if (child == 0) continue;
 | 
				
			||||||
 | 
					                byte[] data = r1.fetchRaw(child);
 | 
				
			||||||
 | 
					                r2.forceInsert(child, data);
 | 
				
			||||||
 | 
					                Object t = tree.SERIALIZER.deserialize(new DataInputOutput(data));
 | 
				
			||||||
 | 
					                if (t instanceof HTreeDirectory) {
 | 
				
			||||||
 | 
					                    ((HTreeDirectory) t).defrag(r1, r2);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void deleteAllChildren() throws IOException {
 | 
				
			||||||
 | 
					        for(long[] ll : _children){
 | 
				
			||||||
 | 
					            if(ll!=null){
 | 
				
			||||||
 | 
					                for(long l:ll ){
 | 
				
			||||||
 | 
					                    if(l!=0){
 | 
				
			||||||
 | 
					                      tree.db.delete(l);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					    // INNER CLASS
 | 
				
			||||||
 | 
					    ////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Utility class to enumerate keys/values in a HTree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    class HDIterator<A> implements Iterator<A> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * True if we're iterating on keys, False if enumerating on values.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private boolean _iterateKeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Stacks of directories & last enumerated child position
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private ArrayList _dirStack;
 | 
				
			||||||
 | 
					        private ArrayList _childStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Current HashDirectory in the hierarchy
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private HTreeDirectory _dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Current child position
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private int _child;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Current bucket iterator
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private Iterator<A> _iter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private A next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * last item returned in next(), is used to remove() last item
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private A last;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private int expectedModCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Construct an iterator on this directory.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param iterateKeys True if iteration supplies keys, False
 | 
				
			||||||
 | 
					         *                    if iterateKeys supplies values.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        HDIterator(boolean iterateKeys)
 | 
				
			||||||
 | 
					                throws IOException {
 | 
				
			||||||
 | 
					            _dirStack = new ArrayList();
 | 
				
			||||||
 | 
					            _childStack = new ArrayList();
 | 
				
			||||||
 | 
					            _dir = HTreeDirectory.this;
 | 
				
			||||||
 | 
					            _child = -1;
 | 
				
			||||||
 | 
					            _iterateKeys = iterateKeys;
 | 
				
			||||||
 | 
					            expectedModCount = tree.modCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            prepareNext();
 | 
				
			||||||
 | 
					            next = next2();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Returns the next object.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public A next2() {
 | 
				
			||||||
 | 
					            A next = null;
 | 
				
			||||||
 | 
					            if (_iter != null && _iter.hasNext()) {
 | 
				
			||||||
 | 
					                next = _iter.next();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    prepareNext();
 | 
				
			||||||
 | 
					                } catch (IOException except) {
 | 
				
			||||||
 | 
					                    throw new IOError(except);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (_iter != null && _iter.hasNext()) {
 | 
				
			||||||
 | 
					                    return next2();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return next;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Prepare internal state so we can answer <code>hasMoreElements</code>
 | 
				
			||||||
 | 
					         * <p/>
 | 
				
			||||||
 | 
					         * Actually, this code prepares an Enumeration on the next
 | 
				
			||||||
 | 
					         * Bucket to enumerate.   If no following bucket is found,
 | 
				
			||||||
 | 
					         * the next Enumeration is set to <code>null</code>.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private void prepareNext() throws IOException {
 | 
				
			||||||
 | 
					            long child_recid = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // get next bucket/directory to enumerate
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                _child++;
 | 
				
			||||||
 | 
					                if (_child >= MAX_CHILDREN) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (_dirStack.isEmpty()) {
 | 
				
			||||||
 | 
					                        // no more directory in the stack, we're finished
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // try next node
 | 
				
			||||||
 | 
					                    _dir = (HTreeDirectory) _dirStack.remove(_dirStack.size() - 1);
 | 
				
			||||||
 | 
					                    _child = ((Integer) _childStack.remove(_childStack.size() - 1)).intValue();
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                child_recid = _dir.getRecid(_child);
 | 
				
			||||||
 | 
					            } while (child_recid == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (child_recid == 0) {
 | 
				
			||||||
 | 
					                throw new Error("child_recid cannot be 0");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
 | 
				
			||||||
 | 
					            // System.out.println("HDEnumeration.get() child is : "+node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (node instanceof HTreeDirectory) {
 | 
				
			||||||
 | 
					                // save current position
 | 
				
			||||||
 | 
					                _dirStack.add(_dir);
 | 
				
			||||||
 | 
					                _childStack.add(new Integer(_child));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _dir = (HTreeDirectory) node;
 | 
				
			||||||
 | 
					                _child = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // recurse into
 | 
				
			||||||
 | 
					                _dir.setPersistenceContext(child_recid);
 | 
				
			||||||
 | 
					                prepareNext();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // node is a bucket
 | 
				
			||||||
 | 
					                HTreeBucket bucket = (HTreeBucket) node;
 | 
				
			||||||
 | 
					                if (_iterateKeys) {
 | 
				
			||||||
 | 
					                    ArrayList keys2 = bucket.getKeys();
 | 
				
			||||||
 | 
					                    _iter = keys2.iterator();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    _iter = bucket.getValues().iterator();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean hasNext() {
 | 
				
			||||||
 | 
					            return next != null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public A next() {
 | 
				
			||||||
 | 
					            if (next == null) throw new NoSuchElementException();
 | 
				
			||||||
 | 
					            if (expectedModCount != tree.modCount)
 | 
				
			||||||
 | 
					                throw new ConcurrentModificationException();
 | 
				
			||||||
 | 
					            last = next;
 | 
				
			||||||
 | 
					            next = next2();
 | 
				
			||||||
 | 
					            return last;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void remove() {
 | 
				
			||||||
 | 
					            if (last == null) throw new IllegalStateException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (expectedModCount != tree.modCount)
 | 
				
			||||||
 | 
					                throw new ConcurrentModificationException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //TODO current delete behaviour may change node layout. INVESTIGATE if this can happen!
 | 
				
			||||||
 | 
					            tree.remove(last);
 | 
				
			||||||
 | 
					            last = null;
 | 
				
			||||||
 | 
					            expectedModCount++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					package org.apache.jdbm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.AbstractSet;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Wrapper for HTree to implement java.util.Map interface
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class HTreeSet<E> extends AbstractSet<E> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final HTree<E, Object> map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HTreeSet(HTree map) {
 | 
				
			||||||
 | 
					        this.map = map;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Iterator<E> iterator() {
 | 
				
			||||||
 | 
					        return map.keySet().iterator();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int size() {
 | 
				
			||||||
 | 
					        return map.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isEmpty() {
 | 
				
			||||||
 | 
					        return map.isEmpty();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean contains(Object o) {
 | 
				
			||||||
 | 
					        return map.containsKey(o);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean add(E e) {
 | 
				
			||||||
 | 
					        return map.put(e, Utils.EMPTY_STRING) == null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean remove(Object o) {
 | 
				
			||||||
 | 
					        return map.remove(o) == Utils.EMPTY_STRING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clear() {
 | 
				
			||||||
 | 
					        map.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user