| /* |
| * Copyright 2000-2011 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.intellij.lang.xpath.context.functions; |
| |
| import com.intellij.lexer.FilterLexer; |
| import com.intellij.lexer.Lexer; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.tree.TokenSet; |
| import org.intellij.lang.xpath.XPath2TokenTypes; |
| import org.intellij.lang.xpath.XPathLexer; |
| import org.intellij.lang.xpath.XPathTokenTypes; |
| import org.intellij.lang.xpath.psi.XPath2SequenceType; |
| import org.intellij.lang.xpath.psi.XPath2Type; |
| import org.intellij.lang.xpath.psi.XPathType; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.xml.namespace.QName; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| /* |
| * Created by IntelliJ IDEA. |
| * User: sweinreuter |
| * Date: 14.01.11 |
| */ |
| public class FunctionDeclarationParsing { |
| public static final String FUNCTION_NAMESPACE = "http://www.w3.org/2005/xpath-functions"; |
| |
| private FunctionDeclarationParsing() { |
| } |
| |
| public static Pair<String, ? extends Function> parseFuntionDeclaration(String decl) { |
| final Lexer lexer = new FilterLexer(XPathLexer.create(true), |
| new FilterLexer.SetFilter(TokenSet.create(XPathTokenTypes.WHITESPACE))); |
| lexer.start(decl); |
| |
| String prefix = ""; |
| if (lexer.getTokenType() == XPathTokenTypes.EXT_PREFIX) { |
| prefix = lexer.getTokenText(); |
| lexer.advance(); |
| match(lexer, XPathTokenTypes.COL); |
| } |
| |
| final String name = match(lexer, XPathTokenTypes.FUNCTION_NAME); |
| match(lexer, XPathTokenTypes.LPAREN); |
| |
| final List<Parameter> parameters = new ArrayList<Parameter>(); |
| while (lexer.getTokenType() != XPathTokenTypes.RPAREN) { |
| if (lexer.getTokenType() == XPathTokenTypes.DOTDOT) { |
| lexer.advance(); |
| match(lexer, XPathTokenTypes.DOT); |
| |
| parameters.add(new Parameter(XPathType.ANY, Parameter.Kind.VARARG)); |
| } else { |
| match(lexer, XPathTokenTypes.DOLLAR); |
| match(lexer, XPathTokenTypes.VARIABLE_NAME); |
| |
| match(lexer, XPath2TokenTypes.AS); |
| |
| final String type = parseType(lexer); |
| |
| final XPath2SequenceType.Cardinality indicator = parseCardinality(lexer); |
| |
| parameters.add(new Parameter(mapType(type, indicator), Parameter.Kind.REQUIRED)); |
| } |
| |
| if (lexer.getTokenType() == XPathTokenTypes.COMMA) { |
| lexer.advance(); |
| } |
| } |
| lexer.advance(); |
| |
| match(lexer, XPath2TokenTypes.AS); |
| |
| final String ret = parseType(lexer); |
| final XPath2SequenceType.Cardinality indicator = parseCardinality(lexer); |
| |
| final XPathType returnType = mapType(ret, indicator); |
| |
| return Pair.create(prefix, new FunctionImpl(name, returnType, parameters.toArray(new Parameter[parameters.size()]))); |
| } |
| |
| public static XPathType mapType(String type, XPath2SequenceType.Cardinality c) { |
| if ("none".equals(type)) { |
| return XPathType.UNKNOWN; |
| } |
| |
| XPathType r = null; |
| if ("numeric".equals(type)) { |
| r = XPath2Type.NUMERIC; |
| } else if (type.startsWith("xs:")) { |
| final String base = type.substring(3); |
| r = XPath2Type.fromName(new QName(XPath2Type.XMLSCHEMA_NS, base)); |
| if (r == null) { |
| r = XPathType.fromString(base); |
| } |
| } else { |
| if (type.endsWith("()")) { |
| r = XPath2Type.fromName(new QName("", type)); |
| } |
| } |
| |
| if (r != null) { |
| if (c != null) { |
| r = XPath2SequenceType.create(r, c); |
| } |
| return r; |
| } |
| return XPathType.fromString(type); |
| } |
| |
| @Nullable |
| public static XPath2SequenceType.Cardinality parseCardinality(Lexer lexer) { |
| if (lexer.getTokenType() == XPath2TokenTypes.QUEST) { |
| lexer.advance(); |
| return XPath2SequenceType.Cardinality.OPTIONAL; |
| } else if (lexer.getTokenType() == XPathTokenTypes.MULT || lexer.getTokenType() == XPathTokenTypes.STAR) { |
| lexer.advance(); |
| return XPath2SequenceType.Cardinality.ZERO_OR_MORE; |
| } else if (lexer.getTokenType() == XPathTokenTypes.PLUS) { |
| lexer.advance(); |
| return XPath2SequenceType.Cardinality.ONE_OR_MORE; |
| } |
| return null; |
| } |
| |
| public static String match(Lexer lexer, IElementType token) { |
| assert lexer.getTokenType() == token : lexer.getTokenType() + ": " + lexer.getTokenText(); |
| final String s = lexer.getTokenText(); |
| lexer.advance(); |
| return s; |
| } |
| |
| @Nullable |
| public static String parseType(Lexer lexer) { |
| String type = parseQName(lexer); |
| if (type == null) { |
| if (lexer.getTokenType() == XPath2TokenTypes.ITEM || lexer.getTokenType() == XPathTokenTypes.FUNCTION_NAME || lexer.getTokenType() == XPathTokenTypes.NODE_TYPE) { |
| type = lexer.getTokenText(); |
| lexer.advance(); |
| match(lexer, XPathTokenTypes.LPAREN); |
| match(lexer, XPathTokenTypes.RPAREN); |
| type += "()"; |
| } else { |
| assert false : "unexpected token: " + lexer.getTokenType(); |
| } |
| } |
| return type; |
| } |
| |
| @Nullable |
| public static String parseQName(Lexer lexer) { |
| String name; |
| if (lexer.getTokenType() == XPathTokenTypes.NCNAME) { |
| name = lexer.getTokenText(); |
| lexer.advance(); |
| if (lexer.getTokenType() == XPathTokenTypes.COL) { |
| lexer.advance(); |
| assert lexer.getTokenType() == XPathTokenTypes.NCNAME; |
| name += (":" + lexer.getTokenText()); |
| lexer.advance(); |
| } |
| } else { |
| name = null; |
| } |
| return name; |
| } |
| |
| public static void addFunction(Map<Pair<QName, Integer>, Function> decls, String s) { |
| final Pair<String, ? extends Function> pair = parseFuntionDeclaration(s); |
| final Function func = pair.second; |
| |
| final boolean fn = pair.first.equals("fn") || pair.first.length() == 0; |
| final boolean xs = pair.first.equals("xs"); |
| final Pair<QName, Integer> key = Pair.create(new QName(fn ? FUNCTION_NAMESPACE : (xs ? XPath2Type.XMLSCHEMA_NS : null), func.getName()), func.getParameters().length); |
| assert !decls.containsKey(key) : key; |
| |
| decls.put(key, func); |
| |
| if (fn) { |
| decls.put(Pair.create(new QName(null, func.getName()), func.getParameters().length), func); |
| } |
| } |
| } |