1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/*
18 * @author Oleg V. Khaschansky
19 * @version $Revision$
20 */
21
22package java.awt.font;
23
24import java.text.AttributedCharacterIterator; //???AWT: import java.text.BreakIterator;
25
26import org.apache.harmony.awt.internal.nls.Messages;
27
28/**
29 * The class LineBreakMeasurer provides methods to measure the graphical
30 * representation of a text in order to determine where to add line breaks so
31 * the resulting line of text fits its wrapping width. The wrapping width
32 * defines the visual width of the paragraph.
33 *
34 * @since Android 1.0
35 */
36public final class LineBreakMeasurer {
37
38    /**
39     * The tm.
40     */
41    private TextMeasurer tm = null;
42
43    // ???AWT private BreakIterator bi = null;
44    /**
45     * The position.
46     */
47    private int position = 0;
48
49    /**
50     * The maxpos.
51     */
52    int maxpos = 0;
53
54    /**
55     * Instantiates a new LineBreakMeasurer object for the specified text.
56     *
57     * @param text
58     *            the AttributedCharacterIterator object which contains text
59     *            with at least one character.
60     * @param frc
61     *            the FontRenderContext represented information about graphic
62     *            device.
63     */
64    public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
65        // ???AWT: this(text, BreakIterator.getLineInstance(), frc);
66    }
67
68    /*
69     * ???AWT public LineBreakMeasurer( AttributedCharacterIterator text,
70     * BreakIterator bi, FontRenderContext frc ) { tm = new TextMeasurer(text,
71     * frc); this.bi = bi; this.bi.setText(text); position =
72     * text.getBeginIndex(); maxpos = tm.aci.getEndIndex(); }
73     */
74
75    /**
76     * Deletes a character from the specified position of the text, updates this
77     * LineBreakMeasurer object.
78     *
79     * @param newText
80     *            the new text.
81     * @param pos
82     *            the position of the character which is deleted.
83     */
84    public void deleteChar(AttributedCharacterIterator newText, int pos) {
85        tm.deleteChar(newText, pos);
86        // ???AWT: bi.setText(newText);
87
88        position = newText.getBeginIndex();
89
90        maxpos--;
91    }
92
93    /**
94     * Gets current position of this LineBreakMeasurer.
95     *
96     * @return the current position of this LineBreakMeasurer
97     */
98    public int getPosition() {
99        return position;
100    }
101
102    /**
103     * Inserts a character at the specified position in the text, updates this
104     * LineBreakMeasurer object.
105     *
106     * @param newText
107     *            the new text.
108     * @param pos
109     *            the position of the character which is inserted.
110     */
111    public void insertChar(AttributedCharacterIterator newText, int pos) {
112        tm.insertChar(newText, pos);
113        // ???AWT: bi.setText(newText);
114
115        position = newText.getBeginIndex();
116
117        maxpos++;
118    }
119
120    /**
121     * Returns the next line of text, updates current position in this
122     * LineBreakMeasurer.
123     *
124     * @param wrappingWidth
125     *            the maximum visible line width.
126     * @param offsetLimit
127     *            the limit point within the text indicating that no further
128     *            text should be included on the line; the paragraph break.
129     * @param requireNextWord
130     *            if true, null is returned (the entire word at the current
131     *            position does not fit within the wrapping width); if false, a
132     *            valid layout is returned that includes at least the character
133     *            at the current position.
134     * @return the next TextLayout which begins at the current position and
135     *         represents the next line of text with width wrappingWidth, null
136     *         is returned if the entire word at the current position does not
137     *         fit within the wrapping width.
138     */
139    public TextLayout nextLayout(float wrappingWidth, int offsetLimit, boolean requireNextWord) {
140        if (position == maxpos) {
141            return null;
142        }
143
144        int nextPosition = nextOffset(wrappingWidth, offsetLimit, requireNextWord);
145
146        if (nextPosition == position) {
147            return null;
148        }
149        TextLayout layout = tm.getLayout(position, nextPosition);
150        position = nextPosition;
151        return layout;
152    }
153
154    /**
155     * Returns the next line of text.
156     *
157     * @param wrappingWidth
158     *            the maximum visible line width.
159     * @return the next line of text.
160     */
161    public TextLayout nextLayout(float wrappingWidth) {
162        return nextLayout(wrappingWidth, maxpos, false);
163    }
164
165    /**
166     * Returns the end position of the next line of text.
167     *
168     * @param wrappingWidth
169     *            the maximum visible line width.
170     * @return the end position of the next line of text.
171     */
172    public int nextOffset(float wrappingWidth) {
173        return nextOffset(wrappingWidth, maxpos, false);
174    }
175
176    /**
177     * Returns the end position of the next line of text.
178     *
179     * @param wrappingWidth
180     *            the maximum visible line width.
181     * @param offsetLimit
182     *            the limit point withing the text indicating that no further
183     *            text should be included on the line; the paragraph break.
184     * @param requireNextWord
185     *            if true, the current position is returned if the entire next
186     *            word does not fit within wrappingWidth; if false, the offset
187     *            returned is at least one greater than the current position.
188     * @return the end position of the next line of text.
189     * @throws IllegalArgumentException
190     *             if the offsetLimit is less than the current position.
191     */
192    public int nextOffset(float wrappingWidth, int offsetLimit, boolean requireNextWord) {
193        if (offsetLimit <= position) {
194            // awt.203=Offset limit should be greater than current position.
195            throw new IllegalArgumentException(Messages.getString("awt.203")); //$NON-NLS-1$
196        }
197
198        if (position == maxpos) {
199            return position;
200        }
201
202        int breakPos = tm.getLineBreakIndex(position, wrappingWidth);
203        int correctedPos = breakPos;
204
205        // This check is required because bi.preceding(maxpos) throws an
206        // exception
207        /*
208         * ???AWT if (breakPos == maxpos) { correctedPos = maxpos; } else if
209         * (Character.isWhitespace(bi.getText().setIndex(breakPos))) {
210         * correctedPos = bi.following(breakPos); } else { correctedPos =
211         * bi.preceding(breakPos); }
212         */
213
214        if (position >= correctedPos) {
215            if (requireNextWord) {
216                correctedPos = position;
217            } else {
218                correctedPos = Math.max(position + 1, breakPos);
219            }
220        }
221
222        return Math.min(correctedPos, offsetLimit);
223    }
224
225    /**
226     * Sets the new position of this LineBreakMeasurer.
227     *
228     * @param pos
229     *            the new position of this LineBreakMeasurer.
230     */
231    public void setPosition(int pos) {
232        if (tm.aci.getBeginIndex() > pos || maxpos < pos) {
233            // awt.33=index is out of range
234            throw new IllegalArgumentException(Messages.getString("awt.33")); //$NON-NLS-1$
235        }
236        position = pos;
237    }
238}
239