/*******************************************************************************
* Copyright (c) 2007, 2009 David Green and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.wikitext.validation;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.mylyn.wikitext.parser.Attributes;
import org.eclipse.mylyn.wikitext.parser.DocumentBuilder;
import org.eclipse.mylyn.wikitext.parser.IdGenerator;
import org.eclipse.mylyn.wikitext.parser.LinkAttributes;
import org.eclipse.mylyn.wikitext.parser.Locator;
import org.eclipse.mylyn.wikitext.parser.markup.MarkupLanguage;
import org.eclipse.mylyn.wikitext.parser.outline.OutlineItem;
import org.eclipse.mylyn.wikitext.parser.outline.OutlineParser;
import org.eclipse.mylyn.wikitext.validation.ValidationProblem.Severity;
/**
* A validation rule that verifies that internal document links resolve to a document id.
*
* @author David Green
* @since 3.0
*/
public abstract class DocumentLocalReferenceValidationRule extends ValidationRule {
private final class LocalReference {
public LocalReference(String name, int offset, int length) {
super();
this.name = name;
this.offset = offset;
this.length = length;
if (length < 0) {
throw new IllegalArgumentException();
}
}
String name;
int offset, length;
}
private final class OutlineParserExtension extends OutlineParser {
public IdGenerator idGenerator;
private final int offset;
private final int length;
private List<LocalReference> references;
private OutlineParserExtension(int offset, int length) {
this.offset = offset;
this.length = length;
}
private final class OutlineBuilderExtension extends OutlineBuilder {
private OutlineBuilderExtension(OutlineItem root, int labelMaxLength) {
super(root, labelMaxLength);
OutlineParserExtension.this.idGenerator = idGenerator;
}
@Override
public void beginSpan(SpanType type, Attributes attributes) {
super.beginSpan(type, attributes);
if (type == SpanType.LINK) {
if (attributes instanceof LinkAttributes) {
LinkAttributes linkAttributes = (LinkAttributes) attributes;
processLink(getLocator(), linkAttributes.getHref());
}
}
}
@Override
public void link(Attributes attributes, String hrefOrHashName, String text) {
super.link(attributes, hrefOrHashName, text);
processLink(getLocator(), hrefOrHashName);
}
@Override
public void imageLink(Attributes linkAttributes, Attributes ImageAttributes, String href, String imageUrl) {
super.imageLink(linkAttributes, ImageAttributes, href, imageUrl);
processLink(getLocator(), href);
}
}
@Override
protected boolean isBlocksOnly() {
return false;
}
@Override
public DocumentBuilder createOutlineUpdater(OutlineItem rootItem) {
return new OutlineBuilderExtension(rootItem, getLabelMaxLength());
}
public void processLink(Locator locator, String href) {
if ((locator.getDocumentOffset() < offset) || (locator.getDocumentOffset() >= (offset + length))) {
return;
}
if (href.length() > 0 && href.charAt(0) == '#') {
if (references == null) {
references = new ArrayList<LocalReference>();
}
String name = href.substring(1);
int length = locator.getLineSegmentEndOffset() - locator.getLineCharacterOffset();
references.add(new LocalReference(name, locator.getDocumentOffset(), length));
}
}
}
protected abstract MarkupLanguage createMarkupLanguage();
@Override
public ValidationProblem findProblem(String markup, int offset, int length) {
throw new UnsupportedOperationException();
}
@Override
public List<ValidationProblem> findProblems(String markup, int offset, int length) {
MarkupLanguage markupLanguage = createMarkupLanguage();
OutlineParserExtension outlineParser = new OutlineParserExtension(offset, length);
outlineParser.setMarkupLanguage(markupLanguage);
outlineParser.parse(markup);
List<ValidationProblem> problems = null;
if (outlineParser.references != null) {
Set<String> anchorNames = outlineParser.idGenerator.getAnchorNames();
for (LocalReference reference : outlineParser.references) {
if (!anchorNames.contains(reference.name)) {
if (problems == null) {
problems = new ArrayList<ValidationProblem>();
}
problems.add(new ValidationProblem(Severity.ERROR,
MessageFormat.format(Messages.getString("DocumentLocalReferenceValidationRule.0"), //$NON-NLS-1$
reference.name),
reference.offset, reference.length));
}
}
}
if (problems == null) {
return Collections.emptyList();
}
return problems;
}
}