summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2012-04-27 16:42:53 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2012-04-28 15:24:54 -0700
commit0fc5af3f29484b2f9714c1546b7973a1a7d4721e (patch)
tree0ef84df1a1ddc55caf1966f71298bad91c40c4b1
parentb01bf0f960ef4a00155dd383adeb1fc031ae6940 (diff)
downloadpsych-0fc5af3f29484b2f9714c1546b7973a1a7d4721e.zip
import the java implementation from JRuby
-rw-r--r--.gitignore1
-rw-r--r--Rakefile24
-rw-r--r--ext/java/PsychEmitter.java299
-rw-r--r--ext/java/PsychLibrary.java63
-rw-r--r--ext/java/PsychParser.java365
-rw-r--r--ext/java/PsychToRuby.java71
-rw-r--r--ext/java/PsychYamlTree.java52
-rw-r--r--lib/snakeyaml-1.10.jarbin0 -> 266956 bytes
8 files changed, 872 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 516f23f..6f050e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
lib/psych/psych.bundle
+lib/psych/psych.jar
tmp
pkg
diff --git a/Rakefile b/Rakefile
index f9aef47..03353e5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -3,9 +3,15 @@
require 'rubygems'
require 'hoe'
+def java?
+ RUBY_PLATFORM =~ /java/
+end
+
class Hoe
remove_const :RUBY_FLAGS
- RUBY_FLAGS = "-I#{%w(lib ext bin test).join(File::PATH_SEPARATOR)}"
+ flags = "-I#{%w(lib ext bin test).join(File::PATH_SEPARATOR)}"
+ flags = "--1.9 " + flags if java?
+ RUBY_FLAGS = flags
end
gem 'rake-compiler', '>= 0.4.1'
@@ -28,8 +34,20 @@ $hoe = Hoe.spec 'psych' do
:required_ruby_version => '>= 1.9.2'
}
- Rake::ExtensionTask.new "psych", spec do |ext|
- ext.lib_dir = File.join(*['lib', ENV['FAT_DIR']].compact)
+ if java?
+ # TODO: clean this section up.
+ require "rake/javaextensiontask"
+ Rake::JavaExtensionTask.new("psych", spec) do |ext|
+ jruby_home = RbConfig::CONFIG['prefix']
+ ext.ext_dir = 'ext/java'
+ ext.lib_dir = 'lib/psych'
+ jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar']
+ ext.classpath = jars.map { |x| File.expand_path x }.join ':'
+ end
+ else
+ Rake::ExtensionTask.new "psych", spec do |ext|
+ ext.lib_dir = File.join(*['lib', ENV['FAT_DIR']].compact)
+ end
end
end
diff --git a/ext/java/PsychEmitter.java b/ext/java/PsychEmitter.java
new file mode 100644
index 0000000..e775357
--- /dev/null
+++ b/ext/java/PsychEmitter.java
@@ -0,0 +1,299 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.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.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2010 Charles O Nutter <headius@headius.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.psych;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.IOOutputStream;
+import org.jruby.util.TypeConverter;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.emitter.EmitterException;
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+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 static org.jruby.runtime.Visibility.*;
+
+public class PsychEmitter extends RubyObject {
+ public static void initPsychEmitter(Ruby runtime, RubyModule psych) {
+ RubyClass psychEmitter = runtime.defineClassUnder("Emitter", runtime.getObject(), new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new PsychEmitter(runtime, klazz);
+ }
+ }, psych);
+
+ psychEmitter.defineAnnotatedMethods(PsychEmitter.class);
+ }
+
+ public PsychEmitter(Ruby runtime, RubyClass klass) {
+ super(runtime, klass);
+ }
+
+ @JRubyMethod(visibility = PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject io) {
+ options = new DumperOptions();
+ options.setIndent(2);
+ emitter = new Emitter(new OutputStreamWriter(new IOOutputStream(io)), options);
+
+ return context.nil;
+ }
+
+ @JRubyMethod
+ public IRubyObject start_stream(ThreadContext context, IRubyObject encoding) {
+ if (!(encoding instanceof RubyFixnum)) {
+ throw context.runtime.newTypeError(encoding, context.runtime.getFixnum());
+ }
+
+ // TODO: do something with encoding? perhaps at the stream level?
+ StreamStartEvent event = new StreamStartEvent(NULL_MARK, NULL_MARK);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject end_stream(ThreadContext context) {
+ StreamEndEvent event = new StreamEndEvent(NULL_MARK, NULL_MARK);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject start_document(ThreadContext context, IRubyObject version, IRubyObject tags, IRubyObject implicit) {
+ Integer[] versionInts = null;
+ boolean implicitBool = implicit.isTrue();
+ Map<String, String> tagsMap = Collections.EMPTY_MAP;
+
+ RubyArray versionAry = version.convertToArray();
+ if (versionAry.size() == 2) {
+ versionInts = new Integer[] {1, 1};
+ versionInts[0] = (int)versionAry.eltInternal(0).convertToInteger().getLongValue();
+ versionInts[1] = (int)versionAry.eltInternal(1).convertToInteger().getLongValue();
+ }
+
+ RubyArray tagsAry = tags.convertToArray();
+ if (tagsAry.size() > 0) {
+ tagsMap = new HashMap<String, String>(tagsAry.size());
+ for (int i = 0; i < tagsAry.size(); i++) {
+ RubyArray tagsTuple = tagsAry.eltInternal(i).convertToArray();
+ if (tagsTuple.size() != 2) {
+ throw context.runtime.newRuntimeError("tags tuple must be of length 2");
+ }
+ IRubyObject key = tagsTuple.eltInternal(0);
+ IRubyObject value = tagsTuple.eltInternal(1);
+ tagsMap.put(
+ key.asJavaString(),
+ value.asJavaString());
+ }
+ }
+
+ DocumentStartEvent event = new DocumentStartEvent(NULL_MARK, NULL_MARK, implicitBool, versionInts, tagsMap);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject end_document(ThreadContext context, IRubyObject implicit) {
+ DocumentEndEvent event = new DocumentEndEvent(NULL_MARK, NULL_MARK, implicit.isTrue());
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod(required = 6)
+ public IRubyObject scalar(ThreadContext context, IRubyObject[] args) {
+ IRubyObject value = args[0];
+ IRubyObject anchor = args[1];
+ IRubyObject tag = args[2];
+ IRubyObject plain = args[3];
+ IRubyObject quoted = args[4];
+ IRubyObject style = args[5];
+
+ if (!(value instanceof RubyString)) {
+ throw context.runtime.newTypeError(value, context.runtime.getString());
+ }
+
+ ScalarEvent event = new ScalarEvent(
+ anchor.isNil() ? null : anchor.asJavaString(),
+ tag.isNil() ? null : tag.asJavaString(),
+ new ImplicitTuple(plain.isTrue(),
+ quoted.isTrue()),
+ value.asJavaString(),
+ NULL_MARK,
+ NULL_MARK,
+ SCALAR_STYLES[(int)style.convertToInteger().getLongValue()]);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod(required = 4)
+ public IRubyObject start_sequence(ThreadContext context, IRubyObject[] args) {
+ IRubyObject anchor = args[0];
+ IRubyObject tag = args[1];
+ IRubyObject implicit = args[2];
+ IRubyObject style = args[3];
+
+ final int SEQUENCE_BLOCK = 1; // see psych/nodes/sequence.rb
+
+ SequenceStartEvent event = new SequenceStartEvent(
+ anchor.isNil() ? null : anchor.asJavaString(),
+ tag.isNil() ? null : tag.asJavaString(),
+ implicit.isTrue(),
+ NULL_MARK,
+ NULL_MARK,
+ SEQUENCE_BLOCK != style.convertToInteger().getLongValue());
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject end_sequence(ThreadContext context) {
+ SequenceEndEvent event = new SequenceEndEvent(NULL_MARK, NULL_MARK);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod(required = 4)
+ public IRubyObject start_mapping(ThreadContext context, IRubyObject[] args) {
+ IRubyObject anchor = args[0];
+ IRubyObject tag = args[1];
+ IRubyObject implicit = args[2];
+ IRubyObject style = args[3];
+
+ final int MAPPING_BLOCK = 1; // see psych/nodes/mapping.rb
+
+ MappingStartEvent event = new MappingStartEvent(
+ anchor.isNil() ? null : anchor.asJavaString(),
+ tag.isNil() ? null : tag.asJavaString(),
+ implicit.isTrue(),
+ NULL_MARK,
+ NULL_MARK,
+ MAPPING_BLOCK != style.convertToInteger().getLongValue());
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject end_mapping(ThreadContext context) {
+ MappingEndEvent event = new MappingEndEvent(NULL_MARK, NULL_MARK);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject alias(ThreadContext context, IRubyObject anchor) {
+ AliasEvent event = new AliasEvent(anchor.asJavaString(), NULL_MARK, NULL_MARK);
+ emit(context, event);
+ return this;
+ }
+
+ @JRubyMethod(name = "canonical=")
+ public IRubyObject canonical_set(ThreadContext context, IRubyObject canonical) {
+ // TODO: unclear if this affects a running emitter
+ options.setCanonical(canonical.isTrue());
+ return canonical;
+ }
+
+ @JRubyMethod
+ public IRubyObject canonical(ThreadContext context) {
+ // TODO: unclear if this affects a running emitter
+ return context.runtime.newBoolean(options.isCanonical());
+ }
+
+ @JRubyMethod(name = "indentation=")
+ public IRubyObject indentation_set(ThreadContext context, IRubyObject level) {
+ // TODO: unclear if this affects a running emitter
+ options.setIndent((int)level.convertToInteger().getLongValue());
+ return level;
+ }
+
+ @JRubyMethod
+ public IRubyObject indentation(ThreadContext context) {
+ // TODO: unclear if this affects a running emitter
+ return context.runtime.newFixnum(options.getIndent());
+ }
+
+ @JRubyMethod(name = "line_width=")
+ public IRubyObject line_width_set(ThreadContext context, IRubyObject width) {
+ options.setWidth((int)width.convertToInteger().getLongValue());
+ return width;
+ }
+
+ @JRubyMethod
+ public IRubyObject line_width(ThreadContext context) {
+ return context.runtime.newFixnum(options.getWidth());
+ }
+
+ private void emit(ThreadContext context, Event event) {
+ try {
+ emitter.emit(event);
+ } catch (IOException ioe) {
+ throw context.runtime.newIOErrorFromException(ioe);
+ } catch (EmitterException ee) {
+ throw context.runtime.newRuntimeError(ee.toString());
+ }
+ }
+
+ Emitter emitter;
+ DumperOptions options = new DumperOptions();
+
+ private static final Mark NULL_MARK = new Mark(null, 0, 0, 0, null, 0);
+
+ // Map style constants from Psych values (ANY = 0 ... FOLDED = 5)
+ // to SnakeYaml values; see psych/nodes/scalar.rb.
+ private static final Character[] SCALAR_STYLES = new Character[] {
+ null, // ANY; we'll choose plain
+ null, // PLAIN
+ '\'', // SINGLE_QUOTED
+ '"', // DOUBLE_QUOTED
+ '|', // LITERAL
+ '>', // FOLDED
+ };
+}
diff --git a/ext/java/PsychLibrary.java b/ext/java/PsychLibrary.java
new file mode 100644
index 0000000..dab346e
--- /dev/null
+++ b/ext/java/PsychLibrary.java
@@ -0,0 +1,63 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.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.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2010 Charles O Nutter <headius@headius.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.psych;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyModule;
+import org.jruby.RubyString;
+import org.jruby.internal.runtime.methods.JavaMethod.JavaMethodZero;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.Visibility;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.load.Library;
+
+public class PsychLibrary implements Library {
+ public void load(final Ruby runtime, boolean wrap) {
+ RubyModule psych = runtime.defineModule("Psych");
+
+ RubyString version = runtime.newString("0.1.4");
+ version.setFrozen(true);
+
+ final RubyArray versionElements = runtime.newArray(runtime.newFixnum(0), runtime.newFixnum(1), runtime.newFixnum(4));
+ versionElements.setFrozen(true);
+
+ psych.setConstant("LIBYAML_VERSION", runtime.newString("0.1.4"));
+ psych.getSingletonClass().addMethod("libyaml_version", new JavaMethodZero(psych, Visibility.PUBLIC) {
+ @Override
+ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
+ return versionElements;
+ }
+ });
+
+ PsychParser.initPsychParser(runtime, psych);
+ PsychEmitter.initPsychEmitter(runtime, psych);
+ PsychToRuby.initPsychToRuby(runtime, psych);
+ PsychYamlTree.initPsychYamlTree(runtime, psych);
+ }
+}
diff --git a/ext/java/PsychParser.java b/ext/java/PsychParser.java
new file mode 100644
index 0000000..59a764e
--- /dev/null
+++ b/ext/java/PsychParser.java
@@ -0,0 +1,365 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.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.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2010 Charles O Nutter <headius@headius.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.psych;
+
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.util.Map;
+import org.jcodings.specific.UTF16BEEncoding;
+import org.jcodings.specific.UTF16LEEncoding;
+import org.jcodings.specific.UTF8Encoding;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyEncoding;
+import org.jruby.RubyException;
+import org.jruby.RubyIO;
+import org.jruby.RubyKernel;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.IOInputStream;
+import org.jruby.util.unsafe.UnsafeFactory;
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.MarkedYAMLException;
+import org.yaml.snakeyaml.events.AliasEvent;
+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.MappingStartEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserException;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.ReaderException;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.scanner.ScannerException;
+import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
+
+public class PsychParser extends RubyObject {
+
+ public static final int YAML_ANY_ENCODING = 0;
+ public static final int YAML_UTF8_ENCODING = UTF8Encoding.INSTANCE.getIndex();
+ public static final int YAML_UTF16LE_ENCODING = UTF16LEEncoding.INSTANCE.getIndex();
+ public static final int YAML_UTF16BE_ENCODING = UTF16BEEncoding.INSTANCE.getIndex();
+
+ public static void initPsychParser(Ruby runtime, RubyModule psych) {
+ RubyClass psychParser = runtime.defineClassUnder("Parser", runtime.getObject(), new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new PsychParser(runtime, klazz);
+ }
+ }, psych);
+
+ psychParser.defineConstant("ANY", runtime.newFixnum(YAML_ANY_ENCODING));
+ psychParser.defineConstant("UTF8", runtime.newFixnum(YAML_UTF8_ENCODING));
+ psychParser.defineConstant("UTF16LE", runtime.newFixnum(YAML_UTF16LE_ENCODING));
+ psychParser.defineConstant("UTF16BE", runtime.newFixnum(YAML_UTF16BE_ENCODING));
+
+ psychParser.defineAnnotatedMethods(PsychParser.class);
+
+ psych.defineClassUnder("SyntaxError", runtime.getSyntaxError(), RubyException.EXCEPTION_ALLOCATOR);
+ }
+
+ public PsychParser(Ruby runtime, RubyClass klass) {
+ super(runtime, klass);
+ }
+
+ @JRubyMethod
+ public IRubyObject parse(ThreadContext context, IRubyObject yaml) {
+ Ruby runtime = context.runtime;
+ boolean tainted = yaml.isTaint();
+
+ // FIXME? only supports Unicode, since we have to produces strings...
+ try {
+ StreamReader reader;
+ if (yaml.respondsTo("read")) {
+ reader = new StreamReader(new InputStreamReader(new IOInputStream(yaml), RubyEncoding.UTF8));
+ if (yaml instanceof RubyIO) {
+ tainted = true;
+ }
+ } else {
+ reader = new StreamReader(new StringReader(yaml.convertToString().asJavaString()));
+ }
+ parser = new ParserImpl(reader);
+ IRubyObject handler = getInstanceVariable("@handler");
+
+ while (true) {
+ event = parser.getEvent();
+
+ // FIXME: Event should expose a getID, so it can be switched
+ if (event.is(ID.StreamStart)) {
+ invoke(
+ context,
+ handler,
+ "start_stream",
+ runtime.newFixnum(YAML_ANY_ENCODING));
+ } else if (event.is(ID.DocumentStart)) {
+ DocumentStartEvent dse = (DocumentStartEvent)event;
+
+ Integer[] versionInts = dse.getVersion();
+ IRubyObject version = versionInts == null ?
+ runtime.getNil() :
+ RubyArray.newArray(runtime, runtime.newFixnum(versionInts[0]), runtime.newFixnum(versionInts[1]));
+
+ Map<String, String> tagsMap = dse.getTags();
+ RubyArray tags = RubyArray.newArray(runtime);
+ if (tags.size() > 0) {
+ for (Map.Entry<String, String> tag : tagsMap.entrySet()) {
+ RubyString key = RubyString.newString(runtime, tag.getKey());
+ RubyString value = RubyString.newString(runtime, tag.getValue());
+ key.setTaint(tainted);
+ value.setTaint(tainted);
+
+ tags.append(RubyArray.newArray(
+ runtime,
+ key,
+ value));
+ }
+ }
+
+ invoke(
+ context,
+ handler,
+ "start_document",
+ version,
+ tags,
+ runtime.newBoolean(!dse.getExplicit()));
+ } else if (event.is(ID.DocumentEnd)) {
+ DocumentEndEvent dee = (DocumentEndEvent)event;
+ invoke(
+ context,
+ handler,
+ "end_document",
+ runtime.newBoolean(!dee.getExplicit()));
+ } else if (event.is(ID.Alias)) {
+ AliasEvent ae = (AliasEvent)event;
+ IRubyObject alias = runtime.getNil();
+ if (ae.getAnchor() != null) {
+ alias = RubyString.newString(runtime, ae.getAnchor());
+ alias.setTaint(tainted);
+ }
+
+ invoke(
+ context,
+ handler,
+ "alias",
+ alias);
+ } else if (event.is(ID.Scalar)) {
+ ScalarEvent se = (ScalarEvent)event;
+ IRubyObject anchor = se.getAnchor() == null ?
+ runtime.getNil() :
+ RubyString.newString(runtime, se.getAnchor());
+ IRubyObject tag = se.getTag() == null ?
+ runtime.getNil() :
+ RubyString.newString(runtime, se.getTag());
+ IRubyObject plain_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInPlainScalar());
+ IRubyObject quoted_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInNonPlainScalar());
+ IRubyObject style = runtime.newFixnum(translateStyle(se.getStyle()));
+ IRubyObject val = RubyString.newString(runtime, se.getValue());
+
+ val.setTaint(tainted);
+ anchor.setTaint(tainted);
+ tag.setTaint(tainted);
+
+ invoke(
+ context,
+ handler,
+ "scalar",
+ val,
+ anchor,
+ tag,
+ plain_implicit,
+ quoted_implicit,
+ style);
+ } else if (event.is(ID.SequenceStart)) {
+ SequenceStartEvent sse = (SequenceStartEvent)event;
+ IRubyObject anchor = sse.getAnchor() == null ?
+ runtime.getNil() :
+ RubyString.newString(runtime, sse.getAnchor());
+ IRubyObject tag = sse.getTag() == null ?
+ runtime.getNil() :
+ RubyString.newString(runtime, sse.getTag());
+ IRubyObject implicit = runtime.newBoolean(sse.getImplicit());
+ IRubyObject style = runtime.newFixnum(translateFlowStyle(sse.getFlowStyle()));
+
+ anchor.setTaint(tainted);
+ tag.setTaint(tainted);
+
+ invoke(
+ context,
+ handler,
+ "start_sequence",
+ anchor,
+ tag,
+ implicit,
+ style);
+ } else if (event.is(ID.SequenceEnd)) {
+ invoke(
+ context,
+ handler,
+ "end_sequence");
+ } else if (event.is(ID.MappingStart)) {
+ MappingStartEvent mse = (MappingStartEvent)event;
+ IRubyObject anchor = mse.getAnchor() == null ?
+ runtime.getNil() :
+ RubyString.newString(runtime, mse.getAnchor());
+ IRubyObject tag = mse.getTag() == null ?
+ runtime.getNil() :
+ RubyString.newString(runtime, mse.getTag());
+ IRubyObject implicit = runtime.newBoolean(mse.getImplicit());
+ IRubyObject style = runtime.newFixnum(translateFlowStyle(mse.getFlowStyle()));
+
+ anchor.setTaint(tainted);
+ tag.setTaint(tainted);
+
+ invoke(
+ context,
+ handler,
+ "start_mapping",
+ anchor,
+ tag,
+ implicit,
+ style);
+ } else if (event.is(ID.MappingEnd)) {
+ invoke(
+ context,
+ handler,
+ "end_mapping");
+ } else if (event.is(ID.StreamEnd)) {
+ invoke(
+ context,
+ handler,
+ "end_stream");
+ break;
+ }
+ }
+ } catch (ParserException pe) {
+ parser = null;
+ RubyKernel.raise(context, runtime.getKernel(),
+ new IRubyObject[] {runtime.getModule("Psych").getConstant("SyntaxError"), runtime.newString(syntaxError(context, yaml, pe))},
+ Block.NULL_BLOCK);
+ } catch (ScannerException se) {
+ parser = null;
+ StringBuilder message = new StringBuilder("syntax error");
+ if (se.getProblemMark() != null) {
+ message.append(se.getProblemMark().toString());
+ }
+ throw runtime.newArgumentError(message.toString());
+ } catch (ReaderException re) {
+ parser = null;
+ RubyKernel.raise(context, runtime.getKernel(),
+ new IRubyObject[] {runtime.getModule("Psych").getConstant("SyntaxError"), runtime.newString(re.getLocalizedMessage())},
+ Block.NULL_BLOCK);
+ } catch (Throwable t) {
+ UnsafeFactory.getUnsafe().throwException(t);
+ return this;
+ }
+
+ return this;
+ }
+
+ private static String syntaxError(ThreadContext context, IRubyObject yaml, MarkedYAMLException mye) {
+ String path;
+ if (yaml.respondsTo("path")) {
+ path = yaml.callMethod(context, "path").toString();
+ } else {
+ path = "<unknown>";
+ }
+ return path + ": couldn't parse YAML at line " + mye.getProblemMark().getLine() + " column " + mye.getProblemMark().getColumn();
+ }
+
+ private static int translateStyle(Character style) {
+ if (style == null) return 0; // any
+
+ switch (style) {
+ case 0: return 1; // plain
+ case '\'': return 2; // single-quoted
+ case '"': return 3; // double-quoted
+ case '|': return 4; // literal
+ case '>': return 5; // folded
+ default: return 0; // any
+ }
+ }
+
+ private static int translateFlowStyle(Boolean flowStyle) {
+ if (flowStyle == null) return 0; // any
+
+ if (flowStyle) return 2;
+ return 1;
+ }
+
+ @JRubyMethod
+ public IRubyObject mark(ThreadContext context) {
+ Ruby runtime = context.runtime;
+
+ Event event = null;
+
+ if (parser != null) {
+ event = parser.peekEvent();
+ if (event == null) {
+ event = this.event;
+ }
+ }
+
+ if (event == null) {
+ return ((RubyClass)context.runtime.getClassFromPath("Psych::Parser::Mark")).newInstance(
+ context,
+ runtime.newFixnum(0),
+ runtime.newFixnum(0),
+ runtime.newFixnum(0),
+ Block.NULL_BLOCK
+ );
+ }
+
+ Mark mark = event.getStartMark();
+
+ return ((RubyClass)context.runtime.getClassFromPath("Psych::Parser::Mark")).newInstance(
+ context,
+ runtime.newFixnum(mark.getIndex()),
+ runtime.newFixnum(mark.getLine()),
+ runtime.newFixnum(mark.getColumn()),
+ Block.NULL_BLOCK
+ );
+ }
+
+ @JRubyMethod(name = "external_encoding=")
+ public IRubyObject external_encoding_set(ThreadContext context, IRubyObject encoding) {
+ // stubbed
+ return encoding;
+ }
+
+ private Parser parser;
+ private Event event;
+}
diff --git a/ext/java/PsychToRuby.java b/ext/java/PsychToRuby.java
new file mode 100644
index 0000000..6838f36
--- /dev/null
+++ b/ext/java/PsychToRuby.java
@@ -0,0 +1,71 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.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.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2010 Charles O Nutter <headius@headius.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.psych;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import static org.jruby.runtime.Visibility.*;
+
+public class PsychToRuby {
+ public static void initPsychToRuby(Ruby runtime, RubyModule psych) {
+ RubyModule visitors = runtime.defineModuleUnder("Visitors", psych);
+ RubyClass visitor = runtime.defineClassUnder("Visitor", runtime.getObject(), runtime.getObject().getAllocator(), visitors);
+ RubyClass psychToRuby = runtime.defineClassUnder("ToRuby", visitor, RubyObject.OBJECT_ALLOCATOR, visitors);
+
+ psychToRuby.defineAnnotatedMethods(PsychToRuby.class);
+ }
+
+ @JRubyMethod(visibility = PRIVATE)
+ public static IRubyObject build_exception(ThreadContext context, IRubyObject self, IRubyObject klass, IRubyObject message) {
+ if (klass instanceof RubyClass) {
+ IRubyObject exception = ((RubyClass)klass).allocate();
+ exception.getInternalVariables().setInternalVariable("mesg", message);
+ return exception;
+ } else {
+ throw context.runtime.newTypeError(klass, context.runtime.getClassClass());
+ }
+ }
+
+ @JRubyMethod(visibility = PRIVATE)
+ public static IRubyObject path2class(ThreadContext context, IRubyObject self, IRubyObject path) {
+ try {
+ return context.runtime.getClassFromPath(path.asJavaString());
+ } catch (RaiseException re) {
+ if (re.getException().getMetaClass() == context.runtime.getNameError()) {
+ throw context.runtime.newArgumentError("undefined class/module " + path);
+ }
+ throw re;
+ }
+ }
+}
diff --git a/ext/java/PsychYamlTree.java b/ext/java/PsychYamlTree.java
new file mode 100644
index 0000000..d53645a
--- /dev/null
+++ b/ext/java/PsychYamlTree.java
@@ -0,0 +1,52 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.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.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2010 Charles O Nutter <headius@headius.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.psych;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import static org.jruby.runtime.Visibility.*;
+
+public class PsychYamlTree {
+ public static void initPsychYamlTree(Ruby runtime, RubyModule psych) {
+ RubyModule visitors = (RubyModule)psych.getConstant("Visitors");
+ RubyClass visitor = (RubyClass)visitors.getConstant("Visitor");
+ RubyClass psychYamlTree = runtime.defineClassUnder("YAMLTree", visitor, RubyObject.OBJECT_ALLOCATOR, visitors);
+
+ psychYamlTree.defineAnnotatedMethods(PsychYamlTree.class);
+ }
+
+ @JRubyMethod(visibility = PRIVATE)
+ public static IRubyObject private_iv_get(ThreadContext context, IRubyObject self, IRubyObject target, IRubyObject prop) {
+ return target.getInstanceVariables().getInstanceVariable(prop.asJavaString());
+ }
+}
diff --git a/lib/snakeyaml-1.10.jar b/lib/snakeyaml-1.10.jar
new file mode 100644
index 0000000..f3f0fd7
--- /dev/null
+++ b/lib/snakeyaml-1.10.jar
Binary files differ