169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/*
269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Javassist, a Java-bytecode translator toolkit.
369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * The contents of this file are subject to the Mozilla Public License Version
669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 1.1 (the "License"); you may not use this file except in compliance with
769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the License.  Alternatively, the contents of this file may be used under
869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the terms of the GNU Lesser General Public License Version 2.1 or later.
969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Software distributed under the License is distributed on an "AS IS" basis,
1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * for the specific language governing rights and limitations under the
1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * License.
1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage javassist.tools.reflect;
1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.lang.reflect.Method;
1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.Serializable;
2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.IOException;
2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.ObjectInputStream;
2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.ObjectOutputStream;
2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/**
2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * A runtime metaobject.
2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * <p>A <code>Metaobject</code> is created for
2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * every object at the base level.  A different reflective object is
2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * associated with a different metaobject.
3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * <p>The metaobject intercepts method calls
3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * on the reflective object at the base-level.  To change the behavior
3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * of the method calls, a subclass of <code>Metaobject</code>
3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * should be defined.
3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * <p>To obtain a metaobject, calls <code>_getMetaobject()</code>
3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * on a reflective object.  For example,
3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * <ul><pre>Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * </pre></ul>
4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @see javassist.tools.reflect.ClassMetaobject
4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @see javassist.tools.reflect.Metalevel
4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class Metaobject implements Serializable {
4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected ClassMetaobject classmetaobject;
4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected Metalevel baseobject;
4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected Method[] methods;
4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Constructs a <code>Metaobject</code>.  The metaobject is
5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * constructed before the constructor is called on the base-level
5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * object.
5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param self      the object that this metaobject is associated with.
5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param args      the parameters passed to the constructor of
5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *                  <code>self</code>.
5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public Metaobject(Object self, Object[] args) {
6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        baseobject = (Metalevel)self;
6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        classmetaobject = baseobject._getClass();
6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        methods = classmetaobject.getReflectiveMethods();
6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Constructs a <code>Metaobject</code> without initialization.
6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * If calling this constructor, a subclass should be responsible
6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * for initialization.
6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected Metaobject() {
7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        baseobject = null;
7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        classmetaobject = null;
7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        methods = null;
7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private void writeObject(ObjectOutputStream out) throws IOException {
7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        out.writeObject(baseobject);
7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private void readObject(ObjectInputStream in)
8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throws IOException, ClassNotFoundException
8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        baseobject = (Metalevel)in.readObject();
8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        classmetaobject = baseobject._getClass();
8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        methods = classmetaobject.getReflectiveMethods();
8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Obtains the class metaobject associated with this metaobject.
9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
9169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @see javassist.tools.reflect.ClassMetaobject
9269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
9369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final ClassMetaobject getClassMetaobject() {
9469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return classmetaobject;
9569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
9669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
9869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Obtains the object controlled by this metaobject.
9969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
10069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final Object getObject() {
10169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return baseobject;
10269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
10369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
10469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
10569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Changes the object controlled by this metaobject.
10669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
10769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param self      the object
10869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
10969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final void setObject(Object self) {
11069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        baseobject = (Metalevel)self;
11169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        classmetaobject = baseobject._getClass();
11269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        methods = classmetaobject.getReflectiveMethods();
11369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // call _setMetaobject() after the metaobject is settled.
11569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        baseobject._setMetaobject(this);
11669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
11769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
11969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns the name of the method specified
12069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * by <code>identifier</code>.
12169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
12269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final String getMethodName(int identifier) {
12369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        String mname = methods[identifier].getName();
12469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int j = ClassMetaobject.methodPrefixLen;
12569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (;;) {
12669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            char c = mname.charAt(j++);
12769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (c < '0' || '9' < c)
12869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                break;
12969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
13069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
13169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return mname.substring(j);
13269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
13369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
13469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
13569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns an array of <code>Class</code> objects representing the
13669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * formal parameter types of the method specified
13769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * by <code>identifier</code>.
13869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
13969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final Class[] getParameterTypes(int identifier) {
14069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return methods[identifier].getParameterTypes();
14169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
14269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
14369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
14469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns a <code>Class</code> objects representing the
14569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * return type of the method specified by <code>identifier</code>.
14669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
14769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final Class getReturnType(int identifier) {
14869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return methods[identifier].getReturnType();
14969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
15069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
15169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
15269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Is invoked when public fields of the base-level
15369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * class are read and the runtime system intercepts it.
15469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * This method simply returns the value of the field.
15569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
15669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>Every subclass of this class should redefine this method.
15769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
15869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public Object trapFieldRead(String name) {
15969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Class jc = getClassMetaobject().getJavaClass();
16069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
16169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return jc.getField(name).get(getObject());
16269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
16369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (NoSuchFieldException e) {
16469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e.toString());
16569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
16669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (IllegalAccessException e) {
16769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e.toString());
16869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
16969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
17069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
17169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
17269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Is invoked when public fields of the base-level
17369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * class are modified and the runtime system intercepts it.
17469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * This method simply sets the field to the given value.
17569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
17669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>Every subclass of this class should redefine this method.
17769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
17869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public void trapFieldWrite(String name, Object value) {
17969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Class jc = getClassMetaobject().getJavaClass();
18069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
18169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            jc.getField(name).set(getObject(), value);
18269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
18369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (NoSuchFieldException e) {
18469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e.toString());
18569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
18669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (IllegalAccessException e) {
18769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e.toString());
18869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
18969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
19069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
19169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
19269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Is invoked when base-level method invocation is intercepted.
19369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * This method simply executes the intercepted method invocation
19469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * with the original parameters and returns the resulting value.
19569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
19669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>Every subclass of this class should redefine this method.
19769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
19869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>Note: this method is not invoked if the base-level method
19969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * is invoked by a constructor in the super class.  For example,
20069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
20169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <ul><pre>abstract class A {
20269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *   abstract void initialize();
20369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *   A() {
20469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *       initialize();    // not intercepted
20569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *   }
20669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * }
20769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
20869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * class B extends A {
20969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *   void initialize() { System.out.println("initialize()"); }
21069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *   B() {
21169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *       super();
21269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *       initialize();    // intercepted
21369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *   }
21469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * }</pre></ul>
21569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
21669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <p>if an instance of B is created,
21769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * the invocation of initialize() in B is intercepted only once.
21869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The first invocation by the constructor in A is not intercepted.
21969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * This is because the link between a base-level object and a
22069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * metaobject is not created until the execution of a
22169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * constructor of the super class finishes.
22269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
22369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public Object trapMethodcall(int identifier, Object[] args)
22469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throws Throwable
22569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
22669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
22769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return methods[identifier].invoke(getObject(), args);
22869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
22969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (java.lang.reflect.InvocationTargetException e) {
23069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw e.getTargetException();
23169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
23269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (java.lang.IllegalAccessException e) {
23369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new CannotInvokeException(e);
23469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
23569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
23669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal}
237