From bac7ba4c5a8e77d28122268702efd2ceb6a5159d Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sun, 23 Oct 2022 10:41:34 -0700 Subject: [PATCH] Updated Upstream (Bukkit/Spigot) (#8445) --- ...-limit-in-YamlConfigOptions-and-incr.patch | 2 +- .../api/Remove-upstream-snakeyaml-fix.patch | 32 - ...actus-bamboo-and-reed-growth-heights.patch | 6 +- ...fier-changing-growth-for-other-crops.patch | 45 +- ...ving-configs-with-more-long-comments.patch | 1702 ----------------- work/Bukkit | 2 +- work/Spigot | 2 +- 7 files changed, 14 insertions(+), 1777 deletions(-) delete mode 100644 patches/api/Remove-upstream-snakeyaml-fix.patch delete mode 100644 patches/server/Fix-saving-configs-with-more-long-comments.patch diff --git a/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch b/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch index b37b835d4d..4a225d3c5c 100644 --- a/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch +++ b/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch @@ -25,7 +25,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public class YamlConfigurationOptions extends FileConfigurationOptions { private int indent = 2; private int width = 80; -+ private int codePointLimit = 64 * 1024 * 1024; // 64 MB // Paper ++ private int codePointLimit = Integer.MAX_VALUE; // Paper - use upstream's default from YamlConfiguration protected YamlConfigurationOptions(@NotNull YamlConfiguration configuration) { super(configuration); diff --git a/patches/api/Remove-upstream-snakeyaml-fix.patch b/patches/api/Remove-upstream-snakeyaml-fix.patch deleted file mode 100644 index b2bac83ef2..0000000000 --- a/patches/api/Remove-upstream-snakeyaml-fix.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jake Potrebic -Date: Wed, 5 Jan 2022 12:12:58 -0800 -Subject: [PATCH] Remove upstream snakeyaml fix - -See Server Patch: Fix saving configs with more long comments - -diff --git a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java -+++ b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java -@@ -0,0 +0,0 @@ public class YamlConfiguration extends FileConfiguration { - yamlLoaderOptions = new LoaderOptions(); - yamlLoaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); // SPIGOT-5881: Not ideal, but was default pre SnakeYAML 1.26 - -- yaml = new BukkitYaml(constructor, representer, yamlDumperOptions, yamlLoaderOptions); -+ yaml = new /*BukkitYaml*/Yaml(constructor, representer, yamlDumperOptions, yamlLoaderOptions); // Paper - don't use upstream BukkitYaml fix, add the whole snakeyaml Emitter class itself with the fix - } - - @NotNull -diff --git a/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java b/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java -+++ b/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java -@@ -0,0 +0,0 @@ public class YamlConfigurationTest extends FileConfigurationTest { - } - - @Test -+ @org.junit.Ignore // Paper - ignore test because our fix doesn't work in testing environment - public void test100Comments() throws InvalidConfigurationException { - StringBuilder commentBuilder = new StringBuilder(); - for (int i = 0; i < 100; i++) { diff --git a/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch b/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch index f224bf8445..4ce612fed5 100644 --- a/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch +++ b/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch @@ -11,7 +11,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/world/level/block/BambooBlock.java +++ b/src/main/java/net/minecraft/world/level/block/BambooBlock.java @@ -0,0 +0,0 @@ public class BambooBlock extends Block implements BonemealableBlock { - if (random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.bambooModifier) * 3)) == 0 && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot + if (random.nextFloat() < (world.spigotConfig.bambooModifier / (100.0f * 3)) && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution int i = this.getHeightBelowUpToMax(world, pos) + 1; - if (i < 16) { @@ -76,7 +76,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (i < world.paperConfig().maxGrowthHeight.cactus) { // Paper - Configurable growth height int j = (Integer) state.getValue(CactusBlock.AGE); - if (j >= (byte) range(3, ((100.0F / world.spigotConfig.cactusModifier) * 15) + 0.5F, 15)) { // Spigot + int modifier = world.spigotConfig.cactusModifier; // Spigot - SPIGOT-7159: Better modifier resolution diff --git a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java @@ -89,4 +89,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (i < world.paperConfig().maxGrowthHeight.reeds) { // Paper - Configurable growth height int j = (Integer) state.getValue(SugarCaneBlock.AGE); - if (j >= (byte) range(3, ((100.0F / world.spigotConfig.caneModifier) * 15) + 0.5F, 15)) { // Spigot + int modifier = world.spigotConfig.caneModifier; // Spigot - SPIGOT-7159: Better modifier resolution diff --git a/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch b/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch index 7252ad3746..150d4128ba 100644 --- a/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch +++ b/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch @@ -38,25 +38,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java +++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java @@ -0,0 +0,0 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - - @Override - public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { -- if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < (100.0D / world.spigotConfig.kelpModifier) * this.growPerTickProbability) { // Spigot -+ // Paper start -+ final int modifier; -+ if (state.is(Blocks.TWISTING_VINES) || state.is(Blocks.TWISTING_VINES_PLANT)) { -+ modifier = world.spigotConfig.twistingVinesModifier; -+ } else if (state.is(Blocks.WEEPING_VINES) || state.is(Blocks.WEEPING_VINES_PLANT)) { -+ modifier = world.spigotConfig.weepingVinesModifier; -+ } else if (state.is(Blocks.CAVE_VINES) || state.is(Blocks.CAVE_VINES_PLANT)) { -+ modifier = world.spigotConfig.caveVinesModifier; -+ } else if (state.is(Blocks.KELP) || state.is(Blocks.KELP_PLANT)) { -+ modifier = world.spigotConfig.kelpModifier; -+ } else { -+ modifier = 100; // Above cases are exhaustive as of 1.18 -+ } -+ if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < (modifier / 100.0D) * this.growPerTickProbability) { // Spigot // Paper - fix growth modifier having the reverse effect -+ // Paper end BlockPos blockposition1 = pos.relative(this.growthDirection); if (this.canGrowInto(world.getBlockState(blockposition1))) { @@ -81,28 +62,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/org/spigotmc/SpigotWorldConfig.java +++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java @@ -0,0 +0,0 @@ public class SpigotWorldConfig - public int bambooModifier; - public int sweetBerryModifier; - public int kelpModifier; -+ // Paper start -+ public int twistingVinesModifier; -+ public int weepingVinesModifier; -+ public int caveVinesModifier; -+ public int glowBerryModifier; -+ // Paper end + public int twistingVinesModifier; + public int weepingVinesModifier; + public int caveVinesModifier; ++ public int glowBerryModifier; // Paper private int getAndValidateGrowth(String crop) { int modifier = this.getInt( "growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100 ); @@ -0,0 +0,0 @@ public class SpigotWorldConfig - this.bambooModifier = this.getAndValidateGrowth( "Bamboo" ); - this.sweetBerryModifier = this.getAndValidateGrowth( "SweetBerry" ); - this.kelpModifier = this.getAndValidateGrowth( "Kelp" ); -+ // Paper start -+ this.twistingVinesModifier = this.getAndValidateGrowth("TwistingVines"); -+ this.weepingVinesModifier = this.getAndValidateGrowth("WeepingVines"); -+ this.caveVinesModifier = this.getAndValidateGrowth("CaveVines"); -+ this.glowBerryModifier = this.getAndValidateGrowth("GlowBerry"); -+ // Paper end + this.twistingVinesModifier = this.getAndValidateGrowth( "TwistingVines" ); + this.weepingVinesModifier = this.getAndValidateGrowth( "WeepingVines" ); + this.caveVinesModifier = this.getAndValidateGrowth( "CaveVines" ); ++ this.glowBerryModifier = this.getAndValidateGrowth("GlowBerry"); // Paper } public double itemMerge; diff --git a/patches/server/Fix-saving-configs-with-more-long-comments.patch b/patches/server/Fix-saving-configs-with-more-long-comments.patch deleted file mode 100644 index c11ff2631f..0000000000 --- a/patches/server/Fix-saving-configs-with-more-long-comments.patch +++ /dev/null @@ -1,1702 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jake Potrebic -Date: Sat, 1 Jan 2022 21:24:50 -0800 -Subject: [PATCH] Fix saving configs with more long comments - -This is a bug with snakeyaml -PR: https://bitbucket.org/snakeyaml/snakeyaml/pull-requests/3 -Issue: https://bitbucket.org/snakeyaml/snakeyaml/issues/518/comments-could-cause-queue-full - -Added the entire Emitter class from snakeyaml because -dev-imports doesn't work with non-mojang-added dependencies - -Replacement for upstream: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/commits/a7505b3cd0498baca152777767f0e4ddebbe4d1a - -diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java -@@ -0,0 +0,0 @@ -+/** -+ * Copyright (c) 2008, SnakeYAML -+ * -+ * 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 org.yaml.snakeyaml.emitter; -+ -+import org.yaml.snakeyaml.DumperOptions; -+import org.yaml.snakeyaml.DumperOptions.ScalarStyle; -+import org.yaml.snakeyaml.DumperOptions.Version; -+import org.yaml.snakeyaml.comments.CommentEventsCollector; -+import org.yaml.snakeyaml.comments.CommentLine; -+import org.yaml.snakeyaml.comments.CommentType; -+import org.yaml.snakeyaml.error.YAMLException; -+import org.yaml.snakeyaml.events.AliasEvent; -+import org.yaml.snakeyaml.events.CollectionEndEvent; -+import org.yaml.snakeyaml.events.CollectionStartEvent; -+import org.yaml.snakeyaml.events.CommentEvent; -+import org.yaml.snakeyaml.events.DocumentEndEvent; -+import org.yaml.snakeyaml.events.DocumentStartEvent; -+import org.yaml.snakeyaml.events.Event; -+import org.yaml.snakeyaml.events.Event.ID; -+import org.yaml.snakeyaml.events.MappingEndEvent; -+import org.yaml.snakeyaml.events.MappingStartEvent; -+import org.yaml.snakeyaml.events.NodeEvent; -+import org.yaml.snakeyaml.events.ScalarEvent; -+import org.yaml.snakeyaml.events.SequenceEndEvent; -+import org.yaml.snakeyaml.events.SequenceStartEvent; -+import org.yaml.snakeyaml.events.StreamEndEvent; -+import org.yaml.snakeyaml.events.StreamStartEvent; -+import org.yaml.snakeyaml.nodes.Tag; -+import org.yaml.snakeyaml.reader.StreamReader; -+import org.yaml.snakeyaml.scanner.Constant; -+import org.yaml.snakeyaml.util.ArrayStack; -+ -+import java.io.IOException; -+import java.io.Writer; -+import java.util.ArrayDeque; -+import java.util.HashMap; -+import java.util.HashSet; -+import java.util.Iterator; -+import java.util.LinkedHashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.Queue; -+import java.util.Set; -+import java.util.TreeSet; -+import java.util.concurrent.ArrayBlockingQueue; -+import java.util.regex.Matcher; -+import java.util.regex.Pattern; -+ -+/** -+ *
-+ * Emitter expects events obeying the following grammar:
-+ * stream ::= STREAM-START document* STREAM-END
-+ * document ::= DOCUMENT-START node DOCUMENT-END
-+ * node ::= SCALAR | sequence | mapping
-+ * sequence ::= SEQUENCE-START node* SEQUENCE-END
-+ * mapping ::= MAPPING-START (node node)* MAPPING-END
-+ * 
-+ */ -+public final class Emitter implements Emitable { -+ public static final int MIN_INDENT = 1; -+ public static final int MAX_INDENT = 10; -+ private static final char[] SPACE = {' '}; -+ -+ private static final Pattern SPACES_PATTERN = Pattern.compile("\\s"); -+ private static final Set INVALID_ANCHOR = new HashSet(); -+ static { -+ INVALID_ANCHOR.add('['); -+ INVALID_ANCHOR.add(']'); -+ INVALID_ANCHOR.add('{'); -+ INVALID_ANCHOR.add('}'); -+ INVALID_ANCHOR.add(','); -+ INVALID_ANCHOR.add('*'); -+ INVALID_ANCHOR.add('&'); -+ } -+ -+ private static final Map ESCAPE_REPLACEMENTS = new HashMap(); -+ static { -+ ESCAPE_REPLACEMENTS.put('\0', "0"); -+ ESCAPE_REPLACEMENTS.put('\u0007', "a"); -+ ESCAPE_REPLACEMENTS.put('\u0008', "b"); -+ ESCAPE_REPLACEMENTS.put('\u0009', "t"); -+ ESCAPE_REPLACEMENTS.put('\n', "n"); -+ ESCAPE_REPLACEMENTS.put('\u000B', "v"); -+ ESCAPE_REPLACEMENTS.put('\u000C', "f"); -+ ESCAPE_REPLACEMENTS.put('\r', "r"); -+ ESCAPE_REPLACEMENTS.put('\u001B', "e"); -+ ESCAPE_REPLACEMENTS.put('"', "\""); -+ ESCAPE_REPLACEMENTS.put('\\', "\\"); -+ ESCAPE_REPLACEMENTS.put('\u0085', "N"); -+ ESCAPE_REPLACEMENTS.put('\u00A0', "_"); -+ ESCAPE_REPLACEMENTS.put('\u2028', "L"); -+ ESCAPE_REPLACEMENTS.put('\u2029', "P"); -+ } -+ -+ private final static Map DEFAULT_TAG_PREFIXES = new LinkedHashMap(); -+ static { -+ DEFAULT_TAG_PREFIXES.put("!", "!"); -+ DEFAULT_TAG_PREFIXES.put(Tag.PREFIX, "!!"); -+ } -+ // The stream should have the methods `write` and possibly `flush`. -+ private final Writer stream; -+ -+ // Encoding is defined by Writer (cannot be overridden by STREAM-START.) -+ // private Charset encoding; -+ -+ // Emitter is a state machine with a stack of states to handle nested -+ // structures. -+ private final ArrayStack states; -+ private EmitterState state; -+ -+ // Current event and the event queue. -+ private final Queue events; -+ private Event event; -+ -+ // The current indentation level and the stack of previous indents. -+ private final ArrayStack indents; -+ private Integer indent; -+ -+ // Flow level. -+ private int flowLevel; -+ -+ // Contexts. -+ private boolean rootContext; -+ private boolean mappingContext; -+ private boolean simpleKeyContext; -+ -+ // -+ // Characteristics of the last emitted character: -+ // - current position. -+ // - is it a whitespace? -+ // - is it an indention character -+ // (indentation space, '-', '?', or ':')? -+ // private int line; this variable is not used -+ private int column; -+ private boolean whitespace; -+ private boolean indention; -+ private boolean openEnded; -+ -+ // Formatting details. -+ private final Boolean canonical; -+ // pretty print flow by adding extra line breaks -+ private final Boolean prettyFlow; -+ -+ private final boolean allowUnicode; -+ private int bestIndent; -+ private final int indicatorIndent; -+ private final boolean indentWithIndicator; -+ private int bestWidth; -+ private final char[] bestLineBreak; -+ private final boolean splitLines; -+ private final int maxSimpleKeyLength; -+ private final boolean emitComments; -+ -+ // Tag prefixes. -+ private Map tagPrefixes; -+ -+ // Prepared anchor and tag. -+ private String preparedAnchor; -+ private String preparedTag; -+ -+ // Scalar analysis and style. -+ private ScalarAnalysis analysis; -+ private DumperOptions.ScalarStyle style; -+ -+ // Comment processing -+ private final CommentEventsCollector blockCommentsCollector; -+ private final CommentEventsCollector inlineCommentsCollector; -+ -+ -+ public Emitter(Writer stream, DumperOptions opts) { -+ // The stream should have the methods `write` and possibly `flush`. -+ this.stream = stream; -+ // Emitter is a state machine with a stack of states to handle nested -+ // structures. -+ this.states = new ArrayStack(100); -+ this.state = new ExpectStreamStart(); -+ // Current event and the event queue. -+ this.events = new ArrayDeque<>(100); // Paper - allow more than 100 events (or comments) -+ // this.events = new ArrayBlockingQueue<>(100); -+ this.event = null; -+ // The current indentation level and the stack of previous indents. -+ this.indents = new ArrayStack(10); -+ this.indent = null; -+ // Flow level. -+ this.flowLevel = 0; -+ // Contexts. -+ mappingContext = false; -+ simpleKeyContext = false; -+ -+ // -+ // Characteristics of the last emitted character: -+ // - current position. -+ // - is it a whitespace? -+ // - is it an indention character -+ // (indentation space, '-', '?', or ':')? -+ column = 0; -+ whitespace = true; -+ indention = true; -+ -+ // Whether the document requires an explicit document indicator -+ openEnded = false; -+ -+ // Formatting details. -+ this.canonical = opts.isCanonical(); -+ this.prettyFlow = opts.isPrettyFlow(); -+ this.allowUnicode = opts.isAllowUnicode(); -+ this.bestIndent = 2; -+ if ((opts.getIndent() > MIN_INDENT) && (opts.getIndent() < MAX_INDENT)) { -+ this.bestIndent = opts.getIndent(); -+ } -+ this.indicatorIndent = opts.getIndicatorIndent(); -+ this.indentWithIndicator = opts.getIndentWithIndicator(); -+ this.bestWidth = 80; -+ if (opts.getWidth() > this.bestIndent * 2) { -+ this.bestWidth = opts.getWidth(); -+ } -+ this.bestLineBreak = opts.getLineBreak().getString().toCharArray(); -+ this.splitLines = opts.getSplitLines(); -+ this.maxSimpleKeyLength = opts.getMaxSimpleKeyLength(); -+ this.emitComments = opts.isProcessComments(); -+ -+ // Tag prefixes. -+ this.tagPrefixes = new LinkedHashMap(); -+ -+ // Prepared anchor and tag. -+ this.preparedAnchor = null; -+ this.preparedTag = null; -+ -+ // Scalar analysis and style. -+ this.analysis = null; -+ this.style = null; -+ -+ // Comment processing -+ this.blockCommentsCollector = new CommentEventsCollector(events, -+ CommentType.BLANK_LINE, CommentType.BLOCK); -+ this.inlineCommentsCollector = new CommentEventsCollector(events, -+ CommentType.IN_LINE); -+ } -+ -+ public void emit(Event event) throws IOException { -+ this.events.add(event); -+ while (!needMoreEvents()) { -+ this.event = this.events.poll(); -+ this.state.expect(); -+ this.event = null; -+ } -+ } -+ -+ // In some cases, we wait for a few next events before emitting. -+ -+ private boolean needMoreEvents() { -+ if (events.isEmpty()) { -+ return true; -+ } -+ -+ Iterator iter = events.iterator(); -+ Event event = iter.next(); // FIXME why without check ??? -+ while(event instanceof CommentEvent) { -+ if (!iter.hasNext()) { -+ return true; -+ } -+ event = iter.next(); -+ } -+ -+ if (event instanceof DocumentStartEvent) { -+ return needEvents(iter, 1); -+ } else if (event instanceof SequenceStartEvent) { -+ return needEvents(iter, 2); -+ } else if (event instanceof MappingStartEvent) { -+ return needEvents(iter, 3); -+ } else if (event instanceof StreamStartEvent) { -+ return needEvents(iter, 2); -+ } else if (event instanceof StreamEndEvent) { -+ return false; -+ } else if (emitComments) { -+ return needEvents(iter, 1); -+ } -+ return false; -+ } -+ -+ private boolean needEvents(Iterator iter, int count) { -+ int level = 0; -+ int actualCount = 0; -+ while (iter.hasNext()) { -+ Event event = iter.next(); -+ if (event instanceof CommentEvent) { -+ continue; -+ } -+ actualCount++; -+ if (event instanceof DocumentStartEvent || event instanceof CollectionStartEvent) { -+ level++; -+ } else if (event instanceof DocumentEndEvent || event instanceof CollectionEndEvent) { -+ level--; -+ } else if (event instanceof StreamEndEvent) { -+ level = -1; -+ } -+ if (level < 0) { -+ return false; -+ } -+ } -+ return actualCount < count; -+ } -+ -+ private void increaseIndent(boolean flow, boolean indentless) { -+ indents.push(indent); -+ if (indent == null) { -+ if (flow) { -+ indent = bestIndent; -+ } else { -+ indent = 0; -+ } -+ } else if (!indentless) { -+ this.indent += bestIndent; -+ } -+ } -+ -+ // States -+ -+ // Stream handlers. -+ -+ private class ExpectStreamStart implements EmitterState { -+ public void expect() throws IOException { -+ if (event instanceof StreamStartEvent) { -+ writeStreamStart(); -+ state = new ExpectFirstDocumentStart(); -+ } else { -+ throw new EmitterException("expected StreamStartEvent, but got " + event); -+ } -+ } -+ } -+ -+ private class ExpectNothing implements EmitterState { -+ public void expect() throws IOException { -+ throw new EmitterException("expecting nothing, but got " + event); -+ } -+ } -+ -+ // Document handlers. -+ -+ private class ExpectFirstDocumentStart implements EmitterState { -+ public void expect() throws IOException { -+ new ExpectDocumentStart(true).expect(); -+ } -+ } -+ -+ private class ExpectDocumentStart implements EmitterState { -+ private final boolean first; -+ -+ public ExpectDocumentStart(boolean first) { -+ this.first = first; -+ } -+ -+ public void expect() throws IOException { -+ if (event instanceof DocumentStartEvent) { -+ DocumentStartEvent ev = (DocumentStartEvent) event; -+ if ((ev.getVersion() != null || ev.getTags() != null) && openEnded) { -+ writeIndicator("...", true, false, false); -+ writeIndent(); -+ } -+ if (ev.getVersion() != null) { -+ String versionText = prepareVersion(ev.getVersion()); -+ writeVersionDirective(versionText); -+ } -+ tagPrefixes = new LinkedHashMap(DEFAULT_TAG_PREFIXES); -+ if (ev.getTags() != null) { -+ Set handles = new TreeSet(ev.getTags().keySet()); -+ for (String handle : handles) { -+ String prefix = ev.getTags().get(handle); -+ tagPrefixes.put(prefix, handle); -+ String handleText = prepareTagHandle(handle); -+ String prefixText = prepareTagPrefix(prefix); -+ writeTagDirective(handleText, prefixText); -+ } -+ } -+ boolean implicit = first && !ev.getExplicit() && !canonical -+ && ev.getVersion() == null -+ && (ev.getTags() == null || ev.getTags().isEmpty()) -+ && !checkEmptyDocument(); -+ if (!implicit) { -+ writeIndent(); -+ writeIndicator("---", true, false, false); -+ if (canonical) { -+ writeIndent(); -+ } -+ } -+ state = new ExpectDocumentRoot(); -+ } else if (event instanceof StreamEndEvent) { -+ writeStreamEnd(); -+ state = new ExpectNothing(); -+ } else if (event instanceof CommentEvent) { -+ blockCommentsCollector.collectEvents(event); -+ writeBlockComment(); -+ // state = state; remains unchanged -+ } else { -+ throw new EmitterException("expected DocumentStartEvent, but got " + event); -+ } -+ } -+ } -+ -+ private class ExpectDocumentEnd implements EmitterState { -+ public void expect() throws IOException { -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ writeBlockComment(); -+ if (event instanceof DocumentEndEvent) { -+ writeIndent(); -+ if (((DocumentEndEvent) event).getExplicit()) { -+ writeIndicator("...", true, false, false); -+ writeIndent(); -+ } -+ flushStream(); -+ state = new ExpectDocumentStart(false); -+ } else { -+ throw new EmitterException("expected DocumentEndEvent, but got " + event); -+ } -+ } -+ } -+ -+ private class ExpectDocumentRoot implements EmitterState { -+ public void expect() throws IOException { -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ if (!blockCommentsCollector.isEmpty()) { -+ writeBlockComment(); -+ if (event instanceof DocumentEndEvent) { -+ new ExpectDocumentEnd().expect(); -+ return; -+ } -+ } -+ states.push(new ExpectDocumentEnd()); -+ expectNode(true, false, false); -+ } -+ } -+ -+ // Node handlers. -+ -+ private void expectNode(boolean root, boolean mapping, boolean simpleKey) throws IOException { -+ rootContext = root; -+ mappingContext = mapping; -+ simpleKeyContext = simpleKey; -+ if (event instanceof AliasEvent) { -+ expectAlias(); -+ } else if (event instanceof ScalarEvent || event instanceof CollectionStartEvent) { -+ processAnchor("&"); -+ processTag(); -+ if (event instanceof ScalarEvent) { -+ expectScalar(); -+ } else if (event instanceof SequenceStartEvent) { -+ if (flowLevel != 0 || canonical || ((SequenceStartEvent) event).isFlow() -+ || checkEmptySequence()) { -+ expectFlowSequence(); -+ } else { -+ expectBlockSequence(); -+ } -+ } else {// MappingStartEvent -+ if (flowLevel != 0 || canonical || ((MappingStartEvent) event).isFlow() -+ || checkEmptyMapping()) { -+ expectFlowMapping(); -+ } else { -+ expectBlockMapping(); -+ } -+ } -+ } else { -+ throw new EmitterException("expected NodeEvent, but got " + event); -+ } -+ } -+ -+ private void expectAlias() throws IOException { -+ if (!(event instanceof AliasEvent)) { -+ throw new EmitterException("Alias must be provided"); -+ } -+ processAnchor("*"); -+ state = states.pop(); -+ } -+ -+ private void expectScalar() throws IOException { -+ increaseIndent(true, false); -+ processScalar(); -+ indent = indents.pop(); -+ state = states.pop(); -+ } -+ -+ // Flow sequence handlers. -+ -+ private void expectFlowSequence() throws IOException { -+ writeIndicator("[", true, true, false); -+ flowLevel++; -+ increaseIndent(true, false); -+ if (prettyFlow) { -+ writeIndent(); -+ } -+ state = new ExpectFirstFlowSequenceItem(); -+ } -+ -+ private class ExpectFirstFlowSequenceItem implements EmitterState { -+ public void expect() throws IOException { -+ if (event instanceof SequenceEndEvent) { -+ indent = indents.pop(); -+ flowLevel--; -+ writeIndicator("]", false, false, false); -+ inlineCommentsCollector.collectEvents(); -+ writeInlineComments(); -+ state = states.pop(); -+ } else if (event instanceof CommentEvent) { -+ blockCommentsCollector.collectEvents(event); -+ writeBlockComment(); -+ } else { -+ if (canonical || (column > bestWidth && splitLines) || prettyFlow) { -+ writeIndent(); -+ } -+ states.push(new ExpectFlowSequenceItem()); -+ expectNode(false, false, false); -+ event = inlineCommentsCollector.collectEvents(event); -+ writeInlineComments(); -+ } -+ } -+ } -+ -+ private class ExpectFlowSequenceItem implements EmitterState { -+ public void expect() throws IOException { -+ if (event instanceof SequenceEndEvent) { -+ indent = indents.pop(); -+ flowLevel--; -+ if (canonical) { -+ writeIndicator(",", false, false, false); -+ writeIndent(); -+ } else if (prettyFlow) { -+ writeIndent(); -+ } -+ writeIndicator("]", false, false, false); -+ inlineCommentsCollector.collectEvents(); -+ writeInlineComments(); -+ if (prettyFlow) { -+ writeIndent(); -+ } -+ state = states.pop(); -+ } else if (event instanceof CommentEvent) { -+ event = blockCommentsCollector.collectEvents(event); -+ } else { -+ writeIndicator(",", false, false, false); -+ writeBlockComment(); -+ if (canonical || (column > bestWidth && splitLines) || prettyFlow) { -+ writeIndent(); -+ } -+ states.push(new ExpectFlowSequenceItem()); -+ expectNode(false, false, false); -+ event = inlineCommentsCollector.collectEvents(event); -+ writeInlineComments(); -+ } -+ } -+ } -+ -+ // Flow mapping handlers. -+ -+ private void expectFlowMapping() throws IOException { -+ writeIndicator("{", true, true, false); -+ flowLevel++; -+ increaseIndent(true, false); -+ if (prettyFlow) { -+ writeIndent(); -+ } -+ state = new ExpectFirstFlowMappingKey(); -+ } -+ -+ private class ExpectFirstFlowMappingKey implements EmitterState { -+ public void expect() throws IOException { -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ writeBlockComment(); -+ if (event instanceof MappingEndEvent) { -+ indent = indents.pop(); -+ flowLevel--; -+ writeIndicator("}", false, false, false); -+ inlineCommentsCollector.collectEvents(); -+ writeInlineComments(); -+ state = states.pop(); -+ } else { -+ if (canonical || (column > bestWidth && splitLines) || prettyFlow) { -+ writeIndent(); -+ } -+ if (!canonical && checkSimpleKey()) { -+ states.push(new ExpectFlowMappingSimpleValue()); -+ expectNode(false, true, true); -+ } else { -+ writeIndicator("?", true, false, false); -+ states.push(new ExpectFlowMappingValue()); -+ expectNode(false, true, false); -+ } -+ } -+ } -+ } -+ -+ private class ExpectFlowMappingKey implements EmitterState { -+ public void expect() throws IOException { -+ if (event instanceof MappingEndEvent) { -+ indent = indents.pop(); -+ flowLevel--; -+ if (canonical) { -+ writeIndicator(",", false, false, false); -+ writeIndent(); -+ } -+ if (prettyFlow) { -+ writeIndent(); -+ } -+ writeIndicator("}", false, false, false); -+ inlineCommentsCollector.collectEvents(); -+ writeInlineComments(); -+ state = states.pop(); -+ } else { -+ writeIndicator(",", false, false, false); -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ writeBlockComment(); -+ if (canonical || (column > bestWidth && splitLines) || prettyFlow) { -+ writeIndent(); -+ } -+ if (!canonical && checkSimpleKey()) { -+ states.push(new ExpectFlowMappingSimpleValue()); -+ expectNode(false, true, true); -+ } else { -+ writeIndicator("?", true, false, false); -+ states.push(new ExpectFlowMappingValue()); -+ expectNode(false, true, false); -+ } -+ } -+ } -+ } -+ -+ private class ExpectFlowMappingSimpleValue implements EmitterState { -+ public void expect() throws IOException { -+ writeIndicator(":", false, false, false); -+ event = inlineCommentsCollector.collectEventsAndPoll(event); -+ writeInlineComments(); -+ states.push(new ExpectFlowMappingKey()); -+ expectNode(false, true, false); -+ inlineCommentsCollector.collectEvents(event); -+ writeInlineComments(); -+ } -+ } -+ -+ private class ExpectFlowMappingValue implements EmitterState { -+ public void expect() throws IOException { -+ if (canonical || (column > bestWidth) || prettyFlow) { -+ writeIndent(); -+ } -+ writeIndicator(":", true, false, false); -+ event = inlineCommentsCollector.collectEventsAndPoll(event); -+ writeInlineComments(); -+ states.push(new ExpectFlowMappingKey()); -+ expectNode(false, true, false); -+ inlineCommentsCollector.collectEvents(event); -+ writeInlineComments(); -+ } -+ } -+ -+ // Block sequence handlers. -+ -+ private void expectBlockSequence() throws IOException { -+ boolean indentless = mappingContext && !indention; -+ increaseIndent(false, indentless); -+ state = new ExpectFirstBlockSequenceItem(); -+ } -+ -+ private class ExpectFirstBlockSequenceItem implements EmitterState { -+ public void expect() throws IOException { -+ new ExpectBlockSequenceItem(true).expect(); -+ } -+ } -+ -+ private class ExpectBlockSequenceItem implements EmitterState { -+ private final boolean first; -+ -+ public ExpectBlockSequenceItem(boolean first) { -+ this.first = first; -+ } -+ -+ public void expect() throws IOException { -+ if (!this.first && event instanceof SequenceEndEvent) { -+ indent = indents.pop(); -+ state = states.pop(); -+ } else if( event instanceof CommentEvent) { -+ blockCommentsCollector.collectEvents(event); -+ } else { -+ writeIndent(); -+ if (!indentWithIndicator || this.first) { -+ writeWhitespace(indicatorIndent); -+ } -+ writeIndicator("-", true, false, true); -+ if (indentWithIndicator && this.first) { -+ indent += indicatorIndent; -+ } -+ if (!blockCommentsCollector.isEmpty()) { -+ increaseIndent(false, false); -+ writeBlockComment(); -+ if(event instanceof ScalarEvent) { -+ analysis = analyzeScalar(((ScalarEvent)event).getValue()); -+ if (!analysis.isEmpty()) { -+ writeIndent(); -+ } -+ } -+ indent = indents.pop(); -+ } -+ states.push(new ExpectBlockSequenceItem(false)); -+ expectNode(false, false, false); -+ inlineCommentsCollector.collectEvents(); -+ writeInlineComments(); -+ } -+ } -+ } -+ -+ // Block mapping handlers. -+ private void expectBlockMapping() throws IOException { -+ increaseIndent(false, false); -+ state = new ExpectFirstBlockMappingKey(); -+ } -+ -+ private class ExpectFirstBlockMappingKey implements EmitterState { -+ public void expect() throws IOException { -+ new ExpectBlockMappingKey(true).expect(); -+ } -+ } -+ -+ private class ExpectBlockMappingKey implements EmitterState { -+ private final boolean first; -+ -+ public ExpectBlockMappingKey(boolean first) { -+ this.first = first; -+ } -+ -+ public void expect() throws IOException { -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ writeBlockComment(); -+ if (!this.first && event instanceof MappingEndEvent) { -+ indent = indents.pop(); -+ state = states.pop(); -+ } else { -+ writeIndent(); -+ if (checkSimpleKey()) { -+ states.push(new ExpectBlockMappingSimpleValue()); -+ expectNode(false, true, true); -+ } else { -+ writeIndicator("?", true, false, true); -+ states.push(new ExpectBlockMappingValue()); -+ expectNode(false, true, false); -+ } -+ } -+ } -+ } -+ -+ private boolean isFoldedOrLiteral(Event event) { -+ if(!event.is(ID.Scalar)) { -+ return false; -+ } -+ ScalarEvent scalarEvent = (ScalarEvent) event; -+ ScalarStyle style = scalarEvent.getScalarStyle(); -+ return style == ScalarStyle.FOLDED || style == ScalarStyle.LITERAL; -+ } -+ -+ private class ExpectBlockMappingSimpleValue implements EmitterState { -+ public void expect() throws IOException { -+ writeIndicator(":", false, false, false); -+ event = inlineCommentsCollector.collectEventsAndPoll(event); -+ if(!isFoldedOrLiteral(event)) { -+ if(writeInlineComments()) { -+ increaseIndent(true, false); -+ writeIndent(); -+ indent = indents.pop(); -+ } -+ } -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ if(!blockCommentsCollector.isEmpty()) { -+ increaseIndent(true, false); -+ writeBlockComment(); -+ writeIndent(); -+ indent = indents.pop(); -+ } -+ states.push(new ExpectBlockMappingKey(false)); -+ expectNode(false, true, false); -+ inlineCommentsCollector.collectEvents(); -+ writeInlineComments(); -+ } -+ } -+ -+ private class ExpectBlockMappingValue implements EmitterState { -+ public void expect() throws IOException { -+ writeIndent(); -+ writeIndicator(":", true, false, true); -+ event = inlineCommentsCollector.collectEventsAndPoll(event); -+ writeInlineComments(); -+ event = blockCommentsCollector.collectEventsAndPoll(event); -+ writeBlockComment(); -+ states.push(new ExpectBlockMappingKey(false)); -+ expectNode(false, true, false); -+ inlineCommentsCollector.collectEvents(event); -+ writeInlineComments(); -+ } -+ } -+ -+ // Checkers. -+ -+ private boolean checkEmptySequence() { -+ return event instanceof SequenceStartEvent && !events.isEmpty() && events.peek() instanceof SequenceEndEvent; -+ } -+ -+ private boolean checkEmptyMapping() { -+ return event instanceof MappingStartEvent && !events.isEmpty() && events.peek() instanceof MappingEndEvent; -+ } -+ -+ private boolean checkEmptyDocument() { -+ if (!(event instanceof DocumentStartEvent) || events.isEmpty()) { -+ return false; -+ } -+ Event event = events.peek(); -+ if (event instanceof ScalarEvent) { -+ ScalarEvent e = (ScalarEvent) event; -+ return e.getAnchor() == null && e.getTag() == null && e.getImplicit() != null && e -+ .getValue().length() == 0; -+ } -+ return false; -+ } -+ -+ private boolean checkSimpleKey() { -+ int length = 0; -+ if (event instanceof NodeEvent && ((NodeEvent) event).getAnchor() != null) { -+ if (preparedAnchor == null) { -+ preparedAnchor = prepareAnchor(((NodeEvent) event).getAnchor()); -+ } -+ length += preparedAnchor.length(); -+ } -+ String tag = null; -+ if (event instanceof ScalarEvent) { -+ tag = ((ScalarEvent) event).getTag(); -+ } else if (event instanceof CollectionStartEvent) { -+ tag = ((CollectionStartEvent) event).getTag(); -+ } -+ if (tag != null) { -+ if (preparedTag == null) { -+ preparedTag = prepareTag(tag); -+ } -+ length += preparedTag.length(); -+ } -+ if (event instanceof ScalarEvent) { -+ if (analysis == null) { -+ analysis = analyzeScalar(((ScalarEvent) event).getValue()); -+ } -+ length += analysis.getScalar().length(); -+ } -+ return length < maxSimpleKeyLength && (event instanceof AliasEvent -+ || (event instanceof ScalarEvent && !analysis.isEmpty() && !analysis.isMultiline()) -+ || checkEmptySequence() || checkEmptyMapping()); -+ } -+ -+ // Anchor, Tag, and Scalar processors. -+ -+ private void processAnchor(String indicator) throws IOException { -+ NodeEvent ev = (NodeEvent) event; -+ if (ev.getAnchor() == null) { -+ preparedAnchor = null; -+ return; -+ } -+ if (preparedAnchor == null) { -+ preparedAnchor = prepareAnchor(ev.getAnchor()); -+ } -+ writeIndicator(indicator + preparedAnchor, true, false, false); -+ preparedAnchor = null; -+ } -+ -+ private void processTag() throws IOException { -+ String tag = null; -+ if (event instanceof ScalarEvent) { -+ ScalarEvent ev = (ScalarEvent) event; -+ tag = ev.getTag(); -+ if (style == null) { -+ style = chooseScalarStyle(); -+ } -+ if ((!canonical || tag == null) && ((style == null && ev.getImplicit() -+ .canOmitTagInPlainScalar()) || (style != null && ev.getImplicit() -+ .canOmitTagInNonPlainScalar()))) { -+ preparedTag = null; -+ return; -+ } -+ if (ev.getImplicit().canOmitTagInPlainScalar() && tag == null) { -+ tag = "!"; -+ preparedTag = null; -+ } -+ } else { -+ CollectionStartEvent ev = (CollectionStartEvent) event; -+ tag = ev.getTag(); -+ if ((!canonical || tag == null) && ev.getImplicit()) { -+ preparedTag = null; -+ return; -+ } -+ } -+ if (tag == null) { -+ throw new EmitterException("tag is not specified"); -+ } -+ if (preparedTag == null) { -+ preparedTag = prepareTag(tag); -+ } -+ writeIndicator(preparedTag, true, false, false); -+ preparedTag = null; -+ } -+ -+ private DumperOptions.ScalarStyle chooseScalarStyle() { -+ ScalarEvent ev = (ScalarEvent) event; -+ if (analysis == null) { -+ analysis = analyzeScalar(ev.getValue()); -+ } -+ if (!ev.isPlain() && ev.getScalarStyle() == DumperOptions.ScalarStyle.DOUBLE_QUOTED || this.canonical) { -+ return DumperOptions.ScalarStyle.DOUBLE_QUOTED; -+ } -+ if (ev.isPlain() && ev.getImplicit().canOmitTagInPlainScalar()) { -+ if (!(simpleKeyContext && (analysis.isEmpty() || analysis.isMultiline())) -+ && ((flowLevel != 0 && analysis.isAllowFlowPlain()) || (flowLevel == 0 && analysis.isAllowBlockPlain()))) { -+ return null; -+ } -+ } -+ if (!ev.isPlain() && (ev.getScalarStyle() == DumperOptions.ScalarStyle.LITERAL || ev.getScalarStyle() == DumperOptions.ScalarStyle.FOLDED)) { -+ if (flowLevel == 0 && !simpleKeyContext && analysis.isAllowBlock()) { -+ return ev.getScalarStyle(); -+ } -+ } -+ if (ev.isPlain() || ev.getScalarStyle() == DumperOptions.ScalarStyle.SINGLE_QUOTED) { -+ if (analysis.isAllowSingleQuoted() && !(simpleKeyContext && analysis.isMultiline())) { -+ return DumperOptions.ScalarStyle.SINGLE_QUOTED; -+ } -+ } -+ return DumperOptions.ScalarStyle.DOUBLE_QUOTED; -+ } -+ -+ private void processScalar() throws IOException { -+ ScalarEvent ev = (ScalarEvent) event; -+ if (analysis == null) { -+ analysis = analyzeScalar(ev.getValue()); -+ } -+ if (style == null) { -+ style = chooseScalarStyle(); -+ } -+ boolean split = !simpleKeyContext && splitLines; -+ if (style == null) { -+ writePlain(analysis.getScalar(), split); -+ } else { -+ switch (style) { -+ case DOUBLE_QUOTED: -+ writeDoubleQuoted(analysis.getScalar(), split); -+ break; -+ case SINGLE_QUOTED: -+ writeSingleQuoted(analysis.getScalar(), split); -+ break; -+ case FOLDED: -+ writeFolded(analysis.getScalar(), split); -+ break; -+ case LITERAL: -+ writeLiteral(analysis.getScalar()); -+ break; -+ default: -+ throw new YAMLException("Unexpected style: " + style); -+ } -+ } -+ analysis = null; -+ style = null; -+ } -+ -+ // Analyzers. -+ -+ private String prepareVersion(Version version) { -+ if (version.major() != 1) { -+ throw new EmitterException("unsupported YAML version: " + version); -+ } -+ return version.getRepresentation(); -+ } -+ -+ private final static Pattern HANDLE_FORMAT = Pattern.compile("^![-_\\w]*!$"); -+ -+ private String prepareTagHandle(String handle) { -+ if (handle.length() == 0) { -+ throw new EmitterException("tag handle must not be empty"); -+ } else if (handle.charAt(0) != '!' || handle.charAt(handle.length() - 1) != '!') { -+ throw new EmitterException("tag handle must start and end with '!': " + handle); -+ } else if (!"!".equals(handle) && !HANDLE_FORMAT.matcher(handle).matches()) { -+ throw new EmitterException("invalid character in the tag handle: " + handle); -+ } -+ return handle; -+ } -+ -+ private String prepareTagPrefix(String prefix) { -+ if (prefix.length() == 0) { -+ throw new EmitterException("tag prefix must not be empty"); -+ } -+ StringBuilder chunks = new StringBuilder(); -+ int start = 0; -+ int end = 0; -+ if (prefix.charAt(0) == '!') { -+ end = 1; -+ } -+ while (end < prefix.length()) { -+ end++; -+ } -+ if (start < end) { -+ chunks.append(prefix, start, end); -+ } -+ return chunks.toString(); -+ } -+ -+ private String prepareTag(String tag) { -+ if (tag.length() == 0) { -+ throw new EmitterException("tag must not be empty"); -+ } -+ if ("!".equals(tag)) { -+ return tag; -+ } -+ String handle = null; -+ String suffix = tag; -+ // shall the tag prefixes be sorted as in PyYAML? -+ for (String prefix : tagPrefixes.keySet()) { -+ if (tag.startsWith(prefix) && ("!".equals(prefix) || prefix.length() < tag.length())) { -+ handle = prefix; -+ } -+ } -+ if (handle != null) { -+ suffix = tag.substring(handle.length()); -+ handle = tagPrefixes.get(handle); -+ } -+ -+ int end = suffix.length(); -+ String suffixText = end > 0 ? suffix.substring(0, end) : ""; -+ -+ if (handle != null) { -+ return handle + suffixText; -+ } -+ return "!<" + suffixText + ">"; -+ } -+ -+ static String prepareAnchor(String anchor) { -+ if (anchor.length() == 0) { -+ throw new EmitterException("anchor must not be empty"); -+ } -+ for (Character invalid : INVALID_ANCHOR) { -+ if (anchor.indexOf(invalid) > -1) { -+ throw new EmitterException("Invalid character '" + invalid + "' in the anchor: " + anchor); -+ } -+ } -+ Matcher matcher = SPACES_PATTERN.matcher(anchor); -+ if (matcher.find()) { -+ throw new EmitterException("Anchor may not contain spaces: " + anchor); -+ } -+ return anchor; -+ } -+ -+ private ScalarAnalysis analyzeScalar(String scalar) { -+ // Empty scalar is a special case. -+ if (scalar.length() == 0) { -+ return new ScalarAnalysis(scalar, true, false, false, true, true, false); -+ } -+ // Indicators and special characters. -+ boolean blockIndicators = false; -+ boolean flowIndicators = false; -+ boolean lineBreaks = false; -+ boolean specialCharacters = false; -+ -+ // Important whitespace combinations. -+ boolean leadingSpace = false; -+ boolean leadingBreak = false; -+ boolean trailingSpace = false; -+ boolean trailingBreak = false; -+ boolean breakSpace = false; -+ boolean spaceBreak = false; -+ -+ // Check document indicators. -+ if (scalar.startsWith("---") || scalar.startsWith("...")) { -+ blockIndicators = true; -+ flowIndicators = true; -+ } -+ // First character or preceded by a whitespace. -+ boolean preceededByWhitespace = true; -+ boolean followedByWhitespace = scalar.length() == 1 || Constant.NULL_BL_T_LINEBR.has(scalar.codePointAt(1)); -+ // The previous character is a space. -+ boolean previousSpace = false; -+ -+ // The previous character is a break. -+ boolean previousBreak = false; -+ -+ int index = 0; -+ -+ while (index < scalar.length()) { -+ int c = scalar.codePointAt(index); -+ // Check for indicators. -+ if (index == 0) { -+ // Leading indicators are special characters. -+ if ("#,[]{}&*!|>'\"%@`".indexOf(c) != -1) { -+ flowIndicators = true; -+ blockIndicators = true; -+ } -+ if (c == '?' || c == ':') { -+ flowIndicators = true; -+ if (followedByWhitespace) { -+ blockIndicators = true; -+ } -+ } -+ if (c == '-' && followedByWhitespace) { -+ flowIndicators = true; -+ blockIndicators = true; -+ } -+ } else { -+ // Some indicators cannot appear within a scalar as well. -+ if (",?[]{}".indexOf(c) != -1) { -+ flowIndicators = true; -+ } -+ if (c == ':') { -+ flowIndicators = true; -+ if (followedByWhitespace) { -+ blockIndicators = true; -+ } -+ } -+ if (c == '#' && preceededByWhitespace) { -+ flowIndicators = true; -+ blockIndicators = true; -+ } -+ } -+ // Check for line breaks, special, and unicode characters. -+ boolean isLineBreak = Constant.LINEBR.has(c); -+ if (isLineBreak) { -+ lineBreaks = true; -+ } -+ if (!(c == '\n' || (0x20 <= c && c <= 0x7E))) { -+ if (c == 0x85 || (c >= 0xA0 && c <= 0xD7FF) -+ || (c >= 0xE000 && c <= 0xFFFD) -+ || (c >= 0x10000 && c <= 0x10FFFF)) { -+ // unicode is used -+ if (!this.allowUnicode) { -+ specialCharacters = true; -+ } -+ } else { -+ specialCharacters = true; -+ } -+ } -+ // Detect important whitespace combinations. -+ if (c == ' ') { -+ if (index == 0) { -+ leadingSpace = true; -+ } -+ if (index == scalar.length() - 1) { -+ trailingSpace = true; -+ } -+ if (previousBreak) { -+ breakSpace = true; -+ } -+ previousSpace = true; -+ previousBreak = false; -+ } else if (isLineBreak) { -+ if (index == 0) { -+ leadingBreak = true; -+ } -+ if (index == scalar.length() - 1) { -+ trailingBreak = true; -+ } -+ if (previousSpace) { -+ spaceBreak = true; -+ } -+ previousSpace = false; -+ previousBreak = true; -+ } else { -+ previousSpace = false; -+ previousBreak = false; -+ } -+ -+ // Prepare for the next character. -+ index += Character.charCount(c); -+ preceededByWhitespace = Constant.NULL_BL_T.has(c) || isLineBreak; -+ followedByWhitespace = true; -+ if (index + 1 < scalar.length()) { -+ int nextIndex = index + Character.charCount(scalar.codePointAt(index)); -+ if (nextIndex < scalar.length()) { -+ followedByWhitespace = (Constant.NULL_BL_T.has(scalar.codePointAt(nextIndex))) || isLineBreak; -+ } -+ } -+ } -+ // Let's decide what styles are allowed. -+ boolean allowFlowPlain = true; -+ boolean allowBlockPlain = true; -+ boolean allowSingleQuoted = true; -+ boolean allowBlock = true; -+ // Leading and trailing whitespaces are bad for plain scalars. -+ if (leadingSpace || leadingBreak || trailingSpace || trailingBreak) { -+ allowFlowPlain = allowBlockPlain = false; -+ } -+ // We do not permit trailing spaces for block scalars. -+ if (trailingSpace) { -+ allowBlock = false; -+ } -+ // Spaces at the beginning of a new line are only acceptable for block -+ // scalars. -+ if (breakSpace) { -+ allowFlowPlain = allowBlockPlain = allowSingleQuoted = false; -+ } -+ // Spaces followed by breaks, as well as special character are only -+ // allowed for double quoted scalars. -+ if (spaceBreak || specialCharacters) { -+ allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false; -+ } -+ // Although the plain scalar writer supports breaks, we never emit -+ // multiline plain scalars in the flow context. -+ if (lineBreaks) { -+ allowFlowPlain = false; -+ } -+ // Flow indicators are forbidden for flow plain scalars. -+ if (flowIndicators) { -+ allowFlowPlain = false; -+ } -+ // Block indicators are forbidden for block plain scalars. -+ if (blockIndicators) { -+ allowBlockPlain = false; -+ } -+ -+ return new ScalarAnalysis(scalar, false, lineBreaks, allowFlowPlain, allowBlockPlain, -+ allowSingleQuoted, allowBlock); -+ } -+ -+ // Writers. -+ -+ void flushStream() throws IOException { -+ stream.flush(); -+ } -+ -+ void writeStreamStart() { -+ // BOM is written by Writer. -+ } -+ -+ void writeStreamEnd() throws IOException { -+ flushStream(); -+ } -+ -+ void writeIndicator(String indicator, boolean needWhitespace, boolean whitespace, -+ boolean indentation) throws IOException { -+ if (!this.whitespace && needWhitespace) { -+ this.column++; -+ stream.write(SPACE); -+ } -+ this.whitespace = whitespace; -+ this.indention = this.indention && indentation; -+ this.column += indicator.length(); -+ openEnded = false; -+ stream.write(indicator); -+ } -+ -+ void writeIndent() throws IOException { -+ int indent; -+ if (this.indent != null) { -+ indent = this.indent; -+ } else { -+ indent = 0; -+ } -+ -+ if (!this.indention || this.column > indent || (this.column == indent && !this.whitespace)) { -+ writeLineBreak(null); -+ } -+ -+ writeWhitespace(indent - this.column); -+ } -+ -+ private void writeWhitespace(int length) throws IOException { -+ if (length <= 0) { -+ return; -+ } -+ this.whitespace = true; -+ char[] data = new char[length]; -+ for (int i = 0; i < data.length; i++) { -+ data[i] = ' '; -+ } -+ this.column += length; -+ stream.write(data); -+ } -+ -+ private void writeLineBreak(String data) throws IOException { -+ this.whitespace = true; -+ this.indention = true; -+ this.column = 0; -+ if (data == null) { -+ stream.write(this.bestLineBreak); -+ } else { -+ stream.write(data); -+ } -+ } -+ -+ void writeVersionDirective(String versionText) throws IOException { -+ stream.write("%YAML "); -+ stream.write(versionText); -+ writeLineBreak(null); -+ } -+ -+ void writeTagDirective(String handleText, String prefixText) throws IOException { -+ // XXX: not sure 4 invocations better then StringBuilders created by str -+ // + str -+ stream.write("%TAG "); -+ stream.write(handleText); -+ stream.write(SPACE); -+ stream.write(prefixText); -+ writeLineBreak(null); -+ } -+ -+ // Scalar streams. -+ private void writeSingleQuoted(String text, boolean split) throws IOException { -+ writeIndicator("'", true, false, false); -+ boolean spaces = false; -+ boolean breaks = false; -+ int start = 0, end = 0; -+ char ch; -+ while (end <= text.length()) { -+ ch = 0; -+ if (end < text.length()) { -+ ch = text.charAt(end); -+ } -+ if (spaces) { -+ if (ch == 0 || ch != ' ') { -+ if (start + 1 == end && this.column > this.bestWidth && split && start != 0 -+ && end != text.length()) { -+ writeIndent(); -+ } else { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ } -+ start = end; -+ } -+ } else if (breaks) { -+ if (ch == 0 || Constant.LINEBR.hasNo(ch)) { -+ if (text.charAt(start) == '\n') { -+ writeLineBreak(null); -+ } -+ String data = text.substring(start, end); -+ for (char br : data.toCharArray()) { -+ if (br == '\n') { -+ writeLineBreak(null); -+ } else { -+ writeLineBreak(String.valueOf(br)); -+ } -+ } -+ writeIndent(); -+ start = end; -+ } -+ } else { -+ if (Constant.LINEBR.has(ch, "\0 '")) { -+ if (start < end) { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ start = end; -+ } -+ } -+ } -+ if (ch == '\'') { -+ this.column += 2; -+ stream.write("''"); -+ start = end + 1; -+ } -+ if (ch != 0) { -+ spaces = ch == ' '; -+ breaks = Constant.LINEBR.has(ch); -+ } -+ end++; -+ } -+ writeIndicator("'", false, false, false); -+ } -+ -+ private void writeDoubleQuoted(String text, boolean split) throws IOException { -+ writeIndicator("\"", true, false, false); -+ int start = 0; -+ int end = 0; -+ while (end <= text.length()) { -+ Character ch = null; -+ if (end < text.length()) { -+ ch = text.charAt(end); -+ } -+ if (ch == null || "\"\\\u0085\u2028\u2029\uFEFF".indexOf(ch) != -1 -+ || !('\u0020' <= ch && ch <= '\u007E')) { -+ if (start < end) { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ start = end; -+ } -+ if (ch != null) { -+ String data; -+ if (ESCAPE_REPLACEMENTS.containsKey(ch)) { -+ data = "\\" + ESCAPE_REPLACEMENTS.get(ch); -+ } else if (!this.allowUnicode || !StreamReader.isPrintable(ch)) { -+ // if !allowUnicode or the character is not printable, -+ // we must encode it -+ if (ch <= '\u00FF') { -+ String s = "0" + Integer.toString(ch, 16); -+ data = "\\x" + s.substring(s.length() - 2); -+ } else if (ch >= '\uD800' && ch <= '\uDBFF') { -+ if (end + 1 < text.length()) { -+ Character ch2 = text.charAt(++end); -+ String s = "000" + Long.toHexString(Character.toCodePoint(ch, ch2)); -+ data = "\\U" + s.substring(s.length() - 8); -+ } else { -+ String s = "000" + Integer.toString(ch, 16); -+ data = "\\u" + s.substring(s.length() - 4); -+ } -+ } else { -+ String s = "000" + Integer.toString(ch, 16); -+ data = "\\u" + s.substring(s.length() - 4); -+ } -+ } else { -+ data = String.valueOf(ch); -+ } -+ this.column += data.length(); -+ stream.write(data); -+ start = end + 1; -+ } -+ } -+ if ((0 < end && end < (text.length() - 1)) && (ch == ' ' || start >= end) -+ && (this.column + (end - start)) > this.bestWidth && split) { -+ String data; -+ if (start >= end) { -+ data = "\\"; -+ } else { -+ data = text.substring(start, end) + "\\"; -+ } -+ if (start < end) { -+ start = end; -+ } -+ this.column += data.length(); -+ stream.write(data); -+ writeIndent(); -+ this.whitespace = false; -+ this.indention = false; -+ if (text.charAt(start) == ' ') { -+ data = "\\"; -+ this.column += data.length(); -+ stream.write(data); -+ } -+ } -+ end += 1; -+ } -+ writeIndicator("\"", false, false, false); -+ } -+ -+ private boolean writeCommentLines(List commentLines) throws IOException { -+ boolean wroteComment = false; -+ if(emitComments) { -+ int indentColumns = 0; -+ boolean firstComment = true; -+ for (CommentLine commentLine : commentLines) { -+ if (commentLine.getCommentType() != CommentType.BLANK_LINE) { -+ if (firstComment) { -+ firstComment = false; -+ writeIndicator("#", commentLine.getCommentType() == CommentType.IN_LINE, false, false); -+ indentColumns = this.column > 0 ? this.column - 1 : 0; -+ } else { -+ writeWhitespace(indentColumns); -+ writeIndicator("#", false, false, false); -+ } -+ stream.write(commentLine.getValue()); -+ writeLineBreak(null); -+ } else { -+ writeLineBreak(null); -+ writeIndent(); -+ } -+ wroteComment = true; -+ } -+ } -+ return wroteComment; -+ } -+ -+ private void writeBlockComment() throws IOException { -+ if(!blockCommentsCollector.isEmpty()) { -+ writeIndent(); -+ writeCommentLines(blockCommentsCollector.consume()); -+ } -+ } -+ -+ private boolean writeInlineComments() throws IOException { -+ return writeCommentLines(inlineCommentsCollector.consume()); -+ } -+ -+ private String determineBlockHints(String text) { -+ StringBuilder hints = new StringBuilder(); -+ if (Constant.LINEBR.has(text.charAt(0), " ")) { -+ hints.append(bestIndent); -+ } -+ char ch1 = text.charAt(text.length() - 1); -+ if (Constant.LINEBR.hasNo(ch1)) { -+ hints.append("-"); -+ } else if (text.length() == 1 || Constant.LINEBR.has(text.charAt(text.length() - 2))) { -+ hints.append("+"); -+ } -+ return hints.toString(); -+ } -+ -+ void writeFolded(String text, boolean split) throws IOException { -+ String hints = determineBlockHints(text); -+ writeIndicator(">" + hints, true, false, false); -+ if (hints.length() > 0 && (hints.charAt(hints.length() - 1) == '+')) { -+ openEnded = true; -+ } -+ if(!writeInlineComments()) { -+ writeLineBreak(null); -+ } -+ boolean leadingSpace = true; -+ boolean spaces = false; -+ boolean breaks = true; -+ int start = 0, end = 0; -+ while (end <= text.length()) { -+ char ch = 0; -+ if (end < text.length()) { -+ ch = text.charAt(end); -+ } -+ if (breaks) { -+ if (ch == 0 || Constant.LINEBR.hasNo(ch)) { -+ if (!leadingSpace && ch != 0 && ch != ' ' && text.charAt(start) == '\n') { -+ writeLineBreak(null); -+ } -+ leadingSpace = ch == ' '; -+ String data = text.substring(start, end); -+ for (char br : data.toCharArray()) { -+ if (br == '\n') { -+ writeLineBreak(null); -+ } else { -+ writeLineBreak(String.valueOf(br)); -+ } -+ } -+ if (ch != 0) { -+ writeIndent(); -+ } -+ start = end; -+ } -+ } else if (spaces) { -+ if (ch != ' ') { -+ if (start + 1 == end && this.column > this.bestWidth && split) { -+ writeIndent(); -+ } else { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ } -+ start = end; -+ } -+ } else { -+ if (Constant.LINEBR.has(ch, "\0 ")) { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ if (ch == 0) { -+ writeLineBreak(null); -+ } -+ start = end; -+ } -+ } -+ if (ch != 0) { -+ breaks = Constant.LINEBR.has(ch); -+ spaces = ch == ' '; -+ } -+ end++; -+ } -+ } -+ -+ void writeLiteral(String text) throws IOException { -+ String hints = determineBlockHints(text); -+ writeIndicator("|" + hints, true, false, false); -+ if (hints.length() > 0 && (hints.charAt(hints.length() - 1)) == '+') { -+ openEnded = true; -+ } -+ if(!writeInlineComments()) { -+ writeLineBreak(null); -+ } -+ boolean breaks = true; -+ int start = 0, end = 0; -+ while (end <= text.length()) { -+ char ch = 0; -+ if (end < text.length()) { -+ ch = text.charAt(end); -+ } -+ if (breaks) { -+ if (ch == 0 || Constant.LINEBR.hasNo(ch)) { -+ String data = text.substring(start, end); -+ for (char br : data.toCharArray()) { -+ if (br == '\n') { -+ writeLineBreak(null); -+ } else { -+ writeLineBreak(String.valueOf(br)); -+ } -+ } -+ if (ch != 0) { -+ writeIndent(); -+ } -+ start = end; -+ } -+ } else { -+ if (ch == 0 || Constant.LINEBR.has(ch)) { -+ stream.write(text, start, end - start); -+ if (ch == 0) { -+ writeLineBreak(null); -+ } -+ start = end; -+ } -+ } -+ if (ch != 0) { -+ breaks = Constant.LINEBR.has(ch); -+ } -+ end++; -+ } -+ } -+ -+ void writePlain(String text, boolean split) throws IOException { -+ if (rootContext) { -+ openEnded = true; -+ } -+ if (text.length() == 0) { -+ return; -+ } -+ if (!this.whitespace) { -+ this.column++; -+ stream.write(SPACE); -+ } -+ this.whitespace = false; -+ this.indention = false; -+ boolean spaces = false; -+ boolean breaks = false; -+ int start = 0, end = 0; -+ while (end <= text.length()) { -+ char ch = 0; -+ if (end < text.length()) { -+ ch = text.charAt(end); -+ } -+ if (spaces) { -+ if (ch != ' ') { -+ if (start + 1 == end && this.column > this.bestWidth && split) { -+ writeIndent(); -+ this.whitespace = false; -+ this.indention = false; -+ } else { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ } -+ start = end; -+ } -+ } else if (breaks) { -+ if (Constant.LINEBR.hasNo(ch)) { -+ if (text.charAt(start) == '\n') { -+ writeLineBreak(null); -+ } -+ String data = text.substring(start, end); -+ for (char br : data.toCharArray()) { -+ if (br == '\n') { -+ writeLineBreak(null); -+ } else { -+ writeLineBreak(String.valueOf(br)); -+ } -+ } -+ writeIndent(); -+ this.whitespace = false; -+ this.indention = false; -+ start = end; -+ } -+ } else { -+ if (Constant.LINEBR.has(ch, "\0 ")) { -+ int len = end - start; -+ this.column += len; -+ stream.write(text, start, len); -+ start = end; -+ } -+ } -+ if (ch != 0) { -+ spaces = ch == ' '; -+ breaks = Constant.LINEBR.has(ch); -+ } -+ end++; -+ } -+ } -+} diff --git a/work/Bukkit b/work/Bukkit index 0994345029..006049902d 160000 --- a/work/Bukkit +++ b/work/Bukkit @@ -1 +1 @@ -Subproject commit 0994345029c4d127696616de3bab3e8044b03749 +Subproject commit 006049902d41e91d01844126dd968ca92fb99c8a diff --git a/work/Spigot b/work/Spigot index 4aa5ead237..89741d3e9f 160000 --- a/work/Spigot +++ b/work/Spigot @@ -1 +1 @@ -Subproject commit 4aa5ead2377692b934a84b6853d188e2d9cbd53a +Subproject commit 89741d3e9ff89cb2aa3b2e34a714a3a073b60d95