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.type; 017 018import java.lang.reflect.Array; 019import java.util.ArrayList; 020import java.util.List; 021 022import org.ametys.plugins.repository.RepositoryConstants; 023import org.ametys.plugins.repository.data.UnknownDataException; 024import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 025import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 026import org.ametys.runtime.model.exception.BadItemTypeException; 027 028/** 029 * Interface for complex types of elements stored in the repository 030 * The complex types ares those which need nodes to be stored 031 * @param <T> Type of the element value 032 */ 033public interface ComplexRepositoryElementType<T> extends RepositoryElementType<T> 034{ 035 /** Data name for storing the type identifier of an element with multiple values*/ 036 public static String TYPE_ID_DATA_NAME = "typeId"; 037 038 @SuppressWarnings("unchecked") 039 public default Object read(RepositoryData parentData, String name) throws BadItemTypeException 040 { 041 if (!parentData.hasValue(name)) 042 { 043 return null; 044 } 045 046 if (!isCompatible(parentData, name)) 047 { 048 throw new BadItemTypeException("Try to get " + getId() + " value from the non '" + getId() + "' data '" + name + "' on '" + parentData + "'"); 049 } 050 051 if (parentData.isMultiple(name)) 052 { 053 RepositoryData multipleParentData = parentData.getRepositoryData(name); 054 List<T> results = new ArrayList<>(); 055 for (String singleDataName : multipleParentData.getDataNames()) 056 { 057 RepositoryData singleData = multipleParentData.getRepositoryData(singleDataName); 058 T singleValue = readSingleValue(singleData); 059 if (singleValue != null) 060 { 061 results.add(singleValue); 062 } 063 } 064 if (!results.isEmpty()) 065 { 066 return results.toArray((T[]) Array.newInstance(getManagedClass(), results.size())); 067 } 068 else 069 { 070 return null; 071 } 072 } 073 else 074 { 075 RepositoryData data = parentData.getRepositoryData(name); 076 return readSingleValue(data); 077 } 078 } 079 080 /** 081 * Read the single value in the given repository data 082 * @param singleValueData repository data containing the value 083 * @return the value. Can return null if the given data does not contain the necessary elements 084 */ 085 public T readSingleValue(RepositoryData singleValueData); 086 087 @SuppressWarnings("unchecked") 088 public default void write(ModifiableRepositoryData parentData, String name, Object value) throws BadItemTypeException 089 { 090 if (value == null) 091 { 092 if (parentData.hasValue(name)) 093 { 094 boolean isMultiple = parentData.isMultiple(name); 095 parentData.removeValue(name); 096 097 if (isMultiple) 098 { 099 ModifiableRepositoryData multipleParentData = parentData.addRepositoryData(name, RepositoryConstants.MULTIPLE_ITEM_NODETYPE); 100 // Add the type identifier in a property to know what the type of the data even if there is no value 101 multipleParentData.setValue(TYPE_ID_DATA_NAME, getId(), RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 102 } 103 else 104 { 105 writeSingleValue(parentData, name, null); 106 } 107 } 108 } 109 else if (getManagedClass().isInstance(value)) 110 { 111 if (parentData.hasValue(name) && removeValueBeforeWritingIt()) 112 { 113 parentData.removeValue(name); 114 } 115 116 writeSingleValue(parentData, name, (T) value); 117 } 118 else if (getManagedClassArray().isInstance(value)) 119 { 120 ModifiableRepositoryData multipleParentData; 121 122 if (parentData.hasValue(name) && !removeValueBeforeWritingIt()) 123 { 124 multipleParentData = parentData.getRepositoryData(name); 125 } 126 else 127 { 128 if (parentData.hasValue(name)) 129 { 130 parentData.removeValue(name); 131 } 132 133 multipleParentData = parentData.addRepositoryData(name, RepositoryConstants.MULTIPLE_ITEM_NODETYPE); 134 // Add the type identifier in a property to know what the type of the data even if there is no value 135 multipleParentData.setValue(TYPE_ID_DATA_NAME, getId(), RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 136 } 137 138 for (int i = 0; i < ((T[]) value).length; i++) 139 { 140 writeSingleValue(multipleParentData, String.valueOf(i + 1), ((T[]) value)[i]); 141 } 142 143 } 144 else 145 { 146 StringBuilder message = new StringBuilder().append("Try to set the non ").append(getId()).append(" value '").append(value); 147 message.append("' to the ").append(getId()).append(" data '").append(name).append("' on '").append(parentData).append("'"); 148 throw new BadItemTypeException(message.toString()); 149 } 150 } 151 152 /** 153 * Determines if the existing value has to be removed before writing the new one 154 * @return <code>true</code> if the previous value has to be removed, <code>false</code> otherwise 155 */ 156 public default boolean removeValueBeforeWritingIt() 157 { 158 return true; 159 } 160 161 /** 162 * Write the single value into the given repository data 163 * This method is called by the {@link #write(ModifiableRepositoryData, String, Object)} method, once for each value if the value is an array 164 * @param parentData repository where to store the single value. 165 * @param name the name of the element to write 166 * @param value the single value to write. Can be null. In this case, an empty data must be created 167 */ 168 public void writeSingleValue(ModifiableRepositoryData parentData, String name, T value); 169 170 public default boolean isCompatible(RepositoryData parentData, String name) throws UnknownDataException 171 { 172 if (getNodeType().equals(parentData.getType(name))) 173 { 174 return true; 175 } 176 else if (RepositoryConstants.MULTIPLE_ITEM_NODETYPE.equals(parentData.getType(name))) 177 { 178 RepositoryData multipleItemNode = parentData.getRepositoryData(name); 179 return getId().equals(multipleItemNode.getString(TYPE_ID_DATA_NAME, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)); 180 } 181 else 182 { 183 return false; 184 } 185 } 186 187 /** 188 * Retrieves the type of node used for this type 189 * @return the type of node used for this type 190 */ 191 public String getNodeType(); 192}