001/*
002 * $Id: IndexEvents.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf.events;
045
046import java.util.ArrayList;
047import java.util.Collections;
048import java.util.Comparator;
049import java.util.HashMap;
050import java.util.List;
051import java.util.Map;
052import java.util.TreeMap;
053
054import com.itextpdf.text.Chunk;
055import com.itextpdf.text.Document;
056import com.itextpdf.text.Rectangle;
057import com.itextpdf.text.pdf.PdfPageEventHelper;
058import com.itextpdf.text.pdf.PdfWriter;
059
060/**
061 * Class for an index.
062 *
063 * @author Michael Niedermair
064 */
065public class IndexEvents extends PdfPageEventHelper {
066
067    /**
068     * keeps the indextag with the pagenumber
069     */
070    private Map<String, Integer> indextag = new TreeMap<String, Integer>();
071
072    /**
073     * All the text that is passed to this event, gets registered in the indexentry.
074     *
075     * @see com.itextpdf.text.pdf.PdfPageEventHelper#onGenericTag(
076     *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document,
077     *      com.itextpdf.text.Rectangle, java.lang.String)
078     */
079    @Override
080    public void onGenericTag(PdfWriter writer, Document document,
081            Rectangle rect, String text) {
082        indextag.put(text, Integer.valueOf(writer.getPageNumber()));
083    }
084
085    // --------------------------------------------------------------------
086    /**
087     * indexcounter
088     */
089    private long indexcounter = 0;
090
091    /**
092     * the list for the index entry
093     */
094    private List<Entry> indexentry = new ArrayList<Entry>();
095
096    /**
097     * Create an index entry.
098     *
099     * @param text  The text for the Chunk.
100     * @param in1   The first level.
101     * @param in2   The second level.
102     * @param in3   The third level.
103     * @return Returns the Chunk.
104     */
105    public Chunk create(final String text, final String in1, final String in2,
106            final String in3) {
107
108        Chunk chunk = new Chunk(text);
109        String tag = "idx_" + indexcounter++;
110        chunk.setGenericTag(tag);
111        chunk.setLocalDestination(tag);
112        Entry entry = new Entry(in1, in2, in3, tag);
113        indexentry.add(entry);
114        return chunk;
115    }
116
117    /**
118     * Create an index entry.
119     *
120     * @param text  The text for the Chunk.
121     * @param in1   The first level.
122     * @return Returns the Chunk.
123     */
124    public Chunk create(final String text, final String in1) {
125        return create(text, in1, "", "");
126    }
127
128    /**
129     * Create an index entry.
130     *
131     * @param text  The text for the Chunk.
132     * @param in1   The first level.
133     * @param in2   The second level.
134     * @return Returns the Chunk.
135     */
136    public Chunk create(final String text, final String in1, final String in2) {
137        return create(text, in1, in2, "");
138    }
139
140    /**
141     * Create an index entry.
142     *
143     * @param text  The text.
144     * @param in1   The first level.
145     * @param in2   The second level.
146     * @param in3   The third level.
147     */
148    public void create(final Chunk text, final String in1, final String in2,
149            final String in3) {
150
151        String tag = "idx_" + indexcounter++;
152        text.setGenericTag(tag);
153        text.setLocalDestination(tag);
154        Entry entry = new Entry(in1, in2, in3, tag);
155        indexentry.add(entry);
156    }
157
158    /**
159     * Create an index entry.
160     *
161     * @param text  The text.
162     * @param in1   The first level.
163     */
164    public void create(final Chunk text, final String in1) {
165        create(text, in1, "", "");
166    }
167
168    /**
169     * Create an index entry.
170     *
171     * @param text  The text.
172     * @param in1   The first level.
173     * @param in2   The second level.
174     */
175    public void create(final Chunk text, final String in1, final String in2) {
176        create(text, in1, in2, "");
177    }
178
179    /**
180     * Comparator for sorting the index
181     */
182    private Comparator<Entry> comparator = new Comparator<Entry>() {
183
184        public int compare(Entry en1, Entry en2) {
185            int rt = 0;
186            if (en1.getIn1() != null && en2.getIn1() != null) {
187                if ((rt = en1.getIn1().compareToIgnoreCase(en2.getIn1())) == 0) {
188                    // in1 equals
189                    if (en1.getIn2() != null && en2.getIn2() != null) {
190                        if ((rt = en1.getIn2()
191                                .compareToIgnoreCase(en2.getIn2())) == 0) {
192                            // in2 equals
193                            if (en1.getIn3() != null && en2.getIn3() != null) {
194                                rt = en1.getIn3().compareToIgnoreCase(
195                                        en2.getIn3());
196                            }
197                        }
198                    }
199                }
200            }
201            return rt;
202        }
203    };
204
205    /**
206     * Set the comparator.
207     * @param aComparator The comparator to set.
208     */
209    public void setComparator(Comparator<Entry> aComparator) {
210        comparator = aComparator;
211    }
212
213    /**
214     * Returns the sorted list with the entries and the collected page numbers.
215     * @return Returns the sorted list with the entries and the collected page numbers.
216     */
217    public List<Entry> getSortedEntries() {
218
219        Map<String, Entry> grouped = new HashMap<String, Entry>();
220
221        for (int i = 0; i < indexentry.size(); i++) {
222            Entry e = indexentry.get(i);
223            String key = e.getKey();
224
225            Entry master = grouped.get(key);
226            if (master != null) {
227                master.addPageNumberAndTag(e.getPageNumber(), e.getTag());
228            } else {
229                e.addPageNumberAndTag(e.getPageNumber(), e.getTag());
230                grouped.put(key, e);
231            }
232        }
233
234        // copy to a list and sort it
235        List<Entry> sorted = new ArrayList<Entry>(grouped.values());
236        Collections.sort(sorted, comparator);
237        return sorted;
238    }
239
240    // --------------------------------------------------------------------
241    /**
242     * Class for an index entry.
243     * <p>
244     * In the first step, only in1, in2,in3 and tag are used.
245     * After the collections of the index entries, pagenumbers are used.
246     * </p>
247     */
248    public class Entry {
249
250        /**
251         * first level
252         */
253        private String in1;
254
255        /**
256         * second level
257         */
258        private String in2;
259
260        /**
261         * third level
262         */
263        private String in3;
264
265        /**
266         * the tag
267         */
268        private String tag;
269
270        /**
271         * the list of all page numbers.
272         */
273        private List<Integer> pagenumbers = new ArrayList<Integer>();
274
275        /**
276         * the list of all tags.
277         */
278        private List<String> tags = new ArrayList<String>();
279
280        /**
281         * Create a new object.
282         * @param aIn1   The first level.
283         * @param aIn2   The second level.
284         * @param aIn3   The third level.
285         * @param aTag   The tag.
286         */
287        public Entry(final String aIn1, final String aIn2, final String aIn3,
288                final String aTag) {
289            in1 = aIn1;
290            in2 = aIn2;
291            in3 = aIn3;
292            tag = aTag;
293        }
294
295        /**
296         * Returns the in1.
297         * @return Returns the in1.
298         */
299        public String getIn1() {
300            return in1;
301        }
302
303        /**
304         * Returns the in2.
305         * @return Returns the in2.
306         */
307        public String getIn2() {
308            return in2;
309        }
310
311        /**
312         * Returns the in3.
313         * @return Returns the in3.
314         */
315        public String getIn3() {
316            return in3;
317        }
318
319        /**
320         * Returns the tag.
321         * @return Returns the tag.
322         */
323        public String getTag() {
324            return tag;
325        }
326
327        /**
328         * Returns the pagenumber for this entry.
329         * @return Returns the pagenumber for this entry.
330         */
331        public int getPageNumber() {
332            int rt = -1;
333            Integer i = indextag.get(tag);
334            if (i != null) {
335                rt = i.intValue();
336            }
337            return rt;
338        }
339
340        /**
341         * Add a pagenumber.
342         * @param number    The page number.
343         * @param tag
344         */
345        public void addPageNumberAndTag(final int number, final String tag) {
346            pagenumbers.add(Integer.valueOf(number));
347            tags.add(tag);
348        }
349
350        /**
351         * Returns the key for the map-entry.
352         * @return Returns the key for the map-entry.
353         */
354        public String getKey() {
355            return in1 + "!" + in2 + "!" + in3;
356        }
357
358        /**
359         * Returns the pagenumbers.
360         * @return Returns the pagenumbers.
361         */
362        public List<Integer> getPagenumbers() {
363            return pagenumbers;
364        }
365
366        /**
367         * Returns the tags.
368         * @return Returns the tags.
369         */
370        public List<String> getTags() {
371            return tags;
372        }
373
374        /**
375         * print the entry (only for test)
376         * @return the toString implementation of the entry
377         */
378        @Override
379        public String toString() {
380            StringBuffer buf = new StringBuffer();
381            buf.append(in1).append(' ');
382            buf.append(in2).append(' ');
383            buf.append(in3).append(' ');
384            for (int i = 0; i < pagenumbers.size(); i++) {
385                buf.append(pagenumbers.get(i)).append(' ');
386            }
387            return buf.toString();
388        }
389    }
390}