com.gs.obevo.impl.reader.TextMarkupDocumentReader Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2017 Goldman Sachs.
* 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.
*/
package com.gs.obevo.impl.reader;
import java.util.List;
import java.util.Objects;
import com.gs.obevo.api.appdata.doc.TextMarkupDocument;
import com.gs.obevo.api.appdata.doc.TextMarkupDocumentSection;
import com.gs.obevo.db.sqlparser.textmarkup.TextMarkupLineSyntaxParserConstants;
import com.gs.obevo.db.sqlparser.textmarkup.TextMarkupParser;
import com.gs.obevo.db.sqlparser.textmarkup.Token;
import com.gs.obevo.impl.text.CommentRemover;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.collections.api.block.function.Function;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.api.tuple.Pair;
import org.eclipse.collections.impl.factory.Lists;
import org.eclipse.collections.impl.factory.Maps;
import org.eclipse.collections.impl.factory.Sets;
import org.eclipse.collections.impl.tuple.Tuples;
public class TextMarkupDocumentReader {
public static final String TAG_CHANGE = "CHANGE";
public static final String TAG_METADATA = "METADATA";
public static final String TAG_BODY = "BODY";
public static final String TAG_DROP_COMMAND = "DROP_COMMAND";
public static final String TAG_ROLLBACK = "ROLLBACK"; // not yet used elsewhere, unlike the
// rollbackIfAlreadyDeployed
public static final String TAG_ROLLBACK_IF_ALREADY_DEPLOYED = "ROLLBACK-IF-ALREADY-DEPLOYED";
public static final String TOGGLE_DISABLE_QUOTED_IDENTIFIERS = "DISABLE_QUOTED_IDENTIFIERS";
public static final String ATTR_UPDATE_TIME_COLUMN = "updateTimeColumn";
public static final String ATTR_DEPENDENCIES = "dependencies";
public static final String ATTR_EXCLUDE_DEPENDENCIES = "excludeDependencies";
public static final String ATTR_INCLUDE_DEPENDENCIES = "includeDependencies";
public static final String ATTR_PRIMARY_KEYS = "primaryKeys";
public static final String INCLUDE_ENVS = "includeEnvs";
public static final String EXCLUDE_ENVS = "excludeEnvs";
public static final String INCLUDE_PLATFORMS = "includePlatforms";
public static final String EXCLUDE_PLATFORMS = "excludePlatforms";
public static final String TAG_PERM_SCHEME = "permissionScheme";
private final ImmutableList firstLevelElements = Lists.immutable.with(TAG_CHANGE, TAG_DROP_COMMAND, TAG_METADATA, TAG_BODY);
private final ImmutableList singleLineElements = Lists.immutable.with(TAG_METADATA);
private final ImmutableList secondLevelElements = Lists.immutable.with(TAG_ROLLBACK_IF_ALREADY_DEPLOYED, TAG_ROLLBACK);
private final boolean legacyMode;
public TextMarkupDocumentReader(boolean legacyMode) {
this.legacyMode = legacyMode;
}
public TextMarkupDocument parseString(String text, final TextMarkupDocumentSection otherSection) {
ImmutableList textMarkupDocumentSections = this.parseString(text, this.firstLevelElements, true, "////");
if (otherSection != null) {
TextMarkupDocumentSection thisSection = textMarkupDocumentSections.detect(it -> Objects.equals(it.getName(), otherSection.getName()));
if (thisSection != null) {
thisSection.mergeAttributes(otherSection);
} else {
// ensure that the "otherSection" for the package metadata is inserted first to respect the RerunnableObjectFile syntax
textMarkupDocumentSections = Lists.mutable.of(otherSection).withAll(textMarkupDocumentSections).toImmutable();
}
}
return new TextMarkupDocument(textMarkupDocumentSections);
}
private ImmutableList parseString(String text, ImmutableList elementsToCheck, final boolean recurse,
final String elementPrefix) {
MutableList> outerSections = splitIntoMainSections(text, elementsToCheck, elementPrefix);
MutableList sections = outerSections.flatCollect(new ConvertOuterSectionToTextSection(recurse, elementPrefix));
// remove any blank sections
return sections.toImmutable().reject(each -> recurse
&& each.getName() == null
&& (StringUtils.isBlank(each.getContent()) || StringUtils.isBlank(CommentRemover.removeComments(each.getContent(), "removing on markup document reader")) // need comments in a separate clause as CommentRemover returns a "null" string on null; will fix eventually
));
}
private MutableList> splitIntoMainSections(String text, ImmutableList elementsToCheck, String elementPrefix) {
MutableList> outerSections = Lists.mutable.empty();
String nextSectionName = null;
boolean startOfSearch = true;
// here, we go in a loop searching for the next referenc of "[elementPrefix] [sectionName]", e.g. //// CHANGE
// By each of those points, we split those into separate text sections and return back to the client.
// We aim to preserve the line breaks found when parsing the sections
while (text != null) {
String currentSectionName = nextSectionName;
String currentSectionText;
int earliestIndex = Integer.MAX_VALUE;
for (String firstLevelElement : elementsToCheck) {
// on the first search, the text may start w/ the section; hence, we set the search fromIndex param to 0.
// Subsequently, the index picks up at the beginning of the next section; hence, we must start
// the search at the next character, so the fromIndex param is 1
int index = text.indexOf(elementPrefix + " " + firstLevelElement, startOfSearch ? 0 : 1);
if (index != -1 && index < earliestIndex) {
earliestIndex = index;
nextSectionName = firstLevelElement;
}
}
startOfSearch = false;
if (earliestIndex == Integer.MAX_VALUE) {
currentSectionText = StringUtils.chomp(text);
text = null;
} else {
currentSectionText = StringUtils.chomp(text.substring(0, earliestIndex));
text = text.substring(earliestIndex);
}
outerSections.add(Tuples.pair(currentSectionName, currentSectionText));
}
return outerSections;
}
private class ConvertOuterSectionToTextSection implements Function, Iterable> {
private final boolean recurse;
private final String elementPrefix;
ConvertOuterSectionToTextSection(boolean recurse, String elementPrefix) {
this.elementPrefix = elementPrefix;
this.recurse = recurse;
}
@Override
public Iterable valueOf(Pair outerSection) {
String currentSectionName = outerSection.getOne();
if (currentSectionName == null) {
return Lists.mutable.with(new TextMarkupDocumentSection(null, outerSection.getTwo()));
} else {
String[] contents = outerSection.getTwo().split("\\r?\\n", 2);
String firstLine = contents[0];
firstLine = firstLine.replaceFirst(elementPrefix + " " + currentSectionName, "");
Pair, ImmutableSet> attrsTogglesPair = parseAttrsAndToggles(firstLine);
ImmutableMap attrs = attrsTogglesPair.getOne();
ImmutableSet toggles = attrsTogglesPair.getTwo();
String sectionContent = contents.length > 1 ? contents[1] : null;
if (singleLineElements.contains(currentSectionName)) {
TextMarkupDocumentSection metadataSection = new TextMarkupDocumentSection(currentSectionName, null, attrs.toImmutable());
metadataSection.setToggles(toggles.toImmutable());
return Lists.mutable.with(metadataSection, new TextMarkupDocumentSection(null, sectionContent));
} else {
ImmutableList finalsubsections = Lists.immutable.empty();
String finalContent;
if (!recurse) {
finalContent = sectionContent;
} else if (sectionContent != null) {
ImmutableList subsections = parseString(sectionContent, secondLevelElements, false, "//");
if (subsections.size() == 1) {
finalContent = sectionContent;
} else {
finalContent = subsections.get(0).getContent();
finalsubsections = subsections.subList(1, subsections.size());
}
} else {
finalContent = null;
}
TextMarkupDocumentSection section = new TextMarkupDocumentSection(currentSectionName, finalContent, attrs.toImmutable());
section.setToggles(toggles.toImmutable());
section.setSubsections(finalsubsections);
return Lists.mutable.with(section);
}
}
}
}
Pair, ImmutableSet> parseAttrsAndToggles(String line) {
MutableMap attrs = Maps.mutable.empty();
MutableSet toggles = Sets.mutable.empty();
if (!legacyMode) {
List tokens = TextMarkupParser.parseTokens(line);
Token curToken = !tokens.isEmpty() ? tokens.get(0) : null;
while (curToken != null && curToken.kind != TextMarkupLineSyntaxParserConstants.EOF) {
switch (curToken.kind) {
case TextMarkupLineSyntaxParserConstants.WHITESPACE:
// skip whitespace if encountered
break;
case TextMarkupLineSyntaxParserConstants.QUOTED_LITERAL:
case TextMarkupLineSyntaxParserConstants.STRING_LITERAL:
// let's check if this is a toggle or an attribute
if (curToken.next.kind == TextMarkupLineSyntaxParserConstants.ASSIGN) {
Token keyToken = curToken;
curToken = curToken.next; // to ASSIGN
curToken = curToken.next; // to the following token
switch (curToken.kind) {
case TextMarkupLineSyntaxParserConstants.QUOTED_LITERAL:
case TextMarkupLineSyntaxParserConstants.STRING_LITERAL:
// in this case, we have an attribute value
String value = curToken.image;
if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') {
value = curToken.image.substring(1, curToken.image.length() - 1);
}
value = value.replaceAll("\\\\\"", "\"");
attrs.put(keyToken.image, value);
break;
case TextMarkupLineSyntaxParserConstants.WHITESPACE:
case TextMarkupLineSyntaxParserConstants.EOF:
// in this case, we will assume a blank value
attrs.put(keyToken.image, "");
break;
case TextMarkupLineSyntaxParserConstants.ASSIGN:
default:
throw new IllegalStateException("Not allowed here");
}
} else {
toggles.add(curToken.image);
}
break;
case TextMarkupLineSyntaxParserConstants.ASSIGN:
toggles.add(curToken.image);
break;
case TextMarkupLineSyntaxParserConstants.EOF:
default:
throw new IllegalStateException("Should not arise");
}
curToken = curToken.next;
}
} else {
// keeping this mode for backwards-compatibility until we can guarantee all clients are fine without it
// This way cannot handle spaces in quotes
String[] args = StringUtils.splitByWholeSeparator(line, " ");
for (String arg : args) {
if (arg.contains("=")) {
String[] attr = arg.split("=");
if (attr.length > 2) {
throw new IllegalArgumentException("Cannot mark = multiple times in a parameter - " + line);
}
String attrVal = attr[1];
if (attrVal.startsWith("\"") && attrVal.endsWith("\"")) {
attrVal = attrVal.substring(1, attrVal.length() - 1);
}
attrs.put(attr[0], attrVal);
} else if (StringUtils.isNotBlank(arg)) {
toggles.add(arg);
}
}
}
return Tuples.pair(attrs.toImmutable(), toggles.toImmutable());
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy