001/* 002 * Copyright 2019 Anyware Services 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 */ 016package org.ametys.plugins.repository.data.repositorydata.impl; 017 018import java.io.InputStream; 019import java.util.Calendar; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.Set; 023import java.util.stream.Collectors; 024 025import org.apache.commons.lang3.StringUtils; 026import org.apache.commons.lang3.tuple.ImmutablePair; 027import org.apache.commons.lang3.tuple.Pair; 028 029import org.ametys.plugins.repository.AmetysRepositoryException; 030import org.ametys.plugins.repository.RepositoryConstants; 031import org.ametys.plugins.repository.data.UnknownDataException; 032import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 033import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 034 035/** 036 * Class for data values management in memory 037 */ 038public class MemoryRepositoryData implements ModifiableRepositoryData 039{ 040 private String _fullName; 041 private Map<String, Pair<String, Object>> _data; 042 private String _defaultPrefix; 043 044 /** 045 * Creates a memory repository data with the given name 046 * @param name the name of the current data 047 */ 048 public MemoryRepositoryData(String name) 049 { 050 this(name, RepositoryConstants.NAMESPACE_PREFIX); 051 } 052 053 /** 054 * Creates a memory repository data with the given name 055 * @param name the name of the current data 056 * @param defaultPrefix prefix to use for properties and child nodes 057 */ 058 public MemoryRepositoryData(String name, String defaultPrefix) 059 { 060 _fullName = _getFullName(name, defaultPrefix); 061 _defaultPrefix = defaultPrefix; 062 _data = new HashMap<>(); 063 } 064 065 public String getString(String name, String prefix) 066 { 067 if (hasValue(name, prefix)) 068 { 069 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 070 if (RepositoryData.STRING_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof String) 071 { 072 return (String) value.getRight(); 073 } 074 else 075 { 076 throw new AmetysRepositoryException("Unable to get the value of string data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 077 } 078 } 079 else 080 { 081 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 082 } 083 } 084 085 public String[] getStrings(String name, String prefix) 086 { 087 if (hasValue(name, prefix)) 088 { 089 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 090 if (RepositoryData.STRING_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof String[]) 091 { 092 return (String[]) value.getRight(); 093 } 094 else 095 { 096 throw new AmetysRepositoryException("Unable to get the value of multiple string data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 097 } 098 } 099 else 100 { 101 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 102 } 103 } 104 105 public Calendar getDate(String name, String prefix) 106 { 107 if (hasValue(name, prefix)) 108 { 109 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 110 if (RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Calendar) 111 { 112 return (Calendar) value.getRight(); 113 } 114 else 115 { 116 throw new AmetysRepositoryException("Unable to get the value of date data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 117 } 118 } 119 else 120 { 121 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 122 } 123 } 124 125 public Calendar[] getDates(String name, String prefix) 126 { 127 if (hasValue(name, prefix)) 128 { 129 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 130 if (RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Calendar[]) 131 { 132 return (Calendar[]) value.getRight(); 133 } 134 else 135 { 136 throw new AmetysRepositoryException("Unable to get the value of multiple date data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 137 } 138 } 139 else 140 { 141 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 142 } 143 } 144 145 public Long getLong(String name, String prefix) 146 { 147 if (hasValue(name, prefix)) 148 { 149 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 150 if (RepositoryData.LONG_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Long) 151 { 152 return (Long) value.getRight(); 153 } 154 else 155 { 156 throw new AmetysRepositoryException("Unable to get the value of long data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 157 } 158 } 159 else 160 { 161 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 162 } 163 } 164 165 public Long[] getLongs(String name, String prefix) 166 { 167 if (hasValue(name, prefix)) 168 { 169 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 170 if (RepositoryData.LONG_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Long[]) 171 { 172 return (Long[]) value.getRight(); 173 } 174 else 175 { 176 throw new AmetysRepositoryException("Unable to get the value of multiple long data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 177 } 178 } 179 else 180 { 181 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 182 } 183 } 184 185 public Double getDouble(String name, String prefix) 186 { 187 if (hasValue(name, prefix)) 188 { 189 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 190 if (RepositoryData.DOUBLE_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Double) 191 { 192 return (Double) value.getRight(); 193 } 194 else 195 { 196 throw new AmetysRepositoryException("Unable to get the value of double data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 197 } 198 } 199 else 200 { 201 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 202 } 203 } 204 205 public Double[] getDoubles(String name, String prefix) 206 { 207 if (hasValue(name, prefix)) 208 { 209 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 210 if (RepositoryData.DOUBLE_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Double[]) 211 { 212 return (Double[]) value.getRight(); 213 } 214 else 215 { 216 throw new AmetysRepositoryException("Unable to get the value of multiple double data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 217 } 218 } 219 else 220 { 221 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 222 } 223 } 224 225 public Boolean getBoolean(String name, String prefix) 226 { 227 if (hasValue(name, prefix)) 228 { 229 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 230 if (RepositoryData.BOOLEAN_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Boolean) 231 { 232 return (Boolean) value.getRight(); 233 } 234 else 235 { 236 throw new AmetysRepositoryException("Unable to get the value of boolean data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 237 } 238 } 239 else 240 { 241 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 242 } 243 } 244 245 public Boolean[] getBooleans(String name, String prefix) 246 { 247 if (hasValue(name, prefix)) 248 { 249 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 250 if (RepositoryData.BOOLEAN_REPOSITORY_DATA_TYPE.equals(value.getLeft()) && value.getRight() instanceof Boolean[]) 251 { 252 return (Boolean[]) value.getRight(); 253 } 254 else 255 { 256 throw new AmetysRepositoryException("Unable to get the value of multiple boolean data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 257 } 258 } 259 else 260 { 261 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 262 } 263 } 264 265 public ModifiableRepositoryData getRepositoryData(String name, String prefix) 266 { 267 if (hasValue(name, prefix)) 268 { 269 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 270 if (value.getRight() instanceof ModifiableRepositoryData) 271 { 272 return (ModifiableRepositoryData) value.getRight(); 273 } 274 else 275 { 276 throw new AmetysRepositoryException("Unable to get the value of stream data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 277 } 278 } 279 else 280 { 281 throw new UnknownDataException("Unknown data '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 282 } 283 } 284 285 public Set<String> getDataNames(String prefix) 286 { 287 final String finalPrefix; 288 if (prefix == null) 289 { 290 finalPrefix = "*:"; 291 } 292 else if (StringUtils.isEmpty(prefix)) 293 { 294 finalPrefix = prefix; 295 } 296 else 297 { 298 finalPrefix = prefix + ":"; 299 } 300 301 return _data.keySet().stream() 302 .filter(name -> name.startsWith(finalPrefix)) 303 .map(name -> name.substring(finalPrefix.length())) 304 .collect(Collectors.toSet()); 305 } 306 307 public String getFullName() 308 { 309 return _fullName; 310 } 311 312 public boolean hasValue(String name, String prefix) 313 { 314 return _data.containsKey(_getFullName(name, prefix)); 315 } 316 317 public String getType(String name, String prefix) 318 { 319 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 320 if (value != null) 321 { 322 return value.getLeft(); 323 } 324 else 325 { 326 throw new UnknownDataException("No data found for '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 327 } 328 } 329 330 public boolean isMultiple(String name, String prefix) 331 { 332 if (hasValue(name, prefix)) 333 { 334 Pair<String, Object> value = _data.get(_getFullName(name, prefix)); 335 return RepositoryConstants.MULTIPLE_ITEM_NODETYPE.equals(value.getLeft()) || value.getRight().getClass().isArray(); 336 } 337 else 338 { 339 throw new UnknownDataException("No data found for '" + prefix + ":" + name + "' in repository data named '" + _fullName + "'."); 340 } 341 } 342 343 public String getDefaultPrefix() 344 { 345 return _defaultPrefix; 346 } 347 348 public ModifiableRepositoryData addRepositoryData(String name, String dataTypeName, String prefix) 349 { 350 ModifiableRepositoryData repositoryData = new MemoryRepositoryData(name, prefix); 351 352 // Has value 353 if (hasValue(name, prefix)) 354 { 355 // Remove existing value to avoid values with same name (node + property) 356 removeValue(name, prefix); 357 } 358 359 _data.put(_getFullName(name, prefix), new ImmutablePair<>(dataTypeName, repositoryData)); 360 361 return repositoryData; 362 } 363 364 public void rename(String newName, String prefix) 365 { 366 _fullName = _getFullName(newName, prefix); 367 } 368 369 public void setValue(String name, String value, String prefix) 370 { 371 _setValue(name, RepositoryData.STRING_REPOSITORY_DATA_TYPE, value, prefix); 372 } 373 374 public void setValues(String name, String[] values, String prefix) 375 { 376 _setValue(name, RepositoryData.STRING_REPOSITORY_DATA_TYPE, values, prefix); 377 } 378 379 public void setValue(String name, Calendar value, String prefix) 380 { 381 _setValue(name, RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE, value, prefix); 382 } 383 384 public void setValues(String name, Calendar[] values, String prefix) 385 { 386 _setValue(name, RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE, values, prefix); 387 } 388 389 public void setValue(String name, Long value, String prefix) 390 { 391 _setValue(name, RepositoryData.LONG_REPOSITORY_DATA_TYPE, value, prefix); 392 } 393 394 public void setValues(String name, Long[] values, String prefix) 395 { 396 _setValue(name, RepositoryData.LONG_REPOSITORY_DATA_TYPE, values, prefix); 397 } 398 399 public void setValue(String name, Double value, String prefix) 400 { 401 _setValue(name, RepositoryData.DOUBLE_REPOSITORY_DATA_TYPE, value, prefix); 402 } 403 404 public void setValues(String name, Double[] values, String prefix) 405 { 406 _setValue(name, RepositoryData.DOUBLE_REPOSITORY_DATA_TYPE, values, prefix); 407 } 408 409 public void setValue(String name, Boolean value, String prefix) 410 { 411 _setValue(name, RepositoryData.BOOLEAN_REPOSITORY_DATA_TYPE, value, prefix); 412 } 413 414 public void setValues(String name, Boolean[] values, String prefix) 415 { 416 _setValue(name, RepositoryData.BOOLEAN_REPOSITORY_DATA_TYPE, values, prefix); 417 } 418 419 private void _setValue(String name, String dataTypeName, Object value, String prefix) 420 { 421 String fullName = _getFullName(name, prefix); 422 423 if (value == null) 424 { 425 throw new NullPointerException("Unable to set a 'null' data for '" + fullName + "' in repository data named '" + _fullName + "'."); 426 } 427 428 _checkDataName(name); 429 430 // Has value 431 if (hasValue(name, prefix)) 432 { 433 // Remove existing value to avoid values with same name (node + property) 434 removeValue(name, prefix); 435 } 436 437 _data.put(fullName, new ImmutablePair<>(dataTypeName, value)); 438 } 439 440 private void _checkDataName(String name) 441 { 442 // Name null 443 if (name == null) 444 { 445 throw new AmetysRepositoryException("Unable to set a value with the invalid data name (null) in repository data named '" + _fullName + "'."); 446 } 447 448 // Valid name 449 if (!DATA_NAME_PATTERN.matcher(name).matches()) 450 { 451 throw new AmetysRepositoryException("Unable to set a value with the invalid data name '" + name + "'. Only [a-zA-Z][a-zA-Z0-9-_]* are allowed."); 452 } 453 } 454 455 public void removeValue(String name, String prefix) 456 { 457 if (hasValue(name, prefix)) 458 { 459 _data.remove(_getFullName(name, prefix)); 460 } 461 else 462 { 463 throw new UnknownDataException("No data found for '" + _getFullName(name, prefix) + "' in repository data named '" + _fullName + "'."); 464 } 465 } 466 467 private String _getFullName(String name, String prefix) 468 { 469 return StringUtils.isEmpty(prefix) ? name : prefix + ":" + name; 470 } 471 472 public InputStream getStream(String name, String prefix) 473 { 474 throw new UnsupportedOperationException("Streams are not supported by the memory repository data"); 475 } 476 477 public Long getStreamLength(String name, String prefix) 478 { 479 throw new UnsupportedOperationException("Streams are not supported by the memory repository data"); 480 } 481 482 public InputStream[] getStreams(String name, String prefix) 483 { 484 throw new UnsupportedOperationException("Streams are not supported by the memory repository data"); 485 } 486 487 public void setValue(String name, InputStream value, String prefix) 488 { 489 throw new UnsupportedOperationException("Streams are not supported by the memory repository data"); 490 } 491 492 public void setValues(String name, InputStream[] values, String prefix) 493 { 494 throw new UnsupportedOperationException("Streams are not supported by the memory repository data"); 495 } 496 497 public String getPath() 498 { 499 throw new UnsupportedOperationException("Paths are not supported by the memory repository data"); 500 } 501}