Replace copy-paste with the correct library
This commit is contained in:
		
							parent
							
								
									d713ee62b9
								
							
						
					
					
						commit
						0c1e5393df
					
				@ -230,6 +230,10 @@
 | 
			
		||||
            <groupId>org.thingsboard.rule-engine</groupId>
 | 
			
		||||
            <artifactId>rule-engine-api</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>io.hypersistence</groupId>
 | 
			
		||||
            <artifactId>hypersistence-utils-hibernate-55</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
    <build>
 | 
			
		||||
        <plugins>
 | 
			
		||||
 | 
			
		||||
@ -16,17 +16,16 @@
 | 
			
		||||
package org.thingsboard.server.dao.model.sql;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import io.hypersistence.utils.hibernate.type.array.StringArrayType;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.hibernate.annotations.Type;
 | 
			
		||||
import org.hibernate.annotations.TypeDef;
 | 
			
		||||
import org.thingsboard.server.common.data.id.WidgetTypeId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.WidgetsBundleId;
 | 
			
		||||
import org.thingsboard.server.common.data.widget.BaseWidgetType;
 | 
			
		||||
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
 | 
			
		||||
import org.thingsboard.server.dao.model.ModelConstants;
 | 
			
		||||
import org.thingsboard.server.dao.util.mapping.JsonStringType;
 | 
			
		||||
import org.thingsboard.server.dao.util.mapping.StringArrayType;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.Column;
 | 
			
		||||
import javax.persistence.Entity;
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.model.sql;
 | 
			
		||||
 | 
			
		||||
import io.hypersistence.utils.hibernate.type.array.StringArrayType;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.hibernate.annotations.Immutable;
 | 
			
		||||
@ -23,7 +24,6 @@ import org.hibernate.annotations.TypeDef;
 | 
			
		||||
import org.thingsboard.server.common.data.widget.BaseWidgetType;
 | 
			
		||||
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
 | 
			
		||||
import org.thingsboard.server.dao.model.ModelConstants;
 | 
			
		||||
import org.thingsboard.server.dao.util.mapping.StringArrayType;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.Column;
 | 
			
		||||
import javax.persistence.Entity;
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
 | 
			
		||||
                    // "OR to_tsvector(lower(array_to_string(wti.tags, ' '))) @@ to_tsquery(lower(:searchText)))))",
 | 
			
		||||
            countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id = :systemTenantId " +
 | 
			
		||||
                    "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " +
 | 
			
		||||
                    "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes) " +
 | 
			
		||||
                    "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " +
 | 
			
		||||
                    "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " +
 | 
			
		||||
                    "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " +
 | 
			
		||||
                    "OR lower(wti.tags\\:\\:text)\\:\\:text[] && string_to_array(lower(:searchText), ' '))))"
 | 
			
		||||
 | 
			
		||||
@ -1,45 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
 | 
			
		||||
import org.hibernate.usertype.DynamicParameterizedType;
 | 
			
		||||
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractArrayType<T>
 | 
			
		||||
        extends AbstractSingleColumnStandardBasicType<T>
 | 
			
		||||
        implements DynamicParameterizedType {
 | 
			
		||||
 | 
			
		||||
    public static final String SQL_ARRAY_TYPE = "sql_array_type";
 | 
			
		||||
 | 
			
		||||
    public AbstractArrayType(AbstractArrayTypeDescriptor<T> arrayTypeDescriptor) {
 | 
			
		||||
        super(
 | 
			
		||||
                ArraySqlTypeDescriptor.INSTANCE,
 | 
			
		||||
                arrayTypeDescriptor
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean registerUnderJavaType() {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setParameterValues(Properties parameters) {
 | 
			
		||||
        ((AbstractArrayTypeDescriptor) getJavaTypeDescriptor()).setParameterValues(parameters);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,119 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
import org.hibernate.HibernateException;
 | 
			
		||||
import org.hibernate.type.descriptor.WrapperOptions;
 | 
			
		||||
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
 | 
			
		||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
 | 
			
		||||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
 | 
			
		||||
import org.hibernate.usertype.DynamicParameterizedType;
 | 
			
		||||
 | 
			
		||||
import java.sql.Array;
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.dao.util.mapping.AbstractArrayType.SQL_ARRAY_TYPE;
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractArrayTypeDescriptor<T>
 | 
			
		||||
        extends AbstractTypeDescriptor<T> implements DynamicParameterizedType {
 | 
			
		||||
 | 
			
		||||
    private Class<T> arrayObjectClass;
 | 
			
		||||
 | 
			
		||||
    private String sqlArrayType;
 | 
			
		||||
 | 
			
		||||
    public AbstractArrayTypeDescriptor(Class<T> arrayObjectClass) {
 | 
			
		||||
        this(arrayObjectClass, (MutabilityPlan<T>) new MutableMutabilityPlan<Object>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected T deepCopyNotNull(Object value) {
 | 
			
		||||
                return ArrayUtil.deepCopy(value);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected AbstractArrayTypeDescriptor(Class<T> arrayObjectClass, MutabilityPlan<T> mutableMutabilityPlan) {
 | 
			
		||||
        super(arrayObjectClass, mutableMutabilityPlan);
 | 
			
		||||
        this.arrayObjectClass = arrayObjectClass;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Class<T> getArrayObjectClass() {
 | 
			
		||||
        return arrayObjectClass;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setArrayObjectClass(Class<T> arrayObjectClass) {
 | 
			
		||||
        this.arrayObjectClass = arrayObjectClass;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setParameterValues(Properties parameters) {
 | 
			
		||||
        if (parameters.containsKey(PARAMETER_TYPE)) {
 | 
			
		||||
            arrayObjectClass = ((ParameterType) parameters.get(PARAMETER_TYPE)).getReturnedClass();
 | 
			
		||||
        }
 | 
			
		||||
        sqlArrayType = parameters.getProperty(SQL_ARRAY_TYPE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean areEqual(T one, T another) {
 | 
			
		||||
        if (one == another) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (one == null || another == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return ArrayUtil.isEquals(one, another);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString(T value) {
 | 
			
		||||
        return Arrays.deepToString(ArrayUtil.wrapArray(value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public T fromString(String string) {
 | 
			
		||||
        return ArrayUtil.fromString(string, arrayObjectClass);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String extractLoggableRepresentation(T value) {
 | 
			
		||||
        return (value == null) ? "null" : toString(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings({"unchecked"})
 | 
			
		||||
    @Override
 | 
			
		||||
    public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
 | 
			
		||||
        return (X) ArrayUtil.wrapArray(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <X> T wrap(X value, WrapperOptions options) {
 | 
			
		||||
        if (value instanceof Array) {
 | 
			
		||||
            Array array = (Array) value;
 | 
			
		||||
            try {
 | 
			
		||||
                return ArrayUtil.unwrapArray((Object[]) array.getArray(), arrayObjectClass);
 | 
			
		||||
            } catch (SQLException e) {
 | 
			
		||||
                throw new HibernateException(
 | 
			
		||||
                        new IllegalArgumentException(e)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return (T) value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected String getSqlArrayType() {
 | 
			
		||||
        return sqlArrayType;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,86 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
import org.hibernate.type.descriptor.ValueBinder;
 | 
			
		||||
import org.hibernate.type.descriptor.ValueExtractor;
 | 
			
		||||
import org.hibernate.type.descriptor.WrapperOptions;
 | 
			
		||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
 | 
			
		||||
import org.hibernate.type.descriptor.sql.BasicBinder;
 | 
			
		||||
import org.hibernate.type.descriptor.sql.BasicExtractor;
 | 
			
		||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
 | 
			
		||||
 | 
			
		||||
import java.sql.CallableStatement;
 | 
			
		||||
import java.sql.PreparedStatement;
 | 
			
		||||
import java.sql.ResultSet;
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
import java.sql.Types;
 | 
			
		||||
 | 
			
		||||
public class ArraySqlTypeDescriptor implements SqlTypeDescriptor {
 | 
			
		||||
 | 
			
		||||
    public static final ArraySqlTypeDescriptor INSTANCE = new ArraySqlTypeDescriptor();
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getSqlType() {
 | 
			
		||||
        return Types.ARRAY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean canBeRemapped() {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
 | 
			
		||||
        return new BasicBinder<X>(javaTypeDescriptor, this) {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
 | 
			
		||||
                AbstractArrayTypeDescriptor<Object> abstractArrayTypeDescriptor = (AbstractArrayTypeDescriptor<Object>) javaTypeDescriptor;
 | 
			
		||||
                st.setArray(index, st.getConnection().createArrayOf(
 | 
			
		||||
                        abstractArrayTypeDescriptor.getSqlArrayType(),
 | 
			
		||||
                        abstractArrayTypeDescriptor.unwrap(value, Object[].class, options)
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
 | 
			
		||||
                    throws SQLException {
 | 
			
		||||
                throw new UnsupportedOperationException("Binding by name is not supported!");
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
 | 
			
		||||
        return new BasicExtractor<X>(javaTypeDescriptor, this) {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
 | 
			
		||||
                return javaTypeDescriptor.wrap(rs.getArray(name), options);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
 | 
			
		||||
                return javaTypeDescriptor.wrap(statement.getArray(index), options);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
 | 
			
		||||
                return javaTypeDescriptor.wrap(statement.getArray(name), options);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,335 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Array;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class ArrayUtil {
 | 
			
		||||
 | 
			
		||||
    public static <T> T deepCopy(Object originalArray) {
 | 
			
		||||
        Class arrayClass = originalArray.getClass();
 | 
			
		||||
 | 
			
		||||
        if (boolean[].class.equals(arrayClass)) {
 | 
			
		||||
            boolean[] array = (boolean[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (byte[].class.equals(arrayClass)) {
 | 
			
		||||
            byte[] array = (byte[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (short[].class.equals(arrayClass)) {
 | 
			
		||||
            short[] array = (short[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (int[].class.equals(arrayClass)) {
 | 
			
		||||
            int[] array = (int[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (long[].class.equals(arrayClass)) {
 | 
			
		||||
            long[] array = (long[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (float[].class.equals(arrayClass)) {
 | 
			
		||||
            float[] array = (float[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (double[].class.equals(arrayClass)) {
 | 
			
		||||
            double[] array = (double[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else if (char[].class.equals(arrayClass)) {
 | 
			
		||||
            char[] array = (char[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        } else {
 | 
			
		||||
            Object[] array = (Object[]) originalArray;
 | 
			
		||||
            return (T) Arrays.copyOf(array, array.length);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Object[] wrapArray(Object originalArray) {
 | 
			
		||||
        Class arrayClass = originalArray.getClass();
 | 
			
		||||
 | 
			
		||||
        if (boolean[].class.equals(arrayClass)) {
 | 
			
		||||
            boolean[] fromArray = (boolean[]) originalArray;
 | 
			
		||||
            Boolean[] array = new Boolean[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (byte[].class.equals(arrayClass)) {
 | 
			
		||||
            byte[] fromArray = (byte[]) originalArray;
 | 
			
		||||
            Byte[] array = new Byte[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (short[].class.equals(arrayClass)) {
 | 
			
		||||
            short[] fromArray = (short[]) originalArray;
 | 
			
		||||
            Short[] array = new Short[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (int[].class.equals(arrayClass)) {
 | 
			
		||||
            int[] fromArray = (int[]) originalArray;
 | 
			
		||||
            Integer[] array = new Integer[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (long[].class.equals(arrayClass)) {
 | 
			
		||||
            long[] fromArray = (long[]) originalArray;
 | 
			
		||||
            Long[] array = new Long[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (float[].class.equals(arrayClass)) {
 | 
			
		||||
            float[] fromArray = (float[]) originalArray;
 | 
			
		||||
            Float[] array = new Float[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (double[].class.equals(arrayClass)) {
 | 
			
		||||
            double[] fromArray = (double[]) originalArray;
 | 
			
		||||
            Double[] array = new Double[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (char[].class.equals(arrayClass)) {
 | 
			
		||||
            char[] fromArray = (char[]) originalArray;
 | 
			
		||||
            Character[] array = new Character[fromArray.length];
 | 
			
		||||
            for (int i = 0; i < fromArray.length; i++) {
 | 
			
		||||
                array[i] = fromArray[i];
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (originalArray instanceof Collection) {
 | 
			
		||||
            return ((Collection) originalArray).toArray();
 | 
			
		||||
        } else {
 | 
			
		||||
            return (Object[]) originalArray;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> T unwrapArray(Object[] originalArray, Class<T> arrayClass) {
 | 
			
		||||
 | 
			
		||||
        if (boolean[].class.equals(arrayClass)) {
 | 
			
		||||
            boolean[] array = new boolean[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Boolean) originalArray[i] : Boolean.FALSE;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (byte[].class.equals(arrayClass)) {
 | 
			
		||||
            byte[] array = new byte[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Byte) originalArray[i] : 0;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (short[].class.equals(arrayClass)) {
 | 
			
		||||
            short[] array = new short[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Short) originalArray[i] : 0;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (int[].class.equals(arrayClass)) {
 | 
			
		||||
            int[] array = new int[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Integer) originalArray[i] : 0;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (long[].class.equals(arrayClass)) {
 | 
			
		||||
            long[] array = new long[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Long) originalArray[i] : 0L;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (float[].class.equals(arrayClass)) {
 | 
			
		||||
            float[] array = new float[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? ((Number) originalArray[i]).floatValue() : 0f;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (double[].class.equals(arrayClass)) {
 | 
			
		||||
            double[] array = new double[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Double) originalArray[i] : 0d;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (char[].class.equals(arrayClass)) {
 | 
			
		||||
            char[] array = new char[originalArray.length];
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? (Character) originalArray[i] : 0;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (Enum[].class.isAssignableFrom(arrayClass)) {
 | 
			
		||||
            T array = arrayClass.cast(Array.newInstance(arrayClass.getComponentType(), originalArray.length));
 | 
			
		||||
            for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                Object objectValue = originalArray[i];
 | 
			
		||||
                if (objectValue != null) {
 | 
			
		||||
                    String stringValue = (objectValue instanceof String) ? (String) objectValue : String.valueOf(objectValue);
 | 
			
		||||
                    objectValue = Enum.valueOf((Class) arrayClass.getComponentType(), stringValue);
 | 
			
		||||
                }
 | 
			
		||||
                Array.set(array, i, objectValue);
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        } else if (java.time.LocalDate[].class.equals(arrayClass) && java.sql.Date[].class.equals(originalArray.getClass())) {
 | 
			
		||||
            // special case because conversion is neither with ctor nor valueOf
 | 
			
		||||
            Object[] array = (Object[]) Array.newInstance(java.time.LocalDate.class, originalArray.length);
 | 
			
		||||
            for (int i = 0; i < array.length; ++i) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? ((java.sql.Date) originalArray[i]).toLocalDate() : null;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (java.time.LocalDateTime[].class.equals(arrayClass) && java.sql.Timestamp[].class.equals(originalArray.getClass())) {
 | 
			
		||||
            // special case because conversion is neither with ctor nor valueOf
 | 
			
		||||
            Object[] array = (Object[]) Array.newInstance(java.time.LocalDateTime.class, originalArray.length);
 | 
			
		||||
            for (int i = 0; i < array.length; ++i) {
 | 
			
		||||
                array[i] = originalArray[i] != null ? ((java.sql.Timestamp) originalArray[i]).toLocalDateTime() : null;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if(arrayClass.getComponentType() != null && arrayClass.getComponentType().isArray()) {
 | 
			
		||||
            int arrayLength = originalArray.length;
 | 
			
		||||
            Object[] array = (Object[]) Array.newInstance(arrayClass.getComponentType(), arrayLength);
 | 
			
		||||
            if (arrayLength > 0) {
 | 
			
		||||
                for (int i = 0; i < originalArray.length; i++) {
 | 
			
		||||
                    array[i] = unwrapArray((Object[]) originalArray[i], arrayClass.getComponentType());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (arrayClass.isInstance(originalArray)) {
 | 
			
		||||
                return (T) originalArray;
 | 
			
		||||
            } else {
 | 
			
		||||
                return (T) Arrays.copyOf(originalArray, originalArray.length, (Class) arrayClass);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> T fromString(String string, Class<T> arrayClass) {
 | 
			
		||||
        String stringArray = string.replaceAll("[\\[\\]]", "");
 | 
			
		||||
        String[] tokens = stringArray.split(",");
 | 
			
		||||
 | 
			
		||||
        int length = tokens.length;
 | 
			
		||||
 | 
			
		||||
        if (boolean[].class.equals(arrayClass)) {
 | 
			
		||||
            boolean[] array = new boolean[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Boolean.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (byte[].class.equals(arrayClass)) {
 | 
			
		||||
            byte[] array = new byte[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Byte.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (short[].class.equals(arrayClass)) {
 | 
			
		||||
            short[] array = new short[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Short.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (int[].class.equals(arrayClass)) {
 | 
			
		||||
            int[] array = new int[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Integer.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (long[].class.equals(arrayClass)) {
 | 
			
		||||
            long[] array = new long[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Long.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (float[].class.equals(arrayClass)) {
 | 
			
		||||
            float[] array = new float[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Float.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (double[].class.equals(arrayClass)) {
 | 
			
		||||
            double[] array = new double[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = Double.valueOf(tokens[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else if (char[].class.equals(arrayClass)) {
 | 
			
		||||
            char[] array = new char[length];
 | 
			
		||||
            for (int i = 0; i < tokens.length; i++) {
 | 
			
		||||
                array[i] = tokens[i].length() > 0 ? tokens[i].charAt(0) : Character.MIN_VALUE;
 | 
			
		||||
            }
 | 
			
		||||
            return (T) array;
 | 
			
		||||
        } else {
 | 
			
		||||
            return (T) tokens;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isEquals(Object firstArray, Object secondArray) {
 | 
			
		||||
        if (firstArray.getClass() != secondArray.getClass()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        Class arrayClass = firstArray.getClass();
 | 
			
		||||
 | 
			
		||||
        if (boolean[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((boolean[]) firstArray, (boolean[]) secondArray);
 | 
			
		||||
        } else if (byte[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((byte[]) firstArray, (byte[]) secondArray);
 | 
			
		||||
        } else if (short[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((short[]) firstArray, (short[]) secondArray);
 | 
			
		||||
        } else if (int[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((int[]) firstArray, (int[]) secondArray);
 | 
			
		||||
        } else if (long[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((long[]) firstArray, (long[]) secondArray);
 | 
			
		||||
        } else if (float[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((float[]) firstArray, (float[]) secondArray);
 | 
			
		||||
        } else if (double[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((double[]) firstArray, (double[]) secondArray);
 | 
			
		||||
        } else if (char[].class.equals(arrayClass)) {
 | 
			
		||||
            return Arrays.equals((char[]) firstArray, (char[]) secondArray);
 | 
			
		||||
        } else {
 | 
			
		||||
            return Arrays.equals((Object[]) firstArray, (Object[]) secondArray);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> Class<T[]> toArrayClass(Class<T> arrayElementClass) {
 | 
			
		||||
 | 
			
		||||
        if (boolean.class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) boolean[].class;
 | 
			
		||||
        } else if (byte.class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) byte[].class;
 | 
			
		||||
        } else if (short.class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) short[].class;
 | 
			
		||||
        } else if (int.class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) int[].class;
 | 
			
		||||
        } else if (long.class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) long[].class;
 | 
			
		||||
        } else if (float.class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) float[].class;
 | 
			
		||||
        } else if (double[].class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) double[].class;
 | 
			
		||||
        } else if (char[].class.equals(arrayElementClass)) {
 | 
			
		||||
            return (Class) char[].class;
 | 
			
		||||
        } else {
 | 
			
		||||
            Object array = Array.newInstance(arrayElementClass, 0);
 | 
			
		||||
            return (Class<T[]>) array.getClass();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> List<T> asList(T[] array) {
 | 
			
		||||
        List<T> list = new ArrayList<T>(array.length);
 | 
			
		||||
        for (int i = 0; i < array.length; i++) {
 | 
			
		||||
            list.add(i, array[i]);
 | 
			
		||||
        }
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,64 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
import org.hibernate.usertype.DynamicParameterizedType;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.Annotation;
 | 
			
		||||
 | 
			
		||||
public class ParameterizedParameterType implements DynamicParameterizedType.ParameterType {
 | 
			
		||||
 | 
			
		||||
    private final Class<?> clasz;
 | 
			
		||||
 | 
			
		||||
    public ParameterizedParameterType(Class<?> clasz) {
 | 
			
		||||
        this.clasz = clasz;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Class getReturnedClass() {
 | 
			
		||||
        return clasz;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Annotation[] getAnnotationsMethod() {
 | 
			
		||||
        return new Annotation[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getCatalog() {
 | 
			
		||||
        throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getSchema() {
 | 
			
		||||
        throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTable() {
 | 
			
		||||
        throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isPrimaryKey() {
 | 
			
		||||
        throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] getColumns() {
 | 
			
		||||
        throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,41 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
import org.hibernate.usertype.DynamicParameterizedType;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
public class StringArrayType extends AbstractArrayType<String[]> {
 | 
			
		||||
 | 
			
		||||
    public static final StringArrayType INSTANCE = new StringArrayType();
 | 
			
		||||
 | 
			
		||||
    public StringArrayType() {
 | 
			
		||||
        super(
 | 
			
		||||
                new StringArrayTypeDescriptor()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StringArrayType(Class arrayClass) {
 | 
			
		||||
        this();
 | 
			
		||||
        Properties parameters = new Properties();
 | 
			
		||||
        parameters.put(DynamicParameterizedType.PARAMETER_TYPE, new ParameterizedParameterType(arrayClass));
 | 
			
		||||
        setParameterValues(parameters);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return "string-array";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,31 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.util.mapping;
 | 
			
		||||
 | 
			
		||||
public class StringArrayTypeDescriptor
 | 
			
		||||
        extends AbstractArrayTypeDescriptor<String[]> {
 | 
			
		||||
 | 
			
		||||
    public StringArrayTypeDescriptor() {
 | 
			
		||||
        super(String[].class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected String getSqlArrayType() {
 | 
			
		||||
        String sqlArrayType = super.getSqlArrayType();
 | 
			
		||||
        return sqlArrayType != null ? sqlArrayType : "text";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								pom.xml
									
									
									
									
									
								
							@ -121,6 +121,7 @@
 | 
			
		||||
        <wire-schema.version>3.4.0</wire-schema.version>
 | 
			
		||||
        <twilio.version>8.17.0</twilio.version>
 | 
			
		||||
        <hibernate-validator.version>6.0.20.Final</hibernate-validator.version>
 | 
			
		||||
        <hypersistence-utils.version>3.5.2</hypersistence-utils.version>
 | 
			
		||||
        <javax.el.version>3.0.0</javax.el.version>
 | 
			
		||||
        <javax.validation-api.version>2.0.1.Final</javax.validation-api.version>
 | 
			
		||||
        <antisamy.version>1.7.2</antisamy.version>
 | 
			
		||||
@ -1923,6 +1924,11 @@
 | 
			
		||||
                <artifactId>hibernate-validator</artifactId>
 | 
			
		||||
                <version>${hibernate-validator.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>io.hypersistence</groupId>
 | 
			
		||||
                <artifactId>hypersistence-utils-hibernate-55</artifactId>
 | 
			
		||||
                <version>${hypersistence-utils.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.glassfish</groupId>
 | 
			
		||||
                <artifactId>javax.el</artifactId>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user