/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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.asakusafw.dmdl.directio.text;
import static com.asakusafw.dmdl.directio.text.TextFormatConstants.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.asakusafw.dmdl.directio.util.MapValue;
import com.asakusafw.dmdl.directio.util.Value;
import com.asakusafw.dmdl.model.AstAttribute;
import com.asakusafw.dmdl.model.AstAttributeElement;
import com.asakusafw.dmdl.semantics.DmdlSemantics;
import com.asakusafw.runtime.io.text.TextUtil;
/**
* Settings of character escape.
* @since 0.9.1
*/
public class EscapeSettings {
private Value<Character> character = Value.undefined();
private MapValue<Character, Character> sequences = new MapValue<>(null);
private Value<Boolean> escapeLineSeparator = Value.undefined();
/**
* Returns the escape character.
* @return the escape character
*/
public Value<Character> getCharacter() {
return character;
}
/**
* Returns the escape sequence map.
* @return the escape sequence map
*/
public MapValue<Character, Character> getSequences() {
return sequences;
}
/**
* Returns whether or not line separators can be escaped.
* @return {@code true} if enabled, otherwise {@code false}
*/
public Value<Boolean> getEscapeLineSeparator() {
return escapeLineSeparator;
}
/**
* Consumes attribute elements about escape settings, and returns corresponding {@link EscapeSettings}.
* @param environment the current environment
* @param attribute the attribute
* @param elements the element map to be consumed
* @return corresponded {@link EscapeSettings}.
*/
public static EscapeSettings consume(
DmdlSemantics environment, AstAttribute attribute,
Map<String, AstAttributeElement> elements) {
AttributeAnalyzer analyzer = new AttributeAnalyzer(environment, attribute);
EscapeSettings settings = new EscapeSettings();
consumeCharacter(settings, analyzer, elements.remove(ELEMENT_ESCAPE_CHARACTER));
consumeSequences(settings, analyzer, elements.remove(ELEMENT_ESCAPE_SEQUENCE_MAP));
consumeEscapeLineSeparator(settings, analyzer, elements.remove(ELEMENT_ESCAPE_LINE_SEPARATOR));
return settings;
}
private static void consumeCharacter(
EscapeSettings settings, AttributeAnalyzer analyzer, AstAttributeElement element) {
if (element != null) {
settings.character = analyzer.toCharacter(element);
}
}
private static void consumeSequences(
EscapeSettings settings, AttributeAnalyzer analyzer, AstAttributeElement element) {
if (element != null) {
settings.sequences = analyzer.toCharacterMapWithNullValue(element);
}
}
private static void consumeEscapeLineSeparator(
EscapeSettings settings, AttributeAnalyzer analyzer, AstAttributeElement element) {
if (element != null) {
settings.escapeLineSeparator = analyzer.toBoolean(element);
}
}
/**
* Verifies this settings.
* @param environment the current environment
* @param attribute the original attribute
* @return {@code true} if the settings seems valid, otherwise {@code false}
*/
public boolean verify(DmdlSemantics environment, AstAttribute attribute) {
AttributeAnalyzer analyzer = new AttributeAnalyzer(environment, attribute);
if (character.isPresent() == false) {
if (sequences.getDeclaration() != null) {
errorMissing(analyzer, sequences.getDeclaration(), ELEMENT_ESCAPE_CHARACTER);
}
if (escapeLineSeparator.isPresent()) {
errorMissing(analyzer, escapeLineSeparator.getDeclaration(), ELEMENT_ESCAPE_CHARACTER);
}
} else {
if (isLineSeparator(character.getEntity())) {
analyzer.error(character.getDeclaration(),
Messages.getString("EscapeSettings.diagnosticConflictLineSeparator")); //$NON-NLS-1$
}
if (sequences.getDeclaration() != null) {
Set<Character> sawKeys = new HashSet<>();
Set<Character> sawValues = new HashSet<>();
for (MapValue.Entry<Character, Character> entry : sequences.getEntries()) {
Character value = entry.getValue();
Character key = entry.getKey();
if (isLineSeparator(key)) {
analyzer.error(sequences.getDeclaration(), entry.getDeclaration(),
"{0} must not be line separator characters"); //$NON-NLS-1$
}
if (sawKeys.contains(key)) {
analyzer.warn(sequences.getDeclaration(), entry.getDeclaration(),
Messages.getString("EscapeSettings.diagnosticEscapeSequenceDuplicateKey"), //$NON-NLS-1$
TextUtil.quote(String.valueOf(key)));
}
if (sawValues.contains(value)) {
analyzer.warn(sequences.getDeclaration(), entry.getDeclaration(),
Messages.getString("EscapeSettings.diagnosticEscapeSequenceDuplicateValue"), //$NON-NLS-1$
value == null ? VALUE_NULL : TextUtil.quote(String.valueOf(value)));
}
sawKeys.add(key);
sawValues.add(value);
}
}
}
return analyzer.hasError() == false;
}
private static boolean isLineSeparator(Character c) {
return c != null && (c == '\r' || c == '\n');
}
private static void errorMissing(AttributeAnalyzer analyzer, AstAttributeElement declaration, String attribute) {
analyzer.error(declaration, Messages.getString("EscapeSettings.diagnosticMissingRelatedAttribute"), attribute); //$NON-NLS-1$
}
}