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 }