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