package org.exist.xquery.xqdoc;
import antlr.RecognitionException;
import antlr.TokenStreamException;
import org.exist.xquery.ExternalModule;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.xqdoc.parser.XQDocLexer;
import org.exist.xquery.xqdoc.parser.XQDocParser;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
/**
* Helper for parsing XQDoc comments on function declarations. XQDoc comments
* are stored in the function signature but not parsed until one of the
* inspection functions accesses them.
*/
public class XQDocHelper {
public static void parse(FunctionSignature signature) {
final String desc = signature.getDescription();
if (desc == null || !desc.startsWith("(:")) {
return;
}
final XQDocHelper helper = parseComment(desc);
if (helper == null)
{return;}
helper.enhance(signature);
}
public static void parse(ExternalModule module) {
final String desc = module.getDescription();
if (desc == null || !desc.startsWith("(:")) {
return;
}
final XQDocHelper helper = parseComment(desc);
if (helper == null)
{return;}
helper.enhance(module);
}
private static XQDocHelper parseComment(String desc) {
final XQDocLexer lexer = new XQDocLexer(new StringReader(desc));
final XQDocParser parser = new XQDocParser(lexer);
try {
final XQDocHelper helper = new XQDocHelper();
parser.xqdocComment(helper);
return helper;
} catch (final RecognitionException e) {
// ignore: comment will be shown unparsed
} catch (final TokenStreamException e) {
// ignore: comment will be shown unparsed
}
return null;
}
private StringBuilder description = new StringBuilder();
private Map<String, String> parameters = new HashMap<String, String>();
private String returnValue = null;
private Map<String, String> meta = new HashMap<String, String>();
public XQDocHelper() {
}
public void addDescription(CharSequence part) {
description.append(part);
}
public void setParameter(String comment) {
final String components[] = comment.trim().split("\\s+", 2);
if(components != null && components.length == 2) {
String var = components[0];
if (var.length() > 0 && var.charAt(0) == '$')
{var = var.substring(1);}
parameters.put(var, components[1]);
}
}
public void setTag(String tag, String content) {
if ("@param".equals(tag)) {
setParameter(content);
} else if ("@return".equals(tag)) {
returnValue = content;
} else {
meta.put(tag, content);
}
}
protected void enhance(FunctionSignature signature) {
signature.setDescription(description.toString());
if (returnValue != null) {
final SequenceType returnType = signature.getReturnType();
final FunctionReturnSequenceType newType =
new FunctionReturnSequenceType(returnType.getPrimaryType(), returnType.getCardinality(), returnValue);
signature.setReturnType(newType);
}
final SequenceType[] args = signature.getArgumentTypes();
for (final SequenceType type : args) {
if (type instanceof FunctionParameterSequenceType) {
final FunctionParameterSequenceType argType = (FunctionParameterSequenceType)type;
final String desc = parameters.get(argType.getAttributeName());
if (desc != null)
{argType.setDescription(desc);}
}
}
for (final Map.Entry<String, String> entry: meta.entrySet()) {
String key = entry.getKey();
if (key.length() > 1 && key.charAt(0) == '@')
{key = key.substring(1);}
signature.addMetadata(key, entry.getValue());
}
}
protected void enhance(ExternalModule module) {
module.setDescription(description.toString());
for (final Map.Entry<String, String> entry: meta.entrySet()) {
String key = entry.getKey();
if (key.length() > 1 && key.charAt(0) == '@')
{key = key.substring(1);}
module.addMetadata(key, entry.getValue());
}
}
public String toString() {
final StringBuilder out = new StringBuilder();
out.append(description).append("\n\n");
for (final Map.Entry<String, String> entry : meta.entrySet()) {
out.append(String.format("%20s\t%s\n", entry.getKey(), entry.getValue()));
}
for (final Map.Entry<String, String> entry : parameters.entrySet()) {
out.append(String.format("%20s\t%s\n", entry.getKey(), entry.getValue()));
}
return out.toString();
}
}