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