<commit_before>/*
 * Copyright 2014, The Thymeleaf Project (http://www.thymeleaf.org/)
 * 
 * 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.thymeleaf.extras.eclipse.contentassist.autocomplete.generators;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.thymeleaf.extras.eclipse.contentassist.autocomplete.proposals.AttributeProcessorCompletionProposal;
import org.thymeleaf.extras.eclipse.dialect.cache.DialectCache;
import org.thymeleaf.extras.eclipse.dialect.xml.AttributeProcessor;
import org.thymeleaf.extras.eclipse.dialect.xml.AttributeRestrictions;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import static org.thymeleaf.extras.eclipse.contentassist.ContentAssistPlugin.findCurrentJavaProject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Proposal generator for Thymeleaf attribute processors.
 * 
 * @author <NAME>
 */
@SuppressWarnings("restriction")
public class AttributeProcessorProposalGenerator
	extends AbstractItemProposalGenerator<AttributeProcessorCompletionProposal> {

	/**
	 * Collect attribute processor suggestions.
	 * 
	 * @param node
	 * @param document
	 * @param cursorposition
	 * @return List of attribute processor suggestions.
	 * @throws BadLocationException
	 */
	@SuppressWarnings("unchecked")
	private static List<AttributeProcessorCompletionProposal> computeAttributeProcessorSuggestions(
		IDOMNode node, IStructuredDocument document, int cursorposition) throws BadLocationException {

		String pattern = findProcessorNamePattern(document, cursorposition);

		List<AttributeProcessor> processors = DialectCache.getAttributeProcessors(
				findCurrentJavaProject(), findNodeNamespaces(node), pattern);
		if (!processors.isEmpty()) {
			ArrayList<AttributeProcessorCompletionProposal> proposals =
					new ArrayList<AttributeProcessorCompletionProposal>();
			NamedNodeMap existingattributes = node.getAttributes();

			// Go through twice so that we create data-* suggestions as well
			createAttributeProcessorSuggestions(pattern, processors, existingattributes,
					node, cursorposition, proposals, false);
			createAttributeProcessorSuggestions(pattern, processors, existingattributes,
					node, cursorposition, proposals, true);

			return proposals;
		}

		return Collections.EMPTY_LIST;
	}

	/**
	 * Creates and adds attribute processor proposals for whether or not they
	 * should use the standard or data-* version.
	 * 
	 * @param pattern            The input string entered by the user so far.
	 * @param processors         List of processors that matched the pattern.
	 * @param existingattributes
	 * @param node
	 * @param cursorposition
	 * @param proposals          List of proposals to add to.
	 * @param dataattr           Use the data-* version of the processor.
	 */
	private static void createAttributeProcessorSuggestions(String pattern,
		List<AttributeProcessor> processors, NamedNodeMap existingattributes, IDOMNode node,
		int cursorposition, ArrayList<AttributeProcessorCompletionProposal> proposals,
		boolean dataattr) {

		for (AttributeProcessor processor: processors) {

			// Double check that the processor type being used this time around
			// matches the pattern
			if ((!dataattr && !processor.getFullName().startsWith(pattern)) ||
				(dataattr && !processor.getFullDataName().startsWith(pattern))) {
				continue;
			}

			AttributeProcessorCompletionProposal proposal = new AttributeProcessorCompletionProposal(
					processor, pattern.length(), cursorposition, dataattr);

			// Only include the proposal if it isn't already in the element
			if (existingattributes.getNamedItem(proposal.getDisplayString()) == null) {
				boolean restricted = false;

				// If a restriction is present, make sure it is satisfied before including the proposal
				if (processor.isSetRestrictions()) {
					AttributeRestrictions restrictions = processor.getRestrictions();

					if (restrictions.isSetTags()) {
						List<String> tags = restrictions.getTags();
						String elementname = node.getNodeName();

						for (String tag: tags) {
							if (tag.startsWith("-")) {
								if (tag.substring(1).equals(elementname)) {
									restricted = true;
									break;
								}
							}
							else if (!tag.equals(elementname)) {
								restricted = true;
								break;
							}
						}
					}

					if (restrictions.isSetAttributes()) {
						for (String attribute: restrictions.getAttributes()) {
							if (!matchAttributeRestriction(attribute, existingattributes)) {
								restricted = true;
								break;
							}
						}
					}
				}

				if (!restricted) {
					proposals.add(proposal);
				}
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */<commit_msg>fixing #68: fix tags restriction conditional<commit_after>/*
 * Copyright 2014, The Thymeleaf Project (http://www.thymeleaf.org/)
 * 
 * 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.thymeleaf.extras.eclipse.contentassist.autocomplete.generators;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.thymeleaf.extras.eclipse.contentassist.autocomplete.proposals.AttributeProcessorCompletionProposal;
import org.thymeleaf.extras.eclipse.dialect.cache.DialectCache;
import org.thymeleaf.extras.eclipse.dialect.xml.AttributeProcessor;
import org.thymeleaf.extras.eclipse.dialect.xml.AttributeRestrictions;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import static org.thymeleaf.extras.eclipse.contentassist.ContentAssistPlugin.findCurrentJavaProject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Proposal generator for Thymeleaf attribute processors.
 * 
 * @author <NAME>
 */
@SuppressWarnings("restriction")
public class AttributeProcessorProposalGenerator
	extends AbstractItemProposalGenerator<AttributeProcessorCompletionProposal> {

	/**
	 * Collect attribute processor suggestions.
	 * 
	 * @param node
	 * @param document
	 * @param cursorposition
	 * @return List of attribute processor suggestions.
	 * @throws BadLocationException
	 */
	@SuppressWarnings("unchecked")
	private static List<AttributeProcessorCompletionProposal> computeAttributeProcessorSuggestions(
		IDOMNode node, IStructuredDocument document, int cursorposition) throws BadLocationException {

		String pattern = findProcessorNamePattern(document, cursorposition);

		List<AttributeProcessor> processors = DialectCache.getAttributeProcessors(
				findCurrentJavaProject(), findNodeNamespaces(node), pattern);
		if (!processors.isEmpty()) {
			ArrayList<AttributeProcessorCompletionProposal> proposals =
					new ArrayList<AttributeProcessorCompletionProposal>();
			NamedNodeMap existingattributes = node.getAttributes();

			// Go through twice so that we create data-* suggestions as well
			createAttributeProcessorSuggestions(pattern, processors, existingattributes,
					node, cursorposition, proposals, false);
			createAttributeProcessorSuggestions(pattern, processors, existingattributes,
					node, cursorposition, proposals, true);

			return proposals;
		}

		return Collections.EMPTY_LIST;
	}

	/**
	 * Creates and adds attribute processor proposals for whether or not they
	 * should use the standard or data-* version.
	 * 
	 * @param pattern            The input string entered by the user so far.
	 * @param processors         List of processors that matched the pattern.
	 * @param existingattributes
	 * @param node
	 * @param cursorposition
	 * @param proposals          List of proposals to add to.
	 * @param dataattr           Use the data-* version of the processor.
	 */
	private static void createAttributeProcessorSuggestions(String pattern,
		List<AttributeProcessor> processors, NamedNodeMap existingattributes, IDOMNode node,
		int cursorposition, ArrayList<AttributeProcessorCompletionProposal> proposals,
		boolean dataattr) {

		for (AttributeProcessor processor: processors) {

			// Double check that the processor type being used this time around
			// matches the pattern
			if ((!dataattr && !processor.getFullName().startsWith(pattern)) ||
				(dataattr && !processor.getFullDataName().startsWith(pattern))) {
				continue;
			}

			AttributeProcessorCompletionProposal proposal = new AttributeProcessorCompletionProposal(
					processor, pattern.length(), cursorposition, dataattr);

			// Only include the proposal if it isn't already in the element
			if (existingattributes.getNamedItem(proposal.getDisplayString()) == null) {
				boolean restricted = false;

				// If a restriction is present, make sure it is satisfied before including the proposal
				if (processor.isSetRestrictions()) {
					AttributeRestrictions restrictions = processor.getRestrictions();

					if (restrictions.isSetTags()) {
						List<String> tags = restrictions.getTags();
						String elementname = node.getNodeName();

						for (String tag: tags) {
							if (tag.startsWith("-")) {
								if (tag.substring(1).equals(elementname)) {
									restricted = true;
									break;
								}
							}
							else {
								if (tag.equals(elementname)) {
									restricted = false;
									break;
								}
								restricted = true;
							}
						}
					}

					if (restrictions.isSetAttributes()) {
						for (String attribute: restrictions.getAttributes()) {
							if (!matchAttributeRestriction(attribute, existingattributes)) {
								restricted = true;
								break;
							}
						}
					}
				}

				if (!restricted) {
					proposals.add(proposal);
				}
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
{'fraction_non_alphanumeric': 0.09059873949579832, 'fraction_numerical': 0.0021008403361344537, 'mean_word_length': 4.093624609897459, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7256373', 'n_tokens_mistral': 3616, 'n_tokens_neox': 2984, 'n_words': 811}