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;
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
030 import javax.jcr.Node;
031 import javax.jcr.NodeIterator;
032 import javax.jcr.PathNotFoundException;
033 import javax.jcr.Property;
034 import javax.jcr.PropertyIterator;
035 import javax.jcr.RepositoryException;
036 import javax.jcr.Value;
037 import java.beans.IntrospectionException;
038
039 /**
040 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
041 * @version $Revision$
042 */
043 public class NodeMetaClass extends MetaClassImpl {
044
045 static {
046 try {
047 MetaClassRegistry registry = GroovySystem.getMetaClassRegistry();
048 Class<? extends Node> eXoNode = (Class<Node>)Thread.currentThread().getContextClassLoader().loadClass("org.exoplatform.services.jcr.impl.core.NodeImpl");
049 NodeMetaClass mc2 = new NodeMetaClass(registry, eXoNode);
050 mc2.initialize();
051 registry.setMetaClass(eXoNode, mc2);
052 }
053 catch (Exception e) {
054 throw new Error("Coult not integrate node meta class");
055 }
056 }
057
058 public static void setup() {
059
060 }
061
062 public NodeMetaClass(MetaClassRegistry registry, Class<? extends Node> theClass) throws IntrospectionException {
063 super(registry, theClass);
064 }
065
066 @Override
067 public Object invokeMethod(Object object, String name, Object[] args) {
068 try {
069 return _invokeMethod(object, name, args);
070 }
071 catch (RepositoryException e) {
072 // Do that better
073 throw new Error(e);
074 }
075 }
076
077 private Object _invokeMethod(Object object, String name, Object[] args) throws RepositoryException {
078 Node node = (Node)object;
079
080 //
081 if (args != null) {
082 if (args.length == 0) {
083 if ("iterator".equals(name)) {
084 return node.getNodes();
085 }
086 }
087 else if (args.length == 1) {
088 Object arg = args[0];
089
090 // This is the trick we need to use because the javax.jcr.Node interface
091 // has a getProperty(String name) method that is shadowed by the GroovyObject
092 // method with the same signature.
093 if (arg instanceof String && "getProperty".equals(name)) {
094 String propertyName = (String)arg;
095 return JCRUtils.getProperty(node, propertyName);
096 }
097 else if (arg instanceof Closure) {
098 Closure closure = (Closure)arg;
099 if ("eachProperty".equals(name)) {
100 PropertyIterator properties = node.getProperties();
101 while (properties.hasNext()) {
102 Property n = properties.nextProperty();
103 closure.call(new Object[]{n});
104 }
105 return null;
106 }/* else if ("eachWithIndex".equals(name)) {
107 NodeIterator nodes = node.getNodes();
108 int index = 0;
109 while (nodes.hasNext()) {
110 Node n = nodes.nextNode();
111 closure.call(new Object[]{n,index++});
112 }
113 return null;
114 }*/
115 }
116 else if ("getAt".equals(name)) {
117 if (arg instanceof Integer) {
118 NodeIterator it = node.getNodes();
119 long size = it.getSize();
120 long index = (Integer)arg;
121
122 // Bounds detection
123 if (index < 0) {
124 if (index < -size) throw new ArrayIndexOutOfBoundsException((int)index);
125 index = size + index;
126 }
127 else if (index >= size) throw new ArrayIndexOutOfBoundsException((int)index);
128
129 //
130 it.skip(index);
131 return it.next();
132 }
133 }
134 }
135 }
136
137 // We let groovy handle the call
138 MetaMethod validMethod = super.getMetaMethod(name, args);
139 if (validMethod != null) {
140 return validMethod.invoke(node, args);
141 }
142
143 //
144 throw new MissingMethodException(name, Node.class, args);
145 }
146
147 @Override
148 public Object getProperty(Object object, String property) {
149 try {
150 return _getProperty(object, property);
151 }
152 catch (RepositoryException e) {
153 throw new Error(e);
154 }
155 }
156
157 private Object _getProperty(Object object, String propertyName) throws RepositoryException {
158 Node node = (Node)object;
159
160 // Access defined properties
161 MetaProperty metaProperty = super.getMetaProperty(propertyName);
162 if (metaProperty != null) {
163 return metaProperty.getProperty(node);
164 }
165
166 // First we try to access a property
167 try {
168 Property property = node.getProperty(propertyName);
169 PropertyType type = PropertyType.fromValue(property.getType());
170 return type.getValue(property);
171 }
172 catch (PathNotFoundException e) {
173 }
174
175 // If we don't find it as a property we try it as a child node
176 try {
177 return node.getNode(propertyName);
178 }
179 catch (PathNotFoundException e) {
180 }
181
182 //
183 return null;
184 }
185
186 @Override
187 public void setProperty(Object object, String property, Object newValue) {
188 try {
189 _setProperty(object, property, newValue);
190 }
191 catch (Exception e) {
192 throw new Error(e);
193 }
194 }
195
196 private void _setProperty(Object object, String propertyName, Object propertyValue) throws RepositoryException {
197 Node node = (Node)object;
198 if (propertyValue == null) {
199 node.setProperty(propertyName, (Value)null);
200 } else {
201
202 // Get property type
203 PropertyType type;
204 try {
205 Property property = node.getProperty(propertyName);
206 type = PropertyType.fromValue(property.getType());
207 } catch (PathNotFoundException e) {
208 type = PropertyType.fromCanonicalType(propertyValue.getClass());
209 }
210
211 // Update the property and get the updated property
212 Property property;
213 if (type != null) {
214 property = type.set(node, propertyName, propertyValue);
215 } else {
216 property = null;
217 }
218
219 //
220 if (property == null && propertyValue instanceof String) {
221 if (propertyValue instanceof String) {
222 // This is likely a conversion from String that should be handled natively by JCR itself
223 node.setProperty(propertyName, (String)propertyValue);
224 } else {
225 throw new MissingPropertyException("Property " + propertyName + " does not have a correct type " + propertyValue.getClass().getName());
226 }
227 }
228 }
229 }
230 }