<commit_before>package ca.concordia.cssanalyser.parser.less;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.NotImplementedException;
import org.slf4j.Logger;

import com.github.sommeri.less4j.LessSource;
import com.github.sommeri.less4j.LessSource.FileSource;
import com.github.sommeri.less4j.LessSource.URLSource;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.AnonymousExpression;
import com.github.sommeri.less4j.core.ast.BinaryExpression;
import com.github.sommeri.less4j.core.ast.ColorExpression;
import com.github.sommeri.less4j.core.ast.ColorExpression.ColorWithAlphaExpression;
import com.github.sommeri.less4j.core.ast.CssClass;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.FixedMediaExpression;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.ast.IdSelector;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.InterpolableName;
import com.github.sommeri.less4j.core.ast.InterpolatedMediaExpression;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.ListExpressionOperator.Operator;
import com.github.sommeri.less4j.core.ast.MediaExpression;
import com.github.sommeri.less4j.core.ast.NamedColorExpression;
import com.github.sommeri.less4j.core.ast.NamedColorWithAlphaExpression;
import com.github.sommeri.less4j.core.ast.NamedExpression;
import com.github.sommeri.less4j.core.ast.NestedSelectorAppender;
import com.github.sommeri.less4j.core.ast.Nth;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.SelectorAttribute;
import com.github.sommeri.less4j.core.ast.SelectorPart;

import ca.concordia.cssanalyser.app.FileLogger;
import ca.concordia.cssanalyser.cssmodel.LocationInfo;
import ca.concordia.cssanalyser.cssmodel.StyleSheet;
import ca.concordia.cssanalyser.cssmodel.declaration.Declaration;
import ca.concordia.cssanalyser.cssmodel.declaration.DeclarationFactory;
import ca.concordia.cssanalyser.cssmodel.declaration.value.DeclarationValue;
import ca.concordia.cssanalyser.cssmodel.declaration.value.DeclarationValueFactory;
import ca.concordia.cssanalyser.cssmodel.declaration.value.ValueType;
import ca.concordia.cssanalyser.cssmodel.media.MediaFeatureExpression;
import ca.concordia.cssanalyser.cssmodel.media.MediaQuery;
import ca.concordia.cssanalyser.cssmodel.media.MediaQuery.MediaQueryPrefix;
import ca.concordia.cssanalyser.cssmodel.media.MediaQueryList;
import ca.concordia.cssanalyser.cssmodel.selectors.AdjacentSiblingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.BaseSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.ChildSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.DescendantSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.GroupingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.NegationPseudoClass;
import ca.concordia.cssanalyser.cssmodel.selectors.PseudoClass;
import ca.concordia.cssanalyser.cssmodel.selectors.PseudoElement;
import ca.concordia.cssanalyser.cssmodel.selectors.Selector;
import ca.concordia.cssanalyser.cssmodel.selectors.SiblingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.SimpleSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.conditions.SelectorCondition;
import ca.concordia.cssanalyser.cssmodel.selectors.conditions.SelectorConditionType;
import ca.concordia.cssanalyser.migration.topreprocessors.less.LessPreprocessorNodeFinder;
import ca.concordia.cssanalyser.parser.ParseException;

/**
 * Adapts a Less StyleSheet object to a CSSAnalyser StyleSheet object 
 * @author <NAME>
 *
 */
public class LessStyleSheetAdapter {
	
	private static final String IMPORTANT = "!important";

	private static Logger LOGGER = FileLogger.getLogger(LessStyleSheetAdapter.class);
	
	private final ASTCssNode lessStyleSheet;
	
	public LessStyleSheetAdapter(ASTCssNode lessStyleSheet) {
		this.lessStyleSheet = lessStyleSheet;
	}

	private void adapt(StyleSheet ourStyleSheet) {
		
 		List<? extends ASTCssNode> nodes = lessStyleSheet.getChilds();
		addSelectorsToStyleSheetFromLessASTNodes(ourStyleSheet, nodes);
		
	}

	private void addSelectorsToStyleSheetFromLessASTNodes(StyleSheet styleSheet, List<? extends ASTCssNode> nodes) {
		addSelectorsToStyleSheetFromLessASTNodes(styleSheet, nodes, null);
	}
	
	private void addSelectorsToStyleSheetFromLessASTNodes(StyleSheet styleSheet, List<? extends ASTCssNode> nodes, MediaQueryList mediaQueries) {
		
		for (ASTCssNode node : nodes) {
 			
 			if (node instanceof RuleSet) {
 				
 				RuleSet ruleSetNode = (RuleSet)node;
 				Selector selector = getSelectorFromLessRuleSet(ruleSetNode);
 				if (mediaQueries != null)
 					selector.addMediaQueryList(mediaQueries);
 				styleSheet.addSelector(selector);
 				
 			} else if (node instanceof com.github.sommeri.less4j.core.ast.Media) {
 				
 				com.github.sommeri.less4j.core.ast.Media lessMedia = (com.github.sommeri.less4j.core.ast.Media)node;
 				MediaQueryList mediaQueryList = getMediaQueryListFromLessMedia(lessMedia);
 				addSelectorsToStyleSheetFromLessASTNodes(styleSheet, lessMedia.getBody().getMembers(), mediaQueryList);
 				
 			}
 	
		}
		
	}
		
	private MediaQueryList getMediaQueryListFromLessMedia(com.github.sommeri.less4j.core.ast.Media lessMedia) {
		
		// x and y, z and t is a list of media queries containing two media queries
		MediaQueryList mediaQueryList = new MediaQueryList();
		
		for (com.github.sommeri.less4j.core.ast.MediaQuery lessMediaQuery : lessMedia.getMediums()) {
			MediaQueryPrefix prefix = null;
			String mediumType = "";
			if (lessMediaQuery.getMedium() != null) {
				if (lessMediaQuery.getMedium().getModifier() != null) {
					switch (lessMediaQuery.getMedium().getModifier().getModifier()) {
					case NOT:
						prefix = MediaQueryPrefix.NOT;
						break;
					case ONLY:
						prefix = MediaQueryPrefix.ONLY;
						break;
					case NONE:
					default:
						prefix = null;
						break;
					}
				}
				
				if (lessMediaQuery.getMedium().getMediumType() != null)
					mediumType = lessMediaQuery.getMedium().getMediumType().getName();
			}
			MediaQuery query = new MediaQuery(prefix, mediumType);
			for (MediaExpression lessMediaExpression : lessMediaQuery.getExpressions()) {
				try {
					String feature = "";
					String expression = "";
					if (lessMediaExpression instanceof FixedMediaExpression) {
						FixedMediaExpression fixedMediaExpression = (FixedMediaExpression)lessMediaExpression;
						if (fixedMediaExpression.getExpression() != null) {
							// Lets re-use a method that we already have, in an ugly manner
							List<DeclarationValue> values = getListOfDeclarationValuesFromLessExpression("fake",  fixedMediaExpression.getExpression());
							for (DeclarationValue value : values)
								expression += value;
						}
						feature = fixedMediaExpression.getFeature().getFeature();
					} else if (lessMediaExpression instanceof InterpolatedMediaExpression) {
						throw new RuntimeException("What is " + lessMediaExpression);
					}
					MediaFeatureExpression featureExpression = new MediaFeatureExpression(feature, expression);
					featureExpression.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMediaExpression));
					query.addMediaFeatureExpression(featureExpression);
				} catch (ParseException ex) {
					LOGGER.warn(String.format("Ignored media expression %s", lessMediaExpression.toString()));
				}
			}
			query.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMediaQuery));
			mediaQueryList.addMediaQuery(query);
		}
		
		mediaQueryList.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMedia));
		
		return mediaQueryList;
	}
	
	public Selector getSelectorFromLessRuleSet(RuleSet ruleSetNode) {
		Selector selector = null;
		
		if (ruleSetNode.getSelectors().size() == 1) { // One selector, this is a base selector
			
			selector = getBaseSelectorFromLessSelector(ruleSetNode.getSelectors().get(0));
			
		} else { // More than 1 selector, this is a grouping selector
			GroupingSelector grouping = new GroupingSelector();

			for (com.github.sommeri.less4j.core.ast.Selector lessSelector : ruleSetNode.getSelectors()) {
				grouping.add(getBaseSelectorFromLessSelector(lessSelector));					
			}
			
			selector = grouping;
		}
		
		selector.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(ruleSetNode));

		// Handle declarations<commit_msg>Added parent grouping selector to its base selectors<commit_after>package ca.concordia.cssanalyser.parser.less;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.NotImplementedException;
import org.slf4j.Logger;

import com.github.sommeri.less4j.LessSource;
import com.github.sommeri.less4j.LessSource.FileSource;
import com.github.sommeri.less4j.LessSource.URLSource;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.AnonymousExpression;
import com.github.sommeri.less4j.core.ast.BinaryExpression;
import com.github.sommeri.less4j.core.ast.ColorExpression;
import com.github.sommeri.less4j.core.ast.ColorExpression.ColorWithAlphaExpression;
import com.github.sommeri.less4j.core.ast.CssClass;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.FixedMediaExpression;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.ast.IdSelector;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.InterpolableName;
import com.github.sommeri.less4j.core.ast.InterpolatedMediaExpression;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.ListExpressionOperator.Operator;
import com.github.sommeri.less4j.core.ast.MediaExpression;
import com.github.sommeri.less4j.core.ast.NamedColorExpression;
import com.github.sommeri.less4j.core.ast.NamedColorWithAlphaExpression;
import com.github.sommeri.less4j.core.ast.NamedExpression;
import com.github.sommeri.less4j.core.ast.NestedSelectorAppender;
import com.github.sommeri.less4j.core.ast.Nth;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.SelectorAttribute;
import com.github.sommeri.less4j.core.ast.SelectorPart;

import ca.concordia.cssanalyser.app.FileLogger;
import ca.concordia.cssanalyser.cssmodel.LocationInfo;
import ca.concordia.cssanalyser.cssmodel.StyleSheet;
import ca.concordia.cssanalyser.cssmodel.declaration.Declaration;
import ca.concordia.cssanalyser.cssmodel.declaration.DeclarationFactory;
import ca.concordia.cssanalyser.cssmodel.declaration.value.DeclarationValue;
import ca.concordia.cssanalyser.cssmodel.declaration.value.DeclarationValueFactory;
import ca.concordia.cssanalyser.cssmodel.declaration.value.ValueType;
import ca.concordia.cssanalyser.cssmodel.media.MediaFeatureExpression;
import ca.concordia.cssanalyser.cssmodel.media.MediaQuery;
import ca.concordia.cssanalyser.cssmodel.media.MediaQuery.MediaQueryPrefix;
import ca.concordia.cssanalyser.cssmodel.media.MediaQueryList;
import ca.concordia.cssanalyser.cssmodel.selectors.AdjacentSiblingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.BaseSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.ChildSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.DescendantSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.GroupingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.NegationPseudoClass;
import ca.concordia.cssanalyser.cssmodel.selectors.PseudoClass;
import ca.concordia.cssanalyser.cssmodel.selectors.PseudoElement;
import ca.concordia.cssanalyser.cssmodel.selectors.Selector;
import ca.concordia.cssanalyser.cssmodel.selectors.SiblingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.SimpleSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.conditions.SelectorCondition;
import ca.concordia.cssanalyser.cssmodel.selectors.conditions.SelectorConditionType;
import ca.concordia.cssanalyser.migration.topreprocessors.less.LessPreprocessorNodeFinder;
import ca.concordia.cssanalyser.parser.ParseException;

/**
 * Adapts a Less StyleSheet object to a CSSAnalyser StyleSheet object 
 * @author <NAME>
 *
 */
public class LessStyleSheetAdapter {
	
	private static final String IMPORTANT = "!important";

	private static Logger LOGGER = FileLogger.getLogger(LessStyleSheetAdapter.class);
	
	private final ASTCssNode lessStyleSheet;
	
	public LessStyleSheetAdapter(ASTCssNode lessStyleSheet) {
		this.lessStyleSheet = lessStyleSheet;
	}

	private void adapt(StyleSheet ourStyleSheet) {
		
 		List<? extends ASTCssNode> nodes = lessStyleSheet.getChilds();
		addSelectorsToStyleSheetFromLessASTNodes(ourStyleSheet, nodes);
		
	}

	private void addSelectorsToStyleSheetFromLessASTNodes(StyleSheet styleSheet, List<? extends ASTCssNode> nodes) {
		addSelectorsToStyleSheetFromLessASTNodes(styleSheet, nodes, null);
	}
	
	private void addSelectorsToStyleSheetFromLessASTNodes(StyleSheet styleSheet, List<? extends ASTCssNode> nodes, MediaQueryList mediaQueries) {
		
		for (ASTCssNode node : nodes) {
 			
 			if (node instanceof RuleSet) {
 				
 				RuleSet ruleSetNode = (RuleSet)node;
 				Selector selector = getSelectorFromLessRuleSet(ruleSetNode);
 				if (mediaQueries != null)
 					selector.addMediaQueryList(mediaQueries);
 				styleSheet.addSelector(selector);
 				
 			} else if (node instanceof com.github.sommeri.less4j.core.ast.Media) {
 				
 				com.github.sommeri.less4j.core.ast.Media lessMedia = (com.github.sommeri.less4j.core.ast.Media)node;
 				MediaQueryList mediaQueryList = getMediaQueryListFromLessMedia(lessMedia);
 				addSelectorsToStyleSheetFromLessASTNodes(styleSheet, lessMedia.getBody().getMembers(), mediaQueryList);
 				
 			}
 	
		}
		
	}
		
	private MediaQueryList getMediaQueryListFromLessMedia(com.github.sommeri.less4j.core.ast.Media lessMedia) {
		
		// x and y, z and t is a list of media queries containing two media queries
		MediaQueryList mediaQueryList = new MediaQueryList();
		
		for (com.github.sommeri.less4j.core.ast.MediaQuery lessMediaQuery : lessMedia.getMediums()) {
			MediaQueryPrefix prefix = null;
			String mediumType = "";
			if (lessMediaQuery.getMedium() != null) {
				if (lessMediaQuery.getMedium().getModifier() != null) {
					switch (lessMediaQuery.getMedium().getModifier().getModifier()) {
					case NOT:
						prefix = MediaQueryPrefix.NOT;
						break;
					case ONLY:
						prefix = MediaQueryPrefix.ONLY;
						break;
					case NONE:
					default:
						prefix = null;
						break;
					}
				}
				
				if (lessMediaQuery.getMedium().getMediumType() != null)
					mediumType = lessMediaQuery.getMedium().getMediumType().getName();
			}
			MediaQuery query = new MediaQuery(prefix, mediumType);
			for (MediaExpression lessMediaExpression : lessMediaQuery.getExpressions()) {
				try {
					String feature = "";
					String expression = "";
					if (lessMediaExpression instanceof FixedMediaExpression) {
						FixedMediaExpression fixedMediaExpression = (FixedMediaExpression)lessMediaExpression;
						if (fixedMediaExpression.getExpression() != null) {
							// Lets re-use a method that we already have, in an ugly manner
							List<DeclarationValue> values = getListOfDeclarationValuesFromLessExpression("fake",  fixedMediaExpression.getExpression());
							for (DeclarationValue value : values)
								expression += value;
						}
						feature = fixedMediaExpression.getFeature().getFeature();
					} else if (lessMediaExpression instanceof InterpolatedMediaExpression) {
						throw new RuntimeException("What is " + lessMediaExpression);
					}
					MediaFeatureExpression featureExpression = new MediaFeatureExpression(feature, expression);
					featureExpression.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMediaExpression));
					query.addMediaFeatureExpression(featureExpression);
				} catch (ParseException ex) {
					LOGGER.warn(String.format("Ignored media expression %s", lessMediaExpression.toString()));
				}
			}
			query.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMediaQuery));
			mediaQueryList.addMediaQuery(query);
		}
		
		mediaQueryList.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMedia));
		
		return mediaQueryList;
	}
	
	public Selector getSelectorFromLessRuleSet(RuleSet ruleSetNode) {
		Selector selector = null;
		
		if (ruleSetNode.getSelectors().size() == 1) { // One selector, this is a base selector
			
			selector = getBaseSelectorFromLessSelector(ruleSetNode.getSelectors().get(0));
			
		} else { // More than 1 selector, this is a grouping selector
			GroupingSelector grouping = new GroupingSelector();

			for (com.github.sommeri.less4j.core.ast.Selector lessSelector : ruleSetNode.getSelectors()) {
				BaseSelector baseSelectorFromLessSelector = getBaseSelectorFromLessSelector(lessSelector);
				grouping.add(baseSelectorFromLessSelector);
				baseSelectorFromLessSelector.setParentGroupSelector(grouping);
			}
			
			selector = grouping;
		}
		
		selector.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(ruleSetNode));

		// Handle declarations
{'fraction_non_alphanumeric': 0.09515813042261405, 'fraction_numerical': 0.004478029666946544, 'mean_word_length': 5.36934046345811, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '2564384', 'n_tokens_mistral': 6107, 'n_tokens_neox': 5491, 'n_words': 828}