com.exadatum.xsuite.xmaven.bash.doc.DocBlockIterator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bash-maven-plugin Show documentation
Show all versions of bash-maven-plugin Show documentation
Bash Maven Plugin is used to generate documentation as well as to run unit test for bash scripts.
The newest version!
package com.exadatum.xsuite.xmaven.bash.doc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
/*-
* #%L
* Bash Unit Test Plugin
* %%
* Copyright (C) 2016 - 2017 Exadatum Software Services Pvt. Ltd.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.commons.lang3.StringUtils;
/**
* Iterates over DocBlock.
*/
public class DocBlockIterator implements Iterator {
/**
* Line Separator.
*/
private static final String LINE_SEPARATOR = System
.getProperty("line.separator");
/**
* Maintain the index of Asterisk in Doc.
*/
private static final int INDEX_OF_STAR = 1;
/**
* Lines of script.
*/
private String[] lines;
/**
* Cursor.
*/
private int cursor = -1;
/**
* Doc block lines.
*/
private ArrayList docBlockLines = new ArrayList<>();
/**
* Description of Doc Block.
*/
private ArrayList descriptionDocBlockLines = new ArrayList<>();
/**
* Target output.
*/
private String target;
/**
* Parser block using parser and lexer generated using the grammar specified
* using antlr4.
*/
private DocBlockParser blockParser;
/**
* Flag for script description.
*/
private boolean scriptDescriptionFound = false;
/**
* Constructor to initialize Lines and DocBlockParser.
*
* @param contents
*/
public DocBlockIterator(final String contents) {
this.lines = contents.split(LINE_SEPARATOR);
blockParser = new DocBlockParser();
}
/**
* Checks if there is any next valid documentation block is available or not.
*
* @return
*/
@Override
public boolean hasNext() {
// Doc block consists of start tag, end tag and the element for which
// the doc block has been written.
boolean startTagFound = false;
boolean endTagFound = false;
if (cursor == -1) {
// this is first call to hasNext.
cursor = 0;
}
docBlockLines.clear();
while (cursor < lines.length && !endTagFound) {
// Start and End tag are expected to be on its own line.
if (isValidStartTag()) {
startTagFound = true;
docBlockLines.add(DocBlockStructure.START.getKeyword().substring(1)
.concat(LINE_SEPARATOR));
} else if (isValidEndTag()) {
if (!startTagFound) {
throwInvalidBlockStructureException("Doc block end tag "
+ "found with no matching start tag at [%d]", cursor);
}
endTagFound = true;
docBlockLines.add(DocBlockStructure.END.getKeyword().substring(1));
} else if (isValidContinuationTag()) {
if (!startTagFound) {
throwInvalidBlockStructureException(
"Doc block continuation tag found with no"
+ " matching start tag at [%d]",
cursor);
}
docBlockLines.add(
lines[cursor].substring(INDEX_OF_STAR).concat(LINE_SEPARATOR));
}
cursor++;
}
if (startTagFound && !endTagFound) {
throwInvalidBlockStructureException(
"No end tag found in " + "the Doc block");
}
int tempCursor = cursor;
while (tempCursor < lines.length) {
String trimmedLine = lines[tempCursor].trim();
if (!StringUtils.isBlank(trimmedLine)) {
if (trimmedLine.startsWith("function ")) {
target = trimmedLine.substring(trimmedLine.indexOf(" "),
trimmedLine.indexOf("("));
break;
} else if (trimmedLine.indexOf("=") != -1) {
target = trimmedLine;
break;
} else if (trimmedLine.startsWith("##") && !scriptDescriptionFound) {
scriptDescriptionFound = true;
target = "Description";
break;
} else {
throwInvalidBlockStructureException(
"Function or constant declaration shall follow document"
+ " block. But found otherwise at [%d]",
cursor);
}
}
tempCursor++;
}
return endTagFound;
}
/**
* Method for Invalid Block Structure Exception.
*
* @param message
* @param args
*/
private void throwInvalidBlockStructureException(final String message,
final Object... args) {
throw new InvalidDocBlockStructure(String.format(message, args));
}
/**
* A valid start tag only contains {@link DocBlockStructure.START} and nothing
* else.
*
* @return if the start tag is valid
*/
private boolean isValidStartTag() {
return isValidTag(DocBlockStructure.START, true);
}
/**
* A valid end tag only contains {@link DocBlockStructure.END} and nothing
* else.
*
* @return if the valid end tag
*/
private boolean isValidEndTag() {
return isValidTag(DocBlockStructure.END, true);
}
/**
* A valid end tag only contains {@link DocBlockStructure.CONTINUATION} and
* nothing else.
*
* @return if the continuation start tag is valid
*/
private boolean isValidContinuationTag() {
return isValidTag(DocBlockStructure.CONTINUATION, false);
}
/**
* Validate if tag structure is correct.
*
* @return if the tag structure is valid
*/
private boolean isValidTag(final DocBlockStructure tag,
final boolean soloTag) {
boolean isValid = false;
String trimmedLine = lines[cursor].trim();
// check if the line starts with the tag
if (trimmedLine.startsWith(tag.getKeyword())) {
// check if the line only contains the tag or is solo tag( start and
// end tags are solo as they come on their own line)
if (trimmedLine.length() == tag.getKeyword().length() || !soloTag) {
isValid = true;
} else {
isValid = false;
throwInvalidBlockStructureException(
"Invalid [%1$s] tag at [%d]. [%1$s] tag shall always be"
+ " on its own line. " + "But some other characters found.",
tag.name(), cursor);
}
}
return isValid;
}
/**
*
* @return
*/
@Override
public DocBlock next() {
if (cursor == -1) {
// developer forgot to call hasNext. Blocks are read in hasNext
// method.
throw new IllegalStateException(
"hasNext() method is not called." + " cursor is not open");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (!descriptionDocBlockLines.isEmpty()) {
for (String docBlockLine : descriptionDocBlockLines) {
try {
baos.write(StringUtils.stripStart(docBlockLine, null)
.getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} else if (!docBlockLines.isEmpty()) {
for (String docBlockLine : docBlockLines) {
try {
baos.write(StringUtils.stripStart(docBlockLine, null)
.getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
try {
DocBlock docBlock = blockParser
.parse(new ByteArrayInputStream(baos.toByteArray()));
docBlock.setTarget(target);
return docBlock;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}