--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/aknlayoutcompiler/src/HtmlParse.cpp Thu Dec 17 09:14:18 2009 +0200
@@ -0,0 +1,668 @@
+/*
+* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*
+*/
+
+
+#include "HtmlParse.h"
+#include <iostream>
+#include <sstream>
+#include <set>
+#include <algorithm>
+using namespace std;
+
+
+const string WhiteSpace(" \t\r\n");
+const string gValidText("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_<>()-+. ");
+const string gValidNum("0123456789+-p ,");
+const string KEllipsis("\205");
+const string KUnknown("unknown");
+
+string TrimWhiteSpace(const string& aString)
+ {
+ int start = aString.find_first_not_of(WhiteSpace);
+ if (start == string::npos)
+ return "";
+ else
+ return aString.substr(start, 1+aString.find_last_not_of(WhiteSpace)-start);
+ }
+
+
+THtmlParseLayoutTable::THtmlParseLayoutTable(TLayout* aTables)
+: TLayoutTable(aTables)
+ {
+ }
+
+THtmlParseLayoutTable::~THtmlParseLayoutTable()
+ {
+ }
+
+bool THtmlParseLayoutTable::Read(istream& in)
+ {
+ iIn = ∈
+
+ if (!SearchForTableStart())
+ return false;
+
+ ExtractTable();
+
+ iIn = 0;
+
+ return true;
+ }
+
+bool THtmlParseLayoutTable::SearchForTableStart()
+ {
+ string text;
+ while (!Eof())
+ {
+ string next = Next();
+ if (IsToken(next))
+ {
+ if (next.substr(0,6) == "<TABLE")
+ {
+ ExtractTitle(text);
+ return true;
+ }
+ }
+ else
+ {
+ if (next.substr(0,5) == "Table")
+ text = next.substr(5);
+ else
+ text += next;
+ }
+ }
+
+ return false;
+ }
+
+set<string> gKnownTitles;
+
+void THtmlParseLayoutTable::ExtractTitle(const string& aText)
+ {
+ iName = UnHtml(aText);
+ int pos = iName.find_first_not_of("1234567890.\t\r\n ");
+ if (pos == string::npos)
+ return;
+ iName = iName.substr(pos);
+ iName = StripTo(iName, gValidText);
+ iName = TrimWhiteSpace(iName);
+
+ while (gKnownTitles.find(iName) != gKnownTitles.end())
+ iName += "_dup";
+
+ gKnownTitles.insert(iName);
+ }
+
+void THtmlParseLayoutTable::ExtractTable()
+ {
+ string cell;
+ string td;
+ Row row;
+ while (!Eof())
+ {
+ string tok = Next();
+ if (IsToken(tok))
+ {
+ if (tok.substr(0,4) == "<TD ")
+ {
+ td = tok;
+ }
+ else if (tok == "</TD>")
+ {
+ row.push_back(CleanCell(cell, row));
+ cell = "";
+ int colspan = GetVal(td, "COLSPAN");
+ for (int i=1; i<colspan; i++)
+ row.push_back("");
+ }
+ else if (tok == "</TR>")
+ {
+ if (!row[1].length())
+ row[1] = "untitled";
+ AddRow(row);
+ row.erase(row.begin(), row.end());
+ }
+ else if (tok == "</TABLE>")
+ {
+ break;
+ }
+ else if (cell.length() && (tok == "<BR>" || tok == "<P>"))
+ {
+ cell += ", ";
+ }
+ }
+ else
+ {
+ cell += tok;
+ }
+ }
+ }
+
+string THtmlParseLayoutTable::CleanCell(const string& cell, const Row& row)
+ {
+ if (iColumnNames.size() < 6) // table header
+ return cell;
+ else if (row.size() < 2 || row.size() == iColumnNames.size()) // free text
+ return StripTo(UnHtml(cell),gValidText+KEllipsis);
+ else
+ return CleanMultiCell(StripTo(UnHtml(cell),gValidText+"/,*")); // value cell
+ }
+
+string THtmlParseLayoutTable::Next()
+ {
+ string s;
+ char c;
+
+ do {
+ c = Get();
+ } while (!Eof() && WhiteSpace.find(c) != string::npos);
+ s += c;
+
+ if (c == '<')
+ {
+ do {
+ c = Get();
+ s += toupper(c);
+ } while (!Eof() && c != '>');
+ }
+ else
+ {
+ do {
+ c = Get();
+ s += c;
+ } while (!Eof() && c != '<');
+
+ if (c == '<')
+ PutBack(c);
+
+ s = s.substr(0, s.find_last_not_of(WhiteSpace));
+ }
+
+ return s;
+ }
+
+bool THtmlParseLayoutTable::IsToken(const string& aText)
+ {
+ return aText.length()>0 && aText[0] == '<';
+ }
+
+void THtmlParseLayoutTable::PutBack(const string& aText)
+ {
+ for (string::const_reverse_iterator pC = aText.rbegin(); pC != aText.rend(); ++pC)
+ PutBack(*pC);
+ }
+
+int THtmlParseLayoutTable::GetVal(const string& aText, const string& aField)
+ {
+ int pos = aText.find(aField+"=");
+ if (pos == string::npos)
+ return 0;
+ string val = aText.substr(pos + aField.length() + 1);
+ val = val.substr(0, val.find_first_of(WhiteSpace + ">"));
+ stringstream s(val);
+ int ret;
+ s >> ret;
+ return ret;
+ }
+
+char THtmlParseLayoutTable::Get()
+ {
+ if (iPutBack.length())
+ {
+ char c = iPutBack[iPutBack.length()-1];
+ iPutBack.erase(iPutBack.length()-1, 1);
+ return c;
+ }
+ else
+ {
+ return iIn->get();
+ }
+ }
+
+void THtmlParseLayoutTable::PutBack(char aChar)
+ {
+ iPutBack += aChar;
+ }
+
+bool THtmlParseLayoutTable::Eof()
+ {
+ return iPutBack.length()==0 && iIn->eof();
+ }
+
+string THtmlParseLayoutTable::CleanMultiCell(const string& aText)
+ {
+ // This function removes trailing commas from the end of cells where there is no
+ // comma in the middle of the cell.
+ // needed because of odd formatting found.
+ if (aText.length()==0)
+ return aText;
+
+ int pos = aText.find_last_not_of(" ,");
+ if (pos == string::npos)
+ return "";
+
+ string text;
+ if (aText.substr(0,pos+1).find(",") == string::npos)
+ text = aText.substr(0,pos+1);
+ else
+ text = aText;
+
+ return text;
+ }
+
+const int KMaxStars = 16;
+struct SCellParamDesc
+ {
+ SCellParamDesc();
+ bool operator==(const SCellParamDesc& a);
+ int iParams;
+ bool iStars[KMaxStars];
+ };
+
+SCellParamDesc::SCellParamDesc()
+ {
+ iParams=0;
+ for (int i=0; i<KMaxStars; i++)
+ iStars[i] = false;
+ }
+
+bool SCellParamDesc::operator==(const SCellParamDesc& a)
+ {
+ if (iParams!=a.iParams)
+ return false;
+
+ for (int i=0; i<KMaxStars; i++)
+ {
+ if (iStars[i]!=a.iStars[i])
+ return false;
+ }
+
+ return true;
+ }
+
+THtmlParseLayoutTable::Row THtmlParseLayoutTable::MakeParamTable(const Row& row, int start, int num)
+ {
+ Row params;
+ vector<SCellParamDesc> paramDescs;
+
+ // initialise params
+ for (Row::const_iterator pR = row.begin(); pR != row.end(); ++pR)
+ {
+ params.push_back("");
+ paramDescs.push_back(SCellParamDesc());
+ }
+
+ // count params and stars
+ int i;
+ for (i=0; i<num; i++)
+ {
+ SCellParamDesc& desc = paramDescs[start+i];
+ const string& cell = row[start+i];
+ desc.iParams = 1;
+
+ int starCount=0;
+ for (string::const_iterator pC = cell.begin(); pC != cell.end(); ++pC)
+ {
+ if (*pC == '*')
+ {
+ starCount++;
+ }
+ else
+ {
+ if (starCount)
+ {
+ desc.iStars[starCount-1] = true;
+ starCount = 0;
+ }
+ if (*pC == ',')
+ {
+ desc.iParams++;
+ }
+ }
+ }
+
+ if (starCount)
+ desc.iStars[starCount-1] = true;
+ }
+
+ // assign parameter names
+ string name("aCommon1");
+ for (i=0; i<num; i++)
+ {
+ SCellParamDesc& desc = paramDescs[start+i];
+ if (desc.iParams == 1)
+ continue;
+
+ string& param = params[start+i];
+
+ int count = 0;
+ bool first = true;
+
+ // look for a similar cell
+ for (int j=0; j<num; j++)
+ {
+ if (paramDescs[start+j] == desc)
+ {
+ count++;
+ if (count == 1 && j<i)
+ {
+ first = false;
+ param = params[start+j];
+ }
+ }
+ }
+
+ // assign a new name if there is no similar cell already named
+ if (count>1 && first)
+ {
+ param = name;
+ name[7]++;
+ }
+ }
+
+ return params;
+ }
+
+void THtmlParseLayoutTable::AddRow(Row& row)
+ {
+ if (row.size()==0)
+ return; // empty row
+
+ const string& first = row[0];
+
+ if (first.length()==0 || first == "P")
+ AddTitleRow(row);
+ else if (first == "No.")
+ AddColumnNameRow(row);
+ else if (first == KEllipsis)
+ iAppend = true;
+ else
+ AddLineRow(row);
+ }
+
+void THtmlParseLayoutTable::AddColumnNameRow(Row& row)
+ {
+ if (row.size()<4)
+ return; // unknown row type
+
+ if (row[1] == "Value")
+ row[1] = "Item";
+
+ if (row[1] == "Font")
+ iType = ETextTable;
+ else if (row[1] == "Item")
+ iType = EWindowTable;
+ else
+ return; // unknown row type
+
+ SetDefaultColumnNames();
+ iColumnNames.insert(iColumnNames.begin()+3, "b/r"); // for skins LAF
+
+ int foundCount = 0;
+ vector<string> foundColNames;
+ for (Row::iterator pCol = row.begin()+1; pCol != row.end(); pCol++)
+ {
+ string cellTitle = *pCol;
+ if (cellTitle == "Remarks.")
+ cellTitle = "Remarks";
+ if (iType == ETextTable && cellTitle == "Margins")
+ {
+ foundColNames.push_back("l");
+ foundColNames.push_back("r");
+ pCol++;
+ }
+ else if (find(iColumnNames.begin(),iColumnNames.end(),cellTitle) != iColumnNames.end())
+ {
+ foundColNames.push_back(cellTitle);
+ foundCount++;
+ }
+ else
+ {
+ foundColNames.push_back(KUnknown);
+ }
+ }
+ iColumnNames = foundColNames;
+
+ if (foundCount < 4)
+ {
+ iColumnNames.clear();
+ iType = EUnknownTable;
+ }
+ }
+
+void THtmlParseLayoutTable::AddLineRow(const Row& row)
+ {
+ if (iColumnNames.size() == 0 || row.size() != iColumnNames.size()+1)
+ return; // unknown row type;
+
+ Row params = MakeParamTable(row, 2, iColumnNames.size()-2);
+
+ Row::const_iterator pCell = row.begin();
+ Row::const_iterator pParam = params.begin();
+
+ int id;
+ stringstream stream(*pCell);
+ stream >> id;
+ if (id == 0)
+ return; // bad id;
+
+ TLayoutLine* line = new TLayoutLine(this, id);
+ push_back(line);
+
+ bool found_l = false;
+ for (vector<string>::iterator pCol = iColumnNames.begin(); pCol != iColumnNames.end(); ++pCol)
+ {
+ string col = *pCol;
+ if (col == "b/r")
+ col = found_l ? "b" : "r";
+
+ ++pCell;
+ ++pParam;
+ TValues values(line, col);
+
+ if (pParam->size())
+ values.iParam = *pParam;
+
+ string cell = *pCell;
+ if (IsValueColumn(col))
+ cell = ConvertToAknName(SplitMultiCell(cell, IsNumericColumn(col) ? gValidNum : gValidText));
+
+ int pos=0;
+ do {
+ int newPos = cell.find(',', pos);
+ values.push_back(TrimWhiteSpace(cell.substr(pos, newPos-pos)));
+ pos = newPos+1;
+ } while (pos);
+
+ if ((values.size()>1 || values[0].size()>0) && col == "l")
+ found_l = true;
+
+ if (col != KUnknown)
+ line->insert(make_pair(col, values));
+ }
+ }
+
+void THtmlParseLayoutTable::AddTitleRow(const Row& row)
+ {
+ if (row.size() < 2)
+ return;
+ if (row[0] == "P")
+ iParentName = TrimWhiteSpace(row[1]);
+ }
+
+string THtmlParseLayoutTable::SplitMultiCell(const string& aCell, const string& aValid)
+ {
+ string cell = aCell;
+
+ // Make sure commas are in correctly!
+ cell = StripTo(UnHtml(cell), aValid+"/,");
+
+ int lastComma = -1;
+ int lastSpace = -1;
+ int lastNum = -1;
+
+ for (int i=0; i<cell.length(); i++)
+ {
+ char c = cell[i];
+ if (c == ',')
+ lastComma = i;
+ else if (c == ' ' || c == '/')
+ lastSpace = i;
+ else
+ {
+ if (lastSpace > lastNum && lastNum > lastComma)
+ {
+ cell[lastSpace] = ',';
+ lastComma = lastSpace;
+ }
+ lastNum = i;
+ }
+ }
+
+ cell = StripTo(UnHtml(cell), aValid+",");
+ if (cell == "-")
+ cell = "";
+
+ return cell;
+ }
+
+string THtmlParseLayoutTable::StripTo(const string& aText, const string& aValid)
+ {
+ string cell("");
+ for (string::const_iterator pC = aText.begin(); pC != aText.end(); ++pC)
+ {
+ if (aValid.find(*pC) != string::npos)
+ cell += *pC;
+ }
+ return cell;
+ }
+
+string THtmlParseLayoutTable::UnHtml(const string& aText)
+ {
+ string str("");
+ for (int i=0; i<aText.size(); i++)
+ {
+ char c = aText[i];
+ if (c == '&')
+ {
+ string s = aText.substr(i);
+ int pos = s.find(";");
+ if (pos != string::npos)
+ {
+ i+=pos;
+ c = HtmlChar(s.substr(1, pos-1));
+ }
+ }
+ else if (c == char(0x96))
+ {
+ c = '-';
+ }
+ str += c;
+ }
+ return str;
+ }
+
+struct THtmlChar {char* iString; char iChar;};
+const THtmlChar gHtmlChars[] =
+ {
+ {"gt", '>'},
+ {"lt", '<'},
+ {"nbsp", ' '},
+ {"#9", '\t'}
+ };
+
+char THtmlParseLayoutTable::HtmlChar(const string& aText)
+ {
+ for (int i=0; i<sizeof(gHtmlChars)/sizeof(THtmlChar); i++)
+ {
+ if (aText == gHtmlChars[i].iString)
+ return gHtmlChars[i].iChar;
+ }
+ return '_';
+ }
+
+struct SConvertAknName
+ {
+ char* iLaf;
+ char* iAkn;
+ };
+
+SConvertAknName gAknNameConversionTable[] =
+ {
+ { "left", "ELayoutAlignLeft" },
+ { "right", "ELayoutAlignRight" },
+ { "centre", "ELayoutAlignCenter" },
+ { "center", "ELayoutAlignCenter" },
+ { "bidi", "ELayoutAlignBidi" },
+ { "qfn_latin_bold_19", "ELatinBold19" },
+ { "qfn_latin_bold_17", "ELatinBold17" },
+ { "qfn_latin_bold_13", "ELatinBold13" },
+ { "qfn_latin_bold_12", "ELatinBold12" },
+ { "qfn_latin_plain_12", "ELatinPlain12" },
+ { "qfn_latin_plain_13", "ELatinPlain12" }, // made up for elaf spec, only needed by navi pane?
+ { "qfn_latin_clock_14", "ELatinClock14" },
+ { "qfn_<ref>_plain_12", "EApacPlain12" },
+ { "qfn_<ref>_plain_16", "EApacPlain16" },
+ { "qfn_china_plain_12", "EApacPlain12" },
+ { "qfn_number_plain_5", "ENumberPlain5" },
+ { "qfn_china_plain_16", "EApacPlain16" },
+ { "qfn_clock_bold_30", "EClockBold30" },
+ { "qfn_number_bold_14", "ELatinClock14" },
+ { "gfn_<ref>_plain_12", "EApacPlain12" },
+ { "gfn_<ref>_plain_16", "EApacPlain16" },
+ { "gfn_latin_bold_16", "ELatinBold16" },
+ { "qfn_calc_21", "ECalcBold21" },
+ { "qfn_calc_oper_21", "ECalcOperBold21" },
+ { "qfn_calc_oper_13", "ECalcOperBold13" }
+ };
+
+string THtmlParseLayoutTable::ConvertToAknName(const string& aText)
+ {
+ string ret = aText;
+ for (int i=0; i<sizeof(gAknNameConversionTable)/sizeof(SConvertAknName); i++)
+ {
+ string laf = gAknNameConversionTable[i].iLaf;
+ string akn = gAknNameConversionTable[i].iAkn;
+ int pos;
+ while ((pos = ret.find(laf)) != string::npos)
+ {
+ ret.erase(pos, laf.length());
+ ret.insert(pos, akn);
+ }
+ }
+ return ret;
+ }
+
+
+void THtmlParseLayout::Parse(istream &aIn)
+ {
+ while (!aIn.eof())
+ {
+ auto_ptr<THtmlParseLayoutTable> table(new THtmlParseLayoutTable(this));
+ if (table->Read(aIn))
+ {
+ if (table->size() > 0)
+ push_back(table.release());
+ }
+ else
+ break;
+ }
+ Compile();
+ }
+
+// End of File