text:
/// \file   X3DImporter_Postprocess.cpp
/// \brief  Convert built scenegraph and objects to Assimp scenegraph.
/// \date   2015-2016
/// \author smal.root@gmail.com

#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER

#include "X3DImporter.hpp"

// Header files, Assimp.
#include <assimp/ai_assert.h>
#include <assimp/StandardShapes.h>
#include <assimp/StringUtils.h>

// Header files, stdlib.
#include <algorithm>
#include <iterator>
#include <string>

namespace Assimp
{

aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const
{
    CX3DImporter_NodeElement* cur_node;
    std::list<aiMatrix4x4> matr;
    aiMatrix4x4 out_matr;

	// starting walk from current element to root
	cur_node = NodeElement_Cur;
	if(cur_node != nullptr)
	{
		do
		{
			// if cur_node is group then store group transformation matrix in list.
			if(cur_node->Type == CX3DImporter_NodeElement::ENET_Group) matr.push_back(((CX3DImporter_NodeElement_Group*)cur_node)->Transformation);

			cur_node = cur_node->Parent;
		} while(cur_node != nullptr);
	}

	// multiplicate all matrices in reverse order
	for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) out_matr = out_matr * (*rit);

	return out_matr;
}

void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const
{
	// walk through childs and find for metadata.
	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it)
	{
		if(((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) ||
			((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) ||
			((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaString))
		{
			pList.push_back(*el_it);
		}
		else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaSet)
		{
			PostprocessHelper_CollectMetadata(**el_it, pList);
		}
	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
}

bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const
{
	if((pType == CX3DImporter_NodeElement::ENET_MetaBoolean) || (pType == CX3DImporter_NodeElement::ENET_MetaDouble) ||
		(pType == CX3DImporter_NodeElement::ENET_MetaFloat) || (pType == CX3DImporter_NodeElement::ENET_MetaInteger) ||
		(pType == CX3DImporter_NodeElement::ENET_MetaString) || (pType == CX3DImporter_NodeElement::ENET_MetaSet))
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const
{
	if((pType == CX3DImporter_NodeElement::ENET_Arc2D) || (pType == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
		(pType == CX3DImporter_NodeElement::ENET_Box) || (pType == CX3DImporter_NodeElement::ENET_Circle2D) ||
		(pType == CX3DImporter_NodeElement::ENET_Cone) || (pType == CX3DImporter_NodeElement::ENET_Cylinder) ||
		(pType == CX3DImporter_NodeElement::ENET_Disk2D) || (pType == CX3DImporter_NodeElement::ENET_ElevationGrid) ||
		(pType == CX3DImporter_NodeElement::ENET_Extrusion) || (pType == CX3DImporter_NodeElement::ENET_IndexedFaceSet) ||
		(pType == CX3DImporter_NodeElement::ENET_IndexedLineSet) || (pType == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
		(pType == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || (pType == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet) ||
		(pType == CX3DImporter_NodeElement::ENET_PointSet) || (pType == CX3DImporter_NodeElement::ENET_LineSet) ||
		(pType == CX3DImporter_NodeElement::ENET_Polyline2D) || (pType == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
		(pType == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pType == CX3DImporter_NodeElement::ENET_Sphere) ||
		(pType == CX3DImporter_NodeElement::ENET_TriangleFanSet) || (pType == CX3DImporter_NodeElement::ENET_TriangleSet) ||
		(pType == CX3DImporter_NodeElement::ENET_TriangleSet2D) || (pType == CX3DImporter_NodeElement::ENET_TriangleStripSet))
	{
		return true;
	}
	else
	{
		return false;
	}
}

void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const
{
    const CX3DImporter_NodeElement_Light& ne = *( ( CX3DImporter_NodeElement_Light* ) &pNodeElement );
    aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent();
    aiLight* new_light = new aiLight;

	new_light->mName = ne.ID;
	new_light->mColorAmbient = ne.Color * ne.AmbientIntensity;
	new_light->mColorDiffuse = ne.Color * ne.Intensity;
	new_light->mColorSpecular = ne.Color * ne.Intensity;
	switch(pNodeElement.Type)
	{
		case CX3DImporter_NodeElement::ENET_DirectionalLight:
			new_light->mType = aiLightSource_DIRECTIONAL;
			new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;

			break;
		case CX3DImporter_NodeElement::ENET_PointLight:
			new_light->mType = aiLightSource_POINT;
			new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
			new_light->mAttenuationConstant = ne.Attenuation.x;
			new_light->mAttenuationLinear = ne.Attenuation.y;
			new_light->mAttenuationQuadratic = ne.Attenuation.z;

			break;
		case CX3DImporter_NodeElement::ENET_SpotLight:
			new_light->mType = aiLightSource_SPOT;
			new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
			new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
			new_light->mAttenuationConstant = ne.Attenuation.x;
			new_light->mAttenuationLinear = ne.Attenuation.y;
			new_light->mAttenuationQuadratic = ne.Attenuation.z;
			new_light->mAngleInnerCone = ne.BeamWidth;
			new_light->mAngleOuterCone = ne.CutOffAngle;

			break;
		default:
			throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + ".");
	}

	pSceneLightList.push_back(new_light);
}

void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const
{
	// check argument
	if(pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr.");
	if(*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr.");

	*pMaterial = new aiMaterial;
	aiMaterial& taimat = **pMaterial;// creating alias for convenience.

	// at this point pNodeElement point to <Appearance> node. Walk through childs and add all stored data.
	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it)
	{
		if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
		{
			aiColor3D tcol3;
			float tvalf;
			CX3DImporter_NodeElement_Material& tnemat = *((CX3DImporter_NodeElement_Material*)*el_it);

			tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity;
			taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT);
			taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE);
			taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE);
			taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR);
			tvalf = 1;
			taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH);
			taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS);
			tvalf = 1.0f - tnemat.Transparency;
			taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY);
		}// if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
		else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
		{
			CX3DImporter_NodeElement_ImageTexture& tnetex = *((CX3DImporter_NodeElement_ImageTexture*)*el_it);
			aiString url_str(tnetex.URL.c_str());
			int mode = aiTextureOp_Multiply;

			taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0));
			taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
			taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
			taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
		}// else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
		else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
		{
			aiUVTransform trans;
			CX3DImporter_NodeElement_TextureTransform& tnetextr = *((CX3DImporter_NodeElement_TextureTransform*)*el_it);

			trans.mTranslation = tnetextr.Translation - tnetextr.Center;
			trans.mScaling = tnetextr.Scale;
			trans.mRotation = tnetextr.Rotation;
			taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
		}// else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
}

void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const
{
	// check argument
	if(pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr.");
	if(*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr.");

	/************************************************************************************************************************************/
	/************************************************************ Geometry2D ************************************************************/
	/************************************************************************************************************************************/
	if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) ||
		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D))
	{
		CX3DImporter_NodeElement_Geometry2D& tnemesh = *((CX3DImporter_NodeElement_Geometry2D*)&pNodeElement);// create alias for convenience
		std::vector<aiVector3D> tarr;

		tarr.reserve(tnemesh.Vertices.size());
		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) tarr.push_back(*it);
		*pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices));// create mesh from vertices using Assimp help.

		return;// mesh is build, nothing to do anymore.
	}
	/************************************************************************************************************************************/
	/************************************************************ Geometry3D ************************************************************/
	/************************************************************************************************************************************/
	//
	// Predefined figures
	//
	if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) ||
		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere))
	{
		CX3DImporter_NodeElement_Geometry3D& tnemesh = *((CX3DImporter_NodeElement_Geometry3D*)&pNodeElement);// create alias for convenience
		std::vector<aiVector3D> tarr;

		tarr.reserve(tnemesh.Vertices.size());
		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) tarr.push_back(*it);

		*pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices));// create mesh from vertices using Assimp help.

		return;// mesh is build, nothing to do anymore.
	}
	//
	// Parametric figures
	//
	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
	{
		CX3DImporter_NodeElement_ElevationGrid& tnemesh = *((CX3DImporter_NodeElement_ElevationGrid*)&pNodeElement);// create alias for convenience

		// at first create mesh from existing vertices.
		*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices);
		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
				MeshGeometry_AddNormal(**pMesh,  ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value, tnemesh.NormalPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
				MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
	//
	// Indexed primitives sets
	//
	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
	{
		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
										tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
										tnemesh.NormalPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)

	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
	{
		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			ai_assert(*pMesh);
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
										tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)

	if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) ||
		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
	{
		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			ai_assert(*pMesh);
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
										tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
										tnemesh.NormalPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \
																	IndexedTriangleStripSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))

	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)
	{
		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience

		*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices);

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)

	//
	// Primitives sets
	//
	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
	{
		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				std::vector<aiVector3D> vec_copy;

				vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
				for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); ++it)
				{
					vec_copy.push_back(*it);
				}

				*pMesh = StandardShapes::MakeMesh(vec_copy, 1);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			ai_assert(*pMesh);
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, true);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)

	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
	{
		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			ai_assert(*pMesh);
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, true);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)

	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
	{
		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if ( nullptr == *pMesh ) {
				break;
			}
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value,tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
										tnemesh.NormalPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)

	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
	{
		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				std::vector<aiVector3D> vec_copy;

				vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
				for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); ++it)
				{
					vec_copy.push_back(*it);
				}

				*pMesh = StandardShapes::MakeMesh(vec_copy, 3);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			ai_assert(*pMesh);
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
										tnemesh.NormalPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)

	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
	{
		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience

		// at first search for <Coordinate> node and create mesh.
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
			{
				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
			}
		}

		// copy additional information from children
		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
		{
			ai_assert(*pMesh);
			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
				{} // skip because already read when mesh created.
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
										tnemesh.NormalPerVertex);
			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
			else
				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + ".");
		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)

		return;// mesh is build, nothing to do anymore.
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)

	throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + ".");
}

void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList,
										std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const
{
    std::list<CX3DImporter_NodeElement*>::const_iterator chit_begin = pNodeElement.Child.begin();
    std::list<CX3DImporter_NodeElement*>::const_iterator chit_end = pNodeElement.Child.end();
    std::list<aiNode*> SceneNode_Child;
    std::list<unsigned int> SceneNode_Mesh;

	// At first read all metadata
	Postprocess_CollectMetadata(pNodeElement, pSceneNode);
	// check if we have deal with grouping node. Which can contain transformation or switch
	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
	{
		const CX3DImporter_NodeElement_Group& tne_group = *((CX3DImporter_NodeElement_Group*)&pNodeElement);// create alias for convenience

		pSceneNode.mTransformation = tne_group.Transformation;
		if(tne_group.UseChoice)
		{
			// If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen.
			if((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Child.size()))
			{
				chit_begin = pNodeElement.Child.end();
				chit_end = pNodeElement.Child.end();
			}
			else
			{
				for(size_t i = 0; i < (size_t)tne_group.Choice; i++) ++chit_begin;// forward iterator to chosen node.

				chit_end = chit_begin;
				++chit_end;// point end iterator to next element after chosen node.
			}
		}// if(tne_group.UseChoice)
	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)

	// Reserve memory for fast access and check children.
	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; ++it)
	{// in this loop we do not read metadata because it's already read at begin.
		if((*it)->Type == CX3DImporter_NodeElement::ENET_Group)
		{
			// if child is group then create new node and do recursive call.
			aiNode* new_node = new aiNode;

			new_node->mName = (*it)->ID;
			new_node->mParent = &pSceneNode;
			SceneNode_Child.push_back(new_node);
			Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList);
		}
		else if((*it)->Type == CX3DImporter_NodeElement::ENET_Shape)
		{
			// shape can contain only one geometry and one appearance nodes.
			Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape*)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList);
		}
		else if(((*it)->Type == CX3DImporter_NodeElement::ENET_DirectionalLight) || ((*it)->Type == CX3DImporter_NodeElement::ENET_PointLight) ||
				((*it)->Type == CX3DImporter_NodeElement::ENET_SpotLight))
		{
			Postprocess_BuildLight(*((CX3DImporter_NodeElement_Light*)*it), pSceneLightList);
		}
		else if(!PostprocessHelper_ElementIsMetadata((*it)->Type))// skip metadata
		{
			throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + ".");
		}
	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)

	// copy data about children and meshes to aiNode.
	if(!SceneNode_Child.empty())
	{
		std::list<aiNode*>::const_iterator it = SceneNode_Child.begin();

		pSceneNode.mNumChildren = static_cast<unsigned int>(SceneNode_Child.size());
		pSceneNode.mChildren = new aiNode*[pSceneNode.mNumChildren];
		for(size_t i = 0; i < pSceneNode.mNumChildren; i++) pSceneNode.mChildren[i] = *it++;
	}

	if(!SceneNode_Mesh.empty())
	{
		std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin();

		pSceneNode.mNumMeshes = static_cast<unsigned int>(SceneNode_Mesh.size());
		pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
		for(size_t i = 0; i < pSceneNode.mNumMeshes; i++) pSceneNode.mMeshes[i] = *it++;
	}

	// that's all. return to previous deals
}

void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd,
							std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const
{
    aiMaterial* tmat = nullptr;
    aiMesh* tmesh = nullptr;
    CX3DImporter_NodeElement::EType mesh_type = CX3DImporter_NodeElement::ENET_Invalid;
    unsigned int mat_ind = 0;

	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); ++it)
	{
		if(PostprocessHelper_ElementIsMesh((*it)->Type))
		{
			Postprocess_BuildMesh(**it, &tmesh);
			if(tmesh != nullptr)
			{
				// if mesh successfully built then add data about it to arrays
				pNodeMeshInd.push_back(static_cast<unsigned int>(pSceneMeshList.size()));
				pSceneMeshList.push_back(tmesh);
				// keep mesh type. Need above for texture coordinate generation.
				mesh_type = (*it)->Type;
			}
		}
		else if((*it)->Type == CX3DImporter_NodeElement::ENET_Appearance)
		{
			Postprocess_BuildMaterial(**it, &tmat);
			if(tmat != nullptr)
			{
				// if material successfully built then add data about it to array
				mat_ind = static_cast<unsigned int>(pSceneMaterialList.size());
				pSceneMaterialList.push_back(tmat);
			}
		}
	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)

	// associate read material with read mesh.
	if((tmesh != nullptr) && (tmat != nullptr))
	{
		tmesh->mMaterialIndex = mat_ind;
		// Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates.
		if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
		{
			int32_t tm;
			aiVector3D tvec3;

			switch(mesh_type)
			{
				case CX3DImporter_NodeElement::ENET_Box:
					tm = aiTextureMapping_BOX;
					break;
				case CX3DImporter_NodeElement::ENET_Cone:
				case CX3DImporter_NodeElement::ENET_Cylinder:
					tm = aiTextureMapping_CYLINDER;
					break;
				case CX3DImporter_NodeElement::ENET_Sphere:
					tm = aiTextureMapping_SPHERE;
					break;
				default:
					tm = aiTextureMapping_PLANE;
					break;
			}// switch(mesh_type)

			tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
		}// if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
	}// if((tmesh != nullptr) && (tmat != nullptr))
}

void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode) const
{
    std::list<CX3DImporter_NodeElement*> meta_list;
    size_t meta_idx;

	PostprocessHelper_CollectMetadata(pNodeElement, meta_list);// find metadata in current node element.
	if ( !meta_list.empty() )
	{
        if ( pSceneNode.mMetaData != nullptr ) {
            throw DeadlyImportError( "Postprocess. MetaData member in node are not nullptr. Something went wrong." );
        }

		// copy collected metadata to output node.
        pSceneNode.mMetaData = aiMetadata::Alloc( static_cast<unsigned int>(meta_list.size()) );
		meta_idx = 0;
		for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx)
		{
			CX3DImporter_NodeElement_Meta* cur_meta = (CX3DImporter_NodeElement_Meta*)*it;

			// due to limitations we can add only first element of value list.
			// Add an element according to its type.
			if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean)
			{
				if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
					pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.begin()));
			}
			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble)
			{
				if(((CX3DImporter_NodeElement_MetaDouble*)cur_meta)->Value.size() > 0)
					pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble*)cur_meta)->Value.begin()));
			}
			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat)
			{
				if(((CX3DImporter_NodeElement_MetaFloat*)cur_meta)->Value.size() > 0)
					pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat*)cur_meta)->Value.begin()));
			}
			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger)
			{
				if(((CX3DImporter_NodeElement_MetaInteger*)cur_meta)->Value.size() > 0)
					pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger*)cur_meta)->Value.begin()));
			}
			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString)
			{
				if(((CX3DImporter_NodeElement_MetaString*)cur_meta)->Value.size() > 0)
				{
					aiString tstr(((CX3DImporter_NodeElement_MetaString*)cur_meta)->Value.begin()->data());

					pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, tstr);
				}
			}
			else
			{
				throw DeadlyImportError("Postprocess. Unknown metadata type.");
			}// if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else
		}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++)
	}// if( !meta_list.empty() )
}

}// namespace Assimp

#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER


text:
import re
import urllib.parse as urlparse

from django.conf import settings

REPLACE_STR = '$encrypted$'

class UriCleaner(object):
    REPLACE_STR = REPLACE_STR
    SENSITIVE_URI_PATTERN = re.compile(r'(\w{1,20}:(\/?\/?)[^\s]+)', re.MULTILINE)  # NOQA

    @staticmethod
    def remove_sensitive(cleartext):
        # exclude_list contains the items that will _not_ be redacted
        exclude_list = [settings.PUBLIC_GALAXY_SERVER['url']]
        if settings.PRIMARY_GALAXY_URL:
            exclude_list += [settings.PRIMARY_GALAXY_URL]
        if settings.FALLBACK_GALAXY_SERVERS:
            exclude_list += [server['url'] for server in settings.FALLBACK_GALAXY_SERVERS]
        redactedtext = cleartext
        text_index = 0
        while True:
            match = UriCleaner.SENSITIVE_URI_PATTERN.search(redactedtext, text_index)
            if not match:
                break
            uri_str = match.group(1)
            # Do not redact items from the exclude list
            if any(uri_str.startswith(exclude_uri) for exclude_uri in exclude_list):
                text_index = match.start() + len(uri_str)
                continue
            try:
                # May raise a ValueError if invalid URI for one reason or another
                o = urlparse.urlsplit(uri_str)

                if not o.username and not o.password:
                    if o.netloc and ":" in o.netloc:
                        # Handle the special case url http://username:password that can appear in SCM url
                        # on account of a bug? in ansible redaction
                        (username, password) = o.netloc.split(':')
                    else:
                        text_index += len(match.group(1))
                        continue
                else:
                    username = o.username
                    password = o.password

                # Given a python MatchObject, with respect to redactedtext, find and
                # replace the first occurance of username and the first and second
                # occurance of password

                uri_str = redactedtext[match.start():match.end()]
                if username:
                    uri_str = uri_str.replace(username, UriCleaner.REPLACE_STR, 1)
                # 2, just in case the password is $encrypted$
                if password:
                    uri_str = uri_str.replace(password, UriCleaner.REPLACE_STR, 2)

                t = redactedtext[:match.start()] + uri_str
                text_index = len(t)
                if (match.end() < len(redactedtext)):
                    t += redactedtext[match.end():]

                redactedtext = t
                if text_index >= len(redactedtext):
                    text_index = len(redactedtext) - 1
            except ValueError:
                # Invalid URI, redact the whole URI to be safe
                redactedtext = redactedtext[:match.start()] + UriCleaner.REPLACE_STR + redactedtext[match.end():]
                text_index = match.start() + len(UriCleaner.REPLACE_STR)

        return redactedtext

class PlainTextCleaner(object):
    REPLACE_STR = REPLACE_STR

    @staticmethod
    def remove_sensitive(cleartext, sensitive):
        if sensitive == '':
            return cleartext
        return re.sub(r'%s' % re.escape(sensitive), '$encrypted$', cleartext)

redpajama_set_name: RedPajamaGithub


text:
using System.IO;
using Aspose.Pdf;
using System;

namespace Aspose.Pdf.Examples.CSharp.AsposePDF.WorkingDocuments
{
    public class GetFileInfo
    {
        public static void Run()
        {
            // ExStart:GetFileInfo
            // The path to the documents directory.
            string dataDir = RunExamples.GetDataDir_AsposePdf_WorkingDocuments();

            // Open document
            Document pdfDocument = new Document(dataDir + "GetFileInfo.pdf");
            // Get document information
            DocumentInfo docInfo = pdfDocument.Info;
            // Show document information
            Console.WriteLine("Author: {0}", docInfo.Author);
            Console.WriteLine("Creation Date: {0}", docInfo.CreationDate);
            Console.WriteLine("Keywords: {0}", docInfo.Keywords);
            Console.WriteLine("Modify Date: {0}", docInfo.ModDate);
            Console.WriteLine("Subject: {0}", docInfo.Subject);
            Console.WriteLine("Title: {0}", docInfo.Title);
            // ExEnd:GetFileInfo
        }
    }
}


text:
package crossword.ext;

import java.io.File;
import java.io.FilenameFilter;

import javax.swing.filechooser.FileFilter;

public class ExtensionFileFilter extends FileFilter implements FilenameFilter {
	private String description;

	private String extensions[];

	public ExtensionFileFilter(String description, String... extensions) {
		if (description == null) {
			this.description = extensions[0];
		} else {
			this.description = description;
		}
		this.extensions = (String[]) extensions.clone();
		toLower(this.extensions);
	}

	private void toLower(String array[]) {
		for (int i = 0, n = array.length; i < n; i++) {
			array[i] = array[i].toLowerCase();
		}
	}

	public String getDescription() {
		return description;
	}

	@Override
	public boolean accept(File file) {
		if (file.isDirectory()) {
			return true;
		} else {
			String path = file.getAbsolutePath().toLowerCase();
			for (int i = 0, n = extensions.length; i < n; i++) {
				String extension = extensions[i];
				if ((path.endsWith(extension) && (path.charAt(path.length()
						- extension.length() - 1)) == '.')) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean accept(File dir, String name) {
		String newName = name.toLowerCase();
		for (int i = 0, n = extensions.length; i < n; i++) {
			String extension = extensions[i];
			if ((newName.endsWith(extension) && (newName.charAt(newName.length()
					- extension.length() - 1)) == '.')) {
				return true;
			}
		}
		return false;
	}
}


text:
set -e

# shellcheck disable=SC2001
#ALIAS_CMD="$(echo "$0" | sed -e 's?/sbin/??')"
#
#case "$ALIAS_CMD" in
#    start|stop|restart|reload|status)
#        exec service $1 $ALIAS_CMD
#        ;;
#esac

# check if there was a command passed
# required by Jenkins Docker plugin: https://github.com/docker-library/official-images#consistency
#if [ "$1" ]; then
#    # execute it
#    exec "$@"
#fi

echo "$0: DEBUG : $1"

# loop around arguments string
for ARG in "$*"; do
    echo "ARGS ITEM: $ARG"
done
# loop around arguments vector
for ARG in "$@"; do
    echo "ARGV ITEM: $ARG"
done

case "$1" in
    list )
        exec service --status-all
        ;;
    reload-configuration )
        exec service $2 restart
        ;;
    start|stop|restart|reload|status)
        exec service $2 $1
        ;;
    debug )
        /usr/local/bin/fixuid && bash
        ;;
    # match anything
    *)
        echo "EXEC : $@"
        exec "$@"
        #exit 0
        ;;
esac


text:
package org.olat.commons.calendar.ui;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.olat.commons.calendar.CalendarManager;
import org.olat.commons.calendar.CalendarManagerFactory;
import org.olat.commons.calendar.CalendarUtils;
import org.olat.commons.calendar.GotoDateEvent;
import org.olat.commons.calendar.ImportCalendarManager;
import org.olat.commons.calendar.model.Kalendar;
import org.olat.commons.calendar.model.KalendarComparator;
import org.olat.commons.calendar.model.KalendarEvent;
import org.olat.commons.calendar.notification.CalendarNotificationManager;
import org.olat.commons.calendar.ui.components.FullCalendarElement;
import org.olat.commons.calendar.ui.components.KalendarRenderWrapper;
import org.olat.commons.calendar.ui.events.KalendarGUIAddEvent;
import org.olat.commons.calendar.ui.events.KalendarGUIEditEvent;
import org.olat.commons.calendar.ui.events.KalendarGUIImportEvent;
import org.olat.commons.calendar.ui.events.KalendarGUIMoveEvent;
import org.olat.commons.calendar.ui.events.KalendarGUIPrintEvent;
import org.olat.commons.calendar.ui.events.KalendarGUISelectEvent;
import org.olat.commons.calendar.ui.events.KalendarModifiedEvent;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.services.notifications.NotificationsManager;
import org.olat.core.commons.services.notifications.PublisherData;
import org.olat.core.commons.services.notifications.SubscriptionContext;
import org.olat.core.commons.services.notifications.ui.ContextualSubscriptionController;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.FormLink;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.util.ComponentUtil;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController;
import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.gui.control.generic.modal.DialogBoxController;
import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
import org.olat.core.gui.control.winmgr.JSCommand;
import org.olat.core.id.UserConstants;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.activity.ILoggingAction;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.CodeHelper;
import org.olat.core.util.Util;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.event.GenericEventListener;
import org.olat.core.util.resource.OresHelper;
import org.olat.group.BusinessGroup;
import org.olat.util.logging.activity.LoggingResourceable;

public class WeeklyCalendarController extends FormBasicController implements Activateable2, CalendarController, GenericEventListener {

	public static final String CALLER_HOME = "home";
	public static final String CALLER_PROFILE = "profile";
	public static final String CALLER_COLLAB = "collab";
	public static final String CALLER_COURSE = "course";

	private List<KalendarRenderWrapper> calendarWrappers;
	private List<KalendarRenderWrapper> importedCalendarWrappers;
	private FullCalendarElement weeklyCalendar;
	private KalendarConfigurationController calendarConfig;
	private ImportedCalendarConfigurationController importedCalendarConfig;
	private KalendarEntryDetailsController editController;
	private SearchAllCalendarsController searchController;
	private ImportCalendarController importCalendarController;
	private CalendarSubscription calendarSubscription;
	private Controller subscriptionController;
	private String caller;
	private boolean dirty = false;
	private Link searchLink;
	private FormLink subscribeButton, unsubscribeButton;

	private CloseableModalController cmc;
	private GotoDateCalendarsForm gotoDateForm;
	private SubscriptionContext subsContext;
	private ContextualSubscriptionController csc;

	private CalendarPrintController printCtrl;
	private CalendarDetailsController eventDetailsCtr;
	private CloseableCalloutWindowController eventCalloutCtr;

	/**
	 * three options:
	 * 1. edit sequence
	 * 2. delete single date
	 * 3. delete whole sequence
	 */
	private DialogBoxController dbcSequence;
	private DialogBoxController deleteSingleYesNoController, deleteSequenceYesNoController;
	private String modifiedCalendarId;
	private boolean modifiedCalenderDirty = false;
	private final boolean eventAlwaysVisible;

	private ILoggingAction calLoggingAction;

	private final CalendarNotificationManager calendarNotificationsManager;

	/**
	 * Display week view of calendar. Add the calendars to be displayed via
	 * addKalendarWrapper(KalendarRenderWrapper kalendarWrapper) method.
	 * @param ureq
	 * @param wControl
	 * @param calendarWrappers
	 * @param caller
	 * @param eventAlwaysVisible  When true, the 'isVis()' check is disabled and events will be displayed always.
	 */
	public WeeklyCalendarController(UserRequest ureq, WindowControl wControl, List<KalendarRenderWrapper> calendarWrappers, String caller, boolean eventAlwaysVisible) {
		this(ureq, wControl, calendarWrappers, new ArrayList<KalendarRenderWrapper>(), caller, null, eventAlwaysVisible);
	}

	/**
	 * Used for Home
	 * @param ureq
	 * @param wControl
	 * @param calendarWrappers
	 * @param importedCalendarWrappers
	 * @param caller
	 * @param eventAlwaysVisible  When true, the 'isVis()' check is disabled and events will be displayed always.
	 */
	public WeeklyCalendarController(UserRequest ureq, WindowControl wControl, List<KalendarRenderWrapper> calendarWrappers, List<KalendarRenderWrapper> importedCalendarWrappers, String caller, boolean eventAlwaysVisible) {
		this(ureq, wControl, calendarWrappers, importedCalendarWrappers, caller, null, eventAlwaysVisible );
	}

	/**
	 *
	 * @param ureq
	 * @param wControl
	 * @param calendarWrappers
	 * @param caller
	 * @param calendarSubscription
	 * @param eventAlwaysVisible  When true, the 'isVis()' check is disabled and events will be displayed always.
	 */
	public WeeklyCalendarController(UserRequest ureq, WindowControl wControl, List<KalendarRenderWrapper> calendarWrappers, String caller, CalendarSubscription calendarSubscription, boolean eventAlwaysVisible) {
		this(ureq, wControl, calendarWrappers, new ArrayList<KalendarRenderWrapper>(), caller, calendarSubscription, eventAlwaysVisible);
	}

	private CalendarPrintMapper printMapper;
	private String printUrl;

	/**
	 * Display week view of calendar. Add the calendars to be displayed via
	 * addKalendarWrapper(KalendarRenderWrapper kalendarWrapper) method.
	 * @param ureq
	 * @param wControl
	 * @param calendarWrappers
	 * @param importedCalendarWrappers
	 * @param caller
	 * @param calendarSubscription
	 * @param eventAlwaysVisible  When true, the 'isVis()' check is disabled and events will be displayed always.
	 */
	public WeeklyCalendarController(UserRequest ureq, WindowControl wControl, List<KalendarRenderWrapper> calendarWrappers, List<KalendarRenderWrapper> importedCalendarWrappers,
			String caller, CalendarSubscription calendarSubscription, boolean eventAlwaysVisible) {
		super(ureq,wControl, "indexWeekly");
		setTranslator(Util.createPackageTranslator(CalendarManager.class, ureq.getLocale(), getTranslator()));

		calendarNotificationsManager = CoreSpringFactory.getImpl(CalendarNotificationManager.class);

		this.eventAlwaysVisible = eventAlwaysVisible;
		this.calendarWrappers = calendarWrappers;
		this.importedCalendarWrappers = importedCalendarWrappers;
		this.calendarSubscription = calendarSubscription;
		this.caller = caller;

		String themeBaseUri = wControl.getWindowBackOffice().getWindow().getGuiTheme().getBaseURI();
		printMapper = new CalendarPrintMapper(themeBaseUri, getTranslator());
		printUrl = registerMapper(ureq, printMapper);

		initForm(ureq);

		CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), OresHelper.lookupType(CalendarManager.class));
	}

	@Override
	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
		// fxdiff OLAT-6399
		boolean isGuest = ureq.getUserSession().getRoles().isGuestOnly();

		/*
		gotoDateForm = new GotoDateCalendarsForm(ureq, wControl, getTranslator());
		listenTo(gotoDateForm);
		vcMain.put("cal.gotodate", gotoDateForm.getInitialComponent());
		searchLink = LinkFactory.createButton("cal.search.button", vcMain, this);
		*/

		subscribeButton = uifactory.addFormLink("cal.subscribe", formLayout, Link.BUTTON_XSMALL);
		unsubscribeButton = uifactory.addFormLink("cal.unsubscribe", formLayout, Link.BUTTON_XSMALL);

		Collections.sort(calendarWrappers, KalendarComparator.getInstance());
		Collections.sort(importedCalendarWrappers, KalendarComparator.getInstance());

		List<KalendarRenderWrapper> allCalendarWrappers = new ArrayList<KalendarRenderWrapper>(calendarWrappers);
		allCalendarWrappers.addAll(importedCalendarWrappers);
		weeklyCalendar = new FullCalendarElement(ureq, "weeklyCalendar", allCalendarWrappers, getTranslator(), eventAlwaysVisible);
		formLayout.add("calendar", weeklyCalendar);

		if(formLayout instanceof FormLayoutContainer) {
			FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
			layoutCont.contextPut("caller", caller);

			// calendarConfiguration component
			calendarConfig = new KalendarConfigurationController(calendarWrappers, ureq, getWindowControl(), eventAlwaysVisible, true);
			listenTo(calendarConfig);
			layoutCont.put("calendarConfig", calendarConfig.getInitialComponent());

			//imported calendar list
			importedCalendarConfig = new ImportedCalendarConfigurationController(importedCalendarWrappers, ureq, getWindowControl(), false);
			listenTo(importedCalendarConfig);
			layoutCont.put("importedCalendarConfig", importedCalendarConfig.getInitialComponent());

			if (!isGuest && !calendarWrappers.isEmpty()) {
				subsContext = calendarNotificationsManager.getSubscriptionContext(calendarWrappers.get(0));
				// if sc is null, then no subscription is desired
				if (subsContext != null) {
					csc = getContextualSubscriptionController(ureq, calendarWrappers.get(0), subsContext);
					layoutCont.put("calsubscription", csc.getInitialComponent());
				}

				if (calendarSubscription == null || isGuest) {
					layoutCont.contextPut("hasSubscription", Boolean.FALSE);
				} else {
					layoutCont.contextPut("hasSubscription", Boolean.TRUE);
					layoutCont.contextPut("isSubscribed", new Boolean(calendarSubscription.isSubscribed()));
				}
			}
		}
	}

	@Override
	protected void formOK(UserRequest ureq) {
		//
	}

	private ContextualSubscriptionController getContextualSubscriptionController(UserRequest ureq, KalendarRenderWrapper kalendarRenderWrapper, SubscriptionContext context) {
		String businessPath = getWindowControl().getBusinessControl().getAsString();
		if ((caller.equals(CalendarController.CALLER_COURSE) || caller.equals(CalendarManager.TYPE_COURSE))) {
			Long courseId = kalendarRenderWrapper.getLinkProvider().getControler().getCourseId();

			PublisherData pdata = new PublisherData(OresHelper.calculateTypeName(CalendarManager.class), String.valueOf(courseId), businessPath);
			return new ContextualSubscriptionController(ureq, getWindowControl(), context, pdata);
		}
		if ((caller.equals(CalendarController.CALLER_COLLAB) || caller.equals(CalendarManager.TYPE_GROUP))) {
			BusinessGroup businessGroup = calendarNotificationsManager.getBusinessGroup(kalendarRenderWrapper);
			if(businessGroup != null) {
				PublisherData pdata = new PublisherData(OresHelper.calculateTypeName(CalendarManager.class), String.valueOf(businessGroup.getResourceableId()), businessPath);
				return new ContextualSubscriptionController(ureq, getWindowControl(), context, pdata);
			}
		}
		return null;
	}

	public void setEnableRemoveFromPersonalCalendar(boolean enable) {
		calendarConfig.setEnableRemoveFromPersonalCalendar(enable);
	}

	public Date getFocus() {
		return weeklyCalendar.getFocusDate();
	}

	@Override
	public void setFocus(Date date) {
		Calendar focus = CalendarUtils.createCalendarInstance(getLocale());
		focus.setTime(date);
		weeklyCalendar.setFocusDate(focus.getTime());
	}

	@Override
	public void setFocusOnEvent(String eventId) {
		if  (eventId.length() > 0) {
			for(KalendarRenderWrapper wrapper:calendarWrappers) {
				KalendarEvent event = wrapper.getKalendar().getEvent(eventId);
				if(event != null) {
					setFocus(event.getBegin());
					break;
				}
			}
		}
	}

	public void setCalendars(List<KalendarRenderWrapper> calendars) {
		setCalendars(calendars, new ArrayList<KalendarRenderWrapper>());
	}

	public void setCalendars(List<KalendarRenderWrapper> calendars, List<KalendarRenderWrapper> importedCalendars) {
		this.calendarWrappers = calendars;
		this.importedCalendarWrappers = importedCalendars;
		Collections.sort(calendarWrappers, KalendarComparator.getInstance());
		Collections.sort(importedCalendarWrappers, KalendarComparator.getInstance());

		List<KalendarRenderWrapper> allCalendarWrappers = new ArrayList<KalendarRenderWrapper>(calendarWrappers);
		allCalendarWrappers.addAll(importedCalendarWrappers);
		weeklyCalendar.setKalendars(allCalendarWrappers);

		calendarConfig.setCalendars(calendarWrappers);
		importedCalendarConfig.setCalendars(importedCalendarWrappers);
	}

	public void setDirty() {
		dirty = true;
	}

	private ILoggingAction getCalLoggingAction() {
		return calLoggingAction;
	}

	private void setCalLoggingAction(ILoggingAction calLoggingAction) {
		this.calLoggingAction = calLoggingAction;
	}

	@Override
	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
		if (source == weeklyCalendar) {
			if (event instanceof KalendarGUIEditEvent) {
				KalendarGUIEditEvent guiEvent = (KalendarGUIEditEvent) event;
				KalendarEvent kalendarEvent = guiEvent.getKalendarEvent();
				if (kalendarEvent == null) {
					// event already deleted
					getWindowControl().setError(translate("cal.error.eventDeleted"));
					return;
				}
				String recurrence = kalendarEvent.getRecurrenceRule();
				boolean isImported = false;
				KalendarRenderWrapper kalendarRenderWrapper = guiEvent.getKalendarRenderWrapper();
				if (kalendarRenderWrapper!=null) {
					isImported = kalendarRenderWrapper.isImported();
				}
				if( !isImported && recurrence != null && !recurrence.equals("") ) {
					List<String> btnLabels = new ArrayList<String>();
					btnLabels.add(translate("cal.edit.dialog.sequence"));
					btnLabels.add(translate("cal.edit.dialog.delete.single"));
					btnLabels.add(translate("cal.edit.dialog.delete.sequence"));
					if (dbcSequence != null) dbcSequence.dispose();
					dbcSequence = DialogBoxUIFactory.createGenericDialog(ureq, getWindowControl(), translate("cal.edit.dialog.title"), translate("cal.edit.dialog.text"), btnLabels);
					dbcSequence.addControllerListener(this);
					dbcSequence.setUserObject(guiEvent);
					dbcSequence.activate();
					return;
				}
				KalendarRenderWrapper kalendarWrapper = guiEvent.getKalendarRenderWrapper();
				pushEditEventController(ureq, kalendarEvent, kalendarWrapper);
			} else if (event instanceof KalendarGUIAddEvent) {
				pushAddEventController((KalendarGUIAddEvent)event, ureq);
			} else if (event instanceof KalendarGUISelectEvent) {
				KalendarGUISelectEvent selectEvent = (KalendarGUISelectEvent)event;
				doOpenEventCallout(ureq, selectEvent.getKalendarEvent(), selectEvent.getKalendarRenderWrapper(), selectEvent.getTargetDomId());
			} else if (event instanceof KalendarGUIMoveEvent) {
				KalendarGUIMoveEvent moveEvent = (KalendarGUIMoveEvent)event;
				doMove(ureq, moveEvent.getKalendarEvent(), moveEvent.getKalendarRenderWrapper(),
						moveEvent.getDayDelta(), moveEvent.getMinuteDelta(), moveEvent.getAllDay());
			}
		} else if (source == subscribeButton || source == unsubscribeButton) {
			removeAsListenerAndDispose(subscriptionController);
			if(calendarSubscription.isSubscribed() == (source == unsubscribeButton)) {
				subscriptionController = calendarSubscription.triggerSubscribeAction();
			}
			if (subscriptionController != null) {
				// activate subscription controller
				listenTo(subscriptionController);
				flc.put("calsubscription", subscriptionController.getInitialComponent());
			} else {
				flc.contextPut("isSubscribed", new Boolean(calendarSubscription.isSubscribed()));
				CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(new KalendarModifiedEvent(), OresHelper.lookupType(CalendarManager.class));
			}
		} else if (event instanceof KalendarGUIPrintEvent) {
			KalendarGUIPrintEvent printEvent = (KalendarGUIPrintEvent)event;
			if(printEvent.getFrom() != null && printEvent.getTo() != null) {
				doPrint(ureq, printEvent.getFrom(), printEvent.getTo());
			} else if(printEvent.getTargetDomId() != null) {
				doPrintEventCallout(ureq, printEvent.getTargetDomId());
			}
		}
		super.formInnerEvent(ureq, source, event);
	}

	public void event(UserRequest ureq, Component source, Event event) {
		if (event == ComponentUtil.VALIDATE_EVENT && dirty) {
			dirty = false;
			fireEvent(ureq, new KalendarModifiedEvent());
		} else if (event == ComponentUtil.VALIDATE_EVENT && weeklyCalendar.getComponent().isDirty() && modifiedCalenderDirty  ) {
			KalendarRenderWrapper kalendarRenderWrapper = weeklyCalendar.getKalendarRenderWrapper(modifiedCalendarId);
			kalendarRenderWrapper.reloadKalendar();
		} else if (source == searchLink) {

			List<KalendarRenderWrapper> allCalendarWrappers = new ArrayList<KalendarRenderWrapper>(calendarWrappers);
			allCalendarWrappers.addAll(importedCalendarWrappers);

			removeAsListenerAndDispose(searchController);
			searchController = new SearchAllCalendarsController(ureq, getWindowControl(), allCalendarWrappers);
			listenTo(searchController);

			removeAsListenerAndDispose(cmc);
			cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent());
			listenTo(cmc);

			cmc.activate();
		}
		super.event(ureq, source, event);
	}

	public void event(UserRequest ureq, Controller source, Event event) {
		Kalendar affectedCal = null;
		if (dirty) {
			dirty = false;
			fireEvent(ureq, new KalendarModifiedEvent());
		}
		if (source == editController) {
			affectedCal = editController.getKalendarEvent().getCalendar();
			cmc.deactivate();
			weeklyCalendar.getComponent().setDirty(true);
			// do logging if affectedCal not null
			if(affectedCal!=null) {
			  ThreadLocalUserActivityLogger.log(getCalLoggingAction(), getClass(), LoggingResourceable.wrap(ureq.getIdentity()), LoggingResourceable.wrap(affectedCal));
			}
			cleanUp();
		} else if (source == eventDetailsCtr) {
			if(event instanceof KalendarGUIEditEvent) {
				eventCalloutCtr.deactivate();

				KalendarGUIEditEvent editEvent = (KalendarGUIEditEvent)event;
				pushEditEventController(ureq, editEvent.getKalendarEvent(), editEvent.getKalendarRenderWrapper());
			}
		} else if(source == printCtrl) {
			if (event instanceof KalendarGUIPrintEvent) {
				KalendarGUIPrintEvent printEvent = (KalendarGUIPrintEvent)event;
				if(printEvent.getFrom() != null && printEvent.getTo() != null) {
					doPrint(ureq, printEvent.getFrom(), printEvent.getTo());
				}
			}
			eventCalloutCtr.deactivate();
		} else if (source == importCalendarController) {
			cmc.deactivate();
		} else if(source == cmc) {
			weeklyCalendar.getComponent().setDirty(true);
			cleanUp();
		} else if (source == calendarConfig || source == importedCalendarConfig) {
			if (event instanceof KalendarGUIAddEvent) {
				pushAddEventController((KalendarGUIAddEvent)event, ureq);
			} else if (event instanceof KalendarGUIImportEvent) {
				pushImportEventController((KalendarGUIImportEvent)event, ureq);
			} else if (event == Event.CHANGED_EVENT) {
				importedCalendarWrappers = ImportCalendarManager.getImportedCalendarsForIdentity(ureq);
				importedCalendarConfig.setCalendars(importedCalendarWrappers);
				setCalendars(calendarWrappers, importedCalendarWrappers);
				weeklyCalendar.getComponent().setDirty(true);
			}
		} else if (source == searchController) {
			if (event instanceof GotoDateEvent) {
				Date gotoDate = ((GotoDateEvent)event).getGotoDate();
			  weeklyCalendar.setFocusDate(gotoDate);
			}
			cmc.deactivate();
		} else if (source == subscriptionController) {
			// nothing to do here
			//TODO jquery mainPanel.setContent(vcMain);
		//TODO jquery vcMain.contextPut("isSubscribed", new Boolean(calendarSubscription.isSubscribed()));
		} else if (source == gotoDateForm) {
			weeklyCalendar.setFocusDate(gotoDateForm.getGotoDate());
		} else if (source == dbcSequence) {
			if(event != Event.CANCELLED_EVENT) {
				int pos = DialogBoxUIFactory.getButtonPos(event);
				KalendarGUIEditEvent guiEvent = (KalendarGUIEditEvent)dbcSequence.getUserObject();
				KalendarRenderWrapper kalendarWrapper = guiEvent.getKalendarRenderWrapper();
				KalendarEvent kalendarEvent = guiEvent.getKalendarEvent();
				if(pos == 0) { // edit the sequence
					// load the parent event of this sequence
					KalendarEvent parentEvent = kalendarWrapper.getKalendar().getEvent(kalendarEvent.getID());
					pushEditEventController(ureq, parentEvent, kalendarWrapper);
				} else if(pos == 1) { // delete a single event of the sequence
					deleteSingleYesNoController = activateYesNoDialog(ureq, null, translate("cal.delete.dialogtext"), deleteSingleYesNoController);
					deleteSingleYesNoController.setUserObject(kalendarEvent);
				} else if(pos == 2) { // delete the whole sequence
					deleteSequenceYesNoController = activateYesNoDialog(ureq, null, translate("cal.delete.dialogtext.sequence"), deleteSequenceYesNoController);
					deleteSequenceYesNoController.setUserObject(kalendarEvent);
				}
			}
			dbcSequence.dispose();
		} else if (source == deleteSingleYesNoController) {
			if (DialogBoxUIFactory.isYesEvent(event)) {
				KalendarEvent kalendarEvent = (KalendarEvent)deleteSingleYesNoController.getUserObject();
				affectedCal = kalendarEvent.getCalendar();
				KalendarEvent kEvent = affectedCal.getEvent(kalendarEvent.getID());
				kEvent.addRecurrenceExc(kalendarEvent.getBegin());
				CalendarManagerFactory.getInstance().getCalendarManager().updateEventFrom(affectedCal, kEvent);
				deleteSingleYesNoController.dispose();
				weeklyCalendar.getComponent().setDirty(true);
			}
		} else if (source == deleteSequenceYesNoController) {
			if (DialogBoxUIFactory.isYesEvent(event)) {
				KalendarEvent kalendarEvent = (KalendarEvent)deleteSequenceYesNoController.getUserObject();
				affectedCal = kalendarEvent.getCalendar();
				CalendarManagerFactory.getInstance().getCalendarManager().removeEventFrom(affectedCal, kalendarEvent);
				deleteSequenceYesNoController.dispose();
				weeklyCalendar.getComponent().setDirty(true);
			}
		}
		if (weeklyCalendar.getComponent().isDirty()) {
			if (subsContext != null) {
				// group or course calendar -> prepared subscription context is the right one
				NotificationsManager.getInstance().markPublisherNews(subsContext, ureq.getIdentity(), true);
			} else if(caller.equals(CALLER_HOME) && affectedCal != null) {
				// one can add/edit/remove dates of group and course calendars from the home calendar view -> choose right subscription context
				for( KalendarRenderWrapper calWrapper : calendarWrappers) {
					if(affectedCal == calWrapper.getKalendar()) {
						SubscriptionContext tmpSubsContext = calendarNotificationsManager.getSubscriptionContext(calWrapper);
						NotificationsManager.getInstance().markPublisherNews(tmpSubsContext, ureq.getIdentity(), true);
					}
				}
			}
		}
	}

	private void cleanUp() {
		removeAsListenerAndDispose(editController);
		removeAsListenerAndDispose(cmc);
		editController = null;
		cmc = null;
	}

	@Override
	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
		if(entries == null || entries.isEmpty()) return;

		String dateEntry = entries.get(0).getOLATResourceable().getResourceableTypeName();
		if(dateEntry.startsWith("date")) {
			Date gotoDate = BusinessControlFactory.getInstance().getDateFromContextEntry(entries.get(0));
			if(gotoDate != null) {
				weeklyCalendar.setFocusDate(gotoDate);
			}
		}
	}

	private void doPrint(UserRequest ureq, Date from, Date to) {
		StringBuilder sb = new StringBuilder();
		sb.append("window.open('" + printUrl + "/print.html', '_print','height=800,left=100,top=100,width=800,toolbar=no,titlebar=0,status=0,menubar=yes,location= no,scrollbars=1');");
		printMapper.setFrom(from);
		printMapper.setTo(to);
		printMapper.setCalendarWrappers(calendarWrappers);
		printMapper.setImportedCalendarWrappers(importedCalendarWrappers);
		getWindowControl().getWindowBackOffice().sendCommandTo(new JSCommand(sb.toString()));
	}

	private void doPrintEventCallout(UserRequest ureq, String targetDomId) {
		removeAsListenerAndDispose(eventCalloutCtr);
		removeAsListenerAndDispose(printCtrl);

		printCtrl = new CalendarPrintController(ureq, getWindowControl());
		listenTo(printCtrl);

		Component eventCmp = printCtrl.getInitialComponent();
		eventCalloutCtr = new CloseableCalloutWindowController(ureq, getWindowControl(), eventCmp, targetDomId,
				translate("print"), true, "b_cal_event_callout");
		listenTo(eventCalloutCtr);
		eventCalloutCtr.activate();
	}

	private void doOpenEventCallout(UserRequest ureq, KalendarEvent calEvent, KalendarRenderWrapper calWrapper, String targetDomId) {
		removeAsListenerAndDispose(eventCalloutCtr);
		removeAsListenerAndDispose(eventDetailsCtr);

		eventDetailsCtr = new CalendarDetailsController(ureq, getWindowControl(), calEvent, calWrapper);
		listenTo(eventDetailsCtr);

		Component eventCmp = eventDetailsCtr.getInitialComponent();
		eventCalloutCtr = new CloseableCalloutWindowController(ureq, getWindowControl(), eventCmp, targetDomId,
				"Event", true, "b_cal_event_callout");
		listenTo(eventCalloutCtr);
		eventCalloutCtr.activate();
	}

	private void doMove(UserRequest ureq, KalendarEvent calEvent, KalendarRenderWrapper calWrapper, Long dayDelta, Long minuteDelta, Boolean allDay) {
		Kalendar cal = calEvent.getCalendar();
		calEvent.setBegin(doMove(calEvent.getBegin(), dayDelta, minuteDelta));
		calEvent.setEnd(doMove(calEvent.getEnd(), dayDelta, minuteDelta));

		if(allDay != null && calEvent.isAllDayEvent() != allDay.booleanValue()) {
			calEvent.setAllDayEvent(allDay.booleanValue());
		}

		CalendarManagerFactory.getInstance().getCalendarManager().updateEventFrom(cal, calEvent);
		weeklyCalendar.getComponent().setDirty(true);
	}

	private Date doMove(Date date, Long dayDelta, Long minuteDelta) {
		if(date == null) {
			return date;
		}

		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		if(dayDelta != null) {
			cal.add(Calendar.DATE, dayDelta.intValue());
		}
		if(minuteDelta != null) {
			cal.add(Calendar.MINUTE, minuteDelta.intValue());
		}
		return cal.getTime();
	}

	/**
	 * @param ureq
	 * @param kalendarEvent
	 * @param kalendarWrapper
	 */
	private void pushEditEventController(UserRequest ureq, KalendarEvent kalendarEvent, KalendarRenderWrapper kalendarWrapper) {
		if(editController != null) return;

		removeAsListenerAndDispose(editController);

		boolean canEdit = false;
		for (Iterator<KalendarRenderWrapper> iter = calendarWrappers.iterator(); iter.hasNext();) {
			KalendarRenderWrapper wrapper = iter.next();
			if (wrapper.getAccess() == KalendarRenderWrapper.ACCESS_READ_WRITE
					&& kalendarWrapper.getKalendar().getCalendarID().equals(wrapper.getKalendar().getCalendarID())) {
				canEdit = true;
			}
		}

		if(canEdit) {
			editController = new KalendarEntryDetailsController(ureq, kalendarEvent, kalendarWrapper,	calendarWrappers, false, caller, getWindowControl());
			listenTo(editController);

			removeAsListenerAndDispose(cmc);
			cmc = new CloseableModalController(getWindowControl(), translate("close"), editController.getInitialComponent());
			listenTo(cmc);

			cmc.activate();

			// set logging action
			setCalLoggingAction(CalendarLoggingAction.CALENDAR_ENTRY_MODIFIED);
		} else {
			showError("cal.error.readonly");
		}
	}

	private void pushAddEventController(KalendarGUIAddEvent addEvent, UserRequest ureq) {
		if(editController != null) return;

		KalendarRenderWrapper calendarWrapper = weeklyCalendar.getKalendarRenderWrapper(addEvent.getCalendarID());
		// create new KalendarEvent
		Date begin = addEvent.getStartDate();

		KalendarEvent newEvent;
		if(addEvent.getEndDate() == null) {
			newEvent = new KalendarEvent(CodeHelper.getGlobalForeverUniqueID(), "", begin, (1000 * 60 * 60 * 1));
		} else {
			newEvent = new KalendarEvent(CodeHelper.getGlobalForeverUniqueID(), "", begin, addEvent.getEndDate());
		}

		if (calendarWrapper != null &&
				(calendarWrapper.getKalendar().getType().equals(CalendarManager.TYPE_COURSE) ||
				 calendarWrapper.getKalendar().getType().equals(CalendarManager.TYPE_GROUP))) {
			newEvent.setClassification(KalendarEvent.CLASS_PUBLIC);
		}

		newEvent.setAllDayEvent(addEvent.isAllDayEvent());
		String lastName  = ureq.getIdentity().getUser().getProperty(UserConstants.LASTNAME, getLocale());
		String firstName = ureq.getIdentity().getUser().getProperty(UserConstants.FIRSTNAME, getLocale());
		newEvent.setCreatedBy(firstName + " " + lastName);
		newEvent.setCreated(new Date().getTime());
		ArrayList<KalendarRenderWrapper> allCalendarWrappers = new ArrayList<KalendarRenderWrapper>(calendarWrappers);
		allCalendarWrappers.addAll(importedCalendarWrappers);

		removeAsListenerAndDispose(editController);
		editController = new KalendarEntryDetailsController(ureq, newEvent, calendarWrapper, allCalendarWrappers, true, caller, getWindowControl());
		listenTo(editController);

		removeAsListenerAndDispose(cmc);
		cmc = new CloseableModalController(getWindowControl(), translate("close"), editController.getInitialComponent());
		listenTo(cmc);

		cmc.activate();

		// set logging action
		setCalLoggingAction(CalendarLoggingAction.CALENDAR_ENTRY_CREATED);
	}

	private void pushImportEventController(KalendarGUIImportEvent importEvent, UserRequest ureq) {
		KalendarRenderWrapper calendarWrapper = weeklyCalendar.getKalendarRenderWrapper(importEvent.getCalendarID());

		removeAsListenerAndDispose(importCalendarController);

		importCalendarController = new ImportCalendarController(ureq, getWindowControl(), calendarWrapper);
		listenTo(importCalendarController);
		removeAsListenerAndDispose(cmc);
		cmc = new CloseableModalController(getWindowControl(), translate("close"), importCalendarController.getInitialComponent());
		cmc.activate();
		listenTo(cmc);
	}

	protected void doDispose() {
		CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, OresHelper.lookupType(CalendarManager.class));
	}

	public void event(Event event) {
		if (event instanceof KalendarModifiedEvent) {
			KalendarModifiedEvent kalendarModifiedEvent = (KalendarModifiedEvent)event;
			if (kalendarModifiedEvent.getType()!=null
					&& kalendarModifiedEvent.getCalendarId()!=null
					&& weeklyCalendar.getKalendarRenderWrapper(kalendarModifiedEvent.getCalendarId())!=null
					&& kalendarModifiedEvent.getType().equals(weeklyCalendar.getKalendarRenderWrapper(kalendarModifiedEvent.getCalendarId()).getKalendar().getType())
					&& kalendarModifiedEvent.getCalendarId().equals(weeklyCalendar.getKalendarRenderWrapper(kalendarModifiedEvent.getCalendarId()).getKalendar().getCalendarID())) {
				// the event is for my calendar => reload it

				//keeping a reference to the dirty calendar as reloading here raises an nested do in sync error. Using the component validation event to reload
				modifiedCalendarId  = kalendarModifiedEvent.getCalendarId();
				modifiedCalenderDirty = true;
				weeklyCalendar.getComponent().setDirty(true);
			}
		}
	}
}


text:
package vn.com.hiringviet.dao.impl;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.hibernate.transform.Transformers;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import vn.com.hiringviet.api.dto.request.LoadMoreRequestDTO;
import vn.com.hiringviet.common.PublishResponseEnum;
import vn.com.hiringviet.common.SearchEnum;
import vn.com.hiringviet.common.StatusEnum;
import vn.com.hiringviet.constant.ConstantValues;
import vn.com.hiringviet.dao.JobDAO;
import vn.com.hiringviet.dto.JobDTO;
import vn.com.hiringviet.dto.SearchDTO;
import vn.com.hiringviet.model.Job;
import vn.com.hiringviet.util.DateUtil;
import vn.com.hiringviet.util.Utils;

// TODO: Auto-generated Javadoc
/**
 * The Class JobDAOImpl.
 */
@Repository
@Transactional
public class JobDAOImpl extends CommonDAOImpl<Job> implements JobDAO {

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#getJobByID(java.lang.Integer)
	 */
	@Override
	public Job getJobByID(Integer jobId) {

		Session session = getSession();

		Job job = (Job) session.get(Job.class, jobId);

		Hibernate.initialize(job.getSkillSet());

		Hibernate.initialize(job.getCommentSet());

		return job;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#getListJob(vn.com.hiringviet.api.dto.request.LoadMoreRequestDTO, java.lang.Integer, java.lang.Integer, java.util.List, boolean, java.lang.String, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Job> getListJob(LoadMoreRequestDTO loadMoreRequestDTO,
			Integer first, Integer max, List<Integer> skills, boolean isHotJob,
			String mode, String keyValue) {

		Session session = getSession();
		Criteria criteria = session.createCriteria(Job.class, "job");
		criteria.createAlias("job.changeLog", "changeLog");
		criteria.createAlias("job.company", "company");
		criteria.createAlias("job.jobCategory", "jobCategory");
		criteria.createAlias("job.position", "position", JoinType.LEFT_OUTER_JOIN);
		criteria.createAlias("job.workAddress", "address", JoinType.LEFT_OUTER_JOIN);
		criteria.createAlias("address.district", "district", JoinType.LEFT_OUTER_JOIN);
		criteria.createAlias("district.province", "province", JoinType.LEFT_OUTER_JOIN);

		if (skills != null
				&& (Utils.isEmptyString(mode)
						|| SearchEnum.ALL.getStatus().equals(mode) || SearchEnum.SKILL
						.getStatus().equals(mode))) {
			criteria.createAlias("job.skillSet", "skillSet", JoinType.LEFT_OUTER_JOIN);
		}

		criteria.add(Restrictions.eq("changeLog.status", StatusEnum.ACTIVE));

		criteria.add(Restrictions.eq("job.publish", PublishResponseEnum.PUBLISH.getValue()));
		criteria.add(Restrictions.gt("job.expiredDate", DateUtil.now()));

		if (!Utils.isEmptyList(skills)) {
			criteria.add(Restrictions.in("skillSet.id", skills));
		}

		if (SearchEnum.SKILL.getStatus().equals(mode)) {

		}

		if (loadMoreRequestDTO != null) {

			if (!Utils.isEmptyList(loadMoreRequestDTO.getCategoryNameList())) {
				criteria.add(Restrictions.in("jobCategory.categoryName",
						loadMoreRequestDTO.getCategoryNameList()));
			}

			if (!Utils.isEmptyList(loadMoreRequestDTO.getCompanyNameList())) {
				criteria.add(Restrictions.in("company.displayName",
						loadMoreRequestDTO.getCompanyNameList()));
			}

			if (!Utils.isEmptyList(loadMoreRequestDTO.getPositionNameList())) {
				criteria.add(Restrictions.in("position.displayName",
						loadMoreRequestDTO.getPositionNameList()));
			}

			if (!Utils.isEmptyList(loadMoreRequestDTO.getProvinceNameList())) {
				criteria.add(Restrictions.in("province.provinceName",
						loadMoreRequestDTO.getProvinceNameList()));
			}

			// if (!Utils.isEmptyList(loadMoreRequestDTO.getSkillNameList())) {
			// Disjunction disjunction = Restrictions.disjunction();
			// for (String skillName : loadMoreRequestDTO.getSkillNameList()) {
			// disjunction.add(Restrictions.or(Restrictions.sqlRestriction
			// ("FIND_IN_SET('" + skillName + "', skillSet)")));
			// }
			// criteria.add(disjunction);
			// }

			if (!Utils.isEmptyNumber(loadMoreRequestDTO.getMaxSalary())) {
				criteria.add(Restrictions.between("job.maxSalary",
						loadMoreRequestDTO.getMinSalary(),
						loadMoreRequestDTO.getMaxSalary()));
			}

			if (!Utils.isEmptyNumber(loadMoreRequestDTO.getDateAgo())) {
				criteria.add(Restrictions.between("job.postDate",
						DateUtil.getDateAgo(loadMoreRequestDTO.getDateAgo()),
						DateUtil.now()));
			}
		}

		criteria.addOrder(Order.desc("company.isVip"));
		criteria.addOrder(Order.desc("changeLog.createdDate"));
		criteria.addOrder(Order.desc("job.minSalary"));
		criteria.addOrder(Order.desc("job.maxSalary"));
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		criteria.setFirstResult(first);
		criteria.setMaxResults(max);

		List<Job> jobList = criteria.list();
		return jobList;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#searchJobByKeyWord(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<JobDTO> searchJobByKeyWord(String keyWord) {

		Session session = getSession();

		Criteria criteria = session.createCriteria(Job.class, "job");
		criteria.setProjection(Projections.projectionList()
				.add(Projections.property("id"), "id")
				.add(Projections.property("title"), "title"));
		criteria.add(Restrictions.eq("job.publish", PublishResponseEnum.PUBLISH.getValue()));
		criteria.add(Restrictions.like("title", "%" + keyWord.replace("\"", "")
				+ "%"));
		criteria.setMaxResults(ConstantValues.MAX_RECORD_COUNT);
		criteria.setResultTransformer(Transformers.aliasToBean(JobDTO.class));

		List<JobDTO> skillDTOs = (List<JobDTO>) criteria.list();

		return skillDTOs;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#getNewJobs(java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<JobDTO> getNewJobs(Integer companyId) {

		Session session = getSession();

		Criteria criteria = session.createCriteria(Job.class, "job");
		criteria.createAlias("job.changeLog", "changeLog", JoinType.LEFT_OUTER_JOIN);
		criteria.createAlias("job.company", "company", JoinType.LEFT_OUTER_JOIN);
		criteria.setProjection(Projections.projectionList()
				.add(Projections.property("id"), "id")
				.add(Projections.property("title"), "title")
				.add(Projections.property("postDate"), "postDate")
				.add(Projections.property("job.workAddress"), "address"));
		criteria.add(Restrictions.eq("company.id", companyId));
		criteria.add(Restrictions.eq("changeLog.status", StatusEnum.ACTIVE));
		criteria.add(Restrictions.eq("job.publish", PublishResponseEnum.PUBLISH.getValue()));
		criteria.setMaxResults(ConstantValues.MAX_RECORD_COUNT);
		criteria.setResultTransformer(Transformers.aliasToBean(JobDTO.class));
		criteria.addOrder(Order.desc("changeLog.createdDate"));
		List<JobDTO> skillDTOs = (List<JobDTO>) criteria.list();

		return skillDTOs;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#updateDescription(java.lang.Integer, java.lang.String)
	 */
	@Override
	public boolean updateDescription(Integer jobId, String description) {
		StringBuffer sb = new StringBuffer();
		sb.append("UPDATE ");
		sb.append("job ");
		sb.append("SET description = :description ");
		sb.append("WHERE id = :jobId");
		Query query = getSession().createSQLQuery(sb.toString());
		query.setParameter("description", description);
		query.setParameter("jobId", jobId);

		if (query.executeUpdate() > 0) {
			return true;
		}

		return false;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#updateRequirement(java.lang.Integer, java.lang.String)
	 */
	@Override
	public boolean updateRequirement(Integer jobId, String requirement) {

		StringBuffer sb = new StringBuffer();
		sb.append("UPDATE ");
		sb.append("job ");
		sb.append("SET requirement = :requirement ");
		sb.append("WHERE id = :jobId");
		Query query = getSession().createSQLQuery(sb.toString());
		query.setParameter("requirement", requirement);
		query.setParameter("jobId", jobId);

		if (query.executeUpdate() > 0) {
			return true;
		}

		return false;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#updateCompanyPolicies(java.lang.Integer, java.lang.String)
	 */
	@Override
	public boolean updateCompanyPolicies(Integer jobId, String cultureDescription) {

		StringBuffer sb = new StringBuffer();
		sb.append("UPDATE ");
		sb.append("job ");
		sb.append("SET culture_description = :cultureDescription ");
		sb.append("WHERE id = :jobId");
		Query query = getSession().createSQLQuery(sb.toString());
		query.setParameter("cultureDescription", cultureDescription);
		query.setParameter("jobId", jobId);

		if (query.executeUpdate() > 0) {
			return true;
		}

		return false;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#countApplies(vn.com.hiringviet.model.Job)
	 */
	@Override
	public long countApplies(Job job) {
		Session session = getSession();
		Query query = session.createQuery("SELECT count(*) FROM Apply a WHERE a.job = ?");
		query.setParameter(0, job);
		return (long) query.uniqueResult();
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#updateVisited(java.lang.Integer)
	 */
	@Override
	public boolean updateVisited(Integer jobId) {

		Query query = null;
		StringBuffer sql = new StringBuffer();
		sql.append("UPDATE job SET number_visited = (number_visited + 1) WHERE id = :jobId");
		query = getSession().createSQLQuery(sql.toString());
		query.setParameter("jobId", jobId);

		if (query.executeUpdate() <= 0) {
			return false;
		}
		return true;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#setPublish(java.lang.Integer, java.lang.Integer)
	 */
	@Override
	public boolean setPublish(Integer jobId, Integer isPublish) {

		Query query = null;
		StringBuffer sql = new StringBuffer();
		sql.append("UPDATE job SET is_publish = :isPublish WHERE id = :jobId");
		query = getSession().createSQLQuery(sql.toString());
		query.setParameter("jobId", jobId);
		query.setParameter("isPublish", isPublish);

		if (query.executeUpdate() <= 0) {
			return false;
		}
		return true;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#searchJob(java.lang.Integer, java.lang.Integer, vn.com.hiringviet.dto.SearchDTO)
	 */
	@Override
	public List<Job> searchJob(Integer first, Integer max, SearchDTO searchDTO) {

		Session session = getSession();
		Criteria criteria = session.createCriteria(Job.class, "job");
		criteria.createAlias("job.changeLog", "changeLog");
		criteria.createAlias("job.company", "company");
		criteria.createAlias("job.jobCategory", "jobCategory");
		criteria.createAlias("job.position", "position");
		criteria.createAlias("job.workAddress", "address");
		criteria.createAlias("address.district", "district", JoinType.LEFT_OUTER_JOIN);
		criteria.createAlias("district.province", "province", JoinType.LEFT_OUTER_JOIN);
		criteria.createAlias("job.skillSet", "skillSet");

		if (searchDTO.getSkillId() != null) {
			criteria.add(Restrictions.eq("skillSet.id", searchDTO.getSkillId()));
		}

		if (!StringUtils.isEmpty(searchDTO.getJobTitle())) {
			criteria.add(Restrictions.like("job.title", searchDTO.getJobTitle(), MatchMode.ANYWHERE));
		}

		if (!StringUtils.isEmpty(searchDTO.getSkill())) {
			criteria.add(Restrictions.like("skillSet.displayName", searchDTO.getSkill(), MatchMode.ANYWHERE));
		}

		if (!StringUtils.isEmpty(searchDTO.getCompanyName())) {
			criteria.add(Restrictions.like("company.displayName", searchDTO.getCompanyName(), MatchMode.ANYWHERE));
		}

		criteria.add(Restrictions.eq("changeLog.status", StatusEnum.ACTIVE));
		criteria.add(Restrictions.eq("job.publish", PublishResponseEnum.PUBLISH.getValue()));
		criteria.add(Restrictions.gt("job.expiredDate", DateUtil.now()));

		criteria.addOrder(Order.desc("company.isVip"));
		criteria.addOrder(Order.desc("changeLog.createdDate"));
		criteria.addOrder(Order.desc("job.minSalary"));
		criteria.addOrder(Order.desc("job.maxSalary"));
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		criteria.setFirstResult(first);
		criteria.setMaxResults(max);

		List<Job> jobList = criteria.list();
		return jobList;
	}

	/* (non-Javadoc)
	 * @see vn.com.hiringviet.dao.JobDAO#insertJobSkill(java.lang.Integer, java.util.List)
	 */
	@Override
	public boolean insertJobSkill(Integer jobId, List<String> skills) {

		Query query = null;
		StringBuffer insertJobSkillSQL = null;
		for(String skillId : skills) {
			insertJobSkillSQL = new StringBuffer();
			insertJobSkillSQL.append("INSERT INTO job_skill (job_id, skill_id) VALUES (:jobId, :skillId)");
			query = getSession().createSQLQuery(insertJobSkillSQL.toString());
			query.setParameter("jobId", jobId);
			query.setParameter("skillId", Integer.parseInt(skillId));
			if (query.executeUpdate() < 0) {
				return false;
			}
		}

		return true;
	}

	// @SuppressWarnings("unchecked")
	// @Override
	// public List<Job> getListJobSuggest(Integer first, Integer max,
	// List<Integer> skills) {
	//
	// Session session = getSession();
	// Criteria criteria = session.createCriteria(Job.class, "job");
	// criteria.createAlias("job.changeLog", "changeLog");
	// criteria.createAlias("job.company", "company");
	//
	// if (!Utils.isEmptyList(skills)) {
	// criteria.createAlias("job.skillSet", "skillSet");
	// }
	//
	// criteria.add(Restrictions.eq("changeLog.status",
	// StatusEnum.ACTIVE.getValue()));
	// criteria.add(Restrictions.gt("job.expiredDate", DateUtil.now()));
	//
	// if (!Utils.isEmptyList(skills)) {
	// criteria.add(Restrictions.in("skillSet.id", skills));
	// }
	//
	// criteria.addOrder(Order.desc("company.isVip"));
	// criteria.addOrder(Order.desc("changeLog.createdDate"));
	// criteria.addOrder(Order.desc("job.minSalary"));
	// criteria.addOrder(Order.desc("job.maxSalary"));
	// criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
	// criteria.setFirstResult(first);
	// criteria.setMaxResults(max);
	//
	// List<Job> jobList = criteria.list();
	// return jobList;
	// }
}