001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
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 * Contributors:
017 *     bstefanescu
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.platform.rendering.wiki;
023
024/**
025 * Table of contents model.
026 * <p>
027 * A simple linked list of toc entries.
028 *
029 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
030 */
031public class Toc {
032
033    protected final Entry head;
034
035    protected Entry tail;
036
037    public Toc() {
038        head = new Entry();
039        tail = head;
040        head.title = "Table of Contents";
041        head.id = null;
042    }
043
044    /**
045     * Adds a heading to the TOC list and returns the ID of that heading (to be used for anchors).
046     *
047     * @param title the heading title
048     * @param level the heading level
049     * @return the heading id
050     */
051    public String addHeading(String title, int level) {
052        Entry entry = new Entry();
053        entry.title = title;
054        entry.level = level;
055        if (level == tail.level) { // same level
056            tail.next = entry;
057            entry.parent = tail.parent;
058            entry.index = tail.index + 1;
059        } else if (level > tail.level) {
060            entry.parent = tail;
061            tail.firstChild = entry;
062            entry.index = 1;
063        } else {
064            Entry prev = tail.parent;
065            while (prev.level > level) {
066                prev = prev.parent;
067            }
068            if (prev.parent == null) {
069                throw new IllegalStateException("Invalid headers. Header levels underflowed");
070            }
071            prev.next = entry;
072            entry.parent = prev.parent;
073            entry.index = prev.index + 1;
074        }
075        if (entry.parent.id != null) {
076            entry.id = entry.parent.id + "." + entry.index;
077        } else {
078            entry.id = "" + entry.index;
079        }
080        tail = entry;
081        return entry.id;
082    }
083
084    public static class Entry {
085        public Entry parent;
086
087        public Entry next;
088
089        public Entry firstChild;
090
091        public String id;
092
093        public String title;
094
095        public int level;
096
097        public int index;
098    }
099
100}