| /* ==================================================================== |
| 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.io.Serializable; |
| 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>, Serializable { |
| public int compare( PlexOfField o1, PlexOfField o2 ) |
| { |
| int thisVal = o1.getFcStart(); |
| int anotherVal = o2.getFcStart(); |
| return thisVal < anotherVal ? -1 : thisVal == anotherVal ? 0 : 1; |
| } |
| } |
| |
| } |