001/* 
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 *
012 * $Id$
013 */
014
015package org.nuxeo.ecm.platform.rendering.wiki;
016
017/**
018 * Table of contents model.
019 * <p>
020 * A simple linked list of toc entries.
021 *
022 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
023 */
024public class Toc {
025
026    protected final Entry head;
027
028    protected Entry tail;
029
030    public Toc() {
031        head = new Entry();
032        tail = head;
033        head.title = "Table of Contents";
034        head.id = null;
035    }
036
037    /**
038     * Adds a heading to the TOC list and returns the ID of that heading (to be used for anchors).
039     *
040     * @param title the heading title
041     * @param level the heading level
042     * @return the heading id
043     */
044    public String addHeading(String title, int level) {
045        Entry entry = new Entry();
046        entry.title = title;
047        entry.level = level;
048        if (level == tail.level) { // same level
049            tail.next = entry;
050            entry.parent = tail.parent;
051            entry.index = tail.index + 1;
052        } else if (level > tail.level) {
053            entry.parent = tail;
054            tail.firstChild = entry;
055            entry.index = 1;
056        } else {
057            Entry prev = tail.parent;
058            // FIXME: null consistency check
059            while (prev.level > level && prev != null) {
060                prev = prev.parent;
061            }
062            // FIXME: null consistency check
063            if (prev == null || prev.parent == null) {
064                throw new IllegalStateException("Invalid headers. Header levels underflowed");
065            }
066            prev.next = entry;
067            entry.parent = prev.parent;
068            entry.index = prev.index + 1;
069        }
070        if (entry.parent.id != null) {
071            entry.id = entry.parent.id + "." + entry.index;
072        } else {
073            entry.id = "" + entry.index;
074        }
075        tail = entry;
076        return entry.id;
077    }
078
079    public static class Entry {
080        public Entry parent;
081
082        public Entry next;
083
084        public Entry firstChild;
085
086        public String id;
087
088        public String title;
089
090        public int level;
091
092        public int index;
093    }
094
095}