| /******************************************************************************* |
| * Copyright (c) 2009, 2019 Xored Software Inc and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * Xored Software Inc - initial API and implementation and/or initial documentation |
| *******************************************************************************/ |
| package org.eclipse.rcptt.core.ecl.formatter.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.rcptt.core.ecl.formatter.EclFormatterOptions; |
| import org.eclipse.rcptt.core.ecl.scanner.EclCharClasses; |
| |
| /* |
| * TODO: |
| * * Substitution spacing cleanup or linebreak/spacing remove flag. |
| * * In-line blocks. |
| */ |
| |
| public class SourceBuilder { |
| public static final Set<String> NO_WRAP_COMMANDS = new HashSet<String>( |
| Arrays.asList("proc", "val")); |
| |
| private final EclFormatterOptions options; |
| |
| public SourceBuilder(EclFormatterOptions options) { |
| this.options = options; |
| } |
| |
| private List<Part> parts = new ArrayList<Part>(); |
| |
| public Part append(String text, Block block, NewLine newLine, Spacing spacing, |
| Glue glue, Quote quote) { |
| Part part = new Part(text, block, newLine, spacing, glue, quote); |
| parts.add(part); |
| return part; |
| } |
| |
| public void invalid(String text) { |
| append(text, Block.None, NewLine.None, |
| Spacing.Left, Glue.None, Quote.Never); |
| } |
| |
| public void standaloneSlComment(String text) { |
| append("// ", Block.None, NewLine.None, |
| Spacing.None, Glue.Right, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.After, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void slCommentAtLineEnd(String text) { |
| append("// ", Block.None, NewLine.None, |
| Spacing.Left, Glue.Left, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.After, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void slCommentAtSequenceEnd(String text) { |
| append("// ", Block.None, NewLine.None, |
| Spacing.Left, Glue.Left, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void softSlCommentAtLineEnd(String text) { |
| append("// ", Block.None, NewLine.None, |
| Spacing.Left, Glue.Both, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| |
| append("", Block.None, NewLine.Soft, |
| Spacing.None, Glue.Right, Quote.Never); |
| } |
| |
| public void standaloneMlComment(String text) { |
| append("/* ", Block.None, NewLine.None, |
| Spacing.None, Glue.Right, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| append(" */", Block.None, NewLine.After, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void inlineMlComment(String text) { |
| append("/* ", Block.None, NewLine.None, |
| Spacing.Left, Glue.Both, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| append(" */", Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void mlCommentAtLineStart(String text) { |
| append("/* ", Block.None, NewLine.None, |
| Spacing.None, Glue.Right, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| append(" */", Block.None, NewLine.None, |
| Spacing.Right, Glue.Both, Quote.Never); |
| } |
| |
| public void mlCommentAtLineEnd(String text) { |
| append("/* ", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| append(" */", Block.None, NewLine.After, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void softInlineMlComment(String text) { |
| append("/* ", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(trimComment(text), Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| append(" */", Block.None, NewLine.None, |
| Spacing.Right, Glue.None, Quote.Never); |
| } |
| |
| public void linebreak() { |
| append("", Block.None, NewLine.After, |
| Spacing.None, Glue.None, Quote.Never); |
| } |
| |
| public void semicolon() { |
| append(";", Block.None, NewLine.None, |
| Spacing.Right, Glue.Left, Quote.Never); |
| } |
| |
| public void pipe() { |
| append("|", Block.None, NewLine.None, |
| Spacing.Both, Glue.Right, Quote.Never); |
| } |
| |
| public void plus() { |
| append("+", Block.None, NewLine.None, |
| Spacing.Left, Glue.Left, Quote.Never); |
| } |
| |
| public void commandName(String name) { |
| append(name, Block.None, NewLine.None, |
| Spacing.None, |
| NO_WRAP_COMMANDS.contains(name.toLowerCase()) ? Glue.Right : Glue.None, |
| Quote.Never); |
| } |
| |
| public void boolArg(String text) { |
| append("-", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(text, Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public Part positionalLiteralArg(String value, boolean wrap) { |
| return append(value, Block.None, NewLine.None, |
| Spacing.Left, Glue.None, Quote.Literal).wrappable(wrap); |
| } |
| |
| public Part positionalQuotedLiteralArg(String value, boolean wrap) { |
| return append(value, Block.None, NewLine.None, |
| Spacing.Left, Glue.None, Quote.Always).wrappable(wrap); |
| } |
| |
| public void positionalVariableArg(String name) { |
| append("$", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(name, Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void variableEmit(String name) { |
| append("$", Block.None, NewLine.None, |
| Spacing.None, Glue.Right, Quote.Never); |
| append(name, Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public Part namedLiteralArg(String name, String value) { |
| namedLiteralArgName(name); |
| return namedLiteralArgValue(value); |
| } |
| |
| public void namedLiteralArgName(String name) { |
| append("-", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(name, Block.None, NewLine.None, |
| Spacing.Right, Glue.Both, Quote.Never); |
| } |
| |
| public Part namedLiteralArgValue(String value) { |
| return append(value, Block.None, NewLine.None, |
| Spacing.Left, Glue.Left, Quote.Literal).wrappable(true); |
| } |
| |
| public void namedVariableArg(String name, String variableName) { |
| append("-", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(name, Block.None, NewLine.None, |
| Spacing.Right, Glue.Both, Quote.Never); |
| append("$", Block.None, NewLine.None, |
| Spacing.Left, Glue.Both, Quote.Never); |
| append(variableName, Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void pipelineArgName(String text) { |
| append("-", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(text, Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| } |
| |
| public void pipelineArgOpen() { |
| append("[", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| } |
| |
| public void pipelineArgClose() { |
| append("]", Block.None, NewLine.None, |
| Spacing.None, Glue.Left, Quote.Never); |
| } |
| |
| public void scriptArgName(String text) { |
| append("-", Block.None, NewLine.None, |
| Spacing.Left, Glue.Right, Quote.Never); |
| append(text, Block.None, NewLine.None, |
| Spacing.None, Glue.Both, Quote.Never); |
| } |
| |
| public void scriptArgOpen() { |
| append("{", Block.Open, NewLine.After, |
| Spacing.Left, Glue.Left, Quote.Never); |
| } |
| |
| public void scriptArgClose() { |
| append("}", Block.Close, NewLine.Before, |
| Spacing.None, Glue.None, Quote.Never); |
| } |
| |
| @Override |
| public String toString() { |
| final StringBuilder output = new StringBuilder(); |
| |
| List<List<Part>> lines = findLines(parts); |
| cleanBlocks(lines); |
| |
| int level = 0; |
| final int last = lines.size() - 1; |
| for (int i = 0; i < lines.size(); ++i) { |
| final List<Part> l = lines.get(i); |
| |
| List<List<Part>> segments = findUnbreakableSegments(l); |
| |
| level += calculatePrelineLevelChange(l); |
| wrap(output, level, segments); |
| level += calculatePostlineLevelChange(l); |
| if (level < 0) |
| level = 0; |
| |
| if (i != last) |
| output.append('\n'); |
| } |
| |
| // remove trailing linebreaks |
| while (output.length() > 0 && output.charAt(output.length() - 1) == '\n') |
| output.setLength(output.length() - 1); |
| |
| return output.toString(); |
| } |
| |
| // -- |
| |
| private void cleanBlocks(List<List<Part>> lines) { |
| for (int i = 0; i < lines.size() - 1; ++i) { |
| Part current = lastPart(lines.get(i)); |
| Part next = firstPart(lines.get(i + 1)); |
| |
| if (current != null |
| && current.block == Block.Open |
| && (next == null || |
| next.newLine == NewLine.After && next.text.length() == 0 |
| && lastPart(lines.get(i + 1)) == next)) |
| lines.remove(i + 1); |
| } |
| |
| for (int i = lines.size() - 1; i >= 1; --i) { |
| Part current = firstPart(lines.get(i)); |
| Part prev = lastPart(lines.get(i - 1)); |
| if (current != null |
| && current.block == Block.Close |
| && (prev == null || |
| prev.newLine == NewLine.After && prev.text.length() == 0 |
| && firstPart(lines.get(i - 1)) == prev)) |
| lines.remove(i - 1); |
| } |
| } |
| |
| private Part firstPart(List<Part> line) { |
| return line.size() > 0 ? line.get(0) : null; |
| } |
| |
| private Part lastPart(List<Part> line) { |
| return line.size() > 0 ? line.get(line.size() - 1) : null; |
| } |
| |
| private int calculatePrelineLevelChange(List<Part> line) { |
| for (Part p : line) |
| if (p.block == Block.Close) |
| return -1; |
| |
| return 0; |
| } |
| |
| private int calculatePostlineLevelChange(List<Part> line) { |
| for (Part p : line) |
| if (p.block == Block.Open) |
| return 1; |
| |
| return 0; |
| } |
| |
| private void wrap(StringBuilder output, int level, List<List<Part>> line) { |
| if (line.size() == 0 || (line.size() == 1 && line.get(0).size() == 1 && line.get(0).get(0).text.length() == 0)) |
| return; // this is a blank line |
| |
| int indent = level * options.indent; |
| int currentLength = 0; |
| |
| boolean wrapped = false; |
| Spacing lastSpacing = Spacing.None; |
| |
| for (List<Part> s : findSoftSegments(line)) { |
| int segmentLength = getSegmentLength(s); |
| |
| boolean spacingAtStart = currentLength > 0 && |
| isSegmentStartHasSpacing(s, lastSpacing); |
| if (spacingAtStart) |
| segmentLength += 1; |
| |
| int fullLength = currentLength + indent + segmentLength + |
| (wrapped ? options.wrapIndent : 0); |
| |
| while (segmentLength > 0 && fullLength > options.wrapAt |
| && firstPart(s).newLine != NewLine.Soft) { |
| List<Part> newSegment = tryToWrapLitterals(s, |
| currentLength + indent + (wrapped ? options.wrapIndent : 0) + |
| (spacingAtStart ? 1 : 0)); |
| |
| if (newSegment != s) { |
| if (currentLength == 0) |
| indent(output, indent + (wrapped ? options.wrapIndent : 0)); |
| else if (spacingAtStart) |
| output.append(' '); |
| formatSegment(output, newSegment); |
| |
| output.append('\n'); |
| |
| // recalculate line info |
| currentLength = 0; |
| segmentLength = getSegmentLength(s); |
| spacingAtStart = false; |
| wrapped = true; |
| |
| fullLength = indent + segmentLength + options.wrapIndent; |
| } else |
| break; |
| } |
| |
| if (currentLength > 0 // allows line to grow up |
| && (segmentLength > 0 && fullLength > options.wrapAt) |
| || firstPart(s).newLine == NewLine.Soft) { |
| output.append('\n'); |
| currentLength = 0; |
| |
| for (Part p : s) |
| if (p.text.length() != 0 || p.newLine != NewLine.Soft) { |
| p.wrapped = true; |
| break; |
| } |
| wrapped = true; |
| } |
| |
| if (currentLength == 0) { |
| indent(output, indent + (wrapped ? options.wrapIndent : 0)); |
| } else if (spacingAtStart) { |
| output.append(' '); |
| ++currentLength; |
| } |
| |
| currentLength += formatSegment(output, s); |
| |
| lastSpacing = s.get(s.size() - 1).spacing; |
| } |
| } |
| |
| private List<Part> tryToWrapLitterals(List<Part> segment, int currentLength) { |
| final List<Part> first = new ArrayList<Part>(); |
| final List<Part> rest = new ArrayList<Part>(segment); |
| while (rest.size() > 0) { |
| final Part p = rest.get(0); |
| first.add(p); |
| rest.remove(0); |
| final int segmentLength = getSegmentLength(first); |
| |
| if (currentLength + segmentLength > options.wrapAt) { |
| if (!p.wrappable) |
| return segment; |
| |
| final int literalLength = p.length(); |
| if (literalLength < options.minLiteralLengthToWrap) |
| return segment; |
| |
| if (p.quote != Quote.Always && p.quote != Quote.OnWrap) |
| return segment; |
| |
| final boolean quoted = p.quote == Quote.Always || p.wrapped; |
| final int excess = currentLength + segmentLength - options.wrapAt; |
| if (literalLength - excess < options.minLiteralLengthToStayOnWrap) |
| return segment; |
| |
| first.remove(first.size() - 1); |
| first.add(new Part(p.text.substring(0, p.text.length() - excess - (quoted ? 2 : 4)), |
| Block.None, NewLine.None, p.spacing, Glue.None, Quote.Always)); |
| first.add(new Part("+", Block.None, NewLine.None, Spacing.Left, Glue.None, |
| Quote.Never)); |
| |
| final Part newLiteral = new Part(p.text.substring(p.text.length() - excess - (quoted ? 2 : 4)), |
| Block.None, NewLine.None, p.spacing, Glue.None, Quote.Always); |
| newLiteral.wrappable = true; |
| rest.add(0, newLiteral); |
| |
| segment.clear(); |
| segment.addAll(rest); |
| return first; |
| } |
| } |
| |
| return segment; |
| } |
| |
| private List<List<Part>> findSoftSegments(List<List<Part>> line) { |
| List<List<Part>> newLine = new ArrayList<List<Part>>(); |
| |
| for (List<Part> s : line) { |
| List<Part> newSegment = new ArrayList<Part>(); |
| newLine.add(newSegment); |
| |
| for (Part p : s) |
| if (p.newLine == NewLine.Soft) { |
| if (newSegment.size() == 0) |
| newSegment.add(p); |
| else { |
| newSegment = new ArrayList<Part>(); |
| newLine.add(newSegment); |
| newSegment.add(p); |
| } |
| } else |
| newSegment.add(p); |
| } |
| return newLine; |
| } |
| |
| private boolean[] buildSpacesMap(List<Part> segment) { |
| boolean map[] = new boolean[segment.size() + 1]; |
| |
| for (int i = 0; i < segment.size(); ++i) { |
| Part p = segment.get(i); |
| |
| switch (p.spacing) { |
| case None: |
| break; |
| case Left: |
| map[i] = true; |
| break; |
| case Right: |
| map[i + 1] = true; |
| break; |
| case Both: |
| map[i] = map[i + 1] = true; |
| break; |
| } |
| } |
| |
| for (int i = 0; i < segment.size(); ++i) { |
| Part p = segment.get(i); |
| if (p.newLine == NewLine.Soft && p.text.length() == 0) |
| map[i + 1] = false; |
| } |
| |
| return map; |
| } |
| |
| private int formatSegment(StringBuilder builder, List<Part> segment) { |
| int length = 0; |
| boolean spacesMap[] = buildSpacesMap(segment); |
| |
| int last = segment.size() - 1; |
| for (int i = 0; i < segment.size(); ++i) { |
| Part p = segment.get(i); |
| length += p.format(builder); |
| if (i != last && spacesMap[i + 1]) { |
| builder.append(' '); |
| ++length; |
| } |
| } |
| |
| return length; |
| } |
| |
| private int indent(StringBuilder builder, int spaces) { |
| int result = spaces; |
| |
| if (options.useTabs) { |
| for (int i = 0; i < spaces / options.tabSize; ++i) |
| builder.append('\t'); |
| spaces = spaces % options.tabSize; |
| } |
| for (int i = 0; i < spaces; ++i) |
| builder.append(' '); |
| |
| return result; |
| } |
| |
| private int getSegmentLength(List<Part> segment) { |
| int length = 0; |
| for (Part p : segment) |
| length += p.length(); |
| |
| boolean spacesMap[] = buildSpacesMap(segment); |
| for (int i = 1; i < segment.size(); ++i) |
| if (spacesMap[i]) |
| ++length; |
| |
| return length; |
| } |
| |
| private boolean isSegmentStartHasSpacing(List<Part> segment, Spacing endSpacing) { |
| Part start = null; |
| |
| for (Part p : segment) |
| if (p.text.length() != 0 || p.newLine != NewLine.Soft) { |
| start = p; |
| break; |
| } |
| |
| if (start == null) |
| return endSpacing == Spacing.Both || endSpacing == Spacing.Right; |
| |
| switch (segment.get(0).spacing) { |
| case None: |
| return endSpacing == Spacing.Both || endSpacing == Spacing.Right; |
| case Left: |
| return true; |
| case Right: |
| return endSpacing == Spacing.Both || endSpacing == Spacing.Right; |
| case Both: |
| return true; |
| default: |
| throw new IllegalStateException(); |
| } |
| } |
| |
| private List<List<Part>> findLines(List<Part> parts) { |
| List<List<Part>> lines = new ArrayList<List<Part>>(); |
| List<Part> line = new ArrayList<Part>(); |
| for (Part p : parts) { |
| switch (p.newLine) { |
| case None: |
| line.add(p); |
| break; |
| case Before: |
| lines.add(line); |
| line = new ArrayList<Part>(); |
| line.add(p); |
| break; |
| case After: |
| line.add(p); |
| lines.add(line); |
| line = new ArrayList<Part>(); |
| break; |
| case Soft: |
| line.add(p); |
| break; |
| } |
| } |
| |
| if (!line.isEmpty()) |
| lines.add(line); |
| |
| return lines; |
| } |
| |
| private static List<List<Part>> findUnbreakableSegments(List<Part> parts) { |
| List<List<Part>> segments = new ArrayList<List<Part>>(); |
| if (parts.size() == 0) |
| return segments; |
| |
| Part prev = parts.get(0); |
| List<Part> segment = newSegment(segments, prev); |
| |
| for (int i = 1; i < parts.size(); ++i) { |
| Part p = parts.get(i); |
| |
| switch (p.glue) { |
| case None: |
| switch (prev.glue) { |
| case None: |
| segment = newSegment(segments, p); |
| break; |
| case Left: |
| segment = newSegment(segments, p); |
| break; |
| case Right: |
| segment.add(p); |
| break; |
| case Both: |
| segment.add(p); |
| break; |
| } |
| break; |
| case Left: |
| switch (prev.glue) { |
| case None: |
| segment.add(p); |
| break; |
| case Left: |
| segment.add(p); |
| break; |
| case Right: |
| segment.add(p); |
| break; |
| case Both: |
| segment.add(p); |
| break; |
| } |
| break; |
| case Right: |
| switch (prev.glue) { |
| case None: |
| segment = newSegment(segments, p); |
| break; |
| case Left: |
| segment = newSegment(segments, p); |
| break; |
| case Right: |
| segment.add(p); |
| break; |
| case Both: |
| segment.add(p); |
| break; |
| } |
| break; |
| case Both: |
| switch (prev.glue) { |
| case None: |
| segment.add(p); |
| break; |
| case Left: |
| segment.add(p); |
| break; |
| case Right: |
| segment.add(p); |
| break; |
| case Both: |
| segment.add(p); |
| break; |
| } |
| break; |
| } |
| |
| prev = p; |
| } |
| |
| // last segment may be empty, remove it |
| if (segments.get(segments.size() - 1).isEmpty()) |
| segments.remove(segments.size() - 1); |
| |
| return segments; |
| } |
| |
| private static List<Part> newSegment(List<List<Part>> segments, Part... parts) { |
| List<Part> segment = new ArrayList<Part>(); |
| for (Part p : parts) |
| segment.add(p); |
| segments.add(segment); |
| return segment; |
| } |
| |
| public static enum Block { |
| None, |
| Open, |
| Close |
| } |
| |
| public static enum NewLine { |
| None, |
| Before, |
| After, |
| Soft |
| } |
| |
| public static enum Spacing { |
| None, |
| Left, |
| Right, |
| Both |
| } |
| |
| public static enum Glue { |
| None, |
| Left, |
| Right, |
| Both |
| } |
| |
| public static enum Quote { |
| Never, |
| OnWrap, |
| Literal, |
| Always |
| } |
| |
| public static class Part { |
| private String text; |
| private Block block; |
| private Spacing spacing; |
| private Glue glue; |
| private NewLine newLine; |
| |
| private Quote quote; |
| |
| private boolean wrappable = false; |
| private boolean wrapped = false; |
| |
| private Part(String text, Block block, NewLine newLine, Spacing spacing, Glue glue, Quote quote) { |
| this.text = text; |
| this.block = block; |
| this.newLine = newLine; |
| this.spacing = spacing; |
| this.glue = glue; |
| this.quote = quote; |
| |
| if (quote == Quote.Literal) |
| handleQuotingAndEscaping(); |
| } |
| |
| public void setQuoting(Quote mode) { |
| quote = mode; |
| } |
| |
| private Part wrappable(boolean value) { |
| wrappable = value; |
| return this; |
| } |
| |
| private int format(StringBuilder builder) { |
| boolean quotes; |
| switch (quote) { |
| case Never: |
| quotes = false; |
| break; |
| case OnWrap: |
| quotes = wrapped; |
| break; |
| case Always: |
| quotes = true; |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| |
| if (quotes) |
| builder.append('"'); |
| builder.append(text); |
| if (quotes) |
| builder.append('"'); |
| |
| return text.length() + (quotes ? 2 : 0); |
| } |
| |
| private int length() { |
| int length = text.length(); |
| switch (quote) { |
| case Never: |
| break; |
| case OnWrap: |
| if (wrapped) |
| length += 2; |
| break; |
| case Always: |
| length += 2; |
| break; |
| } |
| return length; |
| } |
| |
| private void handleQuotingAndEscaping() { |
| if (text.length() == 0) { |
| quote = Quote.Always; |
| return; |
| } |
| |
| if (isNoQuotesIdentifier(text)) { |
| quote = Quote.OnWrap; |
| return; |
| } |
| |
| boolean isNumeric = true; |
| for (int i = 0; i < text.length(); ++i) { |
| char ch = text.charAt(i); |
| |
| if (!(ch >= '0' && ch <= '9')) { |
| isNumeric = false; |
| break; |
| } |
| } |
| |
| if (isNumeric) { |
| quote = Quote.Never; |
| return; |
| } |
| |
| quote = Quote.Always; |
| text = escape(text); |
| } |
| |
| private boolean isNoQuotesIdentifier(String text) { |
| if (text.length() == 0) |
| return false; |
| |
| if (!EclCharClasses.isIdentifierStart(text.charAt(0))) |
| return false; |
| |
| for (int i = 1; i < text.length(); ++i) { |
| char c = text.charAt(i); |
| if (c == '-' || !EclCharClasses.isIdentifier(c)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private String escape(String text) { |
| StringBuilder result = new StringBuilder(); |
| |
| for (int i = 0; i < text.length(); ++i) { |
| final char ch = text.charAt(i); |
| switch (ch) { |
| case '\b': |
| result.append("\\b"); |
| break; |
| case '\t': |
| result.append("\\t"); |
| break; |
| case '\n': |
| result.append("\\n"); |
| break; |
| case '\f': |
| result.append("\\f"); |
| break; |
| case '\r': |
| result.append("\\r"); |
| break; |
| case '\\': |
| result.append("\\\\"); |
| break; |
| case '\'': |
| result.append('\''); |
| break; |
| case '"': |
| result.append("\\\""); |
| break; |
| default: |
| result.append(ch); |
| break; |
| } |
| } |
| |
| return result.toString(); |
| } |
| |
| @Override |
| public String toString() { |
| return text; |
| } |
| } |
| |
| private String trimComment(String comment) { |
| int length = comment.length(); |
| int start = 0; |
| |
| while (start < length && |
| EclCharClasses.isWhitespace(comment.charAt(start))) |
| ++start; |
| |
| while ((start < length) && |
| EclCharClasses.isWhitespace(comment.charAt(length - 1))) { |
| --length; |
| } |
| return ((start > 0) || (length < comment.length())) ? |
| comment.substring(start, length) : comment; |
| } |
| } |