/*
* Copyright 2000-2013 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 com.intellij.coldFusion.model.info;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author vnikolaenko
*/
// TODO: parse attributes descriptions
// TODO: parse functions' parameters descriptions
// TODO: parse predefined variables
public class CfmlTagsDescriptionsParser extends DefaultHandler {
private boolean myIsTagHelpSection = false;
private boolean myIsFunctionHelpSection = false;
private Map<String, CfmlTagDescription> myTags;
private Map<String, CfmlFunctionDescription> myFunctions;
private Map<String, Integer> myPredefinedVariables = new HashMap<>();
private CfmlTagDescription myCurrentTag = null;
private CfmlFunctionDescription myCurrentFunction = null;
private CfmlAttributeDescription myCurrentAttribute = null;
private List<String> myFunctionUpperCased = new LinkedList<>();
private String myCurrentScope = "";
private static final int TAG_STATE = 0;
private static final int FUNCTION_STATE = 1;
private static final int SCOPE_STATE = 2;
private static final int PREDEFINED_VARIABLE_STATE = 3;
private int myState;
public void startDocument() throws SAXException {
myTags = new HashMap<>();
myFunctions = new HashMap<>();
}
public void endDocument() throws SAXException {
}
private final Pattern myPattern = Pattern.compile("\\s{2,}");
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (!myIsTagHelpSection && !myIsFunctionHelpSection) return;
String description = new String(ch, start, length);
description = myPattern.matcher(description).replaceAll(" ");
if (myIsTagHelpSection && myCurrentTag != null) {
String previousDescription = myCurrentTag.getDescription();
myCurrentTag.setDescription(
StringUtil.isEmpty(previousDescription) ?description:previousDescription + "\n" + description);
}
else if (myIsFunctionHelpSection && myCurrentFunction != null) {
String previousDescription = myCurrentFunction.getDescription();
myCurrentFunction.setDescription(
StringUtil.isEmpty(previousDescription) ?description:previousDescription + "\n" + description);
}
}
public void startElement(String namespaceURI, String localName, String qName,
Attributes attr) throws SAXException {
if (localName.equals("tags")) {
myState = TAG_STATE;
}
else if (localName.equals("functions")) {
myState = FUNCTION_STATE;
}
else if (localName.equals("cfscopes")) {
myState = SCOPE_STATE;
}
else if (localName.equals("scopes")) {
myState = PREDEFINED_VARIABLE_STATE;
}
else if (myState == TAG_STATE) {
myIsTagHelpSection = false;
if (localName.equals("tag")) {
final String isSingle = attr.getValue("single");
final String isEndtagrequired = attr.getValue("endtagrequired");
myCurrentTag = new CfmlTagDescription(attr.getValue("name"),
Boolean.valueOf(isSingle), Boolean.valueOf(isEndtagrequired));
}
else if (localName.equals("help")) {
myIsTagHelpSection = true;
}
else if (localName.equals("parameter")) {
String aName = attr.getValue("name");
int aType = CfmlTypesInfo.getTypeByString(attr.getValue("type"));
boolean aRequired = Boolean.valueOf(attr.getValue("required"));
String aDescription = "";
myCurrentAttribute = new CfmlAttributeDescription(aName, aType, aRequired, aDescription);
}
else if (localName.equals("value") && myCurrentAttribute != null) {
myCurrentAttribute.addValue(attr.getValue("option"));
}
}
else if (myState == FUNCTION_STATE) {
myIsFunctionHelpSection = false;
if (localName.equals("function")) {
myCurrentFunction = new CfmlFunctionDescription(attr.getValue("name"), attr.getValue("returns"));
}
else if (localName.equals("parameter") && myCurrentFunction != null) {
String aName = attr.getValue("name");
String aType = attr.getValue("type");
boolean aRequired = Boolean.valueOf(attr.getValue("required"));
myCurrentFunction.addParameter(new CfmlFunctionDescription.CfmlParameterDescription(aName, aType, aRequired));
}
else if (localName.equals("help")) {
myIsFunctionHelpSection = true;
}
}
else if (myState == PREDEFINED_VARIABLE_STATE) {
if (localName.equals("scope")) {
int aType = CfmlTypesInfo.getTypeByString(attr.getValue("type"));
String aName = attr.getValue("value");
myPredefinedVariables.put(aName.toLowerCase(), aType);
}
}
else if (myState == SCOPE_STATE) {
if (localName.equals("scopevar")) {
if (!StringUtil.isEmpty(myCurrentScope) && myCurrentScope.charAt(myCurrentScope.length() - 1) != '.') {
myCurrentScope += ".";
}
myCurrentScope += attr.getValue("name");
}
}
}
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
if (localName.equals("tag") && myCurrentTag != null) {
myTags.put(myCurrentTag.getName(), myCurrentTag);
myCurrentTag = null;
}
else if (localName.equals("function") && myCurrentFunction != null) {
String functioName = myCurrentFunction.getName();
myFunctionUpperCased.add(functioName);
myFunctions.put(functioName.toLowerCase(), myCurrentFunction);
myCurrentFunction = null;
}
else if (localName.equals("parameter") && myCurrentTag != null && myCurrentAttribute != null) {
myCurrentTag.addAttribute(myCurrentAttribute);
myCurrentAttribute = null;
}
else if (localName.equals("scopevar")) {
if (!StringUtil.isEmpty(myCurrentScope)) {
if (myCurrentScope.charAt(myCurrentScope.length() - 1) != '.') {
myPredefinedVariables.put(myCurrentScope.toLowerCase(), CfmlTypesInfo.ANY_TYPE);
}
else {
myCurrentScope = myCurrentScope.substring(0, myCurrentScope.length() - 1);
}
int i = myCurrentScope.lastIndexOf('.');
if (i != -1) {
myCurrentScope = myCurrentScope.substring(0, i + 1);
}
else {
myCurrentScope = "";
}
}
}
}
public Map<String, CfmlFunctionDescription> getFunctions() {
return myFunctions;
}
public String[] getFunctionsList() {
return ArrayUtil.toStringArray(myFunctionUpperCased);
}
public String[] getFunctionsListLowerCased() {
return ArrayUtil.toStringArray(myFunctions.keySet());
}
public Map<String, Integer> getPredefinedVariables() {
return myPredefinedVariables;
}
public Map<String, CfmlTagDescription> getTags() {
return myTags;
}
}