/*
 * Copyright (c) 2021
 * NDE Netzdesign und -entwicklung AG, Hamburg, Germany
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program (see the file LICENSE.txt for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package org.acplt.oncrpc.apps.jrpcgen;

/**
 * The base type class maps base types and implements the type mapping
 * interface.
 * 
 * @author Harald Wirths {@literal <hwirths@nde.ag>}
 *
 */
public enum JrpcgenBaseType implements JrpcgenTypeMapping {
	VOID("void", "XdrVoid"),
	BOOL("bool", "boolean", "XdrBoolean"),
	CHAR("char", "byte", "XdrByte"),
	SHORT("short", "XdrShort"),
	INT("int", "XdrInt"),
	HYPER("hyper", "long", "XdrLong"),
	FLOAT("float", "XdrFloat"),
	DOUBLE("double", "XdrDouble"),
	STRING("string", "String", "XdrString"),
	OPAQUE("opaque", "byte", "XdrOpaque");

	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code void}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code void}, {@code false} otherwise.
	 * 		 
	 */
	public static boolean isVoid(String type) {
		return VOID.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code bool}.
	 *  
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code bool}, {@code false} otherwise.
	 */
	public static boolean isBoolean(String type) {
		return BOOL.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code char}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code char}, {@code false} otherwise.
	 */
	public static boolean isChar(String type) {
		return CHAR.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code short}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code short}, {@code false} otherwise.
	 */
	public static boolean isShort(String type) {
		return SHORT.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code int}.
	 *  
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code int}, {@code false} otherwise.
	 */
	public static boolean isInt(String type) {
		return INT.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code hyper}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code hyper}, {@code false} otherwise.
	 */
	public static boolean isHyper(String type) {
		return HYPER.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code float}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code float}, {@code false} otherwise.
	 */
	public static boolean isFloat(String type) {
		return FLOAT.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code double}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code double}, {@code false} otherwise.
	 */
	public static boolean isDouble(String type) {
		return DOUBLE.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code String}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code String}, {@code false} otherwise.
	 */
	public static boolean isString(String type) {
		return STRING.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name equals the definition name
	 * of the base type {@code opaque}.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name equals the definition
	 *         name of the base type {@code opaque}, {@code false} otherwise.
	 */
	public static boolean isOpaque(String type) {
		return OPAQUE.definitionName.equals(type);
	}
	
	/**
	 * Returns whether the passed type name labels the definition name
	 * of one of the primitive types {@code bool}, {@code char},
	 * {@code short}, {@code int}, {@code hyper}, {@code float} or
	 * {@code double}.
	 *  
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name labels one of
	 *         the mentioned primitive types, {@code false} otherwise.
	 */
	public static boolean isPrimitive(String type) {
		return isBoolean(type) 
				|| isChar(type)
				|| isShort(type)
				|| isInt(type)
				|| isHyper(type)
				|| isFloat(type)
				|| isDouble(type);
	}
	
	/**
	 * Returns whether the passed type name labels one of the base
	 * types defined by this enumeration.
	 * 
	 * @param type The type name to check.
	 * @return {@code true} in case the passed type name labels one
	 *         of the base types defined by this enumeration,
	 *         {@code false} otherwise.
	 */
	public static boolean isBaseType(String type) {
		return isString(type)
				|| isPrimitive(type)
				|| isOpaque(type)
				|| isVoid(type);
	}
	
	/**
	 * Returns the base type mapping to the passed type name as given
	 * in an x-file.
	 * One of the following will be returned:
	 * <ul>
	 * <li>Type name <em>void</em> : {@code JrpcgenBaseType.VOID}</li>
	 * <li>Type name <em>bool</em> : {@code JrpcgenBaseType.BOOL}</li>
	 * <li>Type name <em>char</em> : {@code JrpcgenBaseType.CHAR}</li>
	 * <li>Type name <em>short</em> : {@code JrpcgenBaseType.SHORT}</li>
	 * <li>Type name <em>int</em> : {@code JrpcgenBaseType.INT}</li>
	 * <li>Type name <em>hyper</em> : {@code JrpcgenBaseType.HYPER}</li>
	 * <li>Type name <em>float</em> : {@code JrpcgenBaseType.FLOAT}</li>
	 * <li>Type name <em>double</em> : {@code JrpcgenBaseType.DOUBLE}</li>
	 * <li>Type name <em>string</em> : {@code JrpcgenBaseType.STRING}</li>
	 * <li>Type name <em>opaque</em> : {@code JrpcgenBaseType.OPAQUE}</li> 
	 * </ul>
	 * 
	 * @param type The name of the requested base type.
	 * @return The corresponding base type mapping or {@code null}.
	 */
	public static JrpcgenBaseType getBaseType(String type) {
		if (isVoid(type))
			return VOID;
		
		if (isBoolean(type))
			return BOOL;
		
		if (isChar(type))
			return CHAR;
	
		if (isShort(type))
			return SHORT;
		
		if (isInt(type))
			return INT;
		
		if (isHyper(type))
			return HYPER;
		
		if (isFloat(type))
			return FLOAT;
		
		if (isDouble(type))
			return DOUBLE;
		
		if (isString(type))
			return STRING;
		
		if (isOpaque(type))
			return OPAQUE;
		
		return null;
	}
	
	/**
	 * Returns whether this base type maps the type {@code void}.
	 * 
	 * @return {@code true} in case this is the mapping for type
	 *         {@code void}, {@code false} otherwise.
	 */
	@Override
	final public boolean isVoid() {
		return VOID.equals(this);
	}
	
	/**
	 * Returns whether the mapped type belongs to the family of
	 * the base types. Each type mapping of this enumeration belongs
	 * to one of the base types and {@code true} is returned, always.
	 * 
	 * @return {@code true}
	 */
	@Override
	final public boolean isBaseType() {
		return true;
	}
	
	/**
	 * Returns whether this base type maps the type {@code boolean}.
	 * 
	 * @return {@code true} in case this is the mapping for type
	 *         {@code boolean…}, {@code false} otherwise.
	 */
	@Override
	final public boolean isBooleanType() {
		return BOOL.equals(this);
	}
	
	/**
	 * Returns whether this base type maps the type {@code String}.
	 * 
	 * @return {@code true} in case this base type maps the type
	 *         {@code String}, {@code false} otherwise.
	 */
	@Override
	final public boolean isStringType() {
		return STRING.equals(this);
	}
	
	/**
	 * Returns whether this base type maps the type {@code opaque}.
	 * 
	 * @return {@code true} in case this base type maps the type
	 *         {@code opaque}, {@code false} otherwise.
	 */
	@Override
	final public boolean isOpaqueType() {
		return OPAQUE.equals(this);
	}
	
	/**
	 * Returns the definition name of the mapped type in the x-file.
	 * <ul>
	 * <li>{@code JrpcgenBaseType.VOID} returns the name <em>void</em>.</li>
	 * <li>{@code JrpcgenBaseType.BOOL} returns the name <em>bool</em></li> 
	 * <li>{@code JrpcgenBaseType.CHAR} returns the name <em>char</em></li> 
	 * <li>{@code JrpcgenBaseType.SHORT} returns the name <em>short</em></li> 
	 * <li>{@code JrpcgenBaseType.INT} returns the name <em>int</em></li> 
	 * <li>{@code JrpcgenBaseType.HYPER} returns the name <em>hyper</em></li> 
	 * <li>{@code JrpcgenBaseType.FLOAT} returns the name <em>float</em></li> 
	 * <li>{@code JrpcgenBaseType.DOUBLE} returns the name <em>double</em></li> 
	 * <li>{@code JrpcgenBaseType.STRING} returns the name <em>string</em></li> 
	 * <li>{@code JrpcgenBaseType.OPAQUE} returns the name <em>opaque</em></li> 
	 * </ul>
	 *
	 * @return The definition name of this base type as described above.
	 */
	@Override
	final public String getDefinitionName() {
		return definitionName;
	}
	
	/**
	 * Returns the name of the Java type mapped to the XDR type identified
	 * by the definition name. The returned value depends on the mapped
	 * base type and will be one of the following:
	 * <ul>
	 * <li>{@code void} for the base type mapping {@code JrpcgenBaseType.VOID}.</li>
	 * <li>{@code boolean} for the base type mapping {@code JrpcgenBaseType.BOOL}.</li>
	 * <li>{@code byte} for the base type mapping {@code JrpcgenBaseType.CHAR}.</li>
	 * <li>{@code short} for the base type mapping {@code JrpcgenBaseType.SHORT}.</li>
	 * <li>{@code int} for the base type mapping {@code JrpcgenBaseType.INT}.</li>
	 * <li>{@code long} for the base type mapping {@code JrpcgenBaseType.HYPER}.</li>
	 * <li>{@code float} for the base type mapping {@code JrpcgenBaseType.FLOAT}.</li>
	 * <li>{@code double} for the base type mapping {@code JrpcgenBaseType.DOUBLE}.</li>
	 * <li>{@link String} for the base type mapping {@code JrpcgenBaseType.STRING}.</li>
	 * <li>{@code byte} for the base type mapping {@code JrpcgenBaseType.OPAQUE}.</li>
	 * </ul> 
	 */
	@Override
	final public String getJavaName() {
		return javaName;
	}
	
	/**
	 * Returns the name of the Java class belonging to the mapped type.
	 * The returned value depends on the mapped base type and will be one
	 * of the following:
	 * <ul>
	 * <li>{@link Void} for the base type mapping {@code JrpcgenBaseType.VOID}.</li>
	 * <li>{@link Boolean} for the base type mapping {@code JrpcgenBaseType.BOOL}.</li>
	 * <li>{@link Byte} for the base type mapping {@code JrpcgenBaseType.CHAR}.</li>
	 * <li>{@link Short} for the base type mapping {@code JrpcgenBaseType.SHORT}.</li>
	 * <li>{@link Integer} for the base type mapping {@code JrpcgenBaseType.INT}.</li>
	 * <li>{@link Long} for the base type mapping {@code JrpcgenBaseType.HYPER}.</li>
	 * <li>{@link Float} for the base type mapping {@code JrpcgenBaseType.FLOAT}.</li>
	 * <li>{@link Double} for the base type mapping {@code JrpcgenBaseType.DOUBLE}.</li>
	 * <li>{@link String} for the base type mapping {@code JrpcgenBaseType.STRING}.</li>
	 * <li>{@link Byte} for the base type mapping {@code JrpcgenBaseType.OPAQUE}.</li>
	 * </ul>
	 * 
	 * @return The mapped Java class name for this base type
	 *         as described above. 
	 */
	@Override
	final public String getJavaClass() {
		return javaClass; 
	}
	
	/**
	 * Returns the name of the XDR class providing the coding methods
	 * of the mapped type, in detail the class implementing the interface
	 * {@link XdrAble} for the mapped type. The returned value depends on the
	 * mapped base type and will be one of the following:
	 * <ul>
	 * <li>{@link XdrVoid} for the base type mapping {@code JrpcgenBaseType.VOID}.</li>
	 * <li>{@link XdrBoolean} for the base type mapping {@code JrpcgenBaseType.BOOL}.</li>
	 * <li>{@link XdrByte} for the base type mapping {@code JrpcgenBaseType.CHAR}.</li>
	 * <li>{@link XdrShort} for the base type mapping {@code JrpcgenBaseType.SHORT}.</li>
	 * <li>{@link XdrInt} for the base type mapping {@code JrpcgenBaseType.INT}.</li>
	 * <li>{@link XdrLong} for the base type mapping {@code JrpcgenBaseType.HYPER}.</li>
	 * <li>{@link XdrFloat} for the base type mapping {@code JrpcgenBaseType.FLOAT}.</li>
	 * <li>{@link XdrDouble} for the base type mapping {@code JrpcgenBaseType.DOUBLE}.</li>
	 * <li>{@link XdrString} for the base type mapping {@code JrpcgenBaseType.STRING}.</li>
	 * <li>{@link XdrOpaque} for the base type mapping {@code JrpcgenBaseType.OPAQUE}.</li>
	 * </ul> 
	 */
	@Override
	final public String getXdrClass() {
		return xdrClass;
	}
	
	/**
	 * A constructor call based on the XDR class of the mapped base type
	 * is written to the passed Java file. Dependent on the passed parameter
	 * the constructor call becomes a call without a parameter or with one
	 * parameter.
	 * 
	 * <p>This applies to all base types except for the base type
	 * {@code JrpcgenBaseType.VOID}. The base type {@code JrpcgenBaseType.VOID}
	 * will return a reference to the global instance {@code XdrVoid.XDR_VOID},
	 * always. As this type does not hold a value, a passed parameter different
	 * from {@code null} will be ignored.
	 * 
	 * @param javaFile The Java file, where the XDR constructor call is going
	 *        to be placed.
	 * @param parameter {@code null} to generate a default constructor call,
	 *        a parameter name to generate a constructor call with one parameter.
	 */
	@Override
	public void writeXdrConstructorCall(JrpcgenJavaFile javaFile, String parameter) {
		switch(this) {
		case VOID:
			javaFile.append("XdrVoid.XDR_VOID");
			break;
			
		default:
			javaFile.keywordNew().space().append(getXdrClass()).leftParenthesis();
			
			if (parameter != null) {
				javaFile.append(parameter);
			}
			
			javaFile.rightParenthesis();
		}
	}
	
	/**
	 * A cosntuctor call based on the XDR class of the mapped base type is written
	 * to the passed Java file. The parameter expression is intended to write an
	 * expression, which evaluates to a parameter to the constructor call.
	 * 
	 * <p>This applies to all base types except for the base type
	 * {@code JrpcgenBaseType.VOID}. The base type {@code JrpcgenBaseType.VOID}
	 * will return a reference to the global instance {@code XdrVoid.XDR_VOID},
	 * always. As this type does not hold a value, the passed parameter expression
	 * will be ignored.
	 * 
	 * @param javaFile The Java file, where the constructor call is going to be placed.
	 * @param parameterExpression An expression to be called with the passed Java file,
	 *        when the expression is going to be placed in the Java file.
	 */
	@Override
	public void writeXdrConstructorCall(JrpcgenJavaFile javaFile, JrpcgenJavaFile.Expression parameterExpression) {
		switch(this) {
		case VOID:
			javaFile.append("XdrVoid.XDR_VOID");
			break;
			
		default:
			javaFile.keywordNew().space().append(getXdrClass()).leftParenthesis()
				.expression(parameterExpression).rightParenthesis();
		}
	}
	
	/**
	 * Writes a conversion statement to the passed Java file changing the Java representation
	 * of a variable to the corresponding XDR representation, in order to enable XDR encoding
	 * operations on the referenced variable. With base types an XDR constructor call
	 * will be placed into the Java file, where the passed variable will be placed as parameter
	 * to the constructor call. 
	 * 
	 * @see #writeXdrConstructorCall(JrpcgenJavaFile, String)
	 * 
	 * @param javaFile The java file, where the converison statement is going to be placed.
	 * @param variable The name of a variable in Java representation.
	 */
	@Override
	public void writeJavaToXdr(JrpcgenJavaFile javaFile, String variable) {
		writeXdrConstructorCall(javaFile, variable);
	}
	
	/**
	 * Writes a conversion statement to the passed Java file changing the Java representation
	 * resulting from the passed expression to the corresponding XDR representation, in order
	 * to enable XDR encoding operations on the result of the passed expression. With base
	 * types an XDR constructor call will be placed into the Java file, where the passed
	 * expression will form the parameter to the constructor call.
	 * 
	 * @see #writeXdrConstructorCall(JrpcgenJavaFile, org.acplt.oncrpc.apps.jrpcgen.JrpcgenJavaFile.Expression)
	 * 
	 * @param javaFile The Java file, where the conversion statement is going to be placed.
	 * @param expression An expression to be called with the passed Java file, when the
	 *        expression is going to be placed in the Java file.
	 */
	@Override
	public void writeJavaToXdr(JrpcgenJavaFile javaFile, JrpcgenJavaFile.Expression expression) {
		writeXdrConstructorCall(javaFile, expression);
	}

	/**
	 * Writes a conversion statement to the passed Java file changing the XDR representation
	 * of a variable to the corresponding Java representation, which takes place after
	 * XDR decoding operations.
	 * 
	 * <p>This applies to all base types except for the base type
	 * {@code JrpcgenBaseType.VOID}. No statement is written for this
	 * type, becaus the XDR class {@link XdrVoid} does not hold any value.
	 * 
	 * @param javaFile The Java file, where the conversion statement is going to be placed.
	 * @param variable The name of a variable in XDR representation. 
	 */
	@Override
	public void writeXdrToJava(JrpcgenJavaFile javaFile, String result) {
		switch(this) {
		case VOID:
			break;
			
		case STRING:
			javaFile.append(result).append(".stringValue()");
			break;

		case OPAQUE:
			javaFile.append(result).append(".opaqueValue()");
			break;
			
		default:
			javaFile.append(result).dot().append(getJavaName()).append("Value()");
		}
	}

	/**
	 * Writes an XDR encoding call to the passed Java file using the passed name of an
	 * XDR encoding stream and the passed name of a variable. The referenced variable is
	 * expected to represent a value of the mapped type.
	 * 
	 * <p>This applies to all base types except for the following base types:
	 * <ul>
	 * <li>{@code JrpcgenBaseType.VOID} : This type does not support any coding as it
	 *     does represent no value.</li>
	 * <li>{@code JrpcgenBaseType.STRING} : This type does not support coding in a
	 *     scalar context.</li>
	 * <li>{@code JrpcgenBaseType.OPAQUE} : This type does not support coding in a
	 *     scalar context.</li>
	 * </ul>
	 * For these base types nothing is written to the passed Java file by this method.
	 * 
	 * @param javaFile The Java file, where the XDR encoding call is going to be placed. 
	 * @param xdrStream The name of the XDR encoding stream instance to be used in the statement.
	 * @param variable The name of the variable to be used in the statement.
	 */
	@Override
	public void writeXdrEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, String variable) {
		switch(this) {
		case VOID:
		case STRING:
		case OPAQUE:
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncode", javaName).append('(').append(variable).rightParenthesis();
		}
	}
	
	/**
	 * Writes an XDR encoding call to the passed Java file using the passed name of an
	 * XDR encoding stream and the result of the passed experssion. The result of the passed
	 * expression is expected to represent a value of the mapped type.
	 * 
	 * <p>This applies to all base types except for the following base types:
	 * <ul>
	 * <li>{@code JrpcgenBaseType.VOID} : This type does not support any coding as it
	 *     does represent no value.</li>
	 * <li>{@code JrpcgenBaseType.STRING} : This type does not support coding in a
	 *     scalar context.</li>
	 * <li>{@code JrpcgenBaseType.OPAQUE} : This type does not support coding in a
	 *     scalar context.</li>
	 * </ul>
	 * For these base types nothing is written to the passed Java file by this method.
	 * 
	 * @param javaFile The Java file, where the XDR encoding code is going to be placed.
	 * @param xdrStream The name of the XDR encoding stream instance to be used in the statement.
	 * @param expression An expression to be called with the passed Java file, when the
	 *        expression is going to be placed in the Java file.
	 */
	@Override
	public void writeXdrEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, JrpcgenJavaFile.Expression expression) {
		switch(this) {
		case VOID:
		case STRING:
		case OPAQUE:
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncode", javaName).append('(').expression(expression).rightParenthesis();
		}
	}
	
	/**
	 * Writes an XDR encoding call for a fixed vector to the passed Java file using the passed
	 * name of an XDR encoding stream, the passed name of a variable and the passed size.
	 * The rerferenced variable is expected to repesent an array of the mapped type.
	 * 
	 * <p>This applies to all base types except for the following base types:
	 * <ul>
	 * <li>{@code JrpcgenBaseType.VOID} : This type does not support any coding as it
	 *     does represent no value.</li>
	 * <li>{@code JrpcgenBaseType.STRING} : This type does not support coding in
	 *     a fixed vector context.</li>
	 * </ul>
	 * For these base types nothing is written to the passed Java file by this method.
	 * 
	 * @param javaFile The Java file, where the XDR encoding call is going to be placed.
	 * @param xdrStream The name of the XDR encoding stream instance to be used in the statement.
	 * @param variable The name of the variable to be used in the statement. 
	 * @param size A string specifying the size of the fixed vector.
	 */
	@Override
	public void writeXdrFixedVectorEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, String variable, String size) {
		switch(this) {
		case VOID:
		case STRING:
			break;
			
		case OPAQUE:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncode", definitionName)
				.leftParenthesis().append(variable).append(", ").append(size).rightParenthesis();
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncode", javaName).append("FixedVector(")
				.append(variable).append(", ").append(size).rightParenthesis();
		}
	}
	
	/**
	 * Writes an XDR encoding call for a dynamic vector to the passed Java file using the passed
	 * name of an XDR encoding stream and the passed name of a variable. The referenced variable
	 * is expected to repesent an array of the mapped type.
	 *
	 * <p>This applies to all base types except for the base type
	 * {@code JrpcgenBaseType.VOID}, which does not support any coding as it does
	 * represent no value. Consequently, nothing is written to the passed Java file
	 * by this method for the base type {@code JrpcgenBaseType.VOID}. 
	 * 
	 * @param javaFile The Java file, where the XDR encoding call is going to be placed.
	 * @param xdrStream The name of the XDR encoding stream instance to be used in the statement.
	 * @param variable The name of the variable to be used in the statement.
	 */
	@Override
	public void writeXdrDynamicVectorEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, String variable) {
		switch(this) {
		case VOID:
			break;
			
		case STRING:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncode", javaName).append('(').append(variable).rightParenthesis();
			break;
			
		case OPAQUE:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncodeDynamic", definitionName).leftParenthesis()
				.append(variable).rightParenthesis();
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrEncode", javaName).append("Vector(")
				.append(variable).rightParenthesis();
		}
	}
	
	/**
	 * Writes an XDR decoding call to the passed Java file using the passed name of an XDR decoding
	 * stream.
	 *
	 * <p>This applies to all base types except for the following base types:
	 * <ul>
	 * <li>{@code JrpcgenBaseType.VOID} : This type does not support any coding as it
	 *     does represent no value.</li>
	 * <li>{@code JrpcgenBaseType.STRING} : This type does not support coding in a
	 *     scalar context.</li>
	 * <li>{@code JrpcgenBaseType.OPAQUE} : This type does not support coding in a
	 *     scalar context.</li>
	 * </ul>
	 * For these base types nothing is written to the passed Java file by this method.
	 * 
	 * @param javaFile The Java file, where the XDR decoding call is going to be placed.
	 * @param xdrStream The name of the XDR decoding stream instance to be used in the statement.
	 */
	@Override
	public void writeXdrDecodingCall(JrpcgenJavaFile javaFile, String xdrStream) {
		switch(this) {
		case VOID:
		case STRING:
		case OPAQUE:
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrDecode", javaName).append("()");
		}
	}
	
	/**
	 * Writes an XDR decoding call for a fixed vector to the passed Java file using the passed name
	 * of an XDR decoding stream and the passed size.
	 *
	 * <p>This applies to all base types except for the following base types:
	 * <ul>
	 * <li>{@code JrpcgenBaseType.VOID} : This type does not support any coding as it
	 *     does represent no value.</li>
	 * <li>{@code JrpcgenBaseType.STRING} : This type does not support coding in
	 *     a fixed vector context.</li>
	 * </ul>
	 * For these base types nothing is written to the passed Java file by this method.
	 * 
	 * @param javaFile The Java file, where the XDR decoding call is going to be placed.
	 * @param xdrStream The name of the XDR decoding stream instance to be used in the statement.
	 * @param size A string specifying the size of the fixed vector. 
	 */
	@Override
	public void writeXdrFixedVectorDecodingCall(JrpcgenJavaFile javaFile, String xdrStream, String size) {
		switch(this) {
		case VOID:
		case STRING:
			break;
			
		case OPAQUE:			
			writeCodingMethodStem(javaFile, xdrStream, "xdrDecode", definitionName)
				.leftParenthesis().append(size).rightParenthesis();
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrDecode", javaName)
				.append("FixedVector(").append(size).rightParenthesis();
		}
	}
	
	/**
	 * Writes an XDR decoding call for a dynamic vector to the passed Java file using the passed
	 * name of an XDR decoding stream.
	 * 
	 * <p>This applies to all base types except for the base type
	 * {@code JrpcgenBaseType.VOID}, which does not support any coding as it does
	 * represent no value. Consequently, nothing is written to the passed Java file
	 * by this method for the base type {@code JrpcgenBaseType.VOID}. 
	 * 
	 * @param javaFile The Java file, where the XDR decoding call is going to be placed.
	 * @param xdrStream The name of the XDR decoding stream instance to be used in the statement.
	 */
	@Override
	public void writeXdrDynamicVectorDecodingCall(JrpcgenJavaFile javaFile, String xdrStream) {
		switch(this) {
		case VOID:
			break;
			
		case STRING:
			writeCodingMethodStem(javaFile, xdrStream, "xdrDecode", javaName).append("()");
			break;
			
		case OPAQUE:
			writeCodingMethodStem(javaFile, xdrStream, "xdrDecodeDynamic", definitionName).append("()");
			break;
			
		default:
			writeCodingMethodStem(javaFile, xdrStream, "xdrDecode", javaName).append("Vector()");
		}
	}
	
	/**
	 * Writes an equals expression to the passed Java file using the passed names of a left hand side
	 * and a right hand side variable. The negate parameter controls whether the statement evaluates
	 * to {@code true} on equality or on inequality.
	 *
	 * <p>This applies to all base types except for the base type
	 * {@code JrpcgenBaseType.VOID}, for which nothing is written to the
	 * passed Java file by ths method.
	 * 
	 * @param javaFile The Java file, where the equality expression is going to be placed.
	 * @param variableLeft The name of the variable to be used as the left hand side in the statement.
	 * @param variableRight The name of the variable to be used as the right hand side in the statement.
	 * @param negate {@code false} to let the resulting statement return {@code true} on equality,
	 *        {@code true} to let the resulting statement return {@code true} on inequality.
	 */
	@Override
	public void writeEqualsExpression(JrpcgenJavaFile javaFile, String variableLeft, String variableRight, boolean negate) {
		switch(this) {
		case VOID:
			break;
			
		case STRING:
			javaFile.append(negate ? "! " : "").append("java.util.Objects.equals(").append(variableLeft)
				.append(", ").append(variableRight).rightParenthesis();
			break;
			
		default:
			javaFile.append(variableLeft).append(negate ? " != " : " == ").append(variableRight);
		}
	}
	
	/**
	 * Writes the coding method stem for the mapped base type.
	 * 
	 * @param javaFile The Java file where the method stem is going to be placed.
	 * @param xdrStream The name of the XDR coding stream.
	 * @param codingType The coding type to use, <em>xdrEncode</em> or <em>xdrDecode</em> in most
	 *        of the cases.
	 * @param typeSpecifier The type specifier to append to the coding type, either the Java name
	 *        or the definition name in most of the cases.
	 * @return The passed Java file.
	 */
	public JrpcgenJavaFile writeCodingMethodStem(JrpcgenJavaFile javaFile, String xdrStream, String codingType, String typeSpecifier) {
		return javaFile.append(xdrStream).dot().append(codingType)
					.append(typeSpecifier.substring(0,1).toUpperCase()).append(typeSpecifier.substring(1));
	}
	
	private JrpcgenBaseType(String definitionName, String xdrClass) {
		this.definitionName = definitionName;
		this.javaName = definitionName;
		this.javaClass = getJavaClassTo(definitionName);
		this.xdrClass = xdrClass;
	}
	
	private JrpcgenBaseType(String definitionName, String javaName, String xdrClass) {
		this.definitionName = definitionName;
		this.javaName = javaName;
		this.javaClass = getJavaClassTo(javaName);
		this.xdrClass = xdrClass;
	}
	
	private static String getJavaClassTo(String javaName) {
		switch(javaName) {
		case "int":
			return "Integer";
			
		case "String":
			return javaName;
			
		default:
			return javaName.substring(0,1).toUpperCase().concat(javaName.substring(1));
		}
	}

	private final String definitionName;
	private final String javaName;
	private final String javaClass;
	private final String xdrClass;
}