/*******************************************************************************
* Copyright (c) 2008, 2011 IBM Corporation 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:
* Ary Borenszweig - initial API and implementation? (Descent project)
*******************************************************************************/
package descent.core.ddoc;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import descent.core.ddoc.DdocSection.Parameter;
import dtool.parser.DeeLexingUtil;
/**
* Parser of ddoc documentation comments.
*/
public class DdocParser {
private String text;
private char docChar;
private String docEnd;
private String currentSectionName;
private int currentSectionType;
private StringBuilder currentSectionText;
private String currentParameterName;
private StringBuilder currentParameterText;
private String startOfCodeLine;
private Ddoc ddoc;
private List<Parameter> parameters;
/**
* Creates a parser for the given text.
* @param text the text to parse
*/
public DdocParser(String text) {
this.text = text;
}
/**
* Parses the text and returns the Ddoc information.
* @return the ddoc information
*/
public Ddoc parse() {
try {
return internalParse();
} catch (IOException e) {
return new Ddoc();
}
}
private Ddoc internalParse() throws IOException {
ddoc = new Ddoc();
StringReader stringReader = new StringReader(text);
BufferedReader reader = new BufferedReader(stringReader);
String firstLine = reader.readLine();
if (firstLine == null) {
return ddoc;
}
firstLine = firstLine.trim();
if (firstLine.length() < 3) {
return ddoc;
}
docChar = firstLine.charAt(1);
docEnd = docChar + "/"; //$NON-NLS-1$
currentSectionText = new StringBuilder();
currentSectionText.append(getContentThatMatters(firstLine.substring(3)));
currentSectionType = DdocSection.NORMAL_SECTION;
currentParameterName = null;
currentParameterText = new StringBuilder();
String line;
while((line = reader.readLine()) != null) {
line = getContentThatMatters(line);
if (line.length() == 0 &&
currentSectionName == null &&
currentSectionType == DdocSection.NORMAL_SECTION &&
currentSectionText.length() > 0) {
addCurrentSection();
continue;
}
if (isCodeStartOrEnd(line)) {
if (currentSectionText.length() > 0 || currentSectionName != null) {
addCurrentSection();
}
currentSectionName = null;
if (currentSectionType == DdocSection.CODE_SECTION) {
currentSectionType = DdocSection.NORMAL_SECTION;
startOfCodeLine = null;
} else {
currentSectionType = DdocSection.CODE_SECTION;
startOfCodeLine = line;
}
continue;
}
int colonIndex = getColonOfSectionIndex(line);
if (colonIndex != -1 && DeeLexingUtil.isValidDIdentifier(line.substring(0, colonIndex).trim())) {
if (currentSectionType == DdocSection.CODE_SECTION) {
currentSectionType = DdocSection.NORMAL_SECTION;
}
if (startOfCodeLine != null) {
currentSectionText.insert(0, startOfCodeLine + " "); //$NON-NLS-1$
}
startOfCodeLine = null;
if (currentSectionText.length() > 0 || currentSectionName != null) {
addCurrentSection();
}
currentSectionType = DdocSection.NORMAL_SECTION;
currentSectionName = line.substring(0, colonIndex);
currentSectionText.append(line.substring(colonIndex + 1).trim());
if ("Params".equals(currentSectionName) || "Macros".equals(currentSectionName)) { //$NON-NLS-1$
currentSectionType = "Params".equals(currentSectionName) ?
DdocSection.PARAMS_SECTION : DdocSection.MACROS_SECTION; //$NON-NLS-1$
currentParameterName = null;
currentParameterText.setLength(0);
parameters = new ArrayList<Parameter>();
if (currentSectionText.length() == 0) {
continue;
}
line = currentSectionText.toString();
} else {
continue;
}
}
if (currentSectionType == DdocSection.PARAMS_SECTION || currentSectionType == DdocSection.MACROS_SECTION) {
int equalsIndex = getEqualsIndex(line);
if (equalsIndex == -1) {
if (currentParameterName != null) {
if (currentParameterText.length() > 0) {
appendSpace(currentParameterText);
}
currentParameterText.append(line);
}
continue;
}
if (currentParameterName != null) {
parameters.add(new Parameter(currentParameterName, currentParameterText.toString()));
currentParameterText.setLength(0);
}
currentParameterName = line.substring(0, equalsIndex).trim();
currentParameterText.append(line.substring(equalsIndex + 1).trim());
}
if (currentSectionText.length() > 0) {
appendSpace(currentSectionText);
}
currentSectionText.append(line);
}
if (currentSectionText.length() > 0 || currentSectionName != null) {
addCurrentSection();
}
reader.close();
stringReader.close();
return ddoc;
}
/**
* Trims a line and removes the leading * or +.
*/
private String getContentThatMatters(String line) {
line = line.trim();
if (line.endsWith(docEnd)) {
int i = line.length() - 2;
while(i >= 0 && line.charAt(i) == '*') {
i--;
}
line = line.substring(0, i + 1);
if (currentSectionType != DdocSection.CODE_SECTION) {
line = line.trim();
}
}
int i = 0;
while(i < line.length() && line.charAt(i) == docChar) {
i++;
}
line = line.substring(i);
if (currentSectionType != DdocSection.CODE_SECTION) {
line = line.trim();
}
return line;
}
/**
* Returns the index of the ':' character, if this
* line is a section marker. Returns -1 if
* the line is not a section.
*/
private static int getColonOfSectionIndex(String line) {
if (line.length() == 0 || line.charAt(0) == ':') {
return -1;
}
int i;
for(i = 1; i < line.length(); i++) {
char c = line.charAt(i);
if (Character.isWhitespace(c)) {
return -1;
}
if (c == ':') {
return i;
}
}
return -1;
}
private static int getEqualsIndex(String line) {
int indexOfEquals = line.indexOf('=');
if (indexOfEquals == -1) return -1;
return indexOfEquals;
}
private static boolean isCodeStartOrEnd(String line) {
line = line.trim();
if (line.length() < 3) {
return false;
}
for(int i = 0; i < line.length(); i++) {
if (line.charAt(i) != '-') {
return false;
}
}
return true;
}
private void appendSpace(StringBuilder sb) {
if (currentSectionType == DdocSection.CODE_SECTION) {
sb.append("\n"); //$NON-NLS-1$
} else {
sb.append(" "); //$NON-NLS-1$
}
}
private void addCurrentSection() {
if (parameters != null) {
if (currentParameterName != null) {
parameters.add(new Parameter(currentParameterName, currentParameterText.toString()));
}
ddoc.addSection(new DdocSection(currentSectionName, currentSectionType,
currentSectionText.toString().trim(), parameters.toArray(new Parameter[parameters.size()])));
} else {
ddoc.addSection(new DdocSection(currentSectionName, currentSectionType,
currentSectionText.toString().trim()));
}
currentSectionText.setLength(0);
}
}