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