/******************************************************************************* | |
* Copyright (c) 2020 RBEI and others. | |
* | |
* This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v. 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* Adhith Gopal - Initial API and Implementation | |
*******************************************************************************/ | |
package org.eclipse.blockchain.core; | |
import java.math.BigInteger; | |
import java.util.ArrayList; | |
import java.util.List; | |
import org.eclipse.blockchain.core.model.IOABI; | |
import org.web3j.abi.TypeReference; | |
import org.web3j.abi.datatypes.Address; | |
import org.web3j.abi.datatypes.Utf8String; | |
import org.web3j.abi.datatypes.primitive.Char; | |
import org.web3j.abi.datatypes.primitive.Int; | |
import com.squareup.javapoet.ClassName; | |
import com.squareup.javapoet.ParameterizedTypeName; | |
import com.squareup.javapoet.TypeName; | |
/** | |
* This class is used to create java data types for each solidity data type. The | |
* generated java data type must match with web3j's generated solidity type | |
* because we are dependant on web3j for blockchain interactions | |
*/ | |
public class SolidityToJavaDataTypeResolver { | |
private static SolidityToJavaDataTypeResolver instance; | |
private SolidityToJavaDataTypeResolver() { | |
} | |
/** | |
* @return - Instance | |
*/ | |
public static SolidityToJavaDataTypeResolver getInstance() { | |
if (instance == null) { | |
instance = new SolidityToJavaDataTypeResolver(); | |
} | |
return instance; | |
} | |
/** | |
* This method returns the corresponding javatype for the passed solidity | |
* type. This method is equivalent to buildTypeName(...) in | |
* SolidityFunctionWrapper | |
* | |
* @param solidityType | |
* - The solidity data type | |
* @param ioABI | |
* - The solidity ABI | |
* @throws ClassNotFoundException | |
* - Thrown by TypeReference Class | |
*/ | |
@SuppressWarnings("rawtypes") | |
public void assignJavaType(final String solidityType, final IOABI ioABI) | |
throws ClassNotFoundException { | |
TypeReference typeReference = TypeReference | |
.makeTypeReference(solidityType, false, false); | |
TypeName ty = TypeName.get(typeReference.getType()); | |
if (ty instanceof ParameterizedTypeName) {// This is important | |
ioABI.setIsArray(true); | |
} else { | |
ioABI.setIsArray(false); | |
} | |
String javaString = getJavaNativeType(ty, ioABI).toString(); | |
if (ioABI.getIsArray()) { | |
ioABI.setJavaType(List.class); | |
} else { | |
ioABI.setJavaType(ioABI.getCastType()); | |
} | |
ioABI.setJavaTypeString(javaString); | |
} | |
/** | |
* This is equivalent to getNativeType(TypeName) in SolidityFunctionWrapper | |
* | |
* @param type | |
* @return | |
*/ | |
private TypeName getJavaNativeType(final TypeName type, final IOABI ioABI) {// NOSONAR | |
if (type instanceof ParameterizedTypeName) { | |
ioABI.setDimensionDepth(ioABI.getDimensionDepth() + 1); | |
return getJavaNativeType((ParameterizedTypeName) type, ioABI); | |
} | |
String name = ((ClassName) type).simpleName(); | |
if ((name.equals(Address.class.getSimpleName())) | |
|| (name.equals(Utf8String.class.getSimpleName()))) { | |
ioABI.setCastType(String.class); | |
return TypeName.get(String.class); | |
} else if (name.startsWith("Uint") || name.startsWith("Int")) { | |
ioABI.setCastType(BigInteger.class); | |
return TypeName.get(BigInteger.class); | |
} else if (name.startsWith("Bytes") || name.equals("DynamicBytes")) { | |
ioABI.setCastType(byte[].class); | |
return TypeName.get(byte[].class); | |
} else if (name.startsWith("Bool")) { | |
ioABI.setCastType(Boolean.class); | |
return TypeName.get(java.lang.Boolean.class); | |
} else if (name.equals(Byte.class.getSimpleName())) { | |
ioABI.setCastType(Byte.class); | |
return TypeName.get(java.lang.Byte.class); | |
} else if (name.equals(Char.class.getSimpleName())) { | |
ioABI.setCastType(Character.class); | |
return TypeName.get(Character.class); | |
} else if (name.equals(Double.class.getSimpleName())) { | |
ioABI.setCastType(Double.class); | |
return TypeName.get(java.lang.Double.class); | |
} else if (name.equals(Float.class.getSimpleName())) { | |
ioABI.setCastType(Float.class); | |
return TypeName.get(java.lang.Float.class); | |
} else if (name.equals(Int.class.getSimpleName())) { | |
ioABI.setCastType(Integer.class); | |
return TypeName.get(Integer.class); | |
} else if (name.equals(Long.class.getSimpleName())) { | |
ioABI.setCastType(Long.class); | |
return TypeName.get(java.lang.Long.class); | |
} else if (name.equals(Short.class.getSimpleName())) { | |
ioABI.setCastType(Short.class); | |
return TypeName.get(java.lang.Short.class); | |
} else { | |
throw new UnsupportedOperationException("Unsupported type: " + type | |
+ ", no native type mapping exists."); | |
} | |
} | |
/** | |
* This is equivalent to getNativeType(ParameterizedTypeName) in | |
* SolidityFunctionWrapper | |
* | |
* @param parameterizedTypeName | |
* @return | |
*/ | |
private TypeName getJavaNativeType( | |
final ParameterizedTypeName parameterizedTypeName, | |
final IOABI ioABI) { | |
List<TypeName> typeNames = parameterizedTypeName.typeArguments; | |
List<TypeName> nativeTypeNames = new ArrayList<>(typeNames.size()); | |
for (TypeName enclosedTypeName : typeNames) { | |
nativeTypeNames.add(getJavaNativeType(enclosedTypeName, ioABI)); | |
} | |
return ParameterizedTypeName.get(ClassName.get(List.class), | |
nativeTypeNames.toArray(new TypeName[nativeTypeNames.size()])); | |
} | |
} |