001/* 002 * (C) Copyright 2012-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * vpasquier <vpasquier@nuxeo.com> 019 * slacoin <slacoin@nuxeo.com> 020 */ 021package org.nuxeo.ecm.automation.core; 022 023import org.apache.commons.lang.StringEscapeUtils; 024import org.nuxeo.common.utils.StringUtils; 025import org.nuxeo.common.xmap.annotation.XContent; 026import org.nuxeo.common.xmap.annotation.XNode; 027import org.nuxeo.common.xmap.annotation.XNodeList; 028import org.nuxeo.common.xmap.annotation.XNodeMap; 029import org.nuxeo.common.xmap.annotation.XObject; 030import org.nuxeo.ecm.automation.OperationChain; 031import org.nuxeo.ecm.automation.OperationDocumentation; 032import org.nuxeo.ecm.automation.OperationException; 033import org.nuxeo.ecm.automation.OperationParameters; 034import org.nuxeo.ecm.automation.core.impl.adapters.StringToDocRef; 035import org.nuxeo.ecm.automation.core.scripting.Scripting; 036import org.nuxeo.ecm.automation.core.util.Properties; 037import org.nuxeo.ecm.core.api.impl.DocumentRefListImpl; 038import org.nuxeo.ecm.core.schema.utils.DateParser; 039import org.osgi.framework.Bundle; 040 041import java.io.IOException; 042import java.net.MalformedURLException; 043import java.net.URL; 044import java.util.ArrayList; 045import java.util.HashMap; 046import java.util.Map; 047 048import static org.nuxeo.ecm.automation.core.Constants.T_BOOLEAN; 049import static org.nuxeo.ecm.automation.core.Constants.T_DATE; 050import static org.nuxeo.ecm.automation.core.Constants.T_DOCUMENT; 051import static org.nuxeo.ecm.automation.core.Constants.T_DOCUMENTS; 052import static org.nuxeo.ecm.automation.core.Constants.T_FLOAT; 053import static org.nuxeo.ecm.automation.core.Constants.T_INTEGER; 054import static org.nuxeo.ecm.automation.core.Constants.T_LONG; 055import static org.nuxeo.ecm.automation.core.Constants.T_PROPERTIES; 056import static org.nuxeo.ecm.automation.core.Constants.T_RESOURCE; 057import static org.nuxeo.ecm.automation.core.Constants.T_STRING; 058 059/** 060 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 061 */ 062@XObject("chain") 063public class OperationChainContribution { 064 065 @XNode("@id") 066 protected String id; 067 068 @XNode("@replace") 069 protected boolean replace = true; 070 071 @XNode("description") 072 protected String description; 073 074 @XNodeList(value = "operation", type = Operation[].class, componentType = Operation.class) 075 protected Operation[] ops = new Operation[0]; 076 077 @XNode("public") 078 protected boolean isPublic = true; 079 080 @XNodeList(value = "param", type = OperationDocumentation.Param[].class, componentType = OperationDocumentation.Param.class) 081 protected OperationDocumentation.Param[] params = new OperationDocumentation.Param[0]; 082 083 /** 084 * @since 7.1 085 */ 086 @XNodeList(value = "aliases/alias", type = String[].class, componentType = String.class) 087 protected String[] aliases; 088 089 @XObject("operation") 090 public static class Operation { 091 @XNode("@id") 092 protected String id; 093 094 @XNodeList(value = "param", type = ArrayList.class, componentType = Param.class) 095 protected ArrayList<Param> params; 096 097 public String getId() { 098 return id; 099 } 100 101 public ArrayList<Param> getParams() { 102 return params; 103 } 104 } 105 106 @XObject("param") 107 public static class Param { 108 @XNode("@name") 109 protected String name; 110 111 // string, boolean, date, integer, float, uid, path, expression, 112 // template, resource 113 @XNode("@type") 114 protected String type = "string"; 115 116 // why not XNode here? XContent requires to unescape XML entities, see 117 // below 118 @XContent 119 protected String value; 120 121 // Optional map for properties type values 122 @XNodeMap(value = "property", key = "@key", type = HashMap.class, componentType = String.class, nullByDefault = true) 123 protected Map<String, String> map; 124 125 public String getName() { 126 return name; 127 } 128 129 public String getValue() { 130 return value; 131 } 132 133 public String getType() { 134 return type; 135 } 136 137 public Map<String, String> getMap() { 138 return map; 139 } 140 } 141 142 public OperationDocumentation.Param[] getParams() { 143 return params; 144 } 145 146 public String getId() { 147 return id; 148 } 149 150 public OperationChain toOperationChain(Bundle bundle) throws OperationException { 151 OperationChain chain = new OperationChain(id); 152 chain.setDescription(description); 153 chain.setPublic(isPublic); 154 chain.setAliases(aliases); 155 for (Operation op : ops) { 156 OperationParameters params = chain.add(op.id); 157 for (Param param : op.params) { 158 param.value = param.value.trim(); 159 // decode XML entities in every case 160 param.value = StringEscapeUtils.unescapeXml(param.value); 161 if (param.value.startsWith("expr:")) { 162 param.value = param.value.substring(5); 163 if (param.value.contains("@{")) { 164 params.set(param.name, Scripting.newTemplate(param.value)); 165 } else { 166 params.set(param.name, Scripting.newExpression(param.value)); 167 } 168 } else { 169 Object val = null; 170 String type = param.type.toLowerCase(); 171 char c = type.charAt(0); 172 switch (c) { 173 case 's': // string 174 if (T_STRING.equals(type)) { 175 val = param.value; 176 } 177 break; 178 case 'p': 179 if (T_PROPERTIES.equals(type)) { 180 if (param.map != null && !param.map.isEmpty()) { 181 val = new Properties(param.map); 182 } else { 183 try { 184 val = new Properties(param.value); 185 } catch (IOException e) { 186 throw new OperationException(e); 187 } 188 } 189 } 190 break; 191 case 'i': 192 if (T_INTEGER.equals(type)) { 193 val = Integer.parseInt(param.value); 194 } 195 break; 196 case 'l': 197 if (T_LONG.equals(type)) { 198 val = Long.valueOf(param.value); 199 } 200 break; 201 case 'b': 202 if (T_BOOLEAN.equals(type)) { 203 val = Boolean.valueOf(param.value); 204 } 205 break; 206 case 'd': 207 if (T_DOCUMENT.equals(type)) { 208 if (param.value.startsWith(".")) { 209 val = Scripting.newExpression("Document" + ".resolvePathAsRef(\"" + param.value + "\")"); 210 } else { 211 val = StringToDocRef.createRef(param.value); 212 } 213 } else if (T_DOCUMENTS.equals(type)) { 214 String[] ar = StringUtils.split(param.value, ',', true); 215 DocumentRefListImpl result = new DocumentRefListImpl(ar.length); 216 for (String ref : ar) { 217 result.add(StringToDocRef.createRef(ref)); 218 } 219 val = result; 220 } else if (T_DATE.equals(type)) { 221 val = DateParser.parseW3CDateTime(param.value); 222 } 223 break; 224 case 'f': 225 if (T_FLOAT.equals(type)) { 226 val = Double.valueOf(param.value); 227 } 228 break; 229 case 'r': 230 if (T_RESOURCE.equals(type)) { 231 if (param.value.contains(":/")) { // a real URL 232 try { 233 val = new URL(param.value); 234 } catch (MalformedURLException e) { 235 throw new OperationException(e); 236 } 237 } else { // try with class loader 238 val = bundle.getEntry(param.value); 239 } 240 } 241 break; 242 } 243 if (val == null) { 244 val = param.value; 245 } 246 params.set(param.name, val); 247 } 248 } 249 } 250 return chain; 251 } 252 253 public Operation[] getOps() { 254 return ops; 255 } 256 257 public String getLabel() { 258 return id; 259 } 260 261 public String getRequires() { 262 return ""; 263 } 264 265 public String getCategory() { 266 return Constants.CAT_CHAIN; 267 } 268 269 public String getSince() { 270 return ""; 271 } 272 273 public String getDescription() { 274 return description; 275 } 276 277 public String[] getAliases() { 278 return aliases; 279 } 280}