001    /*
002     * Copyright 2009 - 2009 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: MultiAttributedCharacterIterator.java,v 344ccd357471 2009/09/10 14:15:11 max $ */
018    
019    package net.sourceforge.jeuclid.elements.support.text;
020    
021    import java.text.AttributedCharacterIterator;
022    import java.text.CharacterIterator;
023    import java.util.ArrayList;
024    import java.util.HashSet;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    /**
030     * Joins multiple {@link AttributedCharacterIterator}s into one.
031     * 
032     * @version $Revision: 344ccd357471 $
033     */
034    public class MultiAttributedCharacterIterator implements
035            AttributedCharacterIterator {
036    
037        private final List<AttributedCharacterIterator> realIterators = new ArrayList<AttributedCharacterIterator>();
038    
039        private int currentList;
040    
041        /**
042         * Default constructor.
043         */
044        public MultiAttributedCharacterIterator() {
045            // nothing to do;
046        }
047    
048        // CHECKSTYLE:OFF Clone is disabled.
049        @Override
050        public Object clone() {
051            throw new UnsupportedOperationException();
052        }
053    
054        // CHECKSTYLE: ON
055    
056        /**
057         * Adds a new CharacterIterator
058         * 
059         * @param aci
060         *            the new CharacterIterator to add to the list.
061         */
062        public void appendAttributedCharacterIterator(
063                final AttributedCharacterIterator aci) {
064            this.realIterators.add(aci);
065            this.first();
066        }
067    
068        private int sumUpToNotIncluding(final int limit) {
069            int offset = 0;
070            for (int i = 0; i < limit; i++) {
071                final AttributedCharacterIterator ci = this.realIterators.get(i);
072                offset += ci.getEndIndex() - ci.getBeginIndex();
073            }
074            return offset;
075        }
076    
077        private int currentOffset() {
078            int offset = this.sumUpToNotIncluding(this.currentList);
079            offset -= this.realIterators.get(this.currentList).getBeginIndex();
080            return offset;
081        }
082    
083        /** {@inheritDoc} */
084        public Set<Attribute> getAllAttributeKeys() {
085            final Set<Attribute> retVal = new HashSet<Attribute>();
086            for (final AttributedCharacterIterator ri : this.realIterators) {
087                retVal.addAll(ri.getAllAttributeKeys());
088            }
089            return retVal;
090        }
091    
092        /** {@inheritDoc} */
093        public Object getAttribute(final Attribute attribute) {
094            return this.realIterators.get(this.currentList).getAttribute(attribute);
095        }
096    
097        /** {@inheritDoc} */
098        public Map<Attribute, Object> getAttributes() {
099            return this.realIterators.get(this.currentList).getAttributes();
100        }
101    
102        /** {@inheritDoc} */
103        public int getRunLimit() {
104            return this.realIterators.get(this.currentList).getRunLimit()
105                    + this.currentOffset();
106        }
107    
108        /** {@inheritDoc} */
109        public int getRunLimit(final Attribute attribute) {
110            return this.realIterators.get(this.currentList).getRunLimit(attribute)
111                    + this.currentOffset();
112        }
113    
114        /** {@inheritDoc} */
115        public int getRunLimit(final Set<? extends Attribute> attributes) {
116            return this.realIterators.get(this.currentList).getRunLimit(attributes)
117                    + this.currentOffset();
118        }
119    
120        /** {@inheritDoc} */
121        public int getRunStart() {
122            return this.realIterators.get(this.currentList).getRunStart()
123                    + this.currentOffset();
124        }
125    
126        /** {@inheritDoc} */
127        public int getRunStart(final Attribute attribute) {
128            return this.realIterators.get(this.currentList).getRunStart(attribute)
129                    + this.currentOffset();
130        }
131    
132        /** {@inheritDoc} */
133        public int getRunStart(final Set<? extends Attribute> attributes) {
134            return this.realIterators.get(this.currentList).getRunStart(attributes)
135                    + this.currentOffset();
136        }
137    
138        /** {@inheritDoc} */
139        public char current() {
140            return this.realIterators.get(this.currentList).current();
141        }
142    
143        /** {@inheritDoc} */
144        public char first() {
145            this.currentList = 0;
146            return this.realIterators.get(this.currentList).first();
147        }
148    
149        /** {@inheritDoc} */
150        public int getBeginIndex() {
151            return 0;
152        }
153    
154        /** {@inheritDoc} */
155        public int getEndIndex() {
156            return this.sumUpToNotIncluding(this.realIterators.size());
157        }
158    
159        /** {@inheritDoc} */
160        public int getIndex() {
161            return this.currentOffset()
162                    + this.realIterators.get(this.currentList).getIndex();
163        }
164    
165        /** {@inheritDoc} */
166        public char last() {
167            this.currentList = this.realIterators.size() - 1;
168            return this.realIterators.get(this.currentList).last();
169        }
170    
171        /** {@inheritDoc} */
172        public char next() {
173            char c = this.realIterators.get(this.currentList).next();
174            while ((c == CharacterIterator.DONE)
175                    && (this.currentList < this.realIterators.size() - 1)) {
176                this.currentList++;
177                c = this.realIterators.get(this.currentList).first();
178            }
179            return c;
180        }
181    
182        /** {@inheritDoc} */
183        public char previous() {
184            char c = this.realIterators.get(this.currentList).previous();
185            while ((c == CharacterIterator.DONE) && (this.currentList > 0)) {
186                this.currentList--;
187                c = this.realIterators.get(this.currentList).previous();
188            }
189            return c;
190        }
191    
192        /** {@inheritDoc} */
193        public char setIndex(final int position) {
194            int prev = 0;
195            int offset = 0;
196            for (int i = 0; i < this.realIterators.size(); i++) {
197                final AttributedCharacterIterator ci = this.realIterators.get(i);
198                prev = offset;
199                final int beginIndex = ci.getBeginIndex();
200                offset += ci.getEndIndex() - beginIndex;
201                if (((prev <= position) && (offset > position))
202                        || ((offset == position) && (i == this.realIterators.size() - 1))) {
203                    this.currentList = i;
204                    return ci.setIndex(beginIndex + position - prev);
205                }
206            }
207            throw new IllegalArgumentException();
208        }
209    }