blob: 8cf309910730df21f26f2ec252173281fbd7174a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2013 David Green and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.wikitext.parser.outline;
import org.eclipse.mylyn.wikitext.parser.Attributes;
import org.eclipse.mylyn.wikitext.parser.DocumentBuilder;
import org.eclipse.mylyn.wikitext.parser.HeadingAttributes;
import org.eclipse.mylyn.wikitext.parser.IdGenerator;
import org.eclipse.mylyn.wikitext.parser.MarkupParser;
import org.eclipse.mylyn.wikitext.parser.markup.AbstractMarkupLanguage;
import org.eclipse.mylyn.wikitext.parser.markup.MarkupLanguage;
/**
* A parser for creating an outline of a document based on the headings in the document. Uses {@link MarkupLanguage a
* markup language} to determine where headings start and end.
*
* @see OutlineItem
* @author David Green
* @since 3.0
*/
public class OutlineParser {
private int labelMaxLength = 0;
private MarkupLanguage markupLanguage;
public OutlineParser(MarkupLanguage markupLanguage) {
this.markupLanguage = markupLanguage;
}
public OutlineParser() {
}
public int getLabelMaxLength() {
return labelMaxLength;
}
public void setLabelMaxLength(int labelMaxLength) {
this.labelMaxLength = labelMaxLength;
}
public OutlineItem parse(String markup) {
OutlineItem root = createRootItem();
return parse(root, markup);
}
public MarkupLanguage getMarkupLanguage() {
return markupLanguage;
}
public void setMarkupLanguage(MarkupLanguage markupLanguage) {
this.markupLanguage = markupLanguage;
}
public OutlineItem createRootItem() {
return createOutlineItem(null, 0, "<root>", 0, 0, "<root>"); //$NON-NLS-1$ //$NON-NLS-2$
}
public OutlineItem parse(OutlineItem root, String markup) {
if (markup == null || markup.length() == 0 || markupLanguage == null) {
root.setLength(markup == null ? 0 : markup.length());
return root;
}
root.setLength(markup.length());
MarkupLanguage markupLanguage = this.markupLanguage.clone();
if (markupLanguage instanceof AbstractMarkupLanguage) {
AbstractMarkupLanguage language = (AbstractMarkupLanguage) markupLanguage;
language.setFilterGenerativeContents(true);
language.setBlocksOnly(isBlocksOnly());
}
OutlineBuilder outlineBuilder = (OutlineBuilder) createOutlineUpdater(root);
outlineBuilder.idGenerator.setGenerationStrategy(markupLanguage.getIdGenerationStrategy());
MarkupParser markupParser = new MarkupParser();
markupParser.setBuilder(outlineBuilder);
markupParser.setMarkupLanguage(markupLanguage);
markupParser.parse(markup);
return root;
}
/**
* normally outline parsing is performed only on blocks. Overriding classes may return false if they wish to process
* all content.
*/
protected boolean isBlocksOnly() {
return true;
}
protected OutlineItem createOutlineItem(OutlineItem current, int level, String id, int offset, int length,
String label) {
return new OutlineItem(current, level, id, offset, length, label);
}
public DocumentBuilder createOutlineUpdater(OutlineItem rootItem) {
return new OutlineBuilder(rootItem, labelMaxLength);
}
/**
* A document builder that creates the outline structure based on headings in the document
*/
protected class OutlineBuilder extends DocumentBuilder {
private OutlineItem currentItem;
private int level;
private StringBuilder buf;
protected final IdGenerator idGenerator = new IdGenerator();
private int offset;
private int length;
private final OutlineItem rootItem;
private final int labelMaxLength;
private Attributes attributes;
public OutlineBuilder(OutlineItem root, int labelMaxLength) {
super();
this.currentItem = root;
rootItem = root;
this.labelMaxLength = labelMaxLength;
}
@Override
public void acronym(String text, String definition) {
}
@Override
public void beginBlock(BlockType type, Attributes attributes) {
registerId(attributes.getId());
}
@Override
public void beginDocument() {
rootItem.clear();
currentItem = rootItem;
}
@Override
public void beginHeading(int level, Attributes attributes) {
this.level = level;
this.attributes = attributes;
buf = new StringBuilder();
offset = getLocator().getDocumentOffset();
length = getLocator().getLineLength();
}
@Override
public void beginSpan(SpanType type, Attributes attributes) {
registerId(attributes.getId());
}
@Override
public void characters(String text) {
if (buf != null) {
// bug 374019: strip HTML tags from text used to generate ID
text = text.replaceAll("</?[a-zA-Z0-9]+.*?>", ""); //$NON-NLS-1$//$NON-NLS-2$
buf.append(text);
}
}
@Override
public void charactersUnescaped(String literal) {
}
@Override
public void endBlock() {
}
@Override
public void endDocument() {
}
@Override
public void endHeading() {
boolean includeInToc = true;
if (attributes instanceof HeadingAttributes) {
HeadingAttributes headingAttributes = (HeadingAttributes) attributes;
if (headingAttributes.isOmitFromTableOfContents()) {
includeInToc = false;
}
}
if (includeInToc) {
String label = buf.toString();
String fullLabelText = label;
if (label == null) {
label = ""; //$NON-NLS-1$
} else {
if (labelMaxLength > 0 && label.length() > labelMaxLength) {
label = label.substring(0, labelMaxLength) + "..."; //$NON-NLS-1$
}
}
String kind = "h" + level; //$NON-NLS-1$
while (level <= currentItem.getLevel()) {
currentItem = currentItem.getParent();
}
String id = attributes.getId();
if (id != null) {
idGenerator.reserveId(id);
} else {
id = idGenerator.newId(kind, fullLabelText);
}
currentItem = createOutlineItem(currentItem, level, id, offset, length, label);
currentItem.setTooltip(fullLabelText);
currentItem.setKind(kind);
} else {
registerId(attributes.getId());
}
buf = null;
offset = 0;
length = 0;
attributes = null;
}
@Override
public void endSpan() {
}
@Override
public void entityReference(String entity) {
}
@Override
public void image(Attributes attributes, String url) {
registerId(attributes.getId());
}
@Override
public void imageLink(Attributes linkAttributes, Attributes imageAttributes, String href, String imageUrl) {
registerId(linkAttributes.getId());
registerId(imageAttributes.getId());
}
@Override
public void lineBreak() {
}
@Override
public void link(Attributes attributes, String hrefOrHashName, String text) {
registerId(attributes.getId());
if (buf != null) {
buf.append(text);
}
}
private void registerId(String id) {
idGenerator.reserveId(id);
}
}
}