* @param zclClusterDescriptor
	 * @throws Exception
	 */

	private void generateZclClusterAssets(JCodeModel jModel, JPackage jPackage, ZclClusterDescriptor zclClusterDescriptor,
			boolean isServer) throws Exception {

		boolean needsNotify = true;

		JClass jClusterConnectionClass = getClusterConnectionClass(zclClusterDescriptor, isServer);

		if (jClusterConnectionClass == null) {
			System.out.println("ERROR: class '" + getClusterImplementationName(zclClusterDescriptor, isServer) + "' not found");
			return;
		}

		JDefinedClass jClusterClass = jPackage._class(getZclClusterImplementationName(zclClusterDescriptor, isServer));
		// jClusterClass._implements(jClusterConnectionClassOpposite);
		jClusterClass._implements(jClusterConnectionClass);
		jClusterClass._extends(jModel.ref(ZclServiceCluster.class));

		System.out.println("Generating Zcl Cluster " + jClusterClass.fullName());

		if (needsNotify)
			jClusterClass._implements(jModel.ref(ZigBeeDeviceListener.class));

		if (addJavadoc) {
			jClusterClass.javadoc().add(copyrightClass);
		}

		// CLUSTER_ID static field
		JFieldVar jFieldClusterId = jClusterClass.field(JMod.STATIC | JMod.FINAL | JMod.PUBLIC, short.class, "CLUSTER_ID");
		jFieldClusterId.init(JExpr.lit(zclClusterDescriptor.getId()));

		JFieldVar jFieldAttributesMapByName = null;
		JFieldVar jFieldAttributesMapById = null;

		Vector zclAttributeDescriptors = null;

		if (isServer)
			zclAttributeDescriptors = zclClusterDescriptor.getZclServerAttributesDescriptors();
		else
			zclAttributeDescriptors = zclClusterDescriptor.getZclClientAttributesDescriptors();

		if (zclAttributeDescriptors.size() > 0) {
			jFieldAttributesMapByName = jClusterClass.field(JMod.STATIC, Map.class, "attributesMapByName");
			jFieldAttributesMapByName.init(JExpr._null());

			jFieldAttributesMapById = jClusterClass.field(JMod.STATIC, Map.class, "attributesMapById");
			jFieldAttributesMapById.init(JExpr._null());
		}

		//
		// generates the implementation part
		//

		// generates constructor
		JMethod constructor = jClusterClass.constructor(JMod.PUBLIC);
		constructor.body().directStatement("super();");
		constructor._throws(ApplianceException.class);

		// generate attach, detach methods
		if (needsNotify) {
			if (generateAttachDetach) {
				JMethod jAttachMethod = jClusterClass.method(JMod.PUBLIC, void.class, "zclAttach");
				JMethod jDetachMethod = jClusterClass.method(JMod.PUBLIC, void.class, "zclDetach");
				jAttachMethod.param(ZigBeeDevice.class, "device");
				jDetachMethod.param(ZigBeeDevice.class, "device");

				jAttachMethod.body().directStatement("device.setListener(CLUSTER_ID, this);");
				jDetachMethod.body().directStatement("device.removeListener(CLUSTER_ID, this);");

			}
			// notify method
			generateNotifyMethod(jModel, jPackage, jClusterClass, zclClusterDescriptor, isServer);
		}

		// getClusterId() method
		JMethod jGetClusterIdMethod = jClusterClass.method(JMod.PROTECTED, int.class, "getClusterId");
		jGetClusterIdMethod.body()._return(jFieldClusterId);

		JMethod jGetAttributeDescriptorByNameMethod = null;

		// Generates the getPeerAttributeDescriptors() only if the peer cluster
		// has attributes
		Vector peerAttributeDescriptors = null;
		if (isServer)
			peerAttributeDescriptors = zclClusterDescriptor.getZclClientAttributesDescriptors();
		else
			peerAttributeDescriptors = zclClusterDescriptor.getZclServerAttributesDescriptors();

		if (peerAttributeDescriptors.size() > 0) {
			JMethod jGetPeerClusterAttributeDescriptorsMethod = jClusterClass.method(JMod.PROTECTED,
					jIZclAttributeDescription.array(), "getPeerClusterAttributeDescriptors");
			JClass interfaceClass = getZclClass(zclClusterDescriptor, !isServer);
			if (interfaceClass != null) {
				jGetPeerClusterAttributeDescriptorsMethod.body()._return(interfaceClass.staticRef("attributeDescriptors"));
			}
		}

		if (zclAttributeDescriptors.size() > 0) {
			// getAttributeDescriptor() by name method
			jGetAttributeDescriptorByNameMethod = jClusterClass.method(JMod.PROTECTED, IZclAttributeDescriptor.class,
					"getAttributeDescriptor");

			jGetAttributeDescriptorByNameMethod.param(String.class, "name");
			jGetAttributeDescriptorByNameMethod.body()._return(
					JExpr.direct("(IZclAttributeDescriptor) attributesMapByName.get(name)"));
		}

		// getAttributeDescriptor() by attr id method
		if (manageAttributeReportings && zclAttributeDescriptors.size() > 0) {
			// getAttributeDescriptor() by id method
			jGetAttributeDescriptorByNameMethod = jClusterClass.method(JMod.PROTECTED, IZclAttributeDescriptor.class,
					"getAttributeDescriptor");

			jGetAttributeDescriptorByNameMethod.param(int.class, "attrId");
			jGetAttributeDescriptorByNameMethod.body()._return(
					JExpr.direct("(IZclAttributeDescriptor) attributesMapById.get(attrId)"));
		}

		if (zclAttributeDescriptors.size() > 0) {
			JMethod jGetGetAttributeDescriptorsMethod = jClusterClass.method(JMod.PROTECTED, Collection.class,
					"getAttributeDescriptors");

			// jGetGetAttributeDescriptorsMethod.body()._return(jFieldAttributesMapByName.invoke("values"));
			jGetGetAttributeDescriptorsMethod.body()._return(JExpr.direct("attributesMapByName.values()"));
		}
		// generates command signatures
		Vector zclExecCommandsDescriptors = null;
		Vector zclParseCommandsDescriptors = null;

		if (isServer) {
			zclExecCommandsDescriptors = zclClusterDescriptor.getZclServerCommandsDescriptors();
			zclParseCommandsDescriptors = zclClusterDescriptor.getZclClientCommandsDescriptors();
		} else {
			zclExecCommandsDescriptors = zclClusterDescriptor.getZclClientCommandsDescriptors();
			zclParseCommandsDescriptors = zclClusterDescriptor.getZclServerCommandsDescriptors();
		}

		for (int i = 0; i < zclExecCommandsDescriptors.size(); i++) {
			ZclCommandDescriptor zclCommandDescriptor = (ZclCommandDescriptor) zclExecCommandsDescriptors.get(i);
			try {
				generateZclCommandImplementation(jModel, jClusterClass, jClusterConnectionClass, zclCommandDescriptor);
			} catch (Exception e) {
				continue;
			}
		}

		for (int i = 0; i < zclParseCommandsDescriptors.size(); i++) {
			ZclCommandDescriptor zclCommandDescriptor = (ZclCommandDescriptor) zclParseCommandsDescriptors.get(i);

			if (!zclCommandDescriptor.isResponse())
				generateZclParseCommandImplementation(jModel, jClusterClass, jClusterConnectionClass, zclClusterDescriptor,
						zclCommandDescriptor, isServer);
		}

		// static block is added only if necessary!

		if (zclAttributeDescriptors.size() > 0) {
			// initialize the attribute descriptors!
			JClass jClassAttributeDescriptor = jModel.ref(ZclAttributeDescriptor.class);
			JBlock jStaticBlockAttributes = jClusterClass.init().block();
			JFieldVar jFieldAttributeDescriptors = jClusterClass.field(JMod.STATIC, jClassAttributeDescriptor.array(),
					"attributeDescriptors");
			jStaticBlockAttributes.assign(jFieldAttributeDescriptors,
					JExpr.newArray(jClassAttributeDescriptor, zclAttributeDescriptors.size()));

			String s = "";
			for (int i = 0; i < zclAttributeDescriptors.size(); i++) {
				ZclAttributeDescr zclAttributeDescriptor = (ZclAttributeDescr) zclAttributeDescriptors.get(i);

				if (zclAttributeDescriptor.getAccess() == ZclAttributeDescr.ACCESS_RW)
					generateZclAttributeSetterImplementation(jModel, jClusterClass, jClusterConnectionClass,
							zclAttributeDescriptor, isServer);

				generateZclAttributeGetterImplementation(jModel, jClusterClass, jClusterConnectionClass, zclAttributeDescriptor,
						isServer);

				// generate the AttributeDescriptor
				JType type = getZclDataTypeJClass(jModel, jClusterClass.getPackage(), jClusterConnectionClass,
						zclAttributeDescriptor.getTypeName(), zclAttributeDescriptor);

				JExpression javaClass = JExpr._null();

				if (type == null) {
					System.out.println("Unable to generate type '" + zclAttributeDescriptor.getTypeName() + "'");
					continue;
				}
				JFieldRef jFieldAttributeName = jClusterClass.staticRef(getAttributeNameConstant(zclAttributeDescriptor.getName()));

				// TODO optimize. The following two invoke differs only for
				// the_new(type)

				JInvocation instance = null;

				// jStaticBlockAttributes.assign(jFieldAttributeDescriptors,
				// JExpr._null());

				jFieldAttributeDescriptors.init(JExpr._null());

				if (!zclAttributeDescriptor.hasVariableSize()) {
					instance = JExpr._new(jClassAttributeDescriptor).arg(JExpr.lit(zclAttributeDescriptor.getId()))
							.arg(jFieldAttributeName).arg(JExpr._new(type)).arg(javaClass)
							.arg(JExpr.lit(zclAttributeDescriptor.isReportable()))
							.arg(JExpr.lit(zclAttributeDescriptor.getAccess()));
				} else {
					instance = JExpr._new(jClassAttributeDescriptor).arg(JExpr.lit(zclAttributeDescriptor.getId()))
							.arg(jFieldAttributeName).arg(JExpr._new(type).arg(JExpr.lit(zclAttributeDescriptor.getSize())))
							.arg(javaClass).arg(JExpr.lit(zclAttributeDescriptor.isReportable()))
							.arg(JExpr.lit(zclAttributeDescriptor.getAccess()));
				}
				if (instance != null) {
					jStaticBlockAttributes.assign(jFieldAttributeDescriptors.component(JExpr.lit(i)), instance);
				}
			}

			JBlock jStaticBlockCreateMaps = jClusterClass.init().block();

			if (jFieldAttributesMapByName != null) {
				jStaticBlockCreateMaps.assign(jFieldAttributesMapByName,
						JExpr.invoke("fillAttributesMapsByName").arg(jFieldAttributeDescriptors).arg(jFieldAttributesMapByName));
			}
			if (jFieldAttributesMapById != null) {
				jStaticBlockCreateMaps.assign(jFieldAttributesMapById,
						JExpr.invoke("fillAttributesMapsById").arg(jFieldAttributeDescriptors).arg(jFieldAttributesMapById));
			}
		}

		generateReadAttributeResponseGetSize(jModel, jPackage, jClusterClass, zclClusterDescriptor, isServer);
		generateReadAttributeMethod(jModel, jPackage, jClusterClass, zclClusterDescriptor, isServer);
		generateWriteAttributeMethod(jModel, jPackage, jClusterClass, zclClusterDescriptor, isServer);
	}