00001 #include "XPath.h"
00002 #include "XPathOps.h"
00003
00004 using namespace std;
00005 using namespace judo;
00006 using namespace judo::XPath;
00007
00008 char Query::getNextToken(string::size_type& cur)
00009 {
00010 if (cur == std::string::npos)
00011 return 0;
00012
00013 std::list<char>::iterator it = find (_tokens.begin(), _tokens.end(),
00014 _query[cur]);;
00015
00016 while (it == _tokens.end())
00017 {
00018 cur++;
00019 if (cur > _query.length())
00020 {
00021 cur = _query.length();
00022 return 0;
00023 }
00024 it = find(_tokens.begin(), _tokens.end(), _query[cur]);
00025 }
00026 return *it;
00027 }
00028
00029 std::string Query::getNextIdentifier(string::size_type& pos)
00030 {
00031 string::size_type sp = pos;
00032 getNextToken(pos);
00033 return _query.substr(sp, pos - sp);
00034 }
00035
00036 Op* Query::getOp(std::string::size_type& pos, char in_context)
00037 {
00038 Op* ret_op = NULL;
00039
00040 do
00041 {
00042 Op* tmp_op = NULL;
00043 string::size_type pos_start = pos;
00044 char token = getNextToken(pos);
00045
00046 if (token == 0 && in_context)
00047 throw Invalid();
00048 string::size_type token_start = ++pos;
00049 string ident;
00050
00051 if (pos_start != (token_start - 1))
00052 {
00053 pos = pos_start;
00054 ret_op = new NodeOp(getNextIdentifier(pos), false);
00055 }
00056 else
00057 {
00058
00059
00060 switch(token)
00061 {
00062 case '/':
00063 if (_query[token_start] == '/')
00064 {
00065 pos++;
00066 string temp_ident = getNextIdentifier(pos);
00067 if (!temp_ident.empty())
00068 {
00069 ret_op = new AllOp(temp_ident);
00070 }
00071 }
00072 else
00073 {
00074 string temp_ident = getNextIdentifier(pos);
00075 if (!temp_ident.empty())
00076 {
00077 ret_op = new NodeOp(temp_ident,
00078 (pos_start == 0 ? true : false));
00079 }
00080 }
00081 break;
00082 case '@':
00083 ret_op = new AttributeOp(getNextIdentifier(pos));
00084 break;
00085 case ']':
00086 if (in_context == '[')
00087 {
00088 ret_op = _ops.back();
00089 _ops.pop_back();
00090 in_context = 0;
00091 }
00092 else
00093 {
00094 std::cerr << "Found ']' but not in context" << std::endl;
00095 throw Invalid();
00096 }
00097 break;
00098 case '\'':
00099 case '\"':
00100 pos = _query.find(token, token_start);
00101 ret_op = new Op(Op::OP_LITERAL, _query.substr(token_start, pos -
00102 token_start));
00103 pos++;
00104 break;
00105 case ' ':
00106
00107 ident = getNextIdentifier(pos);
00108 if (ident == "and")
00109 {
00110 pos++;
00111 tmp_op = getOp(pos, in_context);
00112 if (!tmp_op)
00113 {
00114 std::cerr << "Invalid and operation" << std::endl;
00115 throw Invalid();
00116 }
00117 ret_op = new AndOp(_ops.back(), tmp_op);
00118 in_context = 0;
00119 _ops.pop_back();
00120 }
00121 else if(ident == "or")
00122 {
00123 pos++;
00124 tmp_op = getOp(pos, in_context);
00125 ret_op = new OrOp(_ops.back(), tmp_op);
00126 in_context = 0;
00127 _ops.pop_back();
00128 }
00129 break;
00130 case '[':
00131
00132 if (getNextToken(pos) == ']')
00133 {
00134 if (pos == token_start)
00135 {
00136 std::cerr << "Nothing in the []" << std::endl;
00137 throw Invalid();
00138 }
00139
00140 pos = token_start;
00141
00142 std::string temp_ident = getNextIdentifier(pos);
00143 int val = atoi(temp_ident.c_str());
00144
00145
00146 if (val > 0)
00147 {
00148 ret_op = new PositionOp(val);
00149 pos++;
00150 }
00151
00152
00153 else
00154 {
00155 pos = pos_start + 1;
00156 ret_op = new ContextOp(getOp(pos, token));
00157 }
00158 }
00159 else
00160 {
00161 pos = pos_start + 1;
00162 ret_op = new ContextOp(getOp(pos, token));
00163 }
00164 break;
00165 case '(':
00166
00167
00168
00169
00170 tmp_op = _ops.back();
00171 _ops.pop_back();
00172 ident = tmp_op->getValue();
00173 delete tmp_op;
00174
00175 if (!ident.empty())
00176 {
00177 int op_pos = _ops.size();
00178 ret_op = new FunctionOp(ident);
00179
00180 getOp(pos, token);
00181
00182 OpList::iterator it = _ops.begin() + op_pos;
00183 while( it != _ops.end())
00184 {
00185 static_cast<FunctionOp*>(ret_op)->addArg(*it);
00186 it = _ops.erase(it);
00187 }
00188 }
00189 else
00190 {
00191 std::cerr << "No function name specified!\n" <<
00192 std::endl;
00193 throw Invalid();
00194 }
00195 break;
00196 case ')':
00197 if (in_context == '(')
00198 {
00199 ret_op = NULL;
00200 in_context = 0;
00201 }
00202 else
00203 {
00204 std::cerr << "Found ')' but not in context" << std::endl;
00205 throw Invalid();
00206 }
00207 break;
00208 case ',':
00209 if (in_context != '(')
00210 {
00211 std::cerr << "Found ',' but not in a function" << std::endl;
00212 throw Invalid();
00213 }
00214 break;
00215 case '=':
00216 while (!tmp_op)
00217 tmp_op = getOp(pos);
00218
00219 ret_op = new EqualOp(_ops.back(), tmp_op);
00220 _ops.pop_back();
00221 break;
00222 case '!':
00223 if (_query[token_start] != '=')
00224 {
00225 std::cerr << "Badly formed !=" << std::endl;
00226 throw Invalid();
00227 }
00228 pos++;
00229 while (!tmp_op)
00230 tmp_op = getOp(pos);
00231
00232 ret_op = new NotEqualOp(_ops.back(), tmp_op);
00233 _ops.pop_back();
00234 break;
00235 default:
00236 std::cerr << "Unhandled \"" << token << "\"" << std::endl;
00237 ret_op = NULL;
00238 break;
00239 };
00240 }
00241
00242 if (in_context)
00243 {
00244 if (ret_op)
00245 {
00246 _ops.push_back(ret_op);
00247 }
00248 ret_op = NULL;
00249 }
00250
00251 } while (in_context);
00252
00253 return ret_op;
00254 }
00255
00256 bool Query::parseQuery()
00257 {
00258 Op* op = NULL;
00259 string::size_type pos = 0;
00260
00261 while (pos < _query.length())
00262 {
00263 op = getOp(pos);
00264 if (op)
00265 {
00266 _ops.push_back(op);
00267 }
00268 }
00269
00270 return true;
00271 }
00272
00273 bool Query::check(const judo::Element& root)
00274 {
00275 judo::Element check_elem(root);
00276 Value* ctxt = execute(&check_elem);
00277 bool matched = ctxt->check();
00278 delete ctxt;
00279 return matched;
00280 }
00281
00282 Value* Query::execute(judo::Element* root)
00283 {
00284 OpList::iterator it = _ops.begin();
00285
00286 Value* ctxt = new Value(root);
00287
00288 for (;it != _ops.end(); it++)
00289 {
00290
00291 if (!(*it)->isValid(ctxt))
00292 {
00293
00294 ctxt->setMatch(false);
00295 return ctxt;
00296 }
00297 }
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307 ctxt->setMatch(true);
00308 return ctxt;
00309 }