/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.drools.eclipse.editors.completion;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import org.eclipse.jdt.core.Signature;

public class CompletionUtil {

    protected static final Pattern INCOMPLETED_MVEL_EXPRESSION = Pattern.compile( "[\\.\\(\\{\\[]\\Z",
                                                                                Pattern.DOTALL );

    protected static final Pattern COMPLETED_MVEL_EXPRESSION   = Pattern.compile( "]\\)\\}\\]\\Z",
                                                                                Pattern.DOTALL );

    protected static final Pattern MODIFY_PATTERN              = Pattern.compile( ".*modify\\s*\\(\\s*(.*)\\s*\\)(\\s*\\{(.*)\\s*\\}?)?",
                                                                                Pattern.DOTALL );

    protected static final Pattern START_OF_NEW_JAVA_STATEMENT = Pattern.compile( ".*[;{}]\\s*",
                                                                                Pattern.DOTALL );
    protected static final Pattern START_OF_METHOD_ARGS        = Pattern.compile( ".*[\\(]\\s*",
                                                                                Pattern.DOTALL );

    private CompletionUtil() {
    }

    /**
     * Looks behind, gets stuff after the white space. Basically ripping out the
     * last word.
     */
    public static String stripLastWord(String prefix) {
        if ( "".equals( prefix ) ) {
            return prefix;
        }
        if ( prefix.charAt( prefix.length() - 1 ) == ' ' ) {
            return "";
        } else {
            char[] c = prefix.toCharArray();
            int start = 0;
            for ( int i = c.length - 1; i >= 0; i-- ) {
                if ( Character.isWhitespace( c[i] ) || c[i] == '(' || c[i] == ':' || c[i] == ';' || c[i] == '=' || c[i] == '<' || c[i] == '>' || c[i] == '.' || c[i] == '{' || c[i] == '}' ) {
                    start = i + 1;
                    break;
                }
            }
            prefix = prefix.substring( start,
                                       prefix.length() );
            return prefix;
        }
    }

    public static String getPreviousExpression(String backText) {
        int separator = backText.lastIndexOf( ';' );
        if ( separator < 0 ) {
            return backText;
        }
        return backText.substring( 0,
                                   separator + 1 );
    }

    public static String getLastExpression(String backText) {
        StringTokenizer st = new StringTokenizer( backText,
                                                  ";" );
        String last = "";
        while ( st.hasMoreTokens() ) {
            last = st.nextToken();
        }
        if ( last.trim().length() == 0 ) {
            return backText;
        }
        return last;
    }

    public static String getInnerExpression(String backText) {
        String last = getLastExpression( backText ).trim();

        char[] c = last.toCharArray();
        int start = 0;
        for ( int i = c.length - 1; i >= 0; i-- ) {
            if ( Character.isWhitespace( c[i] ) || c[i] == '(' || c[i] == '+' || c[i] == ')' || c[i] == '[' || c[i] == ']' || c[i] == ':' || c[i] == '=' || c[i] == '<' || c[i] == '>' || c[i] == ',' || c[i] == '{' || c[i] == '}' ) {
                start = i + 1;
                break;
            }
        }
        last = last.substring( start );
        return last;
    }

    public static int nestedExpressionIndex(char[] chars,
                                            int start,
                                            char type) {
        int depth = 1;
        char term = type;
        switch ( type ) {
            case '[' :
                term = ']';
                break;
            case '{' :
                term = '}';
                break;
            case '(' :
                term = ')';
                break;
        }

        if ( type == term ) {
            for ( start++; start < chars.length; start++ ) {
                if ( chars[start] == type ) {
                    return start;
                }
            }
        } else {
            for ( start++; start < chars.length; start++ ) {
                if ( chars[start] == '\'' || chars[start] == '"' ) {
                    //start = captureStringLiteral(chars[start], chars, start, chars.length);
                } else if ( chars[start] == type ) {
                    depth++;
                } else if ( chars[start] == term && --depth == 0 ) {
                    return start;
                }
            }
        }

        return -1;
    }

    public static String stripWhiteSpace(String prefix) {
        if ( "".equals( prefix ) ) {
            return prefix;
        }
        if ( prefix.charAt( prefix.length() - 1 ) == ' ' ) {
            return "";
        } else {
            char[] c = prefix.toCharArray();
            int start = 0;
            for ( int i = c.length - 1; i >= 0; i-- ) {
                if ( Character.isWhitespace( c[i] ) ) {
                    start = i + 1;
                    break;
                }
            }
            prefix = prefix.substring( start,
                                       prefix.length() );
            return prefix;
        }
    }

    /**
     * Attempt to enhance a consequence backtext such that it should compile in MVEL
     * @param backText
     * @return a substring of the back text, that should be compilable without
     *         syntax errors by the mvel compiler
     *
     *         TODO: add tests and more use
     *         cases
     */
    public static String getCompilableText(String backText) {
        String trimed = backText.trim();
        if ( trimed.endsWith( ";" ) ) {
            // RHS expression should compile if it ends with ; but to get the last object,
            // we do no want it, to simulate a return statement
            return backText.substring( 0,
                                       backText.length() - 1 );
        } else if ( trimed.endsWith( "." ) || trimed.endsWith( "," ) ) {
            // RHS expression should compile if it ends with no dot or comma
            return backText.substring( 0,
                                       backText.length() - 1 );
        } else if ( CompletionUtil.COMPLETED_MVEL_EXPRESSION.matcher( backText ).matches() ) {
            // RHS expression should compile if closed. just need to close the
            // statement
            return backText + ";";
            //        } else if ( INCOMPLETED_MVEL_EXPRESSION.matcher( backText ).matches() ) {
            //            // remove the last char and close the statement
            //            return backText.substring( 0,
            //                                       backText.length() - 1 );
        } else {
            //TODO: support completion within with {} blocks
            //TODO: support completion within nested expression.

            return backText;
        }
    }

    /*
     * propertyname extraction and bean convention methods names checks
     */

    public static boolean isGetter(String methodName,
                                   int argCount,
                                   String returnedType) {
        return isAccessor( methodName,
                           argCount,
                           0,
                           "get",
                           returnedType,
                           Signature.SIG_VOID,
                           false );
    }

    public static boolean isSetter(String methodName,
                                   int argCount,
                                   String returnedType) {
        return isAccessor( methodName,
                           argCount,
                           1,
                           "set",
                           returnedType,
                           Signature.SIG_VOID,
                           true );
    }

    public static boolean isIsGetter(String methodName,
                                     int argCount,
                                     String returnedType) {
        return isAccessor( methodName,
                           argCount,
                           0,
                           "is",
                           returnedType,
                           Signature.SIG_BOOLEAN,
                           true );
    }

    /**
     * Given a data depicting a method (name, # or params/args, returned type key), tries to return a  bean property name derived from that method.
     * If a bean property name is not found, the initial method name is returned
     * @param methodName
     * @param parameterCount
     * @param returnType
     * @return a bean property name
     */
    public static String getPropertyName(String methodName,
                                         int parameterCount,
                                         String returnType) {
        if ( methodName == null ) {
            return null;
        }
        String simpleName = methodName.replaceAll( "\\(\\)",
                                                   "" );
        int prefixLength = 0;
        if ( isIsGetter( simpleName,
                         parameterCount,
                         returnType ) ) {

            prefixLength = 2;

        } else if ( isGetter( simpleName,
                              parameterCount,
                              returnType ) //
                    || isSetter( simpleName,
                                 parameterCount,
                                 returnType ) ) {

            prefixLength = 3;
        } else {
            return methodName;
        }

        char firstChar = Character.toLowerCase( simpleName.charAt( prefixLength ) );
        String propertyName = firstChar + simpleName.substring( prefixLength + 1 );
        return propertyName;
    }

    public static String getPropertyName(String methodName,
                                         char[] signature) {
        if ( signature == null || methodName == null ) {
            return methodName;
        }

        int parameterCount = Signature.getParameterCount( signature );
        String returnType = new String( Signature.getReturnType( signature ) );

        return getPropertyName( methodName,
                                parameterCount,
                                returnType );
    }

    /**
     * Given a data depicting a method (name, # or params/args, returned type key), tries to return a  writable bean property name derived from that method.
     * If a writable (ie setter) bean property name is not found, the initial method name is returned
     * @param methodName
     * @param parameterCount
     * @param returnType
     * @return a bean property name
     */
    public static String getWritablePropertyName(String methodName,
                                                 int parameterCount,
                                                 String returnType) {
        if ( methodName == null ) {
            return null;
        }
        String simpleName = methodName.replaceAll( "\\(\\)",
                                                   "" );
        if ( !isSetter( simpleName,
                        parameterCount,
                        returnType ) ) {
            return methodName;
        }

        int prefixLength = 3;

        char firstChar = Character.toLowerCase( simpleName.charAt( prefixLength ) );
        String propertyName = firstChar + simpleName.substring( prefixLength + 1 );
        return propertyName;
    }

    public static String getWritablePropertyName(String methodName,
                                                 char[] signature) {
        if ( signature == null || methodName == null ) {
            return methodName;
        }

        int parameterCount = Signature.getParameterCount( signature );
        String returnType = new String( Signature.getReturnType( signature ) );

        return getWritablePropertyName( methodName,
                                        parameterCount,
                                        returnType );
    }

    /**
     * Determine if the given method is a bean accessor (ie getter/setter)
     * @param methodName
     * @param actualParameterCount
     * @param requiredParameterCount
     * @param prefix
     * @param returnType
     * @param requiredReturnType
     * @param includeType
     * @return true if the method is a bean accessor, false otherwise
     */
    private static boolean isAccessor(String methodName,
                                      int actualParameterCount,
                                      int requiredParameterCount,
                                      String prefix,
                                      String returnType,
                                      String requiredReturnType,
                                      boolean includeType) {

        //must be longer than the accessor prefix
        if ( methodName.length() < prefix.length() + 1 ) {
            return false;
        }

        //start with get, set or is
        if ( !methodName.startsWith( prefix ) ) {
            return false;
        }

        if ( actualParameterCount != requiredParameterCount ) {
            return false;
        }

        //if we check for the returned type, verify that the returned type is of the cirrect type signature
        if ( includeType ) {
            if ( !requiredReturnType.equals( returnType ) ) {
                return false;
            }
        } else {
            if ( requiredReturnType.equals( returnType ) ) {
                return false;
            }
        }
        return true;
    }

    public static boolean isStartOfNewStatement(String text,
                                                String prefix) {
        String javaTextWithoutPrefix = text.substring( 0,
                                                       text.length() - prefix.length() );

        if ( "".equals( javaTextWithoutPrefix.trim() ) || CompletionUtil.START_OF_NEW_JAVA_STATEMENT.matcher( javaTextWithoutPrefix ).matches() ) {
            return true;
        }
        return false;
    }

    public static String getLastLine(String text) {
        final BufferedReader reader = new BufferedReader( new StringReader( text ) );
        String line = null;
        String lastLine = null;
        try {
            while ( (line = reader.readLine()) != null ) {
                if ( line.trim().length() > 0 ) {
                    lastLine = line;
                }
            }
        } catch ( final IOException e ) {
            // should never happen, it's just reading over a string.
        }
        return lastLine;
    }

    /**
     * COMPENSATES FOR LACK OF getSimpleName IN java.lang.Class
     * Borrowed and adpated from MVEL's org.mvel.util.ParseTools.getSimpleClassName(Class)
     * @param cls -- class reference
     * @return Simple name of class
     */
    public static String getSimpleClassName(Class<?> cls) {
        int lastIndex = cls.getName().lastIndexOf( '$' );
        if ( lastIndex < 0 ) {
            lastIndex = cls.getName().lastIndexOf( '.' );
        }
        if ( cls.isArray() ) {
            return cls.getName().substring( lastIndex + 1 ) + "[]";
        } else {
            return cls.getName().substring( lastIndex + 1 );
        }
    }

    public static String getTextWithoutPrefix(final String javaText,
                                              final String prefix) {
        int endIndex = javaText.length() - prefix.length();
        String javaTextWithoutPrefix = javaText;
        //javaText can be an empty string.
        if ( endIndex >= 0 ) {
            javaTextWithoutPrefix = javaText.substring( 0,
                                                        endIndex );
        }
        return javaTextWithoutPrefix;
    }

    public static boolean isStartOfDialectExpression(String text) {
        return "".equals( text.trim() ) || CompletionUtil.START_OF_NEW_JAVA_STATEMENT.matcher( text ).matches();
    }

    public static boolean isStartOfMethodArgsExpression(String text) {
        return CompletionUtil.START_OF_NEW_JAVA_STATEMENT.matcher( text ).matches();
    }

}


/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.drools.eclipse.editors.outline;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.drools.compiler.compiler.DroolsParserException;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.core.DroolsElement;
import org.drools.eclipse.core.DroolsModelBuilder;
import org.drools.eclipse.core.Package;
import org.drools.eclipse.core.RuleSet;
import org.drools.eclipse.core.ui.DroolsContentProvider;
import org.drools.eclipse.core.ui.DroolsGroupByRuleGroupContentProvider;
import org.drools.eclipse.core.ui.DroolsLabelProvider;
import org.drools.eclipse.core.ui.DroolsTreeSorter;
import org.drools.eclipse.core.ui.FilterActionGroup;
import org.drools.eclipse.editors.AbstractRuleEditor;
import org.drools.compiler.lang.descr.AttributeDescr;
import org.drools.compiler.lang.descr.PackageDescr;
import org.drools.compiler.lang.descr.RuleDescr;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;

/**
 * Simple outline view of a DRL file. At present this is not wired in with the Parser, so it is fault
 * tolerant of incorrect syntax.
 * Should provide navigation assistance in large rule files.
 */
public class RuleContentOutlinePage extends ContentOutlinePage {

    private AbstractRuleEditor editor;
    private RuleSet ruleSet = DroolsModelBuilder.createRuleSet();
    private Map<String, RuleDescr> rules;

    private boolean groupByRulegroup = false;
    private TreeViewer viewer = null;
    ///////////////////////////////////
    // Patterns that the parser uses
    // TODO: this should just reuse the existing parser to avoid inconsistencies
    //       with for example comments
    ///////////////////////////////////
    private static final Pattern RULE_PATTERN1 = Pattern.compile(
            "\\n\\s*rule\\s+\"([^\"]+)\"", Pattern.DOTALL);

    private static final Pattern RULE_PATTERN2 = Pattern.compile(
            "\\n\\s*rule\\s+([^\\s;#\"]+)", Pattern.DOTALL);

    private static final Pattern PACKAGE_PATTERN = Pattern.compile(
            "\\s*package\\s+([^\\s;#]+);?", Pattern.DOTALL);

    private static final Pattern FUNCTION_PATTERN = Pattern.compile(
            "\\n\\s*function\\s+(\\S+)\\s+(\\S+)\\(.*?\\)", Pattern.DOTALL);

    private static final Pattern TEMPLATE_PATTERN = Pattern.compile(
            "\\n\\s*template\\s+([^\\s;#\"]+)", Pattern.DOTALL);

    private static final Pattern IMPORT_PATTERN = Pattern.compile(
            "\\n\\s*import\\s+([^\\s;#]+);?", Pattern.DOTALL);

    private static final Pattern EXPANDER_PATTERN = Pattern.compile(
            "\\n\\s*expander\\s+([^\\s;#]+);?", Pattern.DOTALL);

    private static final Pattern GLOBAL_PATTERN = Pattern.compile(
            "\\n\\s*global\\s+(\\S+)\\s+([^\\s;#]+);?", Pattern.DOTALL);

    private static final Pattern QUERY_PATTERN1 = Pattern.compile(
            "\\n\\s*query\\s+\"([^\"]+)\"", Pattern.DOTALL);

    private static final Pattern QUERY_PATTERN2 = Pattern.compile(
            "\\n\\s*query\\s+([^\\s;#\"]+)", Pattern.DOTALL);

    public RuleContentOutlinePage(AbstractRuleEditor editor) {
        this.editor = editor;
    }

    DroolsContentProvider contentProvider = null;
    DroolsGroupByRuleGroupContentProvider groupByRuleGroupContentProvider = null;

    private void setContentProvider() {
        IPreferenceStore preferenceStore= DroolsEclipsePlugin.getDefault().getPreferenceStore();
        groupByRulegroup = preferenceStore.getBoolean("GroupByRuleGroupAction.isChecked");

        contentProvider = new DroolsContentProvider();
        groupByRuleGroupContentProvider = new DroolsGroupByRuleGroupContentProvider();

        if (groupByRulegroup) {
            viewer.setContentProvider(groupByRuleGroupContentProvider);
        } else {
            viewer.setContentProvider(contentProvider);
        }
    }

    public void createControl(Composite parent) {
        super.createControl(parent);

        viewer = getTreeViewer();
        setContentProvider();
        viewer.setLabelProvider(new DroolsLabelProvider());
        viewer.setSorter(new DroolsTreeSorter());
        viewer.setInput(ruleSet);
        FilterActionGroup filterActionGroup = new FilterActionGroup(
            viewer, "org.drools.eclipse.editors.outline.RuleContentOutlinePage");
        filterActionGroup.fillActionBars(getSite().getActionBars());
        update();

        // add the listener for navigation of the rule document.
        super.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                Object selectionObj = event.getSelection();
                if (selectionObj != null && selectionObj instanceof StructuredSelection) {
                    StructuredSelection sel = (StructuredSelection) selectionObj;
                    DroolsElement element = (DroolsElement) sel.getFirstElement();
                    if (element != null) {
                        editor.selectAndReveal(element.getOffset(),
                                                element.getLength());
                    }
                }
            }
        });
    }

    /**
     * Updates the outline page.
     */
    public void update() {
        TreeViewer viewer = getTreeViewer();
        if (viewer != null) {
            Control control = viewer.getControl();
            if (control != null && !control.isDisposed()) {
                initRules();
                populatePackageTreeNode();
                viewer.refresh();
                control.setRedraw(false);
                viewer.expandToLevel(2);
                control.setRedraw(true);
            }
        }
    }

    /**
     * populates the PackageTreeNode with all of its child elements
     *
     * @param packageTreeNode the node to populate
     */
    public void populatePackageTreeNode() {
        String ruleFileContents = editor.getContent();
        populatePackageTreeNode(ruleFileContents);
    }

    void populatePackageTreeNode(String ruleFileContents) {
        DroolsModelBuilder.clearRuleSet(ruleSet);
        Matcher matcher = PACKAGE_PATTERN.matcher(ruleFileContents);
        String packageName = null;
        int startChar = 0;
        int endChar = 0;
        if (matcher.find()) {
            packageName = matcher.group(1);
            startChar = matcher.start(1);
            endChar = matcher.end(1);
        }
        Package pkg = DroolsModelBuilder.addPackage(ruleSet, packageName,
            startChar, endChar - startChar);

        matcher = RULE_PATTERN1.matcher(ruleFileContents);
        while (matcher.find()) {
            String ruleName = matcher.group(1);
            RuleDescr descr = (RuleDescr) rules.get(ruleName);
            if (descr != null) {
                DroolsModelBuilder.addRule(pkg, ruleName, null,
                    matcher.start(1), matcher.end(1) - matcher.start(1),
                    extractAttributes(descr));
            }
        }
        matcher = RULE_PATTERN2.matcher(ruleFileContents);
        while (matcher.find()) {
            String ruleName = matcher.group(1);
            RuleDescr descr = (RuleDescr) rules.get(ruleName);
            if (descr != null) {
                DroolsModelBuilder.addRule(pkg, ruleName, null,
                    matcher.start(1), matcher.end(1) - matcher.start(1),
                    extractAttributes(descr));
            }
         }
        matcher = FUNCTION_PATTERN.matcher(ruleFileContents);
        while (matcher.find()) {
            String functionName = matcher.group(2);
            DroolsModelBuilder.addFunction(pkg, functionName + "()", null,
                matcher.start(2), matcher.end(2) - matcher.start(2));
        }
        matcher = EXPANDER_PATTERN.matcher(ruleFileContents);
        if (matcher.find()) {
            String expanderName = matcher.group(1);
            DroolsModelBuilder.addExpander(pkg, expanderName, null,
                matcher.start(1), matcher.end(1) - matcher.start(1));
        }
        matcher = IMPORT_PATTERN.matcher(ruleFileContents);
        while (matcher.find()) {
            String importName = matcher.group(1);
            DroolsModelBuilder.addImport(pkg, importName, null,
                matcher.start(1), matcher.end(1) - matcher.start(1));
        }
        matcher = GLOBAL_PATTERN.matcher(ruleFileContents);
        while (matcher.find()) {
            String globalType = matcher.group(1);
            String globalName = matcher.group(2);
            String name = globalName + " : " + globalType;
            DroolsModelBuilder.addGlobal(pkg, name, null,
                matcher.start(2), matcher.end(2) - matcher.start(2));
        }
        matcher = QUERY_PATTERN1.matcher(ruleFileContents);
        while (matcher.find()) {
            String queryName = matcher.group(1);
            DroolsModelBuilder.addQuery(pkg, queryName, null,
                    matcher.start(1), matcher.end(1) - matcher.start(1));
        }
        matcher = QUERY_PATTERN2.matcher(ruleFileContents);
        while (matcher.find()) {
            String queryName = matcher.group(1);
            DroolsModelBuilder.addQuery(pkg, queryName, null,
                matcher.start(1), matcher.end(1) - matcher.start(1));
        }
        matcher = TEMPLATE_PATTERN.matcher(ruleFileContents);
        while (matcher.find()) {
            String templateName = matcher.group(1);
            DroolsModelBuilder.addTemplate(pkg, templateName, null,
                    matcher.start(1), matcher.end(1) - matcher.start(1));
        }
    }

    RuleSet getRuleSet() {
        return ruleSet;
    }

    private Map<String, String> extractAttributes(RuleDescr ruleDescr) {
        Map<String, String> attributes = null;
        if (ruleDescr != null) {
            attributes = new HashMap<String, String>();
            for (AttributeDescr attribute: ruleDescr.getAttributes().values()) {
                if (attribute != null && attribute.getName() != null) {
                    attributes.put(attribute.getName(), attribute.getValue());
                }
            }
        }
        return attributes;
    }

    public void initRules() {
        rules = new HashMap<String, RuleDescr>();
        try {
            DRLInfo drlInfo = DroolsEclipsePlugin.getDefault().parseResource(editor, true, false);
            if (drlInfo != null) {
                PackageDescr packageDescr = drlInfo.getPackageDescr();
                if (packageDescr != null) {
                    for (RuleDescr ruleDescr: packageDescr.getRules()) {
                        if (ruleDescr != null && ruleDescr.getName() != null) {
                            rules.put(ruleDescr.getName(), ruleDescr);
                        }
                    }
                }
            }
        } catch (DroolsParserException e) {
            DroolsEclipsePlugin.log(e);
        }
    }

    class GroupByRuleGroupAction extends Action {

        public GroupByRuleGroupAction() {
            super();
            setText("Group by Rule Group");
            setToolTipText("Group by Rule Group");
            setDescription("Group by agenda-group, activation-group or ruleflow-group");
            setChecked(groupByRulegroup);
        }

        @Override
        public void run() {
            setGroupByRuleGroup(!groupByRulegroup);
        }

        private void setGroupByRuleGroup(boolean groupBy) {
            groupByRulegroup = groupBy;
            setChecked(groupBy);

            IPreferenceStore preferenceStore= DroolsEclipsePlugin.getDefault().getPreferenceStore();
            preferenceStore.setValue("GroupByRuleGroupAction.isChecked", groupBy);

            setContentProvider();
            viewer.refresh(true);

        }

    }

    @Override
    public void makeContributions(IMenuManager menuManager,
            IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
        // TODO Auto-generated method stub
        GroupByRuleGroupAction groupByAction = new GroupByRuleGroupAction ();
        menuManager.add(groupByAction);
        super.makeContributions(menuManager, toolBarManager, statusLineManager);
    }
}


/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *      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.eclipse.webdav.http.client;

import java.io.*;
import org.eclipse.webdav.IContext;
import org.eclipse.webdav.client.Policy;
import org.eclipse.webdav.client.WebDAVFactory;
import org.eclipse.webdav.internal.kernel.utils.Assert;

/**
 * A common superclass for HTTP messages.  There are two kinds of HTTP
 * message; requests and responses.  They both have in common a context
 * and an input stream.  This class factors out these similarities.
 *

 * Note: This class/interface is part of an interim API that is still under
 * development and expected to change significantly before reaching stability.
 * It is being made available at this early stage to solicit feedback from pioneering
 * adopters on the understanding that any code that uses this API will almost
 * certainly be broken (repeatedly) as the API evolves.
 *

 */
public abstract class Message {
    protected static BufferPool bufferPool = new BufferPool();
    protected InputStream is;
    protected boolean inputRead = false;
    protected boolean hasInputStream = false;
    protected IContext context;

    /**
     * Creates a message.
     *
     * @param context the message header, or null for an empty
     * header
     * @param is an input stream containing the message's body, or
     * null for an empty body
     */
    public Message(IContext context, InputStream is) {
        this.context = context;
        if (context == null)
            this.context = WebDAVFactory.contextFactory.newContext();
        this.is = is;
        if (is == null)
            this.is = new ByteArrayInputStream(new byte[0]);
    }

    /**
     * Closes this message to free up any system resources.  All messages
     * must be closed before finalization.
     *
     * @exception IOException if there is an I/O error
     */
    public void close() throws IOException {
        is.close();
    }

    /**
     * Returns the content length of this message's body, or -1 if the
     * content length is unknown.
     *
     * @return the content length of this message's body
     */
    public long getContentLength() {
        long contentLength = context.getContentLength();
        if (contentLength != -1)
            return contentLength;
        if (is instanceof ByteArrayInputStream)
            return ((ByteArrayInputStream) is).available();
        return -1;
    }

    /**
     * Returns this message's context.
     *
     * @return this message's context
     */
    public IContext getContext() {
        return context;
    }

    /**
     * Returns this message's input stream.
     *
     * @return this message's input stream
     */
    public InputStream getInputStream() {
        hasInputStream = true;
        return is;
    }

    public String toString() {
        return context.toString();
    }

    /**
     * Writes this messages body to the given output stream. This method may
     * only be called once during the lifetime of this message.
     *
     * @param os an output stream
     * @exception IOException if there is an I/O error
     */
    public void write(OutputStream os) throws IOException {
        Assert.isTrue(!inputRead);
        Assert.isTrue(!hasInputStream);

        int bytesRead = 0;
        int totalBytesRead = 0;
        byte[] buffer = bufferPool.getBuffer();
        long contentLength = getContentLength();

        try {
            while (bytesRead != -1 && (contentLength == -1 || contentLength > totalBytesRead)) {
                if (contentLength == -1) {
                    bytesRead = is.read(buffer);
                } else {
                    bytesRead = is.read(buffer, 0, (int) Math.min(buffer.length, contentLength - totalBytesRead));
                }
                if (bytesRead == -1) {
                    if (contentLength >= 0) {
                        throw new IOException(Policy.bind("exception.unexpectedEndStream")); //$NON-NLS-1$
                    }
                } else {
                    totalBytesRead += bytesRead;
                    os.write(buffer, 0, bytesRead);
                }
            }
        } finally {
            bufferPool.putBuffer(buffer);
            inputRead = true;
        }
    }
}


/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.drools.eclipse.debug.core;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.drools.eclipse.DroolsEclipsePlugin;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugModelMessages;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
import org.mvel2.debug.Debugger;

import com.sun.jdi.ClassType;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;

/**
 * Drools Thread supporting MVEL and Java dialect stackframes
 */
public class DroolsThread extends JDIThread {
    public DroolsThread(JDIDebugTarget target,
                        ThreadReference thread) throws ObjectCollectedException {
        super( target,
               thread );
    }

    protected synchronized List<IJavaStackFrame> computeStackFrames(boolean refreshChildren) throws DebugException {
        List<IJavaStackFrame> fStackFrames = getInternalfStackFrames();

        if ( isSuspended() ) {
            if ( isTerminated() ) {
                fStackFrames.clear();
            } else if ( refreshChildren ) {
                List<StackFrame> frames = getInternalUnderlyingFrames();
                int oldSize = fStackFrames.size();
                int newSize = frames.size();
                int discard = oldSize - newSize; // number of old frames to discard, if any
                for ( int i = 0; i < discard; i++ ) {
                    DroolsStackFrame invalid = (DroolsStackFrame) fStackFrames.remove( 0 );
                    invalid.bind( null,
                                  -1 );
                }
                int newFrames = newSize - oldSize; // number of frames to create, if any
                int depth = oldSize;
                for ( int i = newFrames - 1; i >= 0; i-- ) {
                    StackFrame currentFrame = (StackFrame) frames.get( i );
                    JDIStackFrame customFrame = createCustomFrame( this,
                                                                   depth,
                                                                   currentFrame );

                    fStackFrames.add( 0,
                                      customFrame );

                    depth++;
                }
                int numToRebind = Math.min( newSize,
                                            oldSize ); // number of frames to attempt to rebind
                int offset = newSize - 1;
                for ( depth = 0; depth < numToRebind; depth++ ) {
                    DroolsStackFrame oldFrame = (DroolsStackFrame) fStackFrames.get( offset );
                    StackFrame frame = (StackFrame) frames.get( offset );
                    DroolsStackFrame newFrame = (DroolsStackFrame) oldFrame.bind( frame,
                                                                                depth );
                    if ( newFrame != oldFrame ) {
                        fStackFrames.set( offset,
                                          newFrame );
                    }
                    offset--;
                }

            }
            setInternalfRefreshChildren( false );
        } else {
            return Collections.emptyList();
        }
        return fStackFrames;
    }

    public final static synchronized DroolsStackFrame createCustomFrame(DroolsThread thread,
                                                                        int depth,
                                                                        StackFrame currentFrame) {
        DroolsStackFrame customFrame;
        Location loc = currentFrame.location();
        if ( loc.declaringType().name().equals( "org.drools.core.base.mvel.MVELDebugHandler" ) && loc.method().name().equals( "onBreak" ) ) {
            customFrame = new MVELStackFrame( thread,
                                              currentFrame,
                                              depth );
        } else {
            customFrame = new DroolsStackFrame( thread,
                                                currentFrame,
                                                depth );
        }
        return customFrame;
    }

// I don't see the need for any of this custom stepOver stuff, why is it here?
    public synchronized void stepOver() throws DebugException {

        // Detection for active stackframe
        if ( !(getTopStackFrame() instanceof MVELStackFrame) ) {
            super.stepOver();
            return;
        }

        //MVEL step over
        MVELStackFrame mvelStack = (MVELStackFrame) getTopStackFrame();

        if ( !canStepOver() || !mvelStack.canStepOver() ) {
            return;
        }

        if ( !setRemoteOnBreakReturn( Debugger.STEP ) ) {
            return;
        }

        setRunning( true );

        preserveStackFrames();

        fireEvent( new DebugEvent( this,
                                   DebugEvent.RESUME,
                                   DebugEvent.STEP_OVER ) );

        try {
            getUnderlyingThread().resume();
        } catch ( RuntimeException e ) {
            //stepEnd();
            targetRequestFailed(MessageFormat.format(JDIDebugModelMessages.JDIThread_exception_stepping, e.toString()), e);
        }

    }

    private boolean setRemoteOnBreakReturn(int step_over) throws DebugException {

        JDIStackFrame top = (JDIStackFrame) getTopStackFrame();
        if ( top == null || (!(top instanceof MVELStackFrame)) ) {
            return false;
        }

        Iterator<ReferenceType> handleriter = getVM().classesByName( "org.drools.core.base.mvel.MVELDebugHandler" ).iterator();
        Object debugHandlerClass = handleriter.next();

        int line = step_over;

        ReferenceType refType = (ReferenceType) debugHandlerClass;
        Method m = (Method) refType.methodsByName( "setOnBreakReturn" ).iterator().next();
        List<IntegerValue> args = new ArrayList<IntegerValue>();
        IntegerValue lineVal = getVM().mirrorOf( line );
        //ObjectReference realVal = val.getUnderlyingObject();
        args.add( lineVal );

        try {
            ClassType tt = (ClassType) debugHandlerClass;
            tt.invokeMethod( getUnderlyingThread(),
                             m,
                             args,
                             ObjectReference.INVOKE_SINGLE_THREADED );

        } catch ( Exception e ) {
            DroolsEclipsePlugin.log( e );
            return false;
        }
        return true;
    }

    public synchronized void resume() throws DebugException {
        // clear up the step over flag. step over button never calls this method.
        setRemoteOnBreakReturn( Debugger.CONTINUE );
        super.resume();
    }

    protected synchronized void disposeStackFrames() {
        super.disposeStackFrames();
    }

    protected void terminated() {
        super.terminated();
    }

    protected void removeCurrentBreakpoint(IBreakpoint bp) {
        super.removeCurrentBreakpoint( bp );
    }

    protected synchronized void suspendedByVM() {
        super.suspendedByVM();
    }

    protected synchronized void resumedByVM() throws DebugException {
        super.resumedByVM();
    }

    protected void setRunning(boolean running) {
        super.setRunning( running );
    }

    public void setInternalfRefreshChildren(boolean bool) {
        try {
            java.lang.reflect.Field field = JDIThread.class.getDeclaredField( "fRefreshChildren" );
            field.setAccessible( true );
            field.set( this,
                       bool );
        } catch ( Exception e ) {
        }
    }

    @SuppressWarnings("unchecked")
    public List<IJavaStackFrame> getInternalfStackFrames() {
        try {
            java.lang.reflect.Field field = JDIThread.class.getDeclaredField( "fStackFrames" );

            field.setAccessible( true );

            return (List<IJavaStackFrame>) field.get(this);
        } catch ( Exception e ) {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    private List<StackFrame> getInternalUnderlyingFrames() throws DebugException {
        try {
            java.lang.reflect.Method method = JDIThread.class.getDeclaredMethod("getUnderlyingFrames");

            method.setAccessible( true );

            return (List<StackFrame>) method.invoke(this);
        } catch ( Exception e ) {
            return null;
        }
    }

}


/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.drools.eclipse.builder;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.antlr.runtime.RecognitionException;
import org.kie.internal.jci.CompilationProblem;
import org.drools.compiler.compiler.BaseKnowledgeBuilderResultImpl;
import org.drools.compiler.compiler.DescrBuildError;
import org.drools.compiler.compiler.DroolsError;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.compiler.FactTemplateError;
import org.drools.compiler.compiler.FieldTemplateError;
import org.drools.compiler.compiler.FunctionError;
import org.drools.compiler.compiler.GlobalError;
import org.drools.compiler.compiler.ImportError;
import org.drools.compiler.compiler.ParserError;
import org.drools.compiler.compiler.RuleBuildError;
import org.drools.compiler.kproject.models.KieModuleModelImpl;
import org.drools.compiler.lang.ExpanderException;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.ProcessInfo;
import org.drools.eclipse.preferences.IDroolsConstants;
import org.drools.eclipse.util.DroolsClasspathContainer;
import org.drools.eclipse.util.DroolsRuntimeManager;
import org.drools.eclipse.util.ProjectClassLoader;
import org.drools.eclipse.wizard.project.NewDroolsProjectWizard;
import org.drools.template.parser.DecisionTableParseException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
import org.kie.api.io.ResourceType;
import org.kie.eclipse.runtime.IRuntime;
import org.kie.internal.builder.KnowledgeBuilderResult;
import org.kie.internal.io.ResourceFactory;

/**
 * Automatically syntax checks .drl files and adds possible errors or warnings
 * to the problem list. Nominally is triggerd on save.
 */
public class DroolsBuilder extends IncrementalProjectBuilder {

    public static final String       BUILDER_ID = "org.drools.eclipse.droolsbuilder";

    private boolean isKieProject = false;

    protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
        IProject currentProject = getProject();
        if ( currentProject == null || !currentProject.isAccessible() ) {
            return new IProject[0];
        }

        try {
            if ( monitor != null && monitor.isCanceled() ) throw new OperationCanceledException();

            if ( kind == IncrementalProjectBuilder.FULL_BUILD ) {
                fullBuild( monitor );
            } else {
                IResourceDelta delta = getDelta( getProject() );
                if ( delta == null ) {
                    fullBuild( monitor );
                } else {
                    incrementalBuild( delta,
                                      monitor );
                }
            }
        } catch ( CoreException e ) {
            IMarker marker = currentProject.createMarker( IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER );
            marker.setAttribute( IMarker.MESSAGE,
                                 "Error when trying to build Drools project: " + e.getLocalizedMessage() );
            marker.setAttribute( IMarker.SEVERITY,
                                 IMarker.SEVERITY_ERROR );
        }

        return getRequiredProjects( currentProject );
    }

    protected void fullBuild(IProgressMonitor monitor) throws CoreException {
        removeProblemsFor( getProject() );
        IJavaProject project = JavaCore.create( getProject() );

        ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
        ClassLoader newLoader = ProjectClassLoader.getProjectClassLoader( project );
        try {
            Thread.currentThread().setContextClassLoader( newLoader );
            IClasspathEntry[] classpathEntries = project.getRawClasspath();
            for ( int i = 0; i < classpathEntries.length; i++ ) {
                if ( DroolsClasspathContainer.DROOLS_CLASSPATH_CONTAINER_PATH.equals( classpathEntries[i].getPath().toString() ) ) {
                    String[] jars = DroolsRuntimeManager.getDefault().getRuntimeJars( getProject() );
                    if ( jars == null || jars.length == 0 ) {
                        IRuntime runtime = DroolsRuntimeManager.getDefault().getRuntime( getProject() );
                        IMarker marker = getProject().createMarker( IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER );
                        if ( runtime == null ) {
                            marker.setAttribute( IMarker.MESSAGE,
                                                 "Could not find default Drools runtime" );
                        } else {
                            marker.setAttribute( IMarker.MESSAGE,
                                                 "Could not find Drools runtime " + runtime );
                        }
                        marker.setAttribute( IMarker.SEVERITY,
                                             IMarker.SEVERITY_ERROR );
                        return;
                    }
                }
            }

            isKieProject = false;
            DroolsBuilderVisitor droolsBuilderVisitor = new DroolsBuilderVisitor();
            getProject().accept( droolsBuilderVisitor );
            droolsBuilderVisitor.build();
        } finally {
            Thread.currentThread().setContextClassLoader( oldLoader );
        }
    }

    protected void incrementalBuild(IResourceDelta delta,
                                    IProgressMonitor monitor) throws CoreException {
        IPreferenceStore store = DroolsEclipsePlugin.getDefault().getPreferenceStore();
        boolean fullBuild = store.getBoolean( IDroolsConstants.CROSS_BUILD ) || store.getBoolean( IDroolsConstants.BUILD_ALL ) || isKieProject;

        if ( !fullBuild ) {
            fullBuild = DroolsEclipsePlugin.getDefault().resetForceFullBuild();
        }

        if ( fullBuild ) {
            // to make sure that all rules are checked when a java file is changed
            fullBuild( monitor );
        } else {
            delta.accept( new DroolsBuildDeltaVisitor() );
        }
    }

    private class DroolsBuilderVisitor
		    implements
		    IResourceVisitor {

        private final List<IResource> resources = new ArrayList<IResource>();

        public boolean visit(IResource resource) throws CoreException {
            if ( isInOutputDirectory( resource ) || !exists( resource ) ) {
                return false;
            }
            if ( resource instanceof IFile) {
            	isKieProject |= resource.getProjectRelativePath().toString().endsWith(KieModuleModelImpl.KMODULE_JAR_PATH);
            	resources.add(resource);
            }
            return true;
        }

        public void build() throws CoreException {
        	if (isKieProject) {
        		doBuildKieProject();
        	} else if ( DroolsEclipsePlugin.getDefault().getPreferenceStore().getBoolean( IDroolsConstants.CROSS_BUILD ) ) {
            	doBatchBuild();
            } else {
            	doBuild();
            }
        }

        private void doBuildKieProject() throws CoreException {
        	KieServices ks = KieServices.Factory.get();
        	KieFileSystem kfs = ks.newKieFileSystem();

        	Map<String, IResource> resourcesMap = new HashMap<String, IResource>();
        	for (IResource resource : resources) {
        		String resourcePath = resource.getProjectRelativePath().toString();
                if ( ResourceType.determineResourceType( resource.getName() ) != null ) {
                    removeProblemsFor( resource );
                    kfs.write(resourcePath, ResourceFactory.newInputStreamResource( ((IFile) resource).getContents() ));
                    resourcesMap.put(resourcePath, resource);
                } else if ( resourcePath.endsWith(KieModuleModelImpl.KMODULE_JAR_PATH) ) {
                	kfs.writeKModuleXML(new String( Util.getResourceContentsAsCharArray( (IFile)resource ) ));
                }
        	}

        	KieBuilder kieBuilder = ks.newKieBuilder(kfs);
        	List<Message> messages = kieBuilder.buildAll().getResults().getMessages();
        	for (Message message : messages) {
        		IResource resource = resourcesMap.get(message.getPath());
        		if (resource == null) {
        			resource = resourcesMap.get("src/main/resources/" + message.getPath());
        		}
        		// avoid to add markers for java compilation issues since they are already reported by the java compiler
        		if (resource != null && !resource.getName().endsWith(".java")) {
        			createMarker(resource, message.getText(), message.getLine());
        		}
        	}
    	}

        private void doBuild() {
        	for (IResource resource : resources) {
        		parseResource( resource, true );
        	}
    	}

        private void doBatchBuild() {
        	List<ResourceDescr> resourceDescrs = new ArrayList<ResourceDescr>();

        	for (IResource resource : resources) {
                ResourceDescr resourceDescr = ResourceDescr.createResourceDescr( resource );
                if ( resourceDescr != null ) {
                    removeProblemsFor( resource );
                    DroolsEclipsePlugin.getDefault().invalidateResource( resource );
                    resourceDescrs.add( resourceDescr );
                }
        	}

            List<DRLInfo> drlInfos = DroolsEclipsePlugin.getDefault().parseResources( resourceDescrs );
            for ( DRLInfo drlInfo : drlInfos ) {
                appendMarkers( drlInfo );
            }
    	}
    }

    private class DroolsBuildDeltaVisitor
            implements
            IResourceDeltaVisitor {
        public boolean visit(IResourceDelta delta) throws CoreException {
            return parseResource( delta.getResource(),
                                  false );
        }
    }

    private boolean isInOutputDirectory(IResource res) throws JavaModelException {
        IJavaProject project = JavaCore.create( res.getProject() );
        // exclude files that are located in the output directory,
        // unless the output directory is the same as the project location
        return !project.getOutputLocation().equals( project.getPath() )
               && project.getOutputLocation().isPrefixOf( res.getFullPath() );
    }

	private boolean exists(IResource res) {
        if ( !res.exists() ) {
            removeProblemsFor( res );
            DroolsEclipsePlugin.getDefault().invalidateResource( res );
            return false;
        }
        return true;
    }

    protected boolean parseResource(IResource res,
                                    boolean clean) {
        try {
            // exclude .guvnorinfo files
            if ( ".guvnorinfo".equals( res.getName() ) ) {
                return false;
            }
            if ( isInOutputDirectory( res ) ) {
                return false;
            }
        } catch ( JavaModelException e ) {
            // do nothing
        }

        if ( !exists( res ) ) {
            return false;
        }

        if ( res instanceof IFile) {
        	String fileExtension = res.getFileExtension();
	        if ( "drl".equals( fileExtension )
                 || "gdrl".equals( fileExtension )
                 || "rdrl".equals( fileExtension )
                 || "dslr".equals( fileExtension )
                 || "rdslr".equals( fileExtension )
                 || ".package".equals( res.getName() ) ) {
	            removeProblemsFor( res );
	            try {
	                if ( clean ) {
	                    DroolsEclipsePlugin.getDefault().invalidateResource( res );
	                }
	                appendMarkers( res, parseDRLFile( (IFile) res ) );
	            } catch ( Throwable t ) {
	                DroolsEclipsePlugin.log( t );
	                createMarker( res,
	                              t.getMessage(),
	                              -1 );
	            }
	            return false;
	        } else if ( "xls".equals( fileExtension ) ) {
	            removeProblemsFor( res );
	            try {
	                if ( clean ) {
	                    DroolsEclipsePlugin.getDefault().invalidateResource( res );
	                }
	                appendMarkers( res, parseXLSFile( (IFile) res ) );
	            } catch ( Throwable t ) {
	                createMarker( res,
	                              t.getMessage(),
	                              -1 );
	            }
	            return false;
	        } else if ( "csv".equals( fileExtension ) ) {
	            removeProblemsFor( res );
	            try {
	                if ( clean ) {
	                    DroolsEclipsePlugin.getDefault().invalidateResource( res );
	                }
	                appendMarkers( res, parseCSVFile( (IFile) res ) );
	            } catch ( Throwable t ) {
	                createMarker( res,
	                              t.getMessage(),
	                              -1 );
	            }
	            return false;
	        } else if ( "rf".equals( fileExtension ) ) {
	            removeProblemsFor( res );
	            try {
	                if ( clean ) {
	                    DroolsEclipsePlugin.getDefault().invalidateResource( res );
	                }
	                appendMarkers( res, parseRuleFlowFile( (IFile) res ) );
	            } catch ( Throwable t ) {
	                createMarker( res,
	                              t.getMessage(),
	                              -1 );
	            }
	            return false;
	        } else if ( "bpmn".equals( fileExtension )
	                    || "bpmn2".equals( fileExtension ) ) {
	            removeProblemsFor( res );
	            try {
	                if ( clean ) {
	                    DroolsEclipsePlugin.getDefault().invalidateResource( res );
	                }
	                appendMarkers( res, parseRuleFlowFile( (IFile) res ) );
	            } catch ( Throwable t ) {
	                createMarker( res,
	                              t.getMessage(),
	                              -1 );
	            }
	            return false;
	        }
        }

        return true;
    }

    private void appendMarkers(DRLInfo drlInfo) {
        List<DroolsBuildMarker> markers = new ArrayList<DroolsBuildMarker>();
        markParseErrors( markers, drlInfo.getParserErrors() );
        markOtherErrors( markers, drlInfo.getBuilderErrors() );
        appendMarkers( drlInfo.getResource(), markers );
    }

    private void appendMarkers(IResource res,
                               List<DroolsBuildMarker> markers) {
        for ( DroolsBuildMarker marker : markers ) {
            createMarker( res, marker.getText(), marker.getLine() );
        }
    }

    private interface ResourceParser {
        DRLInfo parseResource() throws DroolsParserException,
                               DecisionTableParseException,
                               CoreException,
                               IOException;
    }

    private List<DroolsBuildMarker> parseResource(ResourceParser resourceParser) {
        List<DroolsBuildMarker> markers = new ArrayList<DroolsBuildMarker>();
        try {
            DRLInfo drlInfo = resourceParser.parseResource();

            //parser errors
            markParseErrors( markers, drlInfo.getParserErrors() );
            markOtherErrors( markers, drlInfo.getBuilderErrors() );
        } catch ( DroolsParserException e ) {
            // we have an error thrown from DrlParser
            Throwable cause = e.getCause();
            if ( cause instanceof RecognitionException ) {
                RecognitionException recogErr = (RecognitionException) cause;
                markers.add( new DroolsBuildMarker( recogErr.getMessage(),
                                                    recogErr.line ) ); //flick back the line number
            }
        } catch ( DecisionTableParseException e ) {
            if ( !"No RuleTable's were found in spreadsheet.".equals( e.getMessage() ) ) {
                throw e;
            }
        } catch ( Exception t ) {
            String message = t.getMessage();
            if ( message == null || message.trim().equals( "" ) ) {
                message = "Error: " + t.getClass().getName();
            }
            markers.add( new DroolsBuildMarker( message ) );
        }
        return markers;
    }

    private List<DroolsBuildMarker> parseDRLFile(final IFile file) {
        return parseResource( new ResourceParser() {
            public DRLInfo parseResource() throws DroolsParserException {
                return DroolsEclipsePlugin.getDefault().parseResource( file, true );
            }
        } );
    }

    private List<DroolsBuildMarker> parseXLSFile(final IFile file) {
        return parseResource( new ResourceParser() {
            public DRLInfo parseResource() throws DroolsParserException,
                                          DecisionTableParseException,
                                          CoreException {
                SpreadsheetCompiler converter = new SpreadsheetCompiler();
                String drl = converter.compile( file.getContents(), InputType.XLS );
                return DroolsEclipsePlugin.getDefault().parseXLSResource( drl, file );
            }
        } );
    }

    private List<DroolsBuildMarker> parseCSVFile(final IFile file) {
        return parseResource( new ResourceParser() {
            public DRLInfo parseResource() throws DroolsParserException,
                                          CoreException {
                SpreadsheetCompiler converter = new SpreadsheetCompiler();
                String drl = converter.compile( file.getContents(), InputType.CSV );
                return DroolsEclipsePlugin.getDefault().parseXLSResource( drl, file );
            }
        } );
    }

    protected List<DroolsBuildMarker> parseRuleFlowFile(IFile file) {
        List<DroolsBuildMarker> markers = new ArrayList<DroolsBuildMarker>();
        if ( !file.exists() ) {
            return markers;
        }
        try {
            String input = convertToString( file.getContents() );
            ProcessInfo processInfo =
                    DroolsEclipsePlugin.getDefault().parseProcess( input,
                                                                   file );
            if ( processInfo != null ) {
                markParseErrors( markers,
                                 processInfo.getErrors() );
            }
        } catch ( Exception t ) {
            t.printStackTrace();
            String message = t.getMessage();
            if ( message == null || message.trim().equals( "" ) ) {
                message = "Error: " + t.getClass().getName();
            }
            markers.add( new DroolsBuildMarker( message ) );
        }
        return markers;
    }

    protected static String convertToString(final InputStream inputStream) throws IOException {
        Reader reader = new InputStreamReader( inputStream );
        final StringBuffer text = new StringBuffer();
        final char[] buf = new char[1024];
        int len = 0;
        while ( (len = reader.read( buf )) >= 0 ) {
            text.append( buf,
                         0,
                         len );
        }
        return text.toString();
    }

    /**
     * This will create markers for parse errors. Parse errors mean that antlr
     * has picked up some major typos in the input source.
     */
    protected void markParseErrors(List<DroolsBuildMarker> markers,
                                   List<BaseKnowledgeBuilderResultImpl> parserErrors) {
        for ( Iterator<BaseKnowledgeBuilderResultImpl> iter = parserErrors.iterator(); iter.hasNext(); ) {
            Object error = iter.next();
            if ( error instanceof ParserError ) {
                ParserError err = (ParserError) error;
                markers.add( new DroolsBuildMarker( err.getMessage(),
                                                    err.getRow() ) );
            } else if ( error instanceof KnowledgeBuilderResult) {
                KnowledgeBuilderResult res = (KnowledgeBuilderResult) error;
                int[] errorLines = res.getLines();
                markers.add( new DroolsBuildMarker( res.getMessage(),
                                                    errorLines != null && errorLines.length > 0 ? errorLines[0] : -1 ) );
            } else if ( error instanceof ExpanderException ) {
                ExpanderException exc = (ExpanderException) error;
                // TODO line mapping is incorrect
                markers.add( new DroolsBuildMarker( exc.getMessage(),
                                                    -1 ) );
            } else {
                markers.add( new DroolsBuildMarker( error.toString() ) );
            }
        }
    }

    /**
     * This will create markers for build errors that happen AFTER parsing.
     */
    private void markOtherErrors(List<DroolsBuildMarker> markers,
                                 DroolsError[] buildErrors) {
        // TODO are there warnings too?
        for ( int i = 0; i < buildErrors.length; i++ ) {
            DroolsError error = buildErrors[i];
            if ( error instanceof GlobalError ) {
                GlobalError globalError = (GlobalError) error;
                markers.add( new DroolsBuildMarker( "Global error: " + globalError.getGlobal(),
                                                    -1 ) );
            } else if ( error instanceof RuleBuildError ) {
                RuleBuildError ruleError = (RuleBuildError) error;
                // TODO try to retrieve line number (or even character start-end)
                // disabled for now because line number are those of the rule class,
                // not the rule file itself
                if ( ruleError.getObject() instanceof CompilationProblem[] ) {
                    CompilationProblem[] problems = (CompilationProblem[]) ruleError.getObject();
                    for ( int j = 0; j < problems.length; j++ ) {
                        markers.add( new DroolsBuildMarker( problems[j].getMessage(),
                                                            ruleError.getLine() ) );
                    }
                } else {
                    markers.add( new DroolsBuildMarker( ruleError.getRule().getName() + ":" + ruleError.getMessage(),
                                                        ruleError.getLine() ) );
                }
            } else if ( error instanceof ParserError ) {
                ParserError parserError = (ParserError) error;
                // TODO try to retrieve character start-end
                markers.add( new DroolsBuildMarker( parserError.getMessage(),
                                                    parserError.getRow() ) );
            } else if ( error instanceof FunctionError ) {
                FunctionError functionError = (FunctionError) error;
                // TODO add line to function error
                // TODO try to retrieve character start-end
                if ( functionError.getObject() instanceof CompilationProblem[] ) {
                    CompilationProblem[] problems = (CompilationProblem[]) functionError.getObject();
                    for ( int j = 0; j < problems.length; j++ ) {
                        markers.add( new DroolsBuildMarker( problems[j].getMessage(),
                                                            functionError.getLines()[j] ) );
                    }
                } else {
                    markers.add( new DroolsBuildMarker( functionError.getFunctionDescr().getName() + ":" + functionError.getMessage(),
                                                        -1 ) );
                }
            } else if ( error instanceof FieldTemplateError ) {
                markers.add( new DroolsBuildMarker( error.getMessage(),
                                                    ((FieldTemplateError) error).getLine() ) );
            } else if ( error instanceof FactTemplateError ) {
                markers.add( new DroolsBuildMarker( error.getMessage(),
                                                    ((FactTemplateError) error).getLine() ) );
            } else if ( error instanceof ImportError ) {
                markers.add( new DroolsBuildMarker( "ImportError: " + error.getMessage() ) );
            } else if ( error instanceof DescrBuildError ) {
                markers.add( new DroolsBuildMarker( "BuildError: " + error.getMessage(),
                                                    ((DescrBuildError) error).getLine() ) );
            } else {
                markers.add( new DroolsBuildMarker( "Unknown DroolsError " + error.getClass() + ": " + error ) );
            }
        }
    }

    protected void createMarker(final IResource res,
                                final String message,
                                final int lineNumber) {
        try {
            IWorkspaceRunnable r = new IWorkspaceRunnable() {
                public void run(IProgressMonitor monitor) throws CoreException {
                    IMarker marker = res
                            .createMarker( IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER );
                    marker.setAttribute( IMarker.MESSAGE,
                                         message );
                    marker.setAttribute( IMarker.SEVERITY,
                                         IMarker.SEVERITY_ERROR );
                    marker.setAttribute( IMarker.LINE_NUMBER,
                                         lineNumber );
                }
            };
            res.getWorkspace().run( r,
                                    null,
                                    IWorkspace.AVOID_UPDATE,
                                    null );
        } catch ( CoreException e ) {
            DroolsEclipsePlugin.log( e );
        }
    }

    protected void removeProblemsFor(IResource resource) {
        try {
            if ( resource != null && resource.exists() ) {
                resource.deleteMarkers(
                                        IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER,
                                        false,
                                        IResource.DEPTH_INFINITE );
            }
        } catch ( CoreException e ) {
            DroolsEclipsePlugin.log( e );
        }
    }

    private IProject[] getRequiredProjects(IProject project) {
        IJavaProject javaProject = JavaCore.create( project );
        List<IProject> projects = new ArrayList<IProject>();
        try {
            IClasspathEntry[] entries = javaProject.getResolvedClasspath( true );
            for ( int i = 0, l = entries.length; i < l; i++ ) {
                IClasspathEntry entry = entries[i];
                if ( entry.getEntryKind() == IClasspathEntry.CPE_PROJECT ) {
                    IProject p = project.getWorkspace().getRoot().getProject( entry.getPath().lastSegment() ); // missing projects are considered too
                    if ( p != null && !projects.contains( p ) ) {
                        projects.add( p );
                    }
                }
            }
        } catch ( JavaModelException e ) {
            return new IProject[0];
        }
        return projects.toArray( new IProject[projects.size()] );
    }

}


/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *      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.eclipse.webdav.internal.authentication;

/**
 * A simple parser that is mainly used to parse HTTP header fields.
 */
public class Parser {
    /**
     * The string being parsed.
     */
    public String s;

    /**
     * The index of the next character in s to be parsed.
     */
    public int pos;

    /**
     * Creates a new parser on the given string.
     *
     * @param s the string to be parsed
     */
    public Parser(String s) {
        this.s = s;
    }

    /**
     * Throws a ParserException if pos is out of
     * range.
     */
    public void checkPosition() throws ParserException {
        if (pos < 0 || pos >= s.length())
            throw new ParserException();
    }

    /**
     * Skips the next character in s if it matches c, otherwise a
     * ParserException is thrown.
     *
     * @param c
     * @throws  ParserException
     */
    public void match(char c) throws ParserException {
        checkPosition();
        if (s.charAt(pos) != c)
            throw new ParserException();
        ++pos;
    }

    /**
     * Returns the next quoted string is s (quotes included). Throws a
     * ParserException if the next substring in s is not a
     * quoted string.
     *
     * @return the next quoted string in s (quotes included)
     * @throws ParserException
     */
    public String nextQuotedString() throws ParserException {
        int start = pos;
        match('"');

        checkPosition();
        while (s.charAt(pos) != '"') {
            ++pos;
            checkPosition();
        }
        match('"');

        return s.substring(start, pos);
    }

    /**
     * Returns the next token in s. Throws a ParserException if
     * the next substring in s is not a token.
     *
     * @return the next token in s
     * @throws ParserException
     */
    public String nextToken() throws ParserException {
        int start = pos;

        checkPosition();
        boolean done = false;

        while (!done && pos < s.length()) {
            int c = s.charAt(pos);
            if (c <= 31 //
                    || c == 127 //
                    || c == '(' //
                    || c == ')' //
                    || c == '<' //
                    || c == '>' //
                    || c == '@' //
                    || c == ',' //
                    || c == ';' //
                    || c == ':' //
                    || c == '\\' //
                    || c == '"' //
                    || c == '/' //
                    || c == '[' //
                    || c == ']' //
                    || c == '?' //
                    || c == '=' //
                    || c == '{' //
                    || c == '}' //
                    || Character.isWhitespace((char) c)) {
                done = true;
            } else {
                ++pos;
            }
        }

        if (start == pos) {
            throw new ParserException();
        }

        return s.substring(start, pos);
    }

    /**
     * Skips the next sequence of white space in s. An exception is not
     * thrown if there is no matching white space.
     */
    public void skipWhiteSpace() {
        while (pos < s.length() && Character.isWhitespace(s.charAt(pos)))
            ++pos;
    }
}


/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.guvnor.tools.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.webdav.IResponse;
import org.guvnor.tools.Activator;
import org.guvnor.tools.Messages;
import org.guvnor.tools.utils.webdav.IWebDavClient;
import org.guvnor.tools.utils.webdav.ResourceProperties;
import org.guvnor.tools.utils.webdav.WebDavClientFactory;
import org.guvnor.tools.utils.webdav.WebDavException;
import org.guvnor.tools.utils.webdav.WebDavServerCache;
import org.guvnor.tools.views.model.ResourceHistoryEntry;

/**
 * A set of utilities for dealing with (local) Guvnor metadata.
 */
public class GuvnorMetadataUtils {

    private static final QualifiedName CONTROLLED_PROJECT_PROPERTY = new QualifiedName(Activator.PLUGIN_ID,"guvnorControlled");
	/**
     * Finds the local Guvnor metadata file associated with a given resource.
     * @param resource The resource to locate metadata for.
     * @return The metadata file for the given resource, null if metadata is not found.
     */
    public static IFile findGuvnorMetadata(IResource resource) {
        IFile res = null;
        IPath dir = resource.getFullPath().removeLastSegments(1);
        IPath mdpath = dir.append(".guvnorinfo").append("." + resource.getName()); //$NON-NLS-1$ //$NON-NLS-2$
        IResource mdResource = resource.getWorkspace().getRoot().findMember(mdpath);
        if (mdResource != null
           && mdResource.exists()
           && mdResource instanceof IFile) {
            res = (IFile)mdResource;
        }
        return res;
    }

    /**
     * Adds a resource to Guvnor.
     * @param repLoc The location of the Guvnor repository
     * @param targetLoc The location in Guvnor to add the resource
     * @param selectedFile The resource to add
     * @return true if the resource is added, false if there is already a resource
     *         with the same name in the given location.
     */
    public static boolean addResourceToGuvnor(String repLoc,
                                             String targetLoc,
                                             IFile selectedFile) {
        boolean res = false;
        try {
            String fullPath = targetLoc + selectedFile.getName();
            IWebDavClient client = WebDavServerCache.getWebDavClient(repLoc);
            if (client == null) {
                client = WebDavClientFactory.createClient(new URL(repLoc));
                WebDavServerCache.cacheWebDavClient(repLoc, client);
            }
            try {
//                res = client.createResource(fullPath, selectedFile.getContents(), false);
                // Hack: When creating a file, if the actual contents are passed first,
                // the client hangs for about 20 seconds when closing the InputStream.
                // Don't know why...
                // But, if the file is created with empty contents, and then the contents
                // set, the operation is fast (less than a couple of seconds)
                res = client.createResource(fullPath, new ByteArrayInputStream(new byte[0]), false);
                if (res) {
                    client.putResource(fullPath, selectedFile.getContents());
                }
            } catch (WebDavException wde) {
                if (wde.getErrorCode() != IResponse.SC_UNAUTHORIZED) {
                    // If not an authentication failure, we don't know what to do with it
                    throw wde;
                }
                boolean retry = PlatformUtils.getInstance().
                                    authenticateForServer(repLoc, client);
                if (retry) {
//                    res = client.createResource(fullPath, selectedFile.getContents(), false);
                    // See Hack note immediately above...
                    res = client.createResource(fullPath, new ByteArrayInputStream(new byte[0]), false);
                    if (res) {
                        client.putResource(fullPath, selectedFile.getContents());
                    }
                }
            }
            if (res) {
                GuvnorMetadataUtils.markCurrentGuvnorResource(selectedFile);
                ResourceProperties resProps = client.queryProperties(fullPath);
                GuvnorMetadataProps mdProps =
                        new GuvnorMetadataProps(selectedFile.getName(),
                                               repLoc,
                                               fullPath, resProps.getLastModifiedDate(),
                                               resProps.getRevision());
                GuvnorMetadataUtils.setGuvnorMetadataProps(selectedFile.getFullPath(), mdProps);
            }
        } catch (Exception e) {
            Activator.getDefault().displayError(IStatus.ERROR, e.getMessage(), e, true);
        }
        return res;
    }

    private static void setProjectPropertyForControlledResource(IFile selectedFile) throws Exception{
        IProject project = selectedFile.getProject();
        String value = project.getPersistentProperty(CONTROLLED_PROJECT_PROPERTY);
        if(value == null){
            project.setPersistentProperty(CONTROLLED_PROJECT_PROPERTY, "true");
        }
    }
    /**
     * Commits changes to Guvnor.
     * @param selectedFile The Guvnor controlled file with pending changes
     */
    public static void commitFileChanges(IFile selectedFile) {
        try {
            GuvnorMetadataProps props = GuvnorMetadataUtils.getGuvnorMetadata(selectedFile);
            IWebDavClient client = WebDavServerCache.getWebDavClient(props.getRepository());
            if (client == null) {
                client = WebDavClientFactory.createClient(new URL(props.getRepository()));
                WebDavServerCache.cacheWebDavClient(props.getRepository(), client);
            }
            ResourceProperties remoteProps = null;
            try {
                remoteProps = client.queryProperties(props.getFullpath());
            } catch (WebDavException wde) {
                if (wde.getErrorCode() != IResponse.SC_UNAUTHORIZED) {
                    // If not an authentication failure, we don't know what to do with it
                    throw wde;
                }
                boolean retry = PlatformUtils.getInstance().
                                    authenticateForServer(props.getRepository(), client);
                if (retry) {
                    remoteProps = client.queryProperties(props.getFullpath());
                }
            }
            if (remoteProps == null) {
                throw new Exception("Could not retrieve server version of " + props.getFullpath()); //$NON-NLS-1$
            }
            // Check to make sure that the version in the repository is the same as the base
            // version for the local copy
            boolean proceed = true;
            if (!props.getRevision().equals(remoteProps.getRevision())) {
                String msg = MessageFormat.format(Messages.getString("overwrite.confirmation"), //$NON-NLS-1$
                                                 new Object[] { selectedFile.getName(),
                                                                remoteProps.getRevision(),
                                                                props.getRevision() });
                Display display = PlatformUI.getWorkbench().getDisplay();
                proceed = MessageDialog.openQuestion(display.getActiveShell(),
                                                    Messages.getString("overwrite.confirmation.caption"), msg); //$NON-NLS-1$

            }
            if (proceed) {
                client.putResource(props.getFullpath(), selectedFile.getContents());
                GuvnorMetadataUtils.markCurrentGuvnorResource(selectedFile);
                ResourceProperties resProps = client.queryProperties(props.getFullpath());
                GuvnorMetadataProps mdProps = GuvnorMetadataUtils.getGuvnorMetadata(selectedFile);
                mdProps.setVersion(resProps.getLastModifiedDate());
                mdProps.setRevision(resProps.getRevision());
                GuvnorMetadataUtils.setGuvnorMetadataProps(selectedFile.getFullPath(), mdProps);
            }
        } catch (Exception e) {
            Activator.getDefault().displayError(IStatus.ERROR, e.getMessage(), e, true);
        }
    }

    /**
     * Finds the local Guvnor metadata file associated with a given resource.
     * @param resource The resource path to locate metadata for.
     * @return The metadata file for the given resource, null if metadata is not found.
     */
    public static IFile findGuvnorMetadata(IPath resource) {
        IFile res = null;
        IPath dir = resource.removeLastSegments(1);
        IPath mdpath = dir.append(".guvnorinfo").append("." + resource.lastSegment()); //$NON-NLS-1$ //$NON-NLS-2$
        IResource mdResource = Activator.getDefault().getWorkspace().
                                            getRoot().findMember(mdpath);
        if (mdResource != null
           && mdResource.exists()
           && mdResource instanceof IFile) {
            res = (IFile)mdResource;
        }
        return res;
    }

    public static boolean isGuvnorControlledResource(IResource resource) {
        return findGuvnorMetadata(resource) != null;
    }

    public static GuvnorMetadataProps loadGuvnorMetadata(IFile mdFile) throws Exception {

        setProjectPropertyForControlledResource(mdFile);

        if(!mdFile.isTeamPrivateMember()){
            mdFile.setTeamPrivateMember(true);
            mdFile.setDerived(true);
            if(mdFile.getParent().getName().endsWith(".guvnorinfo")){
                mdFile.getParent().setTeamPrivateMember(true);
                mdFile.getParent().setDerived(true);
            }
        }

        Properties props = new Properties();
        InputStream contents = mdFile.getContents();

        try{
            props.load(contents);
            return new GuvnorMetadataProps(props.getProperty("filename"), //$NON-NLS-1$
                                       props.getProperty("repository"), //$NON-NLS-1$
                                       props.getProperty("fullpath"), //$NON-NLS-1$
                                       props.getProperty("lastmodified"), //$NON-NLS-1$
                                       props.getProperty("revision")); //$NON-NLS-1$
        }
        finally{
            contents.close();
        }
    }

    public static GuvnorMetadataProps getGuvnorMetadata(IResource resource) throws Exception {
        IFile mdFile = findGuvnorMetadata(resource);
        if (mdFile == null) {
            return null;
        }
        return loadGuvnorMetadata(mdFile);
    }

    public static void writeGuvnorMetadataProps(File mdFile,
                                             GuvnorMetadataProps mdProps) throws Exception {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(mdFile);
            Properties props = new Properties();
            props.put("repository",     mdProps.getRepository()); //$NON-NLS-1$
            props.put("fullpath",         mdProps.getFullpath()); //$NON-NLS-1$
            props.put("filename",         mdProps.getFilename()); //$NON-NLS-1$
            props.put("lastmodified",     mdProps.getVersion()); //$NON-NLS-1$
            props.put("revision",        mdProps.getRevision()); //$NON-NLS-1$
            props.store(fos, null);
            fos.flush();
        } finally {
            if (fos != null) {
                fos.close();
            }
        }
    }

    public static void setGuvnorMetadataProps(IPath controlledFile,
                                             GuvnorMetadataProps mdProps) throws Exception {
        IWorkspaceRoot root = Activator.getDefault().getWorkspace().getRoot();
        IFolder mdFolder = root.getFolder(
                            controlledFile.removeLastSegments(1).append(".guvnorinfo")); //$NON-NLS-1$
        if (!mdFolder.exists()) {
            mdFolder.create(true, true, null);
            mdFolder.setTeamPrivateMember(true);
            mdFolder.setDerived(true);
        }

        IFile mdFile = root.getFile(
                        mdFolder.getFullPath().append("." + controlledFile.lastSegment())); //$NON-NLS-1$

        Properties props = new Properties();
        if (!mdFile.exists()) {
            mdFile.create(new ByteArrayInputStream(new byte[] {}), true, null);
            mdFile.setTeamPrivateMember(true);
            mdFile.setDerived(true);
        } else {
            props.load(mdFile.getContents());
        }

        if (mdProps.getRepository() != null) {
            props.put("repository", mdProps.getRepository()); //$NON-NLS-1$
        }
        if (mdProps.getFullpath() != null) {
            props.put("fullpath", mdProps.getFullpath()); //$NON-NLS-1$
        }
        if (mdProps.getFilename() != null) {
            props.put("filename", mdProps.getFilename()); //$NON-NLS-1$
        }
        if (mdProps.getVersion() != null) {
            props.put("lastmodified", mdProps.getVersion()); //$NON-NLS-1$
        }
        if (mdProps.getRevision() != null) {
            props.put("revision", mdProps.getRevision()); //$NON-NLS-1$
        }
        OutputStream os = null;
        try {
            os = new FileOutputStream(new File(mdFile.getLocation().toOSString()));
            props.store(os, null);
            os.flush();
        } finally {
            if (os != null) {
                os.close();
            }
        }
        mdFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
    }

    public static IPath createGuvnorMetadataLocation(String rootPath) throws Exception {
        IPath path = new Path(rootPath + File.separator + ".guvnorinfo"); //$NON-NLS-1$
        if (!path.toFile().exists()) {
            if (!path.toFile().mkdir()) {
                throw new Exception("Could not create directory " + path.toOSString()); //$NON-NLS-1$
            }
        }
        return path;
    }

    public static File getGuvnorMetadataFile(String path, String fname) {
        return new File(path + File.separator + "." + fname); //$NON-NLS-1$
    }

    public static IFile getGuvnorControlledResource(IResource resource) throws Exception {
        IFile res = null;
        if (resource instanceof IFile) {
            Properties props = new Properties();
            props.load(((IFile)resource).getContents());
            if (props.getProperty("filename") != null) { //$NON-NLS-1$
                res = (IFile)Activator.getDefault().getWorkspace().
                                getRoot().findMember(resource.getFullPath().
                                        removeLastSegments(2).append(props.getProperty("filename"))); //$NON-NLS-1$
            }
        }
        return res;
    }

    public static boolean isGuvnorMetadata(IResource resource) {
        return resource.getFullPath().removeLastSegments(1).
                            toOSString().endsWith(".guvnorinfo"); //$NON-NLS-1$
    }

    public static void markCurrentGuvnorResource(IResource resource) throws CoreException {
        resource.setPersistentProperty(generateQualifiedName("version"), "current"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    public static void markExpiredGuvnorResource(IResource resource) throws CoreException {
        resource.setPersistentProperty(generateQualifiedName("version"), "expired"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    public static String getGuvnorResourceProperty(IResource resource) throws CoreException {
        return resource.getPersistentProperty(generateQualifiedName("version")); //$NON-NLS-1$
    }

    public static boolean isGuvnorResourceCurrent(IResource resource) throws CoreException {
        String ver = resource.getPersistentProperty(generateQualifiedName("version")); //$NON-NLS-1$
        if (ver == null) {
            return false;
        }
        return ver.equals("current"); //$NON-NLS-1$
    }

    private static QualifiedName generateQualifiedName(String attr) {
        return new QualifiedName("org.guvnor.tools", attr);  //$NON-NLS-1$
    }

    public static ResourceHistoryEntry[] parseHistoryProperties(Properties entryProps) {
        ResourceHistoryEntry[] entries = new ResourceHistoryEntry[entryProps.size()];
        Enumeration<Object> en = entryProps.keys();
        for (int i = 0; i < entryProps.size(); i++) {
            String oneRevision = (String)en.nextElement();
            String val = entryProps.getProperty(oneRevision);
            StringTokenizer tokens = new StringTokenizer(val, ","); //$NON-NLS-1$
            String verDate = null;
            String author = null;
            String comment = null;
            try {
                verDate = tokens.nextToken();
                author = tokens.nextToken();
                comment = tokens.nextToken();
            } catch (NoSuchElementException e) {
                // Don't care if some fields are missing
            }
            entries[i] = new ResourceHistoryEntry(oneRevision, verDate, author, comment);
        }
        return entries;
    }
}