private JMethod generateZclAttributeGetterImplementation(JCodeModel jModel, JDefinedClass jClusterClass, JClass jParentClass,
			ZclAttributeDescr zclAttributeDescriptor, boolean isServer) throws Exception {

		JMethod jMethod = generateZclAttributeGetterSignature(jModel, jClusterClass, jParentClass, zclAttributeDescriptor, true);

		// parse return value
		JClass type = getZclDataTypeJClass(jModel, jClusterClass.getPackage(), jParentClass, zclAttributeDescriptor.getTypeName(),
				zclAttributeDescriptor);

		int attrId = zclAttributeDescriptor.getId();
		JBlock jBlock = jMethod.body();

		JVar jContextParamVar = null;

		if (generateCacheAccess || optimizeForSize) {
			JVar[] jParams = jMethod.listParams();
			jContextParamVar = jParams[jParams.length - 1];
		}

		JType cachedType = null;

		if (generateCacheAccess) {
			cachedType = getJDKJClass(jModel, jMethod.type());

			if (cachedType != null) {
				// the return value is a basic type, so we have to handle the
				// 'class' version of the type (i.e Boolean for boolean)
				JBlock thenBlock = jBlock._if(jContextParamVar.ne(JExpr._null()))._then();
				JVar objectResultJVar = thenBlock.decl(cachedType, "objectResult").init(JExpr._null());

				thenBlock.assign(
						objectResultJVar,
						JExpr.cast(
								cachedType,
								JExpr.invoke("getValidCachedAttributeObject").arg(JExpr.lit(attrId))
										.arg(jContextParamVar.invoke("getMaxAgeForAttributeValues"))));

				if (jre15) {
					thenBlock._if(objectResultJVar.ne(JExpr._null()))._then()._return(objectResultJVar);
				} else {
					String typename = objectResultJVar.type().name();
					String prefix;
					if (typename.equals("Short") || typename.equals("Long") || typename.equals("Boolean")) {
						prefix = typename.toLowerCase();
					} else if (typename.equals("Integer")) {
						prefix = "int";
					} else {
						throw new Exception("Unknown base type!!!");
					}

					thenBlock._if(objectResultJVar.ne(JExpr._null()))._then()._return(objectResultJVar.invoke(prefix + "Value"));
				}

			} else {
				// the same as above but the return type is a complex type so we
				// use this complex type
				JBlock thenBlock = jBlock._if(jContextParamVar.ne(JExpr._null()))._then();
				JVar objectResultJVar = thenBlock.decl(jMethod.type(), "objectResult").init(JExpr._null());

				thenBlock.assign(
						objectResultJVar,
						JExpr.cast(
								jMethod.type(),
								JExpr.invoke("getValidCachedAttributeObject").arg(JExpr.lit(attrId))
										.arg(jContextParamVar.invoke("getMaxAgeForAttributeValues"))));
				thenBlock._if(objectResultJVar.ne(JExpr._null()))._then()._return(objectResultJVar);
			}
		}

		JTryBlock jTryBlock = null;
		JBlock jTryBlockBody = null;

		if (optimizeForSize) {
			jTryBlockBody = jBlock;
		} else {
			jTryBlock = jBlock._try();
			jTryBlockBody = jTryBlock.body();
		}

		JInvocation readAttributeInvocation = null;

		if (optimizeForSize) {
			readAttributeInvocation = JExpr.invoke("readAttribute").arg(JExpr.lit(attrId)).arg(jContextParamVar);
		} else {
			readAttributeInvocation = JExpr.invoke("readAttribute").arg(jClusterClass.staticRef("CLUSTER_ID"))
					.arg(JExpr.lit(attrId)).arg(JExpr.lit(true));
		}

		JVar jZclFrameVar = jTryBlockBody.decl(jModel.ref(IZclFrame.class), "zclFrame").init(readAttributeInvocation);

		if (!optimizeForSize) {
			if (isServer)
				jTryBlockBody.directStatement("if (zclFrame.isClientToServer())");
			else
				jTryBlockBody.directStatement("if (zclFrame.isServerToClient())");

			jTryBlockBody.directStatement("	throw new ApplianceException(\"bad direction field in ZigBee\");");
		}

		if (generateCacheAccess) {
			JVar jZclTypeVar = jTryBlockBody.decl(jMethod.type(), "v").init(type.staticInvoke("zclParse").arg(jZclFrameVar));
			if (cachedType != null) {
				jTryBlockBody.invoke("setCachedAttributeObject").arg(JExpr.lit(attrId))
						.arg(JExpr._new(cachedType).arg(jZclTypeVar));
			} else {
				jTryBlockBody.invoke("setCachedAttributeObject").arg(JExpr.lit(attrId)).arg(jZclTypeVar);
			}

			jTryBlockBody._return(jZclTypeVar);
		} else {
			jTryBlockBody._return(type.staticInvoke("zclParse").arg(jZclFrameVar));
		}

		if (!optimizeForSize) {
			JCatchBlock jCatchZigBeeExceptionBlock = jTryBlock._catch(jModel.ref(ZigBeeException.class));

			jCatchZigBeeExceptionBlock.body()._throw(
					JExpr._new(jModel.ref(ApplianceException.class)).arg(JExpr.direct("ERROR_PARSING_MESSAGE")));

			JCatchBlock JCatchZclBeeValidatonExceptionBlock = jTryBlock._catch(jModel.ref(ZclValidationException.class));
			JCatchZclBeeValidatonExceptionBlock.body().directStatement(
					"throw new ApplianceException(\"Zcl Validation Exception\");");
		}
		return jMethod;
	}