RubyNameError.java

  1. /***** BEGIN LICENSE BLOCK *****
  2.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Eclipse Public
  5.  * License Version 1.0 (the "License"); you may not use this file
  6.  * except in compliance with the License. You may obtain a copy of
  7.  * the License at http://www.eclipse.org/legal/epl-v10.html
  8.  *
  9.  * Software distributed under the License is distributed on an "AS
  10.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11.  * implied. See the License for the specific language governing
  12.  * rights and limitations under the License.
  13.  *
  14.  * Copyright (C) 2006 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  15.  *
  16.  * Alternatively, the contents of this file may be used under the terms of
  17.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  18.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  19.  * in which case the provisions of the GPL or the LGPL are applicable instead
  20.  * of those above. If you wish to allow use of your version of this file only
  21.  * under the terms of either the GPL or the LGPL, and not to allow others to
  22.  * use your version of this file under the terms of the EPL, indicate your
  23.  * decision by deleting the provisions above and replace them with the notice
  24.  * and other provisions required by the GPL or the LGPL. If you do not delete
  25.  * the provisions above, a recipient may use your version of this file under
  26.  * the terms of any one of the EPL, the GPL or the LGPL.
  27.  ***** END LICENSE BLOCK *****/

  28. package org.jruby;

  29. import static org.jruby.runtime.Visibility.PRIVATE;
  30. import static org.jruby.runtime.Visibility.PROTECTED;

  31. import org.jruby.anno.JRubyMethod;
  32. import org.jruby.anno.JRubyClass;
  33. import org.jruby.exceptions.JumpException;
  34. import org.jruby.runtime.Block;
  35. import org.jruby.runtime.CallType;
  36. import org.jruby.runtime.ObjectAllocator;
  37. import org.jruby.runtime.ThreadContext;
  38. import org.jruby.runtime.Visibility;
  39. import org.jruby.runtime.builtin.IRubyObject;
  40. import org.jruby.util.ByteList;
  41. import org.jruby.util.Sprintf;

  42. /**
  43.  * @author Anders Bengtsson
  44.  */
  45. @JRubyClass(name="NameError", parent="StandardError")
  46. public class RubyNameError extends RubyException {
  47.     private IRubyObject name;

  48.     /**
  49.      * Nested class whose instances act as thunks reacting to to_str method
  50.      * called from (Exception#to_str, Exception#message)
  51.      * MRI equivalent: rb_cNameErrorMesg, class name: "message", construction method: "!",
  52.      * to_str implementation: "name_err_mesg_to_str"
  53.      *
  54.      * TODO: this class should not be lookupable
  55.      */
  56.     @JRubyClass(name = "NameError::Message", parent = "Data")
  57.     public static final class RubyNameErrorMessage extends RubyObject {

  58.         static ObjectAllocator NAMEERRORMESSAGE_ALLOCATOR = new ObjectAllocator() {
  59.             @Override
  60.             public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  61.                 IRubyObject dummy = new RubyObject(runtime, runtime.getObject());
  62.                 return new RubyNameErrorMessage(runtime, dummy, dummy, Visibility.PRIVATE, CallType.VARIABLE);
  63.             }
  64.         };        

  65.         private final IRubyObject object;
  66.         private final IRubyObject method;
  67.         private final Visibility visibility;
  68.         private final CallType callType;

  69.         RubyNameErrorMessage(Ruby runtime, IRubyObject object, IRubyObject method, Visibility visibility, CallType callType) {
  70.             super(runtime, runtime.getNameErrorMessage(), false);
  71.             this.object = object;
  72.             this.method = method;
  73.             this.visibility = visibility;
  74.             this.callType = callType;
  75.         }

  76.         @JRubyMethod(name = "_load", meta = true)
  77.         public static IRubyObject load(IRubyObject recv, IRubyObject arg) {
  78.             return arg;
  79.         }

  80.         @JRubyMethod(name = "_dump")
  81.         public IRubyObject dump(ThreadContext context, IRubyObject arg) {
  82.             return to_str(context);
  83.         }

  84.         @JRubyMethod
  85.         public IRubyObject to_str(ThreadContext context) {
  86.             String format = null;

  87.             if (visibility == PRIVATE) {
  88.                 format = "private method `%s' called for %s";
  89.             } else if (visibility == PROTECTED) {
  90.                 format = "protected method `%s' called for %s";
  91.             } else if (callType == CallType.VARIABLE) {
  92.                 format = "undefined local variable or method `%s' for %s";
  93.             } else if (callType == CallType.SUPER) {
  94.                 format = "super: no superclass method `%s'";
  95.             }

  96.             if (format == null) format = "undefined method `%s' for %s";

  97.             String description = null;

  98.             if (object.isNil()) {
  99.                 description = "nil";
  100.             } else if (object instanceof RubyBoolean && object.isTrue()) {
  101.                 description = "true";
  102.             } else if (object instanceof RubyBoolean && !object.isTrue()) {
  103.                 description = "false";
  104.             } else {
  105.                 try {
  106.                     description = RubyObject.inspect(context, object).toString();
  107.                 } catch(JumpException e) {}

  108.                 if (description == null || description.length() > 65) description = object.anyToString().toString();
  109.             }

  110.             if (description.length() == 0 || (description.length() > 0 && description.charAt(0) != '#')) {
  111.                 description = description + ":" + object.getMetaClass().getRealClass().getName();            
  112.             }

  113.             Ruby runtime = context.runtime;
  114.             RubyArray arr = runtime.newArray(method, runtime.newString(description));
  115.             ByteList msgBytes = new ByteList(format.length() + description.length() + method.toString().length());
  116.             Sprintf.sprintf(msgBytes, format, arr);
  117.             return runtime.newString(msgBytes).infectBy(object);
  118.         }
  119.     }

  120.     private static ObjectAllocator NAMEERROR_ALLOCATOR = new ObjectAllocator() {
  121.         @Override
  122.         public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  123.             return new RubyNameError(runtime, klass);
  124.         }
  125.     };

  126.     public static RubyClass createNameErrorClass(Ruby runtime, RubyClass standardErrorClass) {
  127.         RubyClass nameErrorClass = runtime.defineClass("NameError", standardErrorClass, NAMEERROR_ALLOCATOR);
  128.         nameErrorClass.defineAnnotatedMethods(RubyNameError.class);
  129.         return nameErrorClass;
  130.     }

  131.     public static RubyClass createNameErrorMessageClass(Ruby runtime, RubyClass nameErrorClass) {
  132.         RubyClass messageClass = nameErrorClass.defineClassUnder("Message", runtime.getClass("Data"), RubyNameErrorMessage.NAMEERRORMESSAGE_ALLOCATOR);
  133.         messageClass.defineAnnotatedMethods(RubyNameErrorMessage.class);
  134.         return messageClass;
  135.     }

  136.     protected RubyNameError(Ruby runtime, RubyClass exceptionClass) {
  137.         this(runtime, exceptionClass, exceptionClass.getName());
  138.     }

  139.     public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message) {
  140.         this(runtime, exceptionClass, message, (String) null);
  141.     }

  142.     public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message, String name) {
  143.         super(runtime, exceptionClass, message);
  144.         this.name = name == null ? runtime.getNil() : runtime.newString(name);
  145.     }
  146.    
  147.     public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message, IRubyObject name) {
  148.         super(runtime, exceptionClass, message);
  149.         this.name = name;
  150.     }

  151.     @JRubyMethod(name = "exception", rest = true, meta = true)
  152.     public static RubyException newRubyNameError(IRubyObject recv, IRubyObject[] args) {
  153.         RubyClass klass = (RubyClass)recv;
  154.        
  155.         RubyException newError = (RubyException) klass.allocate();
  156.        
  157.         newError.callInit(args, Block.NULL_BLOCK);
  158.        
  159.         return newError;
  160.     }

  161.     @JRubyMethod(optional = 2, visibility = Visibility.PRIVATE)
  162.     @Override
  163.     public IRubyObject initialize(IRubyObject[] args, Block block) {
  164.         if (args.length > 1) {
  165.             name = args[args.length - 1];
  166.             int newLength = args.length > 2 ? args.length - 2 : args.length - 1;

  167.             IRubyObject []tmpArgs = new IRubyObject[newLength];
  168.             System.arraycopy(args, 0, tmpArgs, 0, newLength);
  169.             args = tmpArgs;
  170.         } else {
  171.             name = getRuntime().getNil();
  172.         }

  173.         super.initialize(args, block);
  174.         return this;
  175.     }

  176.     @JRubyMethod
  177.     @Override
  178.     public IRubyObject to_s() {
  179.         if (message.isNil()) return getRuntime().newString(message.getMetaClass().getName());
  180.         RubyString str = message.convertToString();
  181.         if (str != message) message = str;
  182.         if (isTaint()) message.setTaint(true);
  183.         return message;
  184.     }

  185.     @JRubyMethod
  186.     public IRubyObject name() {
  187.         return name;
  188.     }

  189.     @Override
  190.     public void copySpecialInstanceVariables(IRubyObject clone) {
  191.         super.copySpecialInstanceVariables(clone);
  192.         RubyNameError exception = (RubyNameError)clone;
  193.         exception.name = name;
  194.     }
  195. }