import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.hwpf.usermodel.Bookmarks;
import org.apache.poi.hwpf.usermodel.BookmarksImpl;
+import org.apache.poi.hwpf.usermodel.Field;
+import org.apache.poi.hwpf.usermodel.Fields;
+import org.apache.poi.hwpf.usermodel.FieldsImpl;
import org.apache.poi.hwpf.usermodel.HWPFList;
import org.apache.poi.hwpf.usermodel.Notes;
import org.apache.poi.hwpf.usermodel.NotesImpl;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.Internal;
/**
/** Holds the fields PLCFs */
protected FieldsTables _fieldsTables;
+ /** Holds the fields */
+ protected Fields _fields;
+
protected HWPFDocument()
{
super();
_footnotes = new NotesImpl( _footnotesTables );
_fieldsTables = new FieldsTables(_tableStream, _fib);
+ _fields = new FieldsImpl(_fieldsTables);
}
public TextPieceTable getTextTable()
/**
* @return FieldsTables object, that is able to extract fields descriptors from this document
+ * @deprecated
*/
+ @Deprecated
+ @Internal
public FieldsTables getFieldsTables() {
return _fieldsTables;
}
-
+
+ /**
+ * Returns user-friendly interface to access document {@link Field}s
+ *
+ * @return user-friendly interface to access document {@link Field}s
+ */
+ public Fields getFields()
+ {
+ return _fields;
+ }
+
/**
* Writes out the word file that is represented by an instance of this class.
*
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFDocumentCore;
import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
-import org.apache.poi.hwpf.model.Field;
import org.apache.poi.hwpf.model.FieldsDocumentPart;
import org.apache.poi.hwpf.model.ListFormatOverride;
import org.apache.poi.hwpf.model.ListTables;
import org.apache.poi.hwpf.usermodel.Bookmark;
import org.apache.poi.hwpf.usermodel.CharacterRun;
+import org.apache.poi.hwpf.usermodel.Field;
import org.apache.poi.hwpf.usermodel.Notes;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Picture;
{
if ( document instanceof HWPFDocument )
{
- Field aliveField = ( (HWPFDocument) document )
- .getFieldsTables().lookupFieldByStartOffset(
- FieldsDocumentPart.MAIN,
+ Field aliveField = ( (HWPFDocument) document ).getFields()
+ .getFieldByStartOffset( FieldsDocumentPart.MAIN,
characterRun.getStartOffset() );
if ( aliveField != null )
{
return null;
HWPFDocument hwpfDocument = (HWPFDocument) wordDocument;
- Field field = hwpfDocument.getFieldsTables().lookupFieldByStartOffset(
+ Field field = hwpfDocument.getFields().getFieldByStartOffset(
FieldsDocumentPart.MAIN, startOffset );
if ( field == null )
return null;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import org.apache.poi.hwpf.model.TextPiece;
import org.apache.poi.hwpf.sprm.SprmIterator;
import org.apache.poi.hwpf.sprm.SprmOperation;
+import org.apache.poi.hwpf.usermodel.Field;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Picture;
import org.apache.poi.hwpf.usermodel.Range;
for ( char c : text.toCharArray() )
{
if ( c < 30 )
- stringBuilder.append( "\\0x" + Integer.toHexString( c ) );
+ stringBuilder
+ .append( "\\0x" + Integer.toHexString( c ) );
else
stringBuilder.append( c );
}
for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
{
System.out.println( "=== Document part: " + part + " ===" );
- for ( org.apache.poi.hwpf.model.Field field : document
- .getFieldsTables().getFields( part ) )
+ for ( Field field : document.getFields().getFields( part ) )
{
System.out.println( field );
}
HWPFDocument doc = (HWPFDocument) _doc;
- Field fMainStream = HWPFDocumentCore.class
+ java.lang.reflect.Field fMainStream = HWPFDocumentCore.class
.getDeclaredField( "_mainStream" );
fMainStream.setAccessible( true );
byte[] mainStream = (byte[]) fMainStream.get( _doc );
+++ /dev/null
-package org.apache.poi.hwpf.model;
-
-import org.apache.poi.hwpf.usermodel.Range;
-
-public class Field
-{
- private PlexOfField endPlex;
- private PlexOfField separatorPlex;
- private PlexOfField startPlex;
-
- 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 Range firstSubrange( Range parent )
- {
- if ( hasSeparator() )
- {
- if ( getMarkStartOffset() + 1 == getMarkSeparatorOffset() )
- return null;
-
- return new Range( getMarkStartOffset() + 1,
- getMarkSeparatorOffset(), parent )
- {
- @Override
- public String toString()
- {
- return "FieldSubrange1 (" + super.toString() + ")";
- }
- };
- }
-
- if ( getMarkStartOffset() + 1 == getMarkEndOffset() )
- return null;
-
- return new Range( getMarkStartOffset() + 1, getMarkEndOffset(), parent )
- {
- @Override
- public String toString()
- {
- return "FieldSubrange1 (" + super.toString() + ")";
- }
- };
- }
-
- /**
- * @return character position of first character after field (i.e.
- * {@link #getMarkEndOffset()} + 1)
- */
- public int getFieldEndOffset()
- {
- /*
- * sometimes plex looks like [100, 2000), where 100 is the position of
- * field-end character, and 2000 - some other char position, far away
- * from field (not inside). So taking into account only start --sergey
- */
- return endPlex.getFcStart() + 1;
- }
-
- /**
- * @return character position of first character in field (i.e.
- * {@link #getFieldStartOffset()})
- */
- public int getFieldStartOffset()
- {
- return startPlex.getFcStart();
- }
-
- /**
- * @return character position of end field mark
- */
- public int getMarkEndOffset()
- {
- return endPlex.getFcStart();
- }
-
- /**
- * @return character position of separator field mark (if present,
- * {@link NullPointerException} otherwise)
- */
- public int getMarkSeparatorOffset()
- {
- return separatorPlex.getFcStart();
- }
-
- /**
- * @return character position of start field mark
- */
- public int getMarkStartOffset()
- {
- return startPlex.getFcStart();
- }
-
- public int getType()
- {
- return startPlex.getFld().getFieldType();
- }
-
- public boolean hasSeparator()
- {
- return separatorPlex != null;
- }
-
- public boolean isHasSep()
- {
- return endPlex.getFld().isFHasSep();
- }
-
- public boolean isLocked()
- {
- return endPlex.getFld().isFLocked();
- }
-
- public boolean isNested()
- {
- return endPlex.getFld().isFNested();
- }
-
- public boolean isPrivateResult()
- {
- return endPlex.getFld().isFPrivateResult();
- }
-
- public boolean isResultDirty()
- {
- return endPlex.getFld().isFResultDirty();
- }
-
- public boolean isResultEdited()
- {
- return endPlex.getFld().isFResultEdited();
- }
-
- public boolean isZombieEmbed()
- {
- return endPlex.getFld().isFZombieEmbed();
- }
-
- public Range secondSubrange( Range parent )
- {
- if ( !hasSeparator()
- || getMarkSeparatorOffset() + 1 == getMarkEndOffset() )
- return null;
-
- return new Range( getMarkSeparatorOffset() + 1, getMarkEndOffset(),
- parent )
- {
- @Override
- public String toString()
- {
- return "FieldSubrange2 (" + super.toString() + ")";
- }
- };
- }
-
- @Override
- public String toString()
- {
- return "Field [" + getFieldStartOffset() + "; " + getFieldEndOffset()
- + "] (type: 0x" + Integer.toHexString( getType() ) + " = "
- + getType() + " )";
- }
-}
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
@Deprecated
public static final int PLCFFLDTXBX = 6;
- /**
- * This is port and adaptation of Arrays.binarySearch from Java 6 (Apache
- * Harmony).
- */
- private static <T> int binarySearch( GenericPropertyNode[] array,
- int startIndex, int endIndex, int requiredStartOffset )
- {
- checkIndexForBinarySearch( array.length, startIndex, endIndex );
-
- int low = startIndex, mid = -1, high = endIndex - 1, result = 0;
- while ( low <= high )
- {
- mid = ( low + high ) >>> 1;
- int midStart = array[mid].getStart();
-
- if ( midStart == requiredStartOffset )
- {
- return mid;
- }
- else if ( midStart < requiredStartOffset )
- {
- low = mid + 1;
- }
- else
- {
- high = mid - 1;
- }
- }
- if ( mid < 0 )
- {
- int insertPoint = endIndex;
- for ( int index = startIndex; index < endIndex; index++ )
- {
- if ( requiredStartOffset < array[index].getStart() )
- {
- insertPoint = index;
- }
- }
- return -insertPoint - 1;
- }
- return -mid - ( result >= 0 ? 1 : 2 );
- }
-
- private static void checkIndexForBinarySearch( int length, int start,
- int end )
- {
- if ( start > end )
- {
- throw new IllegalArgumentException();
- }
- if ( length < end || 0 > start )
- {
- throw new ArrayIndexOutOfBoundsException();
- }
- }
-
private static ArrayList<PlexOfField> toArrayList( PlexOfCps plexOfCps )
{
if ( plexOfCps == null )
return new ArrayList<PlexOfField>();
- ArrayList<PlexOfField> fields = new ArrayList<PlexOfField>();
- fields.ensureCapacity( plexOfCps.length() );
-
+ ArrayList<PlexOfField> fields = new ArrayList<PlexOfField>(
+ plexOfCps.length() );
for ( int i = 0; i < plexOfCps.length(); i++ )
{
GenericPropertyNode propNode = plexOfCps.getProperty( i );
return fields;
}
- private Map<FieldsDocumentPart, Map<Integer, Field>> _fieldsByOffset;
-
private Map<FieldsDocumentPart, PlexOfCps> _tables;
- private GenericPropertyNodeComparator comparator = new GenericPropertyNodeComparator();
-
public FieldsTables( byte[] tableStream, FileInformationBlock fib )
{
_tables = new HashMap<FieldsDocumentPart, PlexOfCps>(
FieldsDocumentPart.values().length );
- _fieldsByOffset = new HashMap<FieldsDocumentPart, Map<Integer, Field>>(
- FieldsDocumentPart.values().length );
for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
{
final PlexOfCps plexOfCps = readPLCF( tableStream, fib, part );
-
- _fieldsByOffset.put( part, parseFieldStructure( plexOfCps ) );
_tables.put( part, plexOfCps );
}
}
- public Collection<Field> getFields( FieldsDocumentPart part )
- {
- Map<Integer, Field> map = _fieldsByOffset.get( part );
- if ( map == null || map.isEmpty() )
- return Collections.emptySet();
-
- return Collections.unmodifiableCollection( map.values() );
- }
-
public ArrayList<PlexOfField> getFieldsPLCF( FieldsDocumentPart part )
{
return toArrayList( _tables.get( part ) );
return getFieldsPLCF( FieldsDocumentPart.values()[partIndex] );
}
- public Field lookupFieldByStartOffset( FieldsDocumentPart documentPart,
- int offset )
- {
- Map<Integer, Field> map = _fieldsByOffset.get( documentPart );
- if ( map == null || map.isEmpty() )
- return null;
-
- return map.get( Integer.valueOf( offset ) );
- }
-
- 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.getFieldStartOffset() ), 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 = binarySearch( nodes, next + 1,
- endOffsetExclusive, startNode.getEnd() );
- 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 = binarySearch( nodes,
- nextNodePositionInArray, endOffsetExclusive,
- separatorNode.getEnd() );
- 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 );
-
- if ( endPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_END_MARK )
- {
- /* Not and ending mark */
- next++;
- continue;
- }
-
- 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;
- }
- }
- }
- }
-
private PlexOfCps readPLCF( byte[] tableStream, FileInformationBlock fib,
FieldsDocumentPart documentPart )
{
}
}
- 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;
- }
- }
}
--- /dev/null
+package org.apache.poi.hwpf.usermodel;
+
+public interface Field
+{
+
+ Range firstSubrange( Range parent );
+
+ /**
+ * @return character position of first character after field (i.e.
+ * {@link #getMarkEndOffset()} + 1)
+ */
+ int getFieldEndOffset();
+
+ /**
+ * @return character position of first character in field (i.e.
+ * {@link #getFieldStartOffset()})
+ */
+ int getFieldStartOffset();
+
+ /**
+ * @return character position of end field mark
+ */
+ int getMarkEndOffset();
+
+ /**
+ * @return character position of separator field mark (if present,
+ * {@link NullPointerException} otherwise)
+ */
+ int getMarkSeparatorOffset();
+
+ /**
+ * @return character position of start field mark
+ */
+ int getMarkStartOffset();
+
+ int getType();
+
+ boolean hasSeparator();
+
+ boolean isHasSep();
+
+ boolean isLocked();
+
+ boolean isNested();
+
+ boolean isPrivateResult();
+
+ boolean isResultDirty();
+
+ boolean isResultEdited();
+
+ boolean isZombieEmbed();
+
+ Range secondSubrange( Range parent );
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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.poi.hwpf.usermodel;
+
+import org.apache.poi.hwpf.model.FieldDescriptor;
+import org.apache.poi.hwpf.model.PlexOfField;
+import org.apache.poi.util.Internal;
+
+/**
+ * TODO: document me
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+@Internal
+class FieldImpl implements Field
+{
+ private PlexOfField endPlex;
+ private PlexOfField separatorPlex;
+ private PlexOfField startPlex;
+
+ public FieldImpl( 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 Range firstSubrange( Range parent )
+ {
+ if ( hasSeparator() )
+ {
+ if ( getMarkStartOffset() + 1 == getMarkSeparatorOffset() )
+ return null;
+
+ return new Range( getMarkStartOffset() + 1,
+ getMarkSeparatorOffset(), parent )
+ {
+ @Override
+ public String toString()
+ {
+ return "FieldSubrange1 (" + super.toString() + ")";
+ }
+ };
+ }
+
+ if ( getMarkStartOffset() + 1 == getMarkEndOffset() )
+ return null;
+
+ return new Range( getMarkStartOffset() + 1, getMarkEndOffset(), parent )
+ {
+ @Override
+ public String toString()
+ {
+ return "FieldSubrange1 (" + super.toString() + ")";
+ }
+ };
+ }
+
+ /**
+ * @return character position of first character after field (i.e.
+ * {@link #getMarkEndOffset()} + 1)
+ */
+ public int getFieldEndOffset()
+ {
+ /*
+ * sometimes plex looks like [100, 2000), where 100 is the position of
+ * field-end character, and 2000 - some other char position, far away
+ * from field (not inside). So taking into account only start --sergey
+ */
+ return endPlex.getFcStart() + 1;
+ }
+
+ /**
+ * @return character position of first character in field (i.e.
+ * {@link #getFieldStartOffset()})
+ */
+ public int getFieldStartOffset()
+ {
+ return startPlex.getFcStart();
+ }
+
+ /**
+ * @return character position of end field mark
+ */
+ public int getMarkEndOffset()
+ {
+ return endPlex.getFcStart();
+ }
+
+ /**
+ * @return character position of separator field mark (if present,
+ * {@link NullPointerException} otherwise)
+ */
+ public int getMarkSeparatorOffset()
+ {
+ return separatorPlex.getFcStart();
+ }
+
+ /**
+ * @return character position of start field mark
+ */
+ public int getMarkStartOffset()
+ {
+ return startPlex.getFcStart();
+ }
+
+ public int getType()
+ {
+ return startPlex.getFld().getFieldType();
+ }
+
+ public boolean hasSeparator()
+ {
+ return separatorPlex != null;
+ }
+
+ public boolean isHasSep()
+ {
+ return endPlex.getFld().isFHasSep();
+ }
+
+ public boolean isLocked()
+ {
+ return endPlex.getFld().isFLocked();
+ }
+
+ public boolean isNested()
+ {
+ return endPlex.getFld().isFNested();
+ }
+
+ public boolean isPrivateResult()
+ {
+ return endPlex.getFld().isFPrivateResult();
+ }
+
+ public boolean isResultDirty()
+ {
+ return endPlex.getFld().isFResultDirty();
+ }
+
+ public boolean isResultEdited()
+ {
+ return endPlex.getFld().isFResultEdited();
+ }
+
+ public boolean isZombieEmbed()
+ {
+ return endPlex.getFld().isFZombieEmbed();
+ }
+
+ public Range secondSubrange( Range parent )
+ {
+ if ( !hasSeparator()
+ || getMarkSeparatorOffset() + 1 == getMarkEndOffset() )
+ return null;
+
+ return new Range( getMarkSeparatorOffset() + 1, getMarkEndOffset(),
+ parent )
+ {
+ @Override
+ public String toString()
+ {
+ return "FieldSubrange2 (" + super.toString() + ")";
+ }
+ };
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Field [" + getFieldStartOffset() + "; " + getFieldEndOffset()
+ + "] (type: 0x" + Integer.toHexString( getType() ) + " = "
+ + getType() + " )";
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.poi.hwpf.usermodel;
+
+import java.util.Collection;
+
+import org.apache.poi.hwpf.model.FieldsDocumentPart;
+
+/**
+ * User-friendly interface to access document {@link Field}s
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+public interface Fields
+{
+ Field getFieldByStartOffset( FieldsDocumentPart documentPart, int offset );
+
+ Collection<Field> getFields( FieldsDocumentPart part );
+}
--- /dev/null
+/* ====================================================================
+ 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.poi.hwpf.usermodel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.hwpf.model.FieldDescriptor;
+import org.apache.poi.hwpf.model.FieldsDocumentPart;
+import org.apache.poi.hwpf.model.FieldsTables;
+import org.apache.poi.hwpf.model.PlexOfField;
+import org.apache.poi.util.Internal;
+
+/**
+ * Default implementation of {@link Field}
+ *
+ * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
+ */
+@Internal
+public class FieldsImpl implements Fields
+{
+ /**
+ * This is port and adaptation of Arrays.binarySearch from Java 6 (Apache
+ * Harmony).
+ */
+ private static <T> int binarySearch( List<PlexOfField> list,
+ int startIndex, int endIndex, int requiredStartOffset )
+ {
+ checkIndexForBinarySearch( list.size(), startIndex, endIndex );
+
+ int low = startIndex, mid = -1, high = endIndex - 1, result = 0;
+ while ( low <= high )
+ {
+ mid = ( low + high ) >>> 1;
+ int midStart = list.get( mid ).getFcStart();
+
+ if ( midStart == requiredStartOffset )
+ {
+ return mid;
+ }
+ else if ( midStart < requiredStartOffset )
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ high = mid - 1;
+ }
+ }
+ if ( mid < 0 )
+ {
+ int insertPoint = endIndex;
+ for ( int index = startIndex; index < endIndex; index++ )
+ {
+ if ( requiredStartOffset < list.get( index ).getFcStart() )
+ {
+ insertPoint = index;
+ }
+ }
+ return -insertPoint - 1;
+ }
+ return -mid - ( result >= 0 ? 1 : 2 );
+ }
+
+ private static void checkIndexForBinarySearch( int length, int start,
+ int end )
+ {
+ if ( start > end )
+ {
+ throw new IllegalArgumentException();
+ }
+ if ( length < end || 0 > start )
+ {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ private Map<FieldsDocumentPart, Map<Integer, FieldImpl>> _fieldsByOffset;
+
+ private PlexOfFieldComparator comparator = new PlexOfFieldComparator();
+
+ public FieldsImpl( FieldsTables fieldsTables )
+ {
+ _fieldsByOffset = new HashMap<FieldsDocumentPart, Map<Integer, FieldImpl>>(
+ FieldsDocumentPart.values().length );
+
+ for ( FieldsDocumentPart part : FieldsDocumentPart.values() )
+ {
+ List<PlexOfField> plexOfCps = fieldsTables.getFieldsPLCF( part );
+ _fieldsByOffset.put( part, parseFieldStructure( plexOfCps ) );
+ }
+ }
+
+ public Collection<Field> getFields( FieldsDocumentPart part )
+ {
+ Map<Integer, FieldImpl> map = _fieldsByOffset.get( part );
+ if ( map == null || map.isEmpty() )
+ return Collections.emptySet();
+
+ return Collections.<Field> unmodifiableCollection( map.values() );
+ }
+
+ public FieldImpl getFieldByStartOffset( FieldsDocumentPart documentPart,
+ int offset )
+ {
+ Map<Integer, FieldImpl> map = _fieldsByOffset.get( documentPart );
+ if ( map == null || map.isEmpty() )
+ return null;
+
+ return map.get( Integer.valueOf( offset ) );
+ }
+
+ private Map<Integer, FieldImpl> parseFieldStructure(
+ List<PlexOfField> plexOfFields )
+ {
+ if ( plexOfFields == null || plexOfFields.isEmpty() )
+ return new HashMap<Integer, FieldImpl>();
+
+ Collections.sort( plexOfFields, comparator );
+ List<FieldImpl> fields = new ArrayList<FieldImpl>(
+ plexOfFields.size() / 3 + 1 );
+ parseFieldStructureImpl( plexOfFields, 0, plexOfFields.size(), fields );
+
+ HashMap<Integer, FieldImpl> result = new HashMap<Integer, FieldImpl>(
+ fields.size() );
+ for ( FieldImpl field : fields )
+ {
+ result.put( Integer.valueOf( field.getFieldStartOffset() ), field );
+ }
+ return result;
+ }
+
+ private void parseFieldStructureImpl( List<PlexOfField> plexOfFields,
+ int startOffsetInclusive, int endOffsetExclusive,
+ List<FieldImpl> result )
+ {
+ int next = startOffsetInclusive;
+ while ( next < endOffsetExclusive )
+ {
+ PlexOfField startPlexOfField = plexOfFields.get( next );
+ 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 nextNodePositionInList = binarySearch( plexOfFields, next + 1,
+ endOffsetExclusive, startPlexOfField.getFcEnd() );
+ if ( nextNodePositionInList < 0 )
+ {
+ /*
+ * too bad, this start field mark doesn't have corresponding end
+ * field mark or separator field mark in fields table
+ */
+ next++;
+ continue;
+ }
+ PlexOfField nextPlexOfField = plexOfFields
+ .get( nextNodePositionInList );
+
+ switch ( nextPlexOfField.getFld().getBoundaryType() )
+ {
+ case FieldDescriptor.FIELD_SEPARATOR_MARK:
+ {
+ PlexOfField separatorPlexOfField = nextPlexOfField;
+
+ int endNodePositionInList = binarySearch( plexOfFields,
+ nextNodePositionInList, endOffsetExclusive,
+ separatorPlexOfField.getFcEnd() );
+ if ( endNodePositionInList < 0 )
+ {
+ /*
+ * too bad, this separator field mark doesn't have
+ * corresponding end field mark in fields table
+ */
+ next++;
+ continue;
+ }
+ PlexOfField endPlexOfField = plexOfFields
+ .get( endNodePositionInList );
+
+ if ( endPlexOfField.getFld().getBoundaryType() != FieldDescriptor.FIELD_END_MARK )
+ {
+ /* Not and ending mark */
+ next++;
+ continue;
+ }
+
+ FieldImpl field = new FieldImpl( startPlexOfField,
+ separatorPlexOfField, endPlexOfField );
+ result.add( field );
+
+ // adding included fields
+ if ( startPlexOfField.getFcStart() + 1 < separatorPlexOfField
+ .getFcStart() - 1 )
+ {
+ parseFieldStructureImpl( plexOfFields, next + 1,
+ nextNodePositionInList, result );
+ }
+ if ( separatorPlexOfField.getFcStart() + 1 < endPlexOfField
+ .getFcStart() - 1 )
+ {
+ parseFieldStructureImpl( plexOfFields,
+ nextNodePositionInList + 1, endNodePositionInList,
+ result );
+ }
+
+ next = endNodePositionInList + 1;
+
+ break;
+ }
+ case FieldDescriptor.FIELD_END_MARK:
+ {
+ // we have no separator
+ FieldImpl field = new FieldImpl( startPlexOfField, null,
+ nextPlexOfField );
+ result.add( field );
+
+ // adding included fields
+ if ( startPlexOfField.getFcStart() + 1 < nextPlexOfField
+ .getFcStart() - 1 )
+ {
+ parseFieldStructureImpl( plexOfFields, next + 1,
+ nextNodePositionInList, result );
+ }
+
+ next = nextNodePositionInList + 1;
+ break;
+ }
+ case FieldDescriptor.FIELD_BEGIN_MARK:
+ default:
+ {
+ /* something is wrong, ignoring this mark along with start mark */
+ next++;
+ continue;
+ }
+ }
+ }
+ }
+
+ private static final class PlexOfFieldComparator implements
+ Comparator<PlexOfField>
+ {
+ public int compare( PlexOfField o1, PlexOfField o2 )
+ {
+ int thisVal = o1.getFcStart();
+ int anotherVal = o2.getFcStart();
+ return thisVal < anotherVal ? -1 : thisVal == anotherVal ? 0 : 1;
+ }
+ }
+
+}