add user-friendly way to access field properties if char is a beginning of field
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1144336 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
34b6794422
commit
63c571ca1f
150
src/scratchpad/src/org/apache/poi/hwpf/model/Field.java
Normal file
150
src/scratchpad/src/org/apache/poi/hwpf/model/Field.java
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package org.apache.poi.hwpf.model;
|
||||||
|
|
||||||
|
import org.apache.poi.hwpf.usermodel.Range;
|
||||||
|
|
||||||
|
public class Field
|
||||||
|
{
|
||||||
|
private PlexOfField startPlex;
|
||||||
|
private PlexOfField separatorPlex;
|
||||||
|
private PlexOfField endPlex;
|
||||||
|
|
||||||
|
public Field( PlexOfField startPlex, PlexOfField separatorPlex,
|
||||||
|
PlexOfField endPlex )
|
||||||
|
{
|
||||||
|
if ( startPlex == null )
|
||||||
|
throw new IllegalArgumentException( "startPlex == null" );
|
||||||
|
if ( endPlex == null )
|
||||||
|
throw new IllegalArgumentException( "endPlex == null" );
|
||||||
|
|
||||||
|
if ( startPlex.getFld().getBoundaryType() != FieldDescriptor.FIELD_BEGIN_MARK )
|
||||||
|
throw new IllegalArgumentException( "startPlex (" + startPlex
|
||||||
|
+ ") is not type of FIELD_BEGIN" );
|
||||||
|
|
||||||
|
if ( separatorPlex != null
|
||||||
|
&& separatorPlex.getFld().getBoundaryType() != FieldDescriptor.FIELD_SEPARATOR_MARK )
|
||||||
|
throw new IllegalArgumentException( "separatorPlex" + separatorPlex
|
||||||
|
+ ") is not type of FIELD_SEPARATOR" );
|
||||||
|
|
||||||
|
if ( endPlex.getFld().getBoundaryType() != FieldDescriptor.FIELD_END_MARK )
|
||||||
|
throw new IllegalArgumentException( "endPlex (" + endPlex
|
||||||
|
+ ") is not type of FIELD_END" );
|
||||||
|
|
||||||
|
this.startPlex = startPlex;
|
||||||
|
this.separatorPlex = separatorPlex;
|
||||||
|
this.endPlex = endPlex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartOffset()
|
||||||
|
{
|
||||||
|
return startPlex.getFcStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndOffset()
|
||||||
|
{
|
||||||
|
return endPlex.getFcEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSeparator()
|
||||||
|
{
|
||||||
|
return separatorPlex != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSeparatorOffset()
|
||||||
|
{
|
||||||
|
return separatorPlex.getFcStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType()
|
||||||
|
{
|
||||||
|
return startPlex.getFld().getFieldType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isZombieEmbed()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFZombieEmbed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isResultDirty()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFResultDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isResultEdited()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFResultEdited();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLocked()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrivateResult()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFPrivateResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNested()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFNested();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasSep()
|
||||||
|
{
|
||||||
|
return endPlex.getFld().isFHasSep();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Range firstSubrange( Range parent )
|
||||||
|
{
|
||||||
|
if ( hasSeparator() )
|
||||||
|
{
|
||||||
|
if ( getStartOffset() + 1 == getSeparatorOffset() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Range( getStartOffset() + 1, getSeparatorOffset(),
|
||||||
|
parent )
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "FieldSubrange1 (" + super.toString() + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( getStartOffset() + 1 == getEndOffset() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Range( getStartOffset() + 1, getEndOffset(), parent )
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "FieldSubrange1 (" + super.toString() + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Range secondSubrange( Range parent )
|
||||||
|
{
|
||||||
|
if ( !hasSeparator() || getSeparatorOffset() + 1 == getEndOffset() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Range( getSeparatorOffset() + 1, getEndOffset(), parent )
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "FieldSubrange2 (" + super.toString() + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Field [" + getStartOffset() + "; " + getEndOffset()
|
||||||
|
+ "] (type: 0x" + Integer.toHexString( getType() ) + " = "
|
||||||
|
+ getType() + " )";
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,11 @@ package org.apache.poi.hwpf.model;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
||||||
|
|
||||||
@ -33,6 +37,21 @@ import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
|||||||
*/
|
*/
|
||||||
public class FieldsTables
|
public class FieldsTables
|
||||||
{
|
{
|
||||||
|
private static final byte[] BYTES_EMPTY = new byte[0];
|
||||||
|
|
||||||
|
private static final class GenericPropertyNodeComparator implements
|
||||||
|
Comparator<GenericPropertyNode>
|
||||||
|
{
|
||||||
|
public int compare( GenericPropertyNode o1, GenericPropertyNode o2 )
|
||||||
|
{
|
||||||
|
int thisVal = o1.getStart();
|
||||||
|
int anotherVal = o2.getStart();
|
||||||
|
return thisVal < anotherVal ? -1 : thisVal == anotherVal ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenericPropertyNodeComparator comparator = new GenericPropertyNodeComparator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* annotation subdocument
|
* annotation subdocument
|
||||||
*/
|
*/
|
||||||
@ -65,18 +84,163 @@ public class FieldsTables
|
|||||||
// The size in bytes of the FLD data structure
|
// The size in bytes of the FLD data structure
|
||||||
private static final int FLD_SIZE = 2;
|
private static final int FLD_SIZE = 2;
|
||||||
|
|
||||||
private HashMap<Integer, PlexOfCps> _tables;
|
private Map<Integer, PlexOfCps> _tables;
|
||||||
|
private Map<Integer, Map<Integer, Field>> _fieldsByOffset;
|
||||||
|
|
||||||
public FieldsTables( byte[] tableStream, FileInformationBlock fib )
|
public FieldsTables( byte[] tableStream, FileInformationBlock fib )
|
||||||
{
|
{
|
||||||
_tables = new HashMap<Integer, PlexOfCps>();
|
_tables = new HashMap<Integer, PlexOfCps>( PLCFFLDTXBX - PLCFFLDATN + 1 );
|
||||||
|
_fieldsByOffset = new HashMap<Integer, Map<Integer, Field>>(
|
||||||
|
PLCFFLDTXBX - PLCFFLDATN + 1 );
|
||||||
|
|
||||||
for ( int i = PLCFFLDATN; i <= PLCFFLDTXBX; i++ )
|
for ( int i = PLCFFLDATN; i <= PLCFFLDTXBX; i++ )
|
||||||
{
|
{
|
||||||
_tables.put( Integer.valueOf( i ), readPLCF( tableStream, fib, i ) );
|
final PlexOfCps plexOfCps = readPLCF( tableStream, fib, i );
|
||||||
|
_fieldsByOffset.put( Integer.valueOf( i ),
|
||||||
|
parseFieldStructure( plexOfCps ) );
|
||||||
|
_tables.put( Integer.valueOf( i ), plexOfCps );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<Integer, Field> parseFieldStructure( PlexOfCps plexOfCps )
|
||||||
|
{
|
||||||
|
if (plexOfCps == null)
|
||||||
|
return new HashMap<Integer, Field>();
|
||||||
|
|
||||||
|
GenericPropertyNode[] nodes = plexOfCps.toPropertiesArray();
|
||||||
|
Arrays.sort( nodes, comparator );
|
||||||
|
List<Field> fields = new ArrayList<Field>( nodes.length / 3 + 1 );
|
||||||
|
parseFieldStructureImpl( nodes, 0, nodes.length, fields );
|
||||||
|
|
||||||
|
HashMap<Integer, Field> result = new HashMap<Integer, Field>(
|
||||||
|
fields.size() );
|
||||||
|
for ( Field field : fields )
|
||||||
|
{
|
||||||
|
result.put( Integer.valueOf( field.getStartOffset() ), field );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseFieldStructureImpl( GenericPropertyNode[] nodes,
|
||||||
|
int startOffsetInclusive, int endOffsetExclusive, List<Field> result )
|
||||||
|
{
|
||||||
|
int next = startOffsetInclusive;
|
||||||
|
while ( next < endOffsetExclusive )
|
||||||
|
{
|
||||||
|
GenericPropertyNode startNode = nodes[next];
|
||||||
|
PlexOfField startPlexOfField = new PlexOfField( startNode );
|
||||||
|
if ( startPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_BEGIN_MARK )
|
||||||
|
{
|
||||||
|
/* Start mark seems to be missing */
|
||||||
|
next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we have start node. end offset points to next node, separator or
|
||||||
|
* end
|
||||||
|
*/
|
||||||
|
int nextNodePositionInArray = Arrays.binarySearch(
|
||||||
|
nodes,
|
||||||
|
next + 1,
|
||||||
|
endOffsetExclusive,
|
||||||
|
new GenericPropertyNode( startNode.getEnd(), startNode
|
||||||
|
.getEnd() + 1, BYTES_EMPTY ), comparator );
|
||||||
|
if ( nextNodePositionInArray < 0 )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* too bad, this start field mark doesn't have corresponding end
|
||||||
|
* field mark or separator field mark in fields table
|
||||||
|
*/
|
||||||
|
next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GenericPropertyNode nextNode = nodes[nextNodePositionInArray];
|
||||||
|
PlexOfField nextPlexOfField = new PlexOfField( nextNode );
|
||||||
|
|
||||||
|
switch ( nextPlexOfField.getFld().getBoundaryType() )
|
||||||
|
{
|
||||||
|
case FieldDescriptor.FIELD_SEPARATOR_MARK:
|
||||||
|
{
|
||||||
|
GenericPropertyNode separatorNode = nextNode;
|
||||||
|
PlexOfField separatorPlexOfField = nextPlexOfField;
|
||||||
|
|
||||||
|
int endNodePositionInArray = Arrays.binarySearch( nodes,
|
||||||
|
nextNodePositionInArray, endOffsetExclusive,
|
||||||
|
new GenericPropertyNode( separatorNode.getEnd(),
|
||||||
|
separatorNode.getEnd() + 1, BYTES_EMPTY ),
|
||||||
|
comparator );
|
||||||
|
if ( endNodePositionInArray < 0 )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* too bad, this separator field mark doesn't have
|
||||||
|
* corresponding end field mark in fields table
|
||||||
|
*/
|
||||||
|
next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GenericPropertyNode endNode = nodes[endNodePositionInArray];
|
||||||
|
PlexOfField endPlexOfField = new PlexOfField( endNode );
|
||||||
|
|
||||||
|
Field field = new Field( startPlexOfField,
|
||||||
|
separatorPlexOfField, endPlexOfField );
|
||||||
|
result.add( field );
|
||||||
|
|
||||||
|
// adding included fields
|
||||||
|
if ( startNode.getStart() + 1 < separatorNode.getStart() - 1 )
|
||||||
|
{
|
||||||
|
parseFieldStructureImpl( nodes, next + 1,
|
||||||
|
nextNodePositionInArray, result );
|
||||||
|
}
|
||||||
|
if ( separatorNode.getStart() + 1 < endNode.getStart() - 1 )
|
||||||
|
{
|
||||||
|
parseFieldStructureImpl( nodes,
|
||||||
|
nextNodePositionInArray + 1,
|
||||||
|
endNodePositionInArray, result );
|
||||||
|
}
|
||||||
|
|
||||||
|
next = endNodePositionInArray + 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FieldDescriptor.FIELD_END_MARK:
|
||||||
|
{
|
||||||
|
// we have no separator
|
||||||
|
Field field = new Field( startPlexOfField, null,
|
||||||
|
nextPlexOfField );
|
||||||
|
result.add( field );
|
||||||
|
|
||||||
|
// adding included fields
|
||||||
|
if ( startNode.getStart() + 1 < nextNode.getStart() - 1 )
|
||||||
|
{
|
||||||
|
parseFieldStructureImpl( nodes, next + 1,
|
||||||
|
nextNodePositionInArray, result );
|
||||||
|
}
|
||||||
|
|
||||||
|
next = nextNodePositionInArray + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FieldDescriptor.FIELD_BEGIN_MARK:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
/* something is wrong, ignoring this mark along with start mark */
|
||||||
|
next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field lookupFieldByStartOffset( int documentPart, int offset )
|
||||||
|
{
|
||||||
|
Map<Integer, Field> map = _fieldsByOffset.get( Integer
|
||||||
|
.valueOf( documentPart ) );
|
||||||
|
if ( map == null || map.isEmpty() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return map.get( Integer.valueOf( offset ) );
|
||||||
|
}
|
||||||
|
|
||||||
private PlexOfCps readPLCF( byte[] tableStream, FileInformationBlock fib,
|
private PlexOfCps readPLCF( byte[] tableStream, FileInformationBlock fib,
|
||||||
int type )
|
int type )
|
||||||
{
|
{
|
||||||
|
@ -146,4 +146,12 @@ public final class PlexOfCps
|
|||||||
{
|
{
|
||||||
return ( 4 * ( _iMac + 1 ) ) + ( _cbStruct * index );
|
return ( 4 * ( _iMac + 1 ) ) + ( _cbStruct * index );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GenericPropertyNode[] toPropertiesArray()
|
||||||
|
{
|
||||||
|
if ( _props == null || _props.isEmpty() )
|
||||||
|
return new GenericPropertyNode[0];
|
||||||
|
|
||||||
|
return _props.toArray( new GenericPropertyNode[_props.size()] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,13 @@
|
|||||||
|
|
||||||
package org.apache.poi.hwpf.model;
|
package org.apache.poi.hwpf.model;
|
||||||
|
|
||||||
import junit.framework.*;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import org.apache.poi.hwpf.*;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.apache.poi.hwpf.HWPFDocFixture;
|
||||||
|
|
||||||
public final class TestDocumentProperties
|
public final class TestDocumentProperties
|
||||||
extends TestCase
|
extends TestCase
|
||||||
{
|
{
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
|
|
||||||
package org.apache.poi.hwpf.model;
|
package org.apache.poi.hwpf.model;
|
||||||
|
|
||||||
import junit.framework.*;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import org.apache.poi.hwpf.*;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
import junit.framework.TestCase;
|
||||||
|
import org.apache.poi.hwpf.HWPFDocFixture;
|
||||||
|
|
||||||
public final class TestFileInformationBlock
|
public final class TestFileInformationBlock
|
||||||
extends TestCase
|
extends TestCase
|
||||||
|
Loading…
Reference in New Issue
Block a user