Ejemplo n.º 1
0
bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
	if (sFileName.empty()) {
		DEBUG("Empty filename in CTemplate::Print()");
		return false;
	}

	CFile File(sFileName);

	if (!File.Open()) {
		DEBUG("Unable to open file [" + sFileName + "] in CTemplate::Print()");
		return false;
	}

	CString sLine;
	CString sSetBlockVar;
	bool bValidLastIf = false;
	bool bInSetBlock = false;
	unsigned long uFilePos = 0;
	unsigned long uCurPos = 0;
	unsigned int uLineNum = 0;
	unsigned int uNestedIfs = 0;
	unsigned int uSkip = 0;
	bool bLoopCont = false;
	bool bLoopBreak = false;
	bool bExit = false;

	while (File.ReadLine(sLine)) {
		CString sOutput;
		bool bFoundATag = false;
		bool bTmplLoopHasData = false;
		uLineNum++;
		CString::size_type iPos = 0;
		uCurPos = uFilePos;
		unsigned int uLineSize = sLine.size();
		bool bBroke = false;

		while (1) {
			iPos = sLine.find("<?");

			if (iPos == CString::npos) {
				break;
			}

			uCurPos += iPos;
			bFoundATag = true;

			if (!uSkip) {
				sOutput += sLine.substr(0, iPos);
			}

			sLine = sLine.substr(iPos +2);

			CString::size_type iPos2 = sLine.find("?>");

			// Make sure our tmpl tag is ended properly
			if (iPos2 == CString::npos) {
				DEBUG("Template tag not ended properly in file [" + sFileName + "] [<?" + sLine + "]");
				return false;
			}

			uCurPos += iPos2 +4;

			CString sMid = CString(sLine.substr(0, iPos2)).Trim_n();

			// Make sure we don't have a nested tag
			if (sMid.find("<?") == CString::npos) {
				sLine = sLine.substr(iPos2 +2);
				CString sAction = sMid.Token(0);
				CString sArgs = sMid.Token(1, true);
				bool bNotFound = false;

				// If we're breaking or continuing from within a loop, skip all tags that aren't ENDLOOP
				if ((bLoopCont || bLoopBreak) && !sAction.Equals("ENDLOOP")) {
					continue;
				}

				if (!uSkip) {
					if (sAction.Equals("INC")) {
						if (!Print(ExpandFile(sArgs, true), oOut)) {
							DEBUG("Unable to print INC'd file [" + sArgs + "]");
							return false;
						}
					} else if (sAction.Equals("SETOPTION")) {
						m_spOptions->Parse(sArgs);
					} else if (sAction.Equals("ADDROW")) {
						CString sLoopName = sArgs.Token(0);
						MCString msRow;

						if (sArgs.Token(1, true, " ").OptionSplit(msRow)) {
							CTemplate& NewRow = AddRow(sLoopName);

							for (MCString::iterator it = msRow.begin(); it != msRow.end(); ++it) {
								NewRow[it->first] = it->second;
							}
						}
					} else if (sAction.Equals("SET")) {
						CString sName = sArgs.Token(0);
						CString sValue = sArgs.Token(1, true);

						(*this)[sName] = sValue;
					} else if (sAction.Equals("JOIN")) {
						VCString vsArgs;
						//sArgs.Split(" ", vsArgs, false, "\"", "\"");
						sArgs.QuoteSplit(vsArgs);

						if (vsArgs.size() > 1) {
							CString sDelim = vsArgs[0];
							bool bFoundOne = false;
							CString::EEscape eEscape = CString::EASCII;

							for (unsigned int a = 1; a < vsArgs.size(); a++) {
								const CString& sArg = vsArgs[a];

								if (sArg.Equals("ESC=", false, 4)) {
									eEscape = CString::ToEscape(sArg.LeftChomp_n(4));
								} else {
									CString sValue = GetValue(sArg);

									if (!sValue.empty()) {
										if (bFoundOne) {
											sOutput += sDelim;
										}

										sOutput += sValue.Escape_n(eEscape);
										bFoundOne = true;
									}
								}
							}
						}
					} else if (sAction.Equals("SETBLOCK")) {
						sSetBlockVar = sArgs;
						bInSetBlock = true;
					} else if (sAction.Equals("EXPAND")) {
						sOutput += ExpandFile(sArgs, true);
					} else if (sAction.Equals("VAR")) {
						sOutput += GetValue(sArgs);
					} else if (sAction.Equals("LT")) {
						sOutput += "<?";
					} else if (sAction.Equals("GT")) {
						sOutput += "?>";
					} else if (sAction.Equals("CONTINUE")) {
						CTemplateLoopContext* pContext = GetCurLoopContext();

						if (pContext) {
							uSkip++;
							bLoopCont = true;

							break;
						} else {
							DEBUG("[" + sFileName + ":" + CString(uCurPos - iPos2 -4) + "] <? CONTINUE ?> must be used inside of a loop!");
						}
					} else if (sAction.Equals("BREAK")) {
						// break from loop
						CTemplateLoopContext* pContext = GetCurLoopContext();

						if (pContext) {
							uSkip++;
							bLoopBreak = true;

							break;
						} else {
							DEBUG("[" + sFileName + ":" + CString(uCurPos - iPos2 -4) + "] <? BREAK ?> must be used inside of a loop!");
						}
					} else if (sAction.Equals("EXIT")) {
						bExit = true;
					} else if (sAction.Equals("DEBUG")) {
						DEBUG("CTemplate DEBUG [" + sFileName + "@" + CString(uCurPos - iPos2 -4) + "b] -> [" + sArgs + "]");
					} else if (sAction.Equals("LOOP")) {
						CTemplateLoopContext* pContext = GetCurLoopContext();

						if (!pContext || pContext->GetFilePosition() != uCurPos) {
							// we are at a brand new loop (be it new or a first pass at an inner loop)

							CString sLoopName = sArgs.Token(0);
							bool bReverse = (sArgs.Token(1).Equals("REVERSE"));
							bool bSort = (sArgs.Token(1).Left(4).Equals("SORT"));
							vector<CTemplate*>* pvLoop = GetLoop(sLoopName);

							if (bSort && pvLoop != NULL &&  pvLoop->size() > 1) {
								CString sKey;

								if(sArgs.Token(1).TrimPrefix_n("SORT").Left(4).Equals("ASC=")) {
									sKey = sArgs.Token(1).TrimPrefix_n("SORTASC=");
								} else if(sArgs.Token(1).TrimPrefix_n("SORT").Left(5).Equals("DESC=")) {
									sKey = sArgs.Token(1).TrimPrefix_n("SORTDESC=");
									bReverse = true;
								}

								if (!sKey.empty()) {
									std::sort(pvLoop->begin(), pvLoop->end(), CLoopSorter(sKey));
								}
							}

							if (pvLoop) {
								// If we found data for this loop, add it to our context vector
								//unsigned long uBeforeLoopTag = uCurPos - iPos2 - 4;
								unsigned long uAfterLoopTag = uCurPos;

								for (CString::size_type t = 0; t < sLine.size(); t++) {
									char c = sLine[t];
									if (c == '\r' || c == '\n') {
										uAfterLoopTag++;
									} else {
										break;
									}
								}

								m_vLoopContexts.push_back(new CTemplateLoopContext(uAfterLoopTag, sLoopName, bReverse, pvLoop));
							} else {  // If we don't have data, just skip this loop and everything inside
								uSkip++;
							}
						}
					} else if (sAction.Equals("IF")) {
						if (ValidIf(sArgs)) {
							uNestedIfs++;
							bValidLastIf = true;
						} else {
							uSkip++;
							bValidLastIf = false;
						}
					} else if (sAction.Equals("REM")) {
						uSkip++;
					} else {
						bNotFound = true;
					}
				} else if (sAction.Equals("REM")) {
					uSkip++;
				} else if (sAction.Equals("IF")) {
					uSkip++;
				} else if (sAction.Equals("LOOP")) {
					uSkip++;
				}

				if (sAction.Equals("ENDIF")) {
					if (uSkip) {
						uSkip--;
					} else {
						uNestedIfs--;
					}
				} else if (sAction.Equals("ENDREM")) {
					if (uSkip) {
						uSkip--;
					}
				} else if (sAction.Equals("ENDSETBLOCK")) {
					bInSetBlock = false;
					sSetBlockVar = "";
				} else if (sAction.Equals("ENDLOOP")) {
					if (bLoopCont && uSkip == 1) {
						uSkip--;
						bLoopCont = false;
					}

					if (bLoopBreak && uSkip == 1) {
						uSkip--;
					}

					if (uSkip) {
						uSkip--;
					} else {
						// We are at the end of the loop so we need to inc the index
						CTemplateLoopContext* pContext = GetCurLoopContext();

						if (pContext) {
							pContext->IncRowIndex();

							// If we didn't go out of bounds we need to seek back to the top of our loop
							if (!bLoopBreak && pContext->GetCurRow()) {
								uCurPos = pContext->GetFilePosition();
								uFilePos = uCurPos;
								uLineSize = 0;

								File.Seek(uCurPos);
								bBroke = true;

								if (!sOutput.Trim_n().empty()) {
									pContext->SetHasData();
								}

								break;
							} else {
								if (sOutput.Trim_n().empty()) {
									sOutput.clear();
								}

								bTmplLoopHasData = pContext->HasData();
								DelCurLoopContext();
								bLoopBreak = false;
							}
						}
					}
				} else if (sAction.Equals("ELSE")) {
					if (!bValidLastIf && uSkip == 1) {
						CString sArg = sArgs.Token(0);

						if (sArg.empty() || (sArg.Equals("IF") && ValidIf(sArgs.Token(1, true)))) {
							uSkip = 0;
							bValidLastIf = true;
						}
					} else if (!uSkip) {
						uSkip = 1;
					}
				} else if (bNotFound) {
					// Unknown tag that isn't being skipped...
					vector<CSmartPtr<CTemplateTagHandler> >& vspTagHandlers = GetTagHandlers();

					if (!vspTagHandlers.empty()) { // @todo this should go up to the top to grab handlers
						CTemplate* pTmpl = GetCurTemplate();
						CString sCustomOutput;

						for (unsigned int j = 0; j < vspTagHandlers.size(); j++) {
							CSmartPtr<CTemplateTagHandler> spTagHandler = vspTagHandlers[j];

							if (spTagHandler->HandleTag(*pTmpl, sAction, sArgs, sCustomOutput)) {
								sOutput += sCustomOutput;
								bNotFound = false;
								break;
							}
						}

						if (bNotFound) {
							DEBUG("Unknown/Unhandled tag [" + sAction + "]");
						}
					}
				}

				continue;
			}

			DEBUG("Malformed tag on line " + CString(uLineNum) + " of [" << File.GetLongName() + "]");
			DEBUG("--------------- [" + sLine + "]");
		}

		if (!bBroke) {
			uFilePos += uLineSize;

			if (!uSkip) {
				sOutput += sLine;
			}
		}

		if (!bFoundATag || bTmplLoopHasData || sOutput.find_first_not_of(" \t\r\n") != CString::npos) {
			if (bInSetBlock) {
				CString sName = sSetBlockVar.Token(0);
				//CString sValue = sSetBlockVar.Token(1, true);
				(*this)[sName] += sOutput;
			} else {
				oOut << sOutput;
			}
		}

		if (bExit) {
			break;
		}
	}

	oOut.flush();

	return true;
}