/* "CodeWorker":	a scripting language for parsing and generating text.

Copyright (C) 1996-1997, 1999-2002 Cédric Lemaire

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

To contact the author: codeworker@free.fr
*/

#ifdef WIN32
#pragma warning (disable : 4786)
#endif

#include <stdlib.h>

#include "UtlException.h"
#include "UtlDate.h"
#include "ScpStream.h"

#include "ExprScriptVariable.h"
#include "DtaScriptVariable.h"
#include "DtaProject.h"
#include "GrfReadonlyHook.h"
#include "GrfNop.h"
#include "GrfSetAssignment.h"
#include "CGRuntime.h"

//##markup##"includes"
//##begin##"includes"
#include "GrfAttachOutputToSocket.h"
#include "GrfDetachOutputFromSocket.h"
#include "GrfInsertText.h"
#include "GrfInsertTextOnce.h"
#include "GrfInsertTextToFloatingLocation.h"
#include "GrfInsertTextOnceToFloatingLocation.h"
#include "GrfOverwritePortion.h"
#include "GrfPopulateProtectedArea.h"
#include "GrfResizeOutputStream.h"
#include "GrfSetFloatingLocation.h"
#include "GrfSetOutputLocation.h"
#include "GrfSetProtectedArea.h"
#include "GrfWriteBytes.h"
#include "GrfWriteText.h"
#include "GrfWriteTextOnce.h"
//##end##"includes"

#include "GrfWritefileHook.h"
#include "GrfText.h"
#include "GrfGeneratedFile.h"
#include "GrfAppendedFile.h"
#include "GrfGeneratedString.h"
#include "DtaSharpTagsHandler.h"
#include "DtaProtectedAreasBag.h"
#include "DtaPatternScript.h"

namespace CodeWorker {

	int DtaOutputFile::_iNumberOfTemporaryFiles = 0;

	DtaOutputFile::DtaOutputFile(DtaPatternScript* pPatternScript) : _pInputStream(0L), _pOutputStream(0L), _pPatternScript(pPatternScript), _pOldOutputFile(0L) {
		_pProtectedAreasBag = new DtaProtectedAreasBag;
		if (_pPatternScript != 0) {
			_pOldOutputFile = _pPatternScript->_pOutputFile;
			_pPatternScript->_pOutputFile = this;
		}
		if (!DtaProject::getInstance().getSpeed()) {
			const char* sTemp = getenv("TEMP");
			if (sTemp == 0L) sTemp = getenv("TMP");
			if (sTemp != 0L) _sTemporaryDirectory = sTemp;
		}
	}

	DtaOutputFile::~DtaOutputFile() {
		if (_pPatternScript != 0) {
			_pPatternScript->_pOutputFile = _pOldOutputFile;
		}
		delete _pProtectedAreasBag;
	}

	GrfText* DtaOutputFile::createText(const char* sText) const { return new GrfText(CGRuntime::_pOutputStream, sText); }

	void DtaOutputFile::captureOutputFile(const char* tcFile) {
		if ((tcFile == 0L) || (_pPatternScript == 0L) || (_pPatternScript->getFilenamePtr() == 0L)) return;
		DtaProject::getInstance().captureOutputFile(tcFile, _pPatternScript->getFilenamePtr());
	}

	ScpStream* DtaOutputFile::openGenerate(bool bAutomatic, const char* sFile, ScpStream*& pOldOutputStream) {
		openAndloadProtectedCode(sFile);
		_pOutputStream->setParentStream(CGRuntime::_pOutputStream);
		if (bAutomatic) {
			pOldOutputStream = CGRuntime::_pOutputStream;
			CGRuntime::_pOutputStream = _pOutputStream;
			if (_pPatternScript != 0) CGRuntime::_listOfPatternScripts.push_front(_pPatternScript);
		}
		DtaProject& theProject = DtaProject::getInstance();
		if (!theProject.getGenerationHeader().empty()) {
			UtlDate today;
			(*_pOutputStream) << theProject.getCommentBegin() << "##generation header##";
			(*_pOutputStream) << CGRuntime::getApplicationName() << "##" << CGRuntime::getVersionNumber();
			(*_pOutputStream) << "##" << today.getFormattedDate("%d%b%Y %H:%M:%S") << "##";
			std::string sCommentEnd = theProject.getCommentEnd();
			if ((DtaProject::getInstance().getTextMode() == DtaProject::DOS_MODE) && (sCommentEnd == "\n")) sCommentEnd = "\r\n";
			else if (sCommentEnd.find('\n') == std::string::npos) sCommentEnd += CGRuntime::endl();
			std::string sHeader = theProject.getGenerationHeader();
			if (sHeader.find('\n') != std::string::npos) {
				sHeader = CGRuntime::replaceString("\r", "", sHeader);
				if (sHeader[sHeader.size() - 1] == '\n') sHeader = sHeader.substr(0, sHeader.size() - 1);
				(*_pOutputStream) << sCommentEnd << theProject.getCommentBegin() << "##header start##" << sCommentEnd;
				sHeader = theProject.getCommentBegin() + CGRuntime::replaceString("\n", sCommentEnd + theProject.getCommentBegin(), sHeader) + sCommentEnd;
				(*_pOutputStream) << sHeader;
				(*_pOutputStream) << theProject.getCommentBegin() << "##header end##" << sCommentEnd;
			} else {
				_pOutputStream->writeString(sHeader);
				(*_pOutputStream) << sCommentEnd;
			}
		}
		_iNumberOfTemporaryFiles++;
		return _pOutputStream;
	}

	ScpStream* DtaOutputFile::openAppend(bool bAutomatic, const char* sFile, ScpStream*& pOldOutputStream) {
		_pInputStream = ScpStream::openInputFile(sFile);
		_pOutputStream = new ScpStream;
		if (sFile != 0L) _pOutputStream->setFilename(sFile);
		if (_pInputStream != 0L) _pOutputStream->copy(*_pInputStream, 0);
		if (_pPatternScript != 0) _pPatternScript->allocateLocalParameters();
		if (bAutomatic) {
			pOldOutputStream = CGRuntime::_pOutputStream;
			CGRuntime::_pOutputStream = _pOutputStream;
			if (_pPatternScript != 0) CGRuntime::_listOfPatternScripts.push_front(_pPatternScript);
		}
		_iNumberOfTemporaryFiles++;
		return _pOutputStream;
	}

	std::string DtaOutputFile::catchGenerateExecution(bool bAutomatic, ScpStream* pOldOutputStream, UtlException* pCatchedException) {
		std::string sMessage;
		if (bAutomatic) {
			CGRuntime::_pOutputStream = pOldOutputStream;
			CGRuntime::_listOfPatternScripts.pop_front();
		}
		saveProtectedCodeAndClose();
		if (_pOutputStream != 0L) {
			delete _pOutputStream;
			_pOutputStream = 0L;
		}
		if (_pInputStream != 0L) {
			delete _pInputStream;
			_pInputStream = 0L;
		}
		_iNumberOfTemporaryFiles--;
		if (pCatchedException != 0L) {
			throw UtlException(pCatchedException->getTraceStack(), pCatchedException->getMessage());
		}
		return sMessage;
	}

	std::string DtaOutputFile::closeGenerate(bool bAutomatic, const char* sFile, ScpStream* pOldOutputStream) {
		std::string sOutputString;
		try {
			saveProtectedCodeAndClose();
			copyFromTemporaryFile(sFile);
			if (_pOutputStream != 0L) {
				if (sFile == 0L) sOutputString = _pOutputStream->readBuffer();
				_pOutputStream->close();
				delete _pOutputStream;
				_pOutputStream = 0L;
			}
			if (_pInputStream != 0L) {
				delete _pInputStream;
				_pInputStream = 0L;
			}
			_iNumberOfTemporaryFiles--;
		} catch(std::exception&) {
			if (bAutomatic) {
				CGRuntime::_pOutputStream = pOldOutputStream;
				CGRuntime::_listOfPatternScripts.pop_front();
			}
			if (_pOutputStream != 0L) {
				delete _pOutputStream;
				_pOutputStream = 0L;
			}
			if (_pInputStream != 0L) {
				delete _pInputStream;
				_pInputStream = 0L;
			}
			_iNumberOfTemporaryFiles--;
			throw;
		}
		if (bAutomatic) {
			CGRuntime::_pOutputStream = pOldOutputStream;
			CGRuntime::_listOfPatternScripts.pop_front();
		}
		return sOutputString;
	}

	// Loading all protected areas
	void DtaOutputFile::openAndloadProtectedCode(const char* sFile) {
		if (sFile == 0L) _pInputStream = new ScpStream;
		else {
			_pInputStream = ScpStream::openInputFile(sFile);
			if (_pInputStream != 0L) {
				try {
					getProtectedAreasBag().recoverProtectedCodes(*_pInputStream);
				} catch(UtlException& e) {
					std::string sMessage = sFile;
					sMessage += ", ";
					sMessage += e.getMessage();
					_pInputStream->close();
					delete _pInputStream;
					_pInputStream = 0L;
					throw UtlException(e.getTraceStack(), sMessage);
				} catch(std::exception&) {
					_pInputStream->close();
					delete _pInputStream;
					_pInputStream = 0L;
					throw;
				}
				_pInputStream->setInputLocation(0);
			}
		}
		_pOutputStream = new ScpStream;
		if (sFile != 0L) _pOutputStream->setFilename(sFile);
		if (_pPatternScript != 0) _pPatternScript->allocateLocalParameters();
	}

	// writing of protected areas that have been forgotten
	void DtaOutputFile::saveProtectedCode() {
		std::string sCommentEnd;
		if ((DtaProject::getInstance().getTextMode() == DtaProject::DOS_MODE) && (DtaProject::getInstance().getCommentEnd() == "\n")) sCommentEnd = "\r\n";
		else sCommentEnd = DtaProject::getInstance().getCommentEnd();
		bool bAtLeastOneFloatingCode = false;
		const std::map<std::string, DtaProtectedArea*>& listOfCodes = getProtectedAreasBag().getProtectedAreas();
		for (std::map<std::string, DtaProtectedArea*>::const_iterator i = listOfCodes.begin(); i != listOfCodes.end(); i++) {
			if (!i->second->isAlreadyGenerated()) {
				if (!bAtLeastOneFloatingCode) {
					bAtLeastOneFloatingCode = true;
					(*_pOutputStream) << DtaProject::getInstance().getCommentBegin();
					(*_pOutputStream) << "*********************************************************************" << sCommentEnd;
					(*_pOutputStream) << DtaProject::getInstance().getCommentBegin();
					(*_pOutputStream) << " Please find below the protected areas that the template-based script" << sCommentEnd;
					(*_pOutputStream) << DtaProject::getInstance().getCommentBegin();
					(*_pOutputStream) << " leading the generation hasn't recognized." << sCommentEnd;
					(*_pOutputStream) << DtaProject::getInstance().getCommentBegin();
					(*_pOutputStream) << "*********************************************************************" << sCommentEnd;
				}
				setProtectedArea(i->first);
			}
		}
		getProtectedAreasBag().clearAll();
	}

	void DtaOutputFile::saveProtectedCodeAndClose() {
		if (_pOutputStream != 0L) saveProtectedCode();
		else getProtectedAreasBag().clearAll();
		if (_pPatternScript != 0) {
			_pPatternScript->_bExecMode = false;
			_pPatternScript->freeLocalParameters();
		}
	}

	// Updating the old version of the file
	void DtaOutputFile::copyFromTemporaryFile(const char* sFile) {
		if (sFile == 0L) return;
		if (_pInputStream != 0L) {
			int iPosition;
			if (!DtaScript::equalsIgnoringGenerationHeader(*_pInputStream, *_pOutputStream, iPosition)) {
				if (!_sTemporaryDirectory.empty()) {
					char tcTemporaryFile[1024];
					sprintf(tcTemporaryFile, "%s/~generator%d.tmp", _sTemporaryDirectory.c_str(), _iNumberOfTemporaryFiles);
					_pOutputStream->saveIntoFile(tcTemporaryFile, false);
				}
				GrfWritefileHook* pHook = DtaProject::getInstance().getWritefileHook();
				if (pHook != 0L) {
					std::string sSuccess;
					try {
						sSuccess = pHook->executeHook(CGRuntime::getThisInternalNode(), sFile, iPosition, false);
					} catch(UtlException& exc) {
						throw UtlException(std::string("FATAL ERROR! Before writing file \"") + sFile + "\", a call to 'writefileHook' has raised the following error:" + CGRuntime::endl() + "\"" + exc.getMessage() + "\"");
					}
					if (sSuccess.empty()) return;
				}
				std::auto_ptr<ScpStream> pFile(ScpStream::createFile(sFile));
				if (pFile.get() == 0L) {
					std::auto_ptr<std::ifstream> pReadonlyFile(openInputFile(sFile));
					if (pReadonlyFile.get() != 0L) {
						pReadonlyFile->close();
						GrfReadonlyHook* pHook = DtaProject::getInstance().getReadonlyHook();
						if (pHook != 0L) {
							try {
								pHook->executeHook(CGRuntime::getThisInternalNode(), sFile);
							} catch(UtlException& exc) {
								throw UtlException(std::string("FATAL ERROR! File \"") + sFile + "\" is read-only and call to the hook has launched the following error:\n\"" + exc.getMessage() + "\"");
							}
							std::auto_ptr<ScpStream> pFileAgain(ScpStream::createFile(sFile));
							pFile = pFileAgain;
							if (pFile.get() == 0L) {
								throw UtlException(std::string("FATAL ERROR! File \"") + sFile + "\" is read-only and call to the hook didn't change its state.");
							}
						} else {
							throw UtlException(std::string("FATAL ERROR! File \"") + sFile + "\" is read-only and no hook has been implemented for changing its state (see 'readonlyHook' instruction).");
						}
					} else {
						throw UtlException(std::string("Unable to open file \"") + sFile + "\" for writing, but should be ?!");
					}
				}
				try {
					_pOutputStream->saveIntoFile(sFile, false);
				} catch(...) {
					char tcTemporaryFile[1024];
					if (_sTemporaryDirectory.empty()) {
						const char* sTemp = getenv("TEMP");
						if (sTemp == 0L) sTemp = getenv("TMP");
						sprintf(tcTemporaryFile, "%s/~generator%d.tmp", sTemp, _iNumberOfTemporaryFiles);
						_pOutputStream->saveIntoFile(tcTemporaryFile, false);
					} else {
						sprintf(tcTemporaryFile, "%s/~generator%d.tmp", _sTemporaryDirectory.c_str(), _iNumberOfTemporaryFiles);
					}
					throw UtlException("DANGER! File error while replacing old version of file \"" + std::string(sFile) + "\" by the new one. Its content may be corrupted. If so, recover it by temporary file \"" + std::string(tcTemporaryFile) + "\".");
				}
			}
		} else {
			GrfWritefileHook* pHook = DtaProject::getInstance().getWritefileHook();
			if (pHook != 0L) {
				std::string sSuccess;
				try {
					sSuccess = pHook->executeHook(CGRuntime::getThisInternalNode(), sFile, 0, true);
				} catch(UtlException& exc) {
					throw UtlException(std::string("FATAL ERROR! Before creating file \"") + sFile + "\", a call to 'writefileHook' has raised the following error:" + CGRuntime::endl() + "\"" + exc.getMessage() + "\"");
				}
				if (sSuccess.empty()) return;
			}
			_pOutputStream->saveIntoFile(sFile, true);
		}
	}

	SEQUENCE_INTERRUPTION_LIST DtaOutputFile::generate(const char* tcFile, DtaScriptVariable& thisContext) {
		if (_pPatternScript == 0) throw UtlException("internal error in 'DtaOutputFile::generate()': the pattern script is null");
		SEQUENCE_INTERRUPTION_LIST result = NO_INTERRUPTION;
		ScpStream* pOldOutputStream;
		openGenerate(true, tcFile, pOldOutputStream);
		try {
			result = _pPatternScript->execute(thisContext);
			captureOutputFile(tcFile);
		} catch(UtlException& exception) {
			catchGenerateExecution(true, pOldOutputStream, &exception);
		} catch(std::exception&) {
			catchGenerateExecution(true, pOldOutputStream, 0);
			throw;
		}
		closeGenerate(true, tcFile, pOldOutputStream);
		return result;
	}

	SEQUENCE_INTERRUPTION_LIST DtaOutputFile::generateString(std::string& sOutput, DtaScriptVariable& thisContext) {
		if (_pPatternScript == 0) throw UtlException("internal error in 'DtaOutputFile::generateString()': the pattern script is null");
		SEQUENCE_INTERRUPTION_LIST result = NO_INTERRUPTION;
		ScpStream* pOldOutputStream;
		openGenerate(true, 0L, pOldOutputStream);
		(*_pInputStream) << sOutput;
		try {
			result = _pPatternScript->execute(thisContext);
			switch(result) {
				case CONTINUE_INTERRUPTION:
				case BREAK_INTERRUPTION:
				case RETURN_INTERRUPTION:
					result = NO_INTERRUPTION;
					break;
			}
		} catch(UtlException& exception) {
			catchGenerateExecution(true, pOldOutputStream, &exception);
		} catch(std::exception&) {
			catchGenerateExecution(true, pOldOutputStream, 0);
			throw;
		}
		sOutput = closeGenerate(true, 0L, pOldOutputStream);
		return result;
	}

	SEQUENCE_INTERRUPTION_LIST DtaOutputFile::expand(const char* tcFile, DtaScriptVariable& thisContext) {
		if (_pPatternScript == 0) throw UtlException("internal error in 'DtaOutputFile::expand()': the pattern script is null");
		SEQUENCE_INTERRUPTION_LIST result = NO_INTERRUPTION;
		_pInputStream = ScpStream::openInputFile(tcFile);
		if (_pInputStream != 0L) {
			_pOutputStream = new ScpStream;
			if (tcFile != 0L) _pOutputStream->setFilename(tcFile);
			ScpStream* pOldOutputStream = CGRuntime::_pOutputStream;
			CGRuntime::_pOutputStream = _pOutputStream;
			CGRuntime::_listOfPatternScripts.push_front(_pPatternScript);
			int iEnd = 0;
			_pPatternScript->allocateLocalParameters();
			_iNumberOfTemporaryFiles++;
			if (!DtaProject::getInstance().getCommentBegin().empty()) {
				DtaSharpTagsHandler tagsHandler(_pInputStream);
				while (tagsHandler.findExpansionMarkup()) {
					try {
						// extracting the script/data blocks
						std::string sScript;
						std::string sData;
						while ((sScript.empty() && tagsHandler.readMarkupScript(sScript)) ||
								(sData.empty() && tagsHandler.readMarkupData(sData))) {
								// '##script##' or '##data##'
						}
						DtaProject::getInstance().setMarkupValue(sData);

						// copy of the source up to the last script/data block or announcement
						int iBegin = _pInputStream->getInputLocation();
						_pOutputStream->copy(*_pInputStream, iEnd, iBegin - iEnd);
						// extracting all protected areas, included between the current
						// position and the end of the markup definition
						getProtectedAreasBag().recoverMarker(tagsHandler);
						iEnd = _pInputStream->getInputLocation();
						std::auto_ptr<ScpStream> pOutputStream(_pOutputStream);
						_pOutputStream = new ScpStream;
						if (tcFile != 0L) _pOutputStream->setFilename(tcFile);
						_pOutputStream->setParentStream(pOutputStream.get());
						CGRuntime::_pOutputStream = _pOutputStream;
						result = _pPatternScript->executeExpansion(sScript, thisContext);
						tagsHandler.writeMarkupBegin(*pOutputStream);
						saveProtectedCode();
						(*pOutputStream) << (*_pOutputStream);
						tagsHandler.writeMarkupEnd(*pOutputStream);
						delete _pOutputStream;
						_pOutputStream = pOutputStream.release();
						CGRuntime::_pOutputStream = 0L;
						_pInputStream->setInputLocation(iEnd);
					} catch(UtlException& e) {
						_pPatternScript->freeLocalParameters();
						_pInputStream->close();
						delete _pInputStream;
						_pInputStream = 0L;
						if (_pOutputStream != 0L) {
							delete _pOutputStream;
							_pOutputStream = 0L;
							CGRuntime::_pOutputStream = pOldOutputStream;
							CGRuntime::_listOfPatternScripts.pop_front();
						}
						_iNumberOfTemporaryFiles--;
						std::string sException = e.getMessage();
						std::string sMessage = "\"";
						if (_pPatternScript->getFilenamePtr() == 0L) sMessage += "(unknown)";
						else sMessage += _pPatternScript->getFilenamePtr();
						sMessage += "\":" + CGRuntime::endl() + sException;
						throw UtlException(e.getTraceStack(), sMessage);
					} catch(std::exception&) {
						_pPatternScript->freeLocalParameters();
						_pInputStream->close();
						delete _pInputStream;
						_pInputStream = 0L;
						if (_pOutputStream != 0L) {
							delete _pOutputStream;
							_pOutputStream = 0L;
							CGRuntime::_pOutputStream = pOldOutputStream;
							CGRuntime::_listOfPatternScripts.pop_front();
						}
						_iNumberOfTemporaryFiles--;
						throw;
					}
				}
			}
			_pPatternScript->freeLocalParameters();
			_pOutputStream->copy(*_pInputStream, iEnd);
			copyFromTemporaryFile(tcFile);
			_iNumberOfTemporaryFiles--;
			_pOutputStream->close();
			delete _pOutputStream;
			_pOutputStream = 0L;
			CGRuntime::_pOutputStream = pOldOutputStream;
			CGRuntime::_listOfPatternScripts.pop_front();
			captureOutputFile(tcFile);
		} else throw UtlException("unable to open marked file \"" + std::string(tcFile) + "\"");
		return result;
	}

	//////////////////////////////////////////////////////////////////////////
	// Building a protected area at the current location of the output file //
	//////////////////////////////////////////////////////////////////////////
	//
	// Called by 'setProtectedArea' procedure.
	//////////////////////////////////////////////////////////////////////////

	std::string DtaOutputFile::getProtectedAreaIndentation() const {
		std::string sIndentation;
		int iInputPosition = _pOutputStream->getInputLocation();
		int iCurrent = _pOutputStream->getOutputLocation();
		while (iCurrent > 0) {
			iCurrent--;
			_pOutputStream->setInputLocation(iCurrent);
			int iChar = _pOutputStream->readChar();
			if (iChar == '\t') sIndentation = std::string("\t") + sIndentation;
			else if (iChar == '\r') {
				break;//sIndentation = std::string("\r") + sIndentation;
			} else if ((iChar == '\r') || (iChar == '\n')) break;
			else sIndentation = std::string(" ") + sIndentation;
		}
		_pOutputStream->setInputLocation(iInputPosition);
		return sIndentation;
	}

	void DtaOutputFile::populateProtectedArea(const std::string& sProtectedAreaName, const std::string& sContent) {
		getProtectedAreasBag().setProtection(sProtectedAreaName.c_str(), sContent.c_str());
		setProtectedArea(sProtectedAreaName);
	}

	void DtaOutputFile::setProtectedArea(const std::string& sProtectedAreaName) {
		std::string sIndentation = getProtectedAreaIndentation();
		const DtaProtectedArea& myProtectedArea = getProtectedAreasBag().registerNewProtection(sProtectedAreaName.c_str());
		DtaSharpTagsHandler tagsHandler(0L);
		tagsHandler.writeProtectBegin(*_pOutputStream, sProtectedAreaName, myProtectedArea.getDefine());
		if (!myProtectedArea.getText().empty()) {
			if (!myProtectedArea.getDefine().empty()) writeTargetedArea(myProtectedArea.getText(), myProtectedArea.getDefine());
			else writeStandardArea(myProtectedArea.getText());
		}
		tagsHandler.writeProtectEnd(*_pOutputStream, sProtectedAreaName, myProtectedArea.getDefine());
	}

	bool DtaOutputFile::removeProtectedArea(const std::string& sProtectedAreaName) {
		return getProtectedAreasBag().removeProtection(sProtectedAreaName.c_str());
	}

	void DtaOutputFile::writeStandardArea(const std::string& sAreaCode) {
		DtaProject& myProject = DtaProject::getInstance();
		std::string sCommentBegin = myProject.getCommentBegin();
		std::string sCommentEnd = myProject.getCommentEnd();
		std::string::size_type iNextIndex = sAreaCode.find(sCommentBegin + "##");
		if (iNextIndex < 0) {
			(*_pOutputStream) << sAreaCode;
		} else {
			std::string::size_type iPreviousIndex = 0;
			do {
				std::string::size_type iEndIndex;
				bool bChange;
				const char* sCommand = sAreaCode.c_str() + (iNextIndex + 2/*sizeof("##")*/ + sCommentBegin.size());
				if (strncmp(sCommand, "ifdef ", 6) == 0) {
					int iIndex = iNextIndex + 8 + sCommentBegin.size();
					iEndIndex = sAreaCode.find("##", iIndex);
					std::string sTarget = sAreaCode.substr(iIndex, iEndIndex - iIndex);
					bChange = (!myProject.getDefineTarget(sTarget).empty());
				} else if (strncmp(sCommand, "ifndef ", 7) == 0) {
					int iIndex = iNextIndex + 9 + sCommentBegin.size();
					iEndIndex = sAreaCode.find("##", iIndex);
					std::string sTarget = sAreaCode.substr(iIndex, iEndIndex - iIndex);
					bChange = (myProject.getDefineTarget(sTarget).empty());
				} else if (strncmp(sCommand, "temporary", 9) == 0) {
					int iIndex = iNextIndex + 11 + sCommentBegin.size();
					iEndIndex = sAreaCode.find(sCommentBegin + "##temporary", iIndex);
					iEndIndex = sAreaCode.find(sCommentEnd, iEndIndex);
					std::string sText = sAreaCode.substr(iPreviousIndex, iNextIndex - iPreviousIndex);
					(*_pOutputStream) << sText;
					iPreviousIndex = iEndIndex + sCommentEnd.size();
					bChange = false;
				} else {
					bChange = false;
					iEndIndex = sAreaCode.find(sCommentEnd, iNextIndex + 2/*sizeof("##")*/ + sCommentBegin.size());
				}
				if (bChange) {
					iEndIndex += 2/*sizeof("##")*/;
					std::string sText = sAreaCode.substr(iPreviousIndex, iEndIndex - iPreviousIndex);
					(*_pOutputStream) << sText;
					iPreviousIndex = iEndIndex;
					iEndIndex = sAreaCode.find(sCommentEnd, iPreviousIndex);
					sText = sAreaCode.substr(iPreviousIndex, iEndIndex + sCommentEnd.size() - iPreviousIndex);
					(*_pOutputStream) << sText;
					(*_pOutputStream) << sCommentBegin + "##temporary BEGIN##" << sCommentEnd;
					(*_pOutputStream) << sText;
					(*_pOutputStream) << sCommentBegin + "##temporary END##" << sCommentEnd;
					iPreviousIndex = iEndIndex + sCommentEnd.size();
				}
				iNextIndex = sAreaCode.find(sCommentBegin + "##", iEndIndex);
			} while (iNextIndex != std::string::npos);
			if (iPreviousIndex < sAreaCode.size()) {
				std::string sText = sAreaCode.substr(iPreviousIndex);
				(*_pOutputStream) << sText;
			}
		}
	}

	void DtaOutputFile::writeTargetedArea(const std::string& sAreaCode, const std::string& sAreaTarget) {
		DtaProject& myProject = DtaProject::getInstance();
		if ((sAreaTarget[0] == '!' && myProject.getDefineTarget(sAreaTarget.substr(1)).empty()) ||
			(sAreaTarget[0] != '!' && !myProject.getDefineTarget(sAreaTarget).empty())) {
			writeStandardArea(sAreaCode);
		} else {
			std::string sCommentBegin = myProject.getCommentBegin();
			std::string sCommentEnd = myProject.getCommentEnd();
			std::string::size_type iPreviousIndex = 0;
			std::string::size_type iNextIndex = 0;
			do {
				if (strncmp(sAreaCode.c_str() + iNextIndex, sCommentBegin.c_str(), sCommentBegin.size()) != 0) {
					if (iNextIndex != iPreviousIndex) {
						std::string sText = sAreaCode.substr(iPreviousIndex, iNextIndex - iPreviousIndex);
						(*_pOutputStream) << sText;
					}
					(*_pOutputStream) << sCommentBegin << "##ifdef " << sAreaTarget << "##";
					iPreviousIndex = iNextIndex;
				}
				iNextIndex = sAreaCode.find(sCommentEnd, iNextIndex);
				iNextIndex += sCommentEnd.size();
			} while (iNextIndex < sAreaCode.size());
			if (iPreviousIndex < sAreaCode.size()) {
				std::string sText = sAreaCode.substr(iPreviousIndex);
				(*_pOutputStream) << sText;
			}
		}
	}

	std::list<std::string> DtaOutputFile::getProtectionKeys() const {
		return _pProtectedAreasBag->getProtectionKeys();
	}

	std::list<std::string> DtaOutputFile::remainingProtectionKeys() const {
		return _pProtectedAreasBag->remainingProtectionKeys();
	}


	//////////////////////////////////////////////////////////////////////////
	//                          A pattern script                            //
	//////////////////////////////////////////////////////////////////////////

	DtaPatternScript::DtaPatternScript() : DtaScript(0L), _bExecMode(false), _executeFunction(0L), _iExecStartLocation(-1), _pOutputFile(0L) {
		_pOutputFile = new DtaOutputFile(this);
		freeLocalParameters();
	}

	DtaPatternScript::DtaPatternScript(GrfBlock* pParentBlock, bool bExecMode) : DtaScript(pParentBlock), _bExecMode(bExecMode), _executeFunction(0L), _iExecStartLocation(-1), _pOutputFile(0L) {
		_pOutputFile = new DtaOutputFile(this);
		freeLocalParameters();
	}

	DtaPatternScript::DtaPatternScript(EXECUTE_FUNCTION executeFunction, bool bExecMode) : DtaScript(0L), _bExecMode(bExecMode), _executeFunction(executeFunction), _iExecStartLocation(-1), _pOutputFile(0L) {
		_pOutputFile = new DtaOutputFile(this);
		freeLocalParameters();
	}

	DtaPatternScript::~DtaPatternScript() {
		delete _pOutputFile;
	}

	DtaScriptFactory::SCRIPT_TYPE DtaPatternScript::getType() const { return DtaScriptFactory::PATTERN_SCRIPT; }
	bool DtaPatternScript::isAGenerateScript() const { return true; }

	void DtaPatternScript::traceEngine() const {
		if (getFilenamePtr() == 0L) CGRuntime::traceLine("template-based script (no filename):");
		else CGRuntime::traceLine("template-based script \"" + std::string(getFilenamePtr()) + "\":");
		traceInternalEngine();
	}


	/////////////////////////////////////////////////////////
	// Processing a 'generate' or 'expand' text generation //
	/////////////////////////////////////////////////////////
	SEQUENCE_INTERRUPTION_LIST DtaPatternScript::execute(DtaScriptVariable& thisContext) {
		if (_executeFunction == 0L) return DtaScript::execute(thisContext);
		CGThisModifier thisModifier(&thisContext);
		_executeFunction();
		return NO_INTERRUPTION;
	}

	SEQUENCE_INTERRUPTION_LIST DtaPatternScript::executeExpansion(const std::string& sScriptContent, DtaScriptVariable& thisContext) {
		if (sScriptContent.empty()) return execute(thisContext);
		ScpStream theStream;
		theStream << sScriptContent;
		DtaPatternScript theEmbeddedScript(&_graph, true);
		try {
			theEmbeddedScript.parseStream(theStream);
		} catch(UtlException& exception) {
			std::string sMessage = exception.getMessage() + CGRuntime::endl() + "... while expanding \"" + CGRuntime::composeCLikeString(DtaProject::getInstance().getMarkupKey()) + "\", in embedded script:" + CGRuntime::endl() + sScriptContent + CGRuntime::endl();
			throw UtlException(exception.getTraceStack(), sMessage);
		}
		return theEmbeddedScript.execute(thisContext);
	}


	////////////////////////////////////////////////////////////////////////////
	// Parsing of specific functions and procedure of 'generate' and 'expand' //
	////////////////////////////////////////////////////////////////////////////

	bool DtaPatternScript::betweenCommands(ScpStream& script, GrfBlock& block) {
		bool bNewCommand = false;
		if (_bExecMode) {
			script.skipEmpty();
			if (script.isEqualTo('@') || script.isEqualTo("%>")) _bExecMode = false;
		}
		if (!_bExecMode) {
			char sBuffer[16384];
			int iIndex = 0;
			int iChar = script.readChar();
			while (iChar > 0) {
				if (iChar == (int) '@') {
					_bExecMode = true;
					break;
				} else if (iChar == (int) '\\') {
					iChar = script.readChar();
					if ((iChar == (int) '@') || (iChar == (int) '<') || (iChar == (int) '%')) sBuffer[iIndex++] = (char) iChar;
					else {
						sBuffer[iIndex++] = '\\';
						if (iChar > 0) sBuffer[iIndex++] = (char) iChar;
					}
				} else if (iChar == (int) '<') {
					iChar = script.readChar();
					if (iChar == (int) '%') {
						_bExecMode = true;
						break;
					}
					script.goBack();
					sBuffer[iIndex++] = '<';
				} else {
					sBuffer[iIndex++] = (char) iChar;
				}
				if (iIndex >= 16384 - 2) {
					sBuffer[iIndex] = '\0';
					iIndex = 0;
					bNewCommand = true;
					block.add(_pOutputFile->createText(sBuffer));
				}
				iChar = script.readChar();
			}
			if (iIndex > 0) {
				sBuffer[iIndex] = '\0';
				bNewCommand = true;
				block.add(_pOutputFile->createText(sBuffer));
			}
			if (_bExecMode) {
				script.skipEmpty();
				_iExecStartLocation = script.getInputLocation();
				int a = script.peekChar();
				if ((a == (int) '$') || (a == (int) '(') || (a == (int) '"') || (a == (int) '\'')) {
					std::auto_ptr<ExprScriptExpression> pExpr(parseExpression(block, script));
					script.skipEmpty();
					if (!handleEmbeddedExpression(script, block, pExpr)) {
						throw UtlException(script, "expression (closed by '@' or '%>') or instruction expected behind a '@' or '<%'");
					}
				}
			}
		}
		return bNewCommand;
	}

	bool DtaPatternScript::handleEmbeddedExpression(ScpStream& script, GrfBlock& block, std::auto_ptr<ExprScriptExpression>& pExpr) {
		bool bEmbeddedExpression = script.isEqualTo('@');
		if (!bEmbeddedExpression) {
			bEmbeddedExpression = script.isEqualTo("%>");
			if (bEmbeddedExpression) script.goBack();
			else bEmbeddedExpression = !script.skipEmpty();
		}
		if (bEmbeddedExpression) {
			script.goBack();
			GrfWriteText* pWriteText = new GrfWriteText;
			pWriteText->setText(pExpr.release());
			if (requiresParsingInformation()) pWriteText->setParsingInformation(getFilenamePtr(), script);
			block.add(pWriteText);
		}
		return bEmbeddedExpression;
	}

	void DtaPatternScript::handleUnknownCommand(const std::string& sCommand, ScpStream& script, GrfBlock& block) {
		if (script.getInputLocation() - (int) sCommand.size() == _iExecStartLocation) {
			script.setInputLocation(_iExecStartLocation);
			std::auto_ptr<ExprScriptExpression> pExpr(parseKeyTemplateExpression(block, script));
			script.skipEmpty();
			if (script.isEqualTo(';')) {
				if (!pExpr->isAFunctionExpression()) {
					script.setInputLocation(_iExecStartLocation);
					throw UtlException(script, "unknown command or procedure or function \"" + sCommand + "\"");
				}
				GrfNop* pNop = new GrfNop;
				if (requiresParsingInformation()) pNop->setParsingInformation(getFilenamePtr(), script);
				block.add(pNop);
				pNop->setExpression(pExpr.release());
				return;
			} else if (script.isEqualTo('+') || script.isEqualTo('=')) {
				script.goBack();
				GrfSetAssignment* pAssignment = new GrfSetAssignment;
				if (requiresParsingInformation()) pAssignment->setParsingInformation(getFilenamePtr(), script);
				block.add(pAssignment);
				ExprScriptVariable* pVariable = dynamic_cast<ExprScriptVariable*>(pExpr.get());
				if (pVariable == 0L) throw UtlException(script, "variable expected on the left side of an assignment operator");
				pAssignment->setVariable(pVariable);
				pExpr.release();
				bool bConcat = script.isEqualTo('+');
				if (!script.isEqualTo('=')) throw UtlException(script, "syntax error: assignment expected ('+=' or '=' operator)");
				script.skipEmpty();
				pAssignment->setValue(parseExpression(block, script), bConcat);
				script.skipEmpty();
				if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
				return;
			} else {
				if (handleEmbeddedExpression(script, block, pExpr)) return;
			}
			script.setInputLocation(_iExecStartLocation);
		}
		DtaScript::handleUnknownCommand(sCommand, script, block);
	}

	void DtaPatternScript::handleNotAWordCommand(ScpStream& script, GrfBlock& block) {
		if (_bExecMode && (script.isEqualTo('@') || script.isEqualTo("%>"))) {
			_bExecMode = false;
			betweenCommands(script, block);
		} else {
			DtaScript::handleNotAWordCommand(script, block);
		}
	}

//##markup##"parsing"
//##begin##"parsing"
void DtaPatternScript::parseAttachOutputToSocket(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'attachOutputToSocket' is available on pattern scripts only");
	GrfAttachOutputToSocket* pAttachOutputToSocket = new GrfAttachOutputToSocket;
	if (requiresParsingInformation()) pAttachOutputToSocket->setParsingInformation(getFilenamePtr(), script);
	block.add(pAttachOutputToSocket);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pAttachOutputToSocket->setSocket(parseExpression(block, script));
	else pAttachOutputToSocket->setSocket(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseDetachOutputFromSocket(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'detachOutputFromSocket' is available on pattern scripts only");
	GrfDetachOutputFromSocket* pDetachOutputFromSocket = new GrfDetachOutputFromSocket;
	if (requiresParsingInformation()) pDetachOutputFromSocket->setParsingInformation(getFilenamePtr(), script);
	block.add(pDetachOutputFromSocket);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pDetachOutputFromSocket->setSocket(parseExpression(block, script));
	else pDetachOutputFromSocket->setSocket(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseInsertText(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'insertText' is available on pattern scripts only");
	GrfInsertText* pInsertText = new GrfInsertText;
	if (requiresParsingInformation()) pInsertText->setParsingInformation(getFilenamePtr(), script);
	block.add(pInsertText);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pInsertText->setLocation(parseExpression(block, script));
	else pInsertText->setLocation(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pInsertText->setText(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseInsertTextOnce(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'insertTextOnce' is available on pattern scripts only");
	GrfInsertTextOnce* pInsertTextOnce = new GrfInsertTextOnce;
	if (requiresParsingInformation()) pInsertTextOnce->setParsingInformation(getFilenamePtr(), script);
	block.add(pInsertTextOnce);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pInsertTextOnce->setLocation(parseExpression(block, script));
	else pInsertTextOnce->setLocation(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pInsertTextOnce->setText(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseInsertTextToFloatingLocation(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'insertTextToFloatingLocation' is available on pattern scripts only");
	GrfInsertTextToFloatingLocation* pInsertTextToFloatingLocation = new GrfInsertTextToFloatingLocation;
	if (requiresParsingInformation()) pInsertTextToFloatingLocation->setParsingInformation(getFilenamePtr(), script);
	block.add(pInsertTextToFloatingLocation);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pInsertTextToFloatingLocation->setLocation(parseExpression(block, script));
	else pInsertTextToFloatingLocation->setLocation(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pInsertTextToFloatingLocation->setText(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseInsertTextOnceToFloatingLocation(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'insertTextOnceToFloatingLocation' is available on pattern scripts only");
	GrfInsertTextOnceToFloatingLocation* pInsertTextOnceToFloatingLocation = new GrfInsertTextOnceToFloatingLocation;
	if (requiresParsingInformation()) pInsertTextOnceToFloatingLocation->setParsingInformation(getFilenamePtr(), script);
	block.add(pInsertTextOnceToFloatingLocation);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pInsertTextOnceToFloatingLocation->setLocation(parseExpression(block, script));
	else pInsertTextOnceToFloatingLocation->setLocation(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pInsertTextOnceToFloatingLocation->setText(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseOverwritePortion(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'overwritePortion' is available on pattern scripts only");
	GrfOverwritePortion* pOverwritePortion = new GrfOverwritePortion;
	if (requiresParsingInformation()) pOverwritePortion->setParsingInformation(getFilenamePtr(), script);
	block.add(pOverwritePortion);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pOverwritePortion->setLocation(parseExpression(block, script));
	else pOverwritePortion->setLocation(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pOverwritePortion->setText(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pOverwritePortion->setSize(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parsePopulateProtectedArea(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'populateProtectedArea' is available on pattern scripts only");
	GrfPopulateProtectedArea* pPopulateProtectedArea = new GrfPopulateProtectedArea;
	if (requiresParsingInformation()) pPopulateProtectedArea->setParsingInformation(getFilenamePtr(), script);
	block.add(pPopulateProtectedArea);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pPopulateProtectedArea->setProtectedAreaName(parseExpression(block, script));
	else pPopulateProtectedArea->setProtectedAreaName(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pPopulateProtectedArea->setContent(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseResizeOutputStream(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'resizeOutputStream' is available on pattern scripts only");
	GrfResizeOutputStream* pResizeOutputStream = new GrfResizeOutputStream;
	if (requiresParsingInformation()) pResizeOutputStream->setParsingInformation(getFilenamePtr(), script);
	block.add(pResizeOutputStream);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pResizeOutputStream->setNewSize(parseExpression(block, script));
	else pResizeOutputStream->setNewSize(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseSetFloatingLocation(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'setFloatingLocation' is available on pattern scripts only");
	GrfSetFloatingLocation* pSetFloatingLocation = new GrfSetFloatingLocation;
	if (requiresParsingInformation()) pSetFloatingLocation->setParsingInformation(getFilenamePtr(), script);
	block.add(pSetFloatingLocation);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pSetFloatingLocation->setKey(parseExpression(block, script));
	else pSetFloatingLocation->setKey(pMethodCaller);
	if ((pMethodCaller == 0L) && !script.isEqualTo(',')) throw UtlException(script, "syntax error: ',' expected");
	script.skipEmpty();
	pSetFloatingLocation->setLocation(parseExpression(block, script));
	script.skipEmpty();
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseSetOutputLocation(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'setOutputLocation' is available on pattern scripts only");
	GrfSetOutputLocation* pSetOutputLocation = new GrfSetOutputLocation;
	if (requiresParsingInformation()) pSetOutputLocation->setParsingInformation(getFilenamePtr(), script);
	block.add(pSetOutputLocation);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pSetOutputLocation->setLocation(parseExpression(block, script));
	else pSetOutputLocation->setLocation(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseSetProtectedArea(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'setProtectedArea' is available on pattern scripts only");
	GrfSetProtectedArea* pSetProtectedArea = new GrfSetProtectedArea;
	if (requiresParsingInformation()) pSetProtectedArea->setParsingInformation(getFilenamePtr(), script);
	block.add(pSetProtectedArea);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pSetProtectedArea->setProtectedAreaName(parseExpression(block, script));
	else pSetProtectedArea->setProtectedAreaName(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseWriteBytes(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'writeBytes' is available on pattern scripts only");
	GrfWriteBytes* pWriteBytes = new GrfWriteBytes;
	if (requiresParsingInformation()) pWriteBytes->setParsingInformation(getFilenamePtr(), script);
	block.add(pWriteBytes);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pWriteBytes->setBytes(parseExpression(block, script));
	else pWriteBytes->setBytes(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseWriteText(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'writeText' is available on pattern scripts only");
	GrfWriteText* pWriteText = new GrfWriteText;
	if (requiresParsingInformation()) pWriteText->setParsingInformation(getFilenamePtr(), script);
	block.add(pWriteText);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pWriteText->setText(parseExpression(block, script));
	else pWriteText->setText(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

void DtaPatternScript::parseWriteTextOnce(GrfBlock& block, ScpStream& script, ExprScriptVariable* pMethodCaller) {
	if (!isAGenerateScript()) throw UtlException(script, "command 'writeTextOnce' is available on pattern scripts only");
	GrfWriteTextOnce* pWriteTextOnce = new GrfWriteTextOnce;
	if (requiresParsingInformation()) pWriteTextOnce->setParsingInformation(getFilenamePtr(), script);
	block.add(pWriteTextOnce);
	script.skipEmpty();
	if (!script.isEqualTo('(')) throw UtlException(script, "syntax error: '(' expected");
	script.skipEmpty();
	if (pMethodCaller == 0L) pWriteTextOnce->setText(parseExpression(block, script));
	else pWriteTextOnce->setText(pMethodCaller);
	if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
	script.skipEmpty();
	if (!script.isEqualTo(';')) throw UtlException(script, "syntax error: ';' expected");
}

//##end##"parsing"

	void DtaPatternScript::parseGeneratedFile(GrfBlock& block, ScpStream& script) {
		if (!isAGenerateScript()) throw UtlException(script, "statement modifier 'generated_file' is available on pattern scripts only");
		GrfGeneratedFile* pGeneratedFileBlock = new GrfGeneratedFile(this, &block);
		if (requiresParsingInformation()) pGeneratedFileBlock->setParsingInformation(getFilenamePtr(), script);
		block.add(pGeneratedFileBlock);
		script.skipEmpty();
		if (script.isEqualTo('(')) {
			script.skipEmpty();
			pGeneratedFileBlock->setOutputFile(parseExpression(block, script));
			script.skipEmpty();
			if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
		}
		parseBlock(script, *pGeneratedFileBlock);
	}

	void DtaPatternScript::parseGeneratedString(GrfBlock& block, ScpStream& script) {
		if (!isAGenerateScript()) throw UtlException(script, "statement modifier 'generated_string' is available on pattern scripts only");
		GrfGeneratedString* pGeneratedStringBlock = new GrfGeneratedString(this, &block);
		if (requiresParsingInformation()) pGeneratedStringBlock->setParsingInformation(getFilenamePtr(), script);
		block.add(pGeneratedStringBlock);
		script.skipEmpty();
		if (script.isEqualTo('(')) {
			script.skipEmpty();
			pGeneratedStringBlock->setOutputString(parseVariableExpression(block, script));
			script.skipEmpty();
			if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected after the variable");
		}
		parseBlock(script, *pGeneratedStringBlock);
	}

	void DtaPatternScript::parseAppendedFile(GrfBlock& block, ScpStream& script) {
		if (!isAGenerateScript()) throw UtlException(script, "statement modifier 'Append_file' is available on pattern scripts only");
		GrfAppendedFile* pAppendedFileBlock = new GrfAppendedFile(this, &block);
		if (requiresParsingInformation()) pAppendedFileBlock->setParsingInformation(getFilenamePtr(), script);
		block.add(pAppendedFileBlock);
		script.skipEmpty();
		if (script.isEqualTo('(')) {
			script.skipEmpty();
			pAppendedFileBlock->setOutputFile(parseExpression(block, script));
			script.skipEmpty();
			if (!script.isEqualTo(')')) throw UtlException(script, "syntax error: ')' expected");
		}
		parseBlock(script, *pAppendedFileBlock);
	}
}
