001 /*
002 * Copyright (C) 2003-2009 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019 package org.crsh.jcr.groovy;
020
021 import groovy.lang.Closure;
022 import groovy.lang.GroovySystem;
023 import groovy.lang.MetaClassImpl;
024 import groovy.lang.MetaClassRegistry;
025 import groovy.lang.MetaMethod;
026 import groovy.lang.MetaProperty;
027 import groovy.lang.MissingMethodException;
028 import groovy.lang.MissingPropertyException;
029 import org.crsh.jcr.JCRUtils;
030 import org.crsh.jcr.PropertyType;
031
032 import javax.jcr.Node;
033 import javax.jcr.NodeIterator;
034 import javax.jcr.PathNotFoundException;
035 import javax.jcr.Property;
036 import javax.jcr.PropertyIterator;
037 import javax.jcr.RepositoryException;
038 import javax.jcr.Value;
039 import java.beans.IntrospectionException;
040
041 /**
042 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
043 * @version $Revision$
044 */
045 public class NodeMetaClass extends MetaClassImpl {
046
047 public static void setup() {
048
049 }
050
051 public NodeMetaClass(MetaClassRegistry registry, Class<? extends Node> theClass) throws IntrospectionException {
052 super(registry, theClass);
053 }
054
055 @Override
056 public Object invokeMethod(Object object, String name, Object[] args) {
057 try {
058 return _invokeMethod(object, name, args);
059 }
060 catch (RepositoryException e) {
061 // Do that better
062 throw new Error(e);
063 }
064 }
065
066 private Object _invokeMethod(Object object, String name, Object[] args) throws RepositoryException {
067 Node node = (Node)object;
068
069 //
070 if (args != null) {
071 if (args.length == 0) {
072 if ("iterator".equals(name)) {
073 return node.getNodes();
074 }
075 }
076 else if (args.length == 1) {
077 Object arg = args[0];
078
079 // This is the trick we need to use because the javax.jcr.Node interface
080 // has a getProperty(String name) method that is shadowed by the GroovyObject
081 // method with the same signature.
082 if (arg instanceof String && "getProperty".equals(name)) {
083 String propertyName = (String)arg;
084 return JCRUtils.getProperty(node, propertyName);
085 }
086 else if (arg instanceof Closure) {
087 Closure closure = (Closure)arg;
088 if ("eachProperty".equals(name)) {
089 PropertyIterator properties = node.getProperties();
090 while (properties.hasNext()) {
091 Property n = properties.nextProperty();
092 closure.call(new Object[]{n});
093 }
094 return null;
095 }/* else if ("eachWithIndex".equals(name)) {
096 NodeIterator nodes = node.getNodes();
097 int index = 0;
098 while (nodes.hasNext()) {
099 Node n = nodes.nextNode();
100 closure.call(new Object[]{n,index++});
101 }
102 return null;
103 }*/
104 }
105 else if ("getAt".equals(name)) {
106 if (arg instanceof Integer) {
107 NodeIterator it = node.getNodes();
108 long size = it.getSize();
109 long index = (Integer)arg;
110
111 // Bounds detection
112 if (index < 0) {
113 if (index < -size) throw new ArrayIndexOutOfBoundsException((int)index);
114 index = size + index;
115 }
116 else if (index >= size) throw new ArrayIndexOutOfBoundsException((int)index);
117
118 //
119 it.skip(index);
120 return it.next();
121 }
122 }
123 }
124 }
125
126 // We let groovy handle the call
127 MetaMethod validMethod = super.getMetaMethod(name, args);
128 if (validMethod != null) {
129 return validMethod.invoke(node, args);
130 }
131
132 //
133 throw new MissingMethodException(name, Node.class, args);
134 }
135
136 @Override
137 public Object getProperty(Object object, String property) {
138 try {
139 return _getProperty(object, property);
140 }
141 catch (RepositoryException e) {
142 throw new Error(e);
143 }
144 }
145
146 private Object _getProperty(Object object, String propertyName) throws RepositoryException {
147 Node node = (Node)object;
148
149 // Access defined properties
150 MetaProperty metaProperty = super.getMetaProperty(propertyName);
151 if (metaProperty != null) {
152 return metaProperty.getProperty(node);
153 }
154
155 // First we try to access a property
156 try {
157 Property property = node.getProperty(propertyName);
158 PropertyType type = PropertyType.fromValue(property.getType());
159 return type.get(property);
160 }
161 catch (PathNotFoundException e) {
162 }
163
164 // If we don't find it as a property we try it as a child node
165 try {
166 return node.getNode(propertyName);
167 }
168 catch (PathNotFoundException e) {
169 }
170
171 //
172 return null;
173 }
174
175 @Override
176 public void setProperty(Object object, String property, Object newValue) {
177 try {
178 _setProperty(object, property, newValue);
179 }
180 catch (Exception e) {
181 throw new Error(e);
182 }
183 }
184
185 private void _setProperty(Object object, String propertyName, Object propertyValue) throws RepositoryException {
186 Node node = (Node)object;
187 if (propertyValue == null) {
188 node.setProperty(propertyName, (Value)null);
189 } else {
190
191 // Get property type
192 PropertyType type;
193 try {
194 Property property = node.getProperty(propertyName);
195 type = PropertyType.fromValue(property.getType());
196 } catch (PathNotFoundException e) {
197 type = PropertyType.fromCanonicalType(propertyValue.getClass());
198 }
199
200 // Update the property and get the updated property
201 Property property;
202 if (type != null) {
203 property = type.set(node, propertyName, propertyValue);
204 } else {
205 property = null;
206 }
207
208 //
209 if (property == null && propertyValue instanceof String) {
210 if (propertyValue instanceof String) {
211 // This is likely a conversion from String that should be handled natively by JCR itself
212 node.setProperty(propertyName, (String)propertyValue);
213 } else {
214 throw new MissingPropertyException("Property " + propertyName + " does not have a correct type " + propertyValue.getClass().getName());
215 }
216 }
217 }
218 }
219 }