/*
* Copyright (c) 2007-2009, Osmorc Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.osmorc.manifest.lang;
import com.intellij.lang.ASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiParser;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.osmorc.manifest.lang.headerparser.HeaderParser;
import org.osmorc.manifest.lang.headerparser.HeaderParserRepository;
/**
* @author Robert F. Beeger (robert@beeger.net)
*/
class ManifestParser implements PsiParser {
ManifestParser(@NotNull final HeaderParserRepository headerParserRepository) {
this.headerParserRepository = headerParserRepository;
}
@NotNull
public ASTNode parse(IElementType root, PsiBuilder builder) {
builder.setDebugMode(DEBUG_MODE /*|| ApplicationManager.getApplication().isUnitTestMode()*/);
final PsiBuilder.Marker rootMarker = builder.mark();
while (!builder.eof()) {
parse(builder);
}
closeAll();
rootMarker.done(root);
return builder.getTreeBuilt();
}
private void closeAll() {
closeHeaderValuePart();
closeAssignmentMarker();
closeClause();
closeHeader();
closeSection();
}
protected void parse(PsiBuilder builder) {
if (sectionMarker == null) {
sectionMarker = builder.mark();
}
final IElementType tokenType = builder.getTokenType();
if (tokenType == ManifestTokenType.HEADER_NAME) {
parseHeaderName(builder);
} else if (tokenType == ManifestTokenType.SECTION_END) {
closeAll();
builder.advanceLexer();
} else if (tokenType == ManifestTokenType.SIGNIFICANT_SPACE ||
tokenType == TokenType.BAD_CHARACTER ||
tokenType == ManifestTokenType.NEWLINE) {
builder.advanceLexer();
} else if (currentHeaderIsSimpleHeader) {
parseSimpleHeaderValue(builder);
} else {
parseComplexHeaderValue(builder);
}
}
private void parseSimpleHeaderValue(PsiBuilder builder) {
clauseMarker = builder.mark();
headerValuePartMarker = builder.mark();
while (!builder.eof() &&
builder.getTokenType() != ManifestTokenType.SECTION_END &&
builder.getTokenType() != ManifestTokenType.HEADER_NAME) {
builder.advanceLexer();
}
closeHeaderValuePart();
closeClause();
closeHeader();
}
private void parseComplexHeaderValue(PsiBuilder builder) {
while (!builder.eof() &&
builder.getTokenType() != ManifestTokenType.SECTION_END &&
builder.getTokenType() != ManifestTokenType.HEADER_NAME) {
if (clauseMarker == null) {
clauseMarker = builder.mark();
}
if (headerValuePartMarker == null) {
headerValuePartMarker = builder.mark();
}
boolean lexerAdvanced = parseQuotedString(builder) ||
parseClause(builder) ||
parseParameter(builder) ||
parseDirective(builder) ||
parseAttribute(builder);
if (!lexerAdvanced) {
builder.advanceLexer();
}
}
closeHeaderValuePart();
closeAssignmentMarker();
closeClause();
closeHeader();
}
private boolean parseQuotedString(PsiBuilder builder) {
if (builder.getTokenType() == ManifestTokenType.QUOTE) {
do {
builder.advanceLexer();
} while (!builder.eof() &&
builder.getTokenType() != ManifestTokenType.QUOTE &&
builder.getTokenType() != ManifestTokenType.SECTION_END &&
builder.getTokenType() != ManifestTokenType.HEADER_NAME);
if (builder.getTokenType() == ManifestTokenType.QUOTE) {
builder.advanceLexer();
}
return true;
}
return false;
}
private boolean parseClause(PsiBuilder builder) {
if (builder.getTokenType() == ManifestTokenType.COMMA) {
closeHeaderValuePart();
closeAssignmentMarker();
closeClause();
builder.advanceLexer();
return true;
}
return false;
}
private boolean parseParameter(PsiBuilder builder) {
if (builder.getTokenType() == ManifestTokenType.SEMICOLON) {
closeHeaderValuePart();
closeAssignmentMarker();
builder.advanceLexer();
return true;
}
return false;
}
private boolean parseDirective(PsiBuilder builder) {
if (builder.getTokenType() == ManifestTokenType.COLON && assignmentMarkerType == null) {
assignmentMarker = headerValuePartMarker.precede();
assignmentMarkerType = ManifestElementTypes.DIRECTIVE;
closeHeaderValuePart();
builder.advanceLexer();
if (builder.getTokenType() == ManifestTokenType.NEWLINE) {
builder.advanceLexer();
if (builder.getTokenType() == ManifestTokenType.SIGNIFICANT_SPACE) {
builder.advanceLexer();
}
}
if (builder.getTokenType() == ManifestTokenType.EQUALS) {
builder.advanceLexer();
}
return true;
}
return false;
}
private boolean parseAttribute(PsiBuilder builder) {
if (builder.getTokenType() == ManifestTokenType.EQUALS && assignmentMarkerType == null) {
assignmentMarker = headerValuePartMarker.precede();
assignmentMarkerType = ManifestElementTypes.ATTRIBUTE;
closeHeaderValuePart();
builder.advanceLexer();
return true;
}
return false;
}
private void parseHeaderName(PsiBuilder builder) {
closeHeader();
headerMarker = builder.mark();
currentHeaderName = builder.getTokenText();
HeaderParser headerParser = headerParserRepository.getHeaderParser(currentHeaderName);
currentHeaderIsSimpleHeader = headerParser == null || headerParser.isSimpleHeader();
builder.advanceLexer();
while (!builder.eof() && builder.getTokenType() != ManifestTokenType.COLON && builder.getTokenType() != ManifestTokenType.NEWLINE) {
builder.advanceLexer();
}
builder.advanceLexer();
}
private void closeHeader() {
if (headerMarker != null) {
headerMarker.done(ManifestElementTypes.HEADER);
headerMarker = null;
}
}
private void closeClause() {
if (clauseMarker != null) {
clauseMarker.done(ManifestElementTypes.CLAUSE);
clauseMarker = null;
}
}
private void closeSection() {
if (sectionMarker != null) {
sectionMarker.done(ManifestElementTypes.SECTION);
sectionMarker = null;
}
}
private void closeAssignmentMarker() {
if (assignmentMarker != null) {
assignmentMarker.done(assignmentMarkerType);
assignmentMarker = null;
assignmentMarkerType = null;
}
}
private void closeHeaderValuePart() {
if (headerValuePartMarker != null) {
headerValuePartMarker.done(ManifestElementTypes.HEADER_VALUE_PART);
headerValuePartMarker = null;
}
}
private String currentHeaderName;
private boolean currentHeaderIsSimpleHeader;
private final HeaderParserRepository headerParserRepository;
private PsiBuilder.Marker headerValuePartMarker;
private PsiBuilder.Marker sectionMarker;
private PsiBuilder.Marker headerMarker;
private PsiBuilder.Marker clauseMarker;
private PsiBuilder.Marker assignmentMarker;
private IElementType assignmentMarkerType;
private static final boolean DEBUG_MODE = Boolean.getBoolean("Osmorc.debug");
}