package ca.concordia.cssanalyser.app;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import ca.concordia.cssanalyser.analyser.CSSAnalyser;
import ca.concordia.cssanalyser.crawler.Crawler;
import ca.concordia.cssanalyser.cssmodel.StyleSheet;
import ca.concordia.cssanalyser.cssmodel.selectors.BaseSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.GroupingSelector;
import ca.concordia.cssanalyser.cssmodel.selectors.Selector;
import ca.concordia.cssanalyser.io.CSVColumns;
import ca.concordia.cssanalyser.io.IOHelper;
import ca.concordia.cssanalyser.migration.topreprocessors.TransformationStatus;
import ca.concordia.cssanalyser.migration.topreprocessors.TransformationStatus.TransformationStatusEntry;
import ca.concordia.cssanalyser.migration.topreprocessors.less.LessHelper;
import ca.concordia.cssanalyser.migration.topreprocessors.less.LessMigrationOpportunitiesDetector;
import ca.concordia.cssanalyser.migration.topreprocessors.less.LessMixinMigrationOpportunity;
import ca.concordia.cssanalyser.migration.topreprocessors.less.LessPrinter;
import ca.concordia.cssanalyser.migration.topreprocessors.mixin.MixinDeclaration;
import ca.concordia.cssanalyser.migration.topreprocessors.mixin.MixinMigrationOpportunity;
import ca.concordia.cssanalyser.parser.CSSParser;
import ca.concordia.cssanalyser.parser.CSSParserFactory;
import ca.concordia.cssanalyser.parser.CSSParserFactory.CSSParserType;
import ca.concordia.cssanalyser.parser.ParseException;
import ca.concordia.cssanalyser.parser.less.LessCSSParser;
import ca.concordia.cssanalyser.parser.less.ModifiedLessFileSource;
import ca.concordia.cssanalyser.preprocessors.constructsinfo.LessASTQueryHandler;
import ca.concordia.cssanalyser.preprocessors.constructsinfo.LessMixinDeclaration;
import ca.concordia.cssanalyser.preprocessors.empiricalstudy.EmpiricalStudy;
import ca.concordia.cssanalyser.preprocessors.util.less.ImportInliner;
public class CSSAnalyserCLI {
public static Logger LOGGER = FileLogger.getLogger(CSSAnalyserCLI.class);
public static void main(String[] args) throws IOException {
ParametersParser params = new ParametersParser(args);
switch (params.getProgramMode()) {
case CRAWL:
doCloneRefactoringInCrawlMode(params);
break;
case FOLDER:
doCloneRefactoringInFolderMode(params);
break;
case NODOM:
doCloneRefactoringInNoDomMode(params);
break;
case DIFF:
throw new RuntimeException("Not yet implemented");
case MIXIN_MIGRATION_EMPIRICAL:
doMixinsExtractionEmpiricalStudy(params);
break;
case PREP:
doPreprocessorMigration(params);
break;
case EMPIRICAL_STUDY:
doLessEmpiricalStudy(params);
break;
case INLINE_IMPORTS:
doInlineImports(params);
break;
default:
}
}
interface ProcessLessFiles {
void process(int percentage, String website, String pathToMainFile);
}
private static void processLessFiles(ParametersParser params, ProcessLessFiles intrface) {
List<String> folders = getFolderPathsFromParameters(params);
String outfolder = params.getOutputFolderPath();
if (folders.size() > 0) {
for (String folder : folders) {
FileLogger.addFileAppender(outfolder + "/log.log", false);
List<File> listOfFilesContainingMainFiles = IOHelper.searchForFiles(folder, "mainfiles.txt", true);
for (int mainFileIndex = 0; mainFileIndex < listOfFilesContainingMainFiles.size(); mainFileIndex++) {
File mainFilesPathsFile = listOfFilesContainingMainFiles.get(mainFileIndex);
String website = mainFilesPathsFile.getParentFile().getName();
LOGGER.info(String.format("%3s%%: %s", Math.round((float)(mainFileIndex + 1)/ listOfFilesContainingMainFiles.size() * 100), mainFilesPathsFile.getAbsolutePath()));
try {
String[] mainFilesRelativePaths = IOHelper.readFileToString(mainFilesPathsFile.getAbsolutePath()).split("\n");
List<String> filesToConsider = new ArrayList<>();
for (String mainFileRelativePath : mainFilesRelativePaths) {
mainFileRelativePath = mainFileRelativePath.replace("\r", "");
if (!"".equals(mainFileRelativePath)) {
String absolutePathToMainLessFile = mainFilesPathsFile.getParentFile().getAbsolutePath() + File.separator + mainFileRelativePath;
filesToConsider.add(absolutePathToMainLessFile);
}
}
for (String pathToLessFile : filesToConsider) {
intrface.process(Math.round((float)(mainFileIndex + 1)/ listOfFilesContainingMainFiles.size() * 100), website, pathToLessFile);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
}
private static void doInlineImports(ParametersParser params) throws IOException {
String inputFile = params.getFilePath();
File file = new File(inputFile);
if (file.exists()) {
ImportInliner.replaceImports(inputFile, false);
} else {
LOGGER.error("File %s not found.", file.getCanonicalPath());
}
}
private static void doLessEmpiricalStudy(ParametersParser params) {
List<String> folders = CSSAnalyserCLI.getFolderPathsFromParameters(params);
String outfolder = params.getOutputFolderPath();
EmpiricalStudy.doEmpiricalStudy(folders, outfolder);
}
private static void doCloneRefactoringInNoDomMode(ParametersParser params) throws IOException {
CSSAnalyser cssAnalyser = null;
if (params.getInputFolderPath() != null) {
try {
cssAnalyser = new CSSAnalyser(params.getInputFolderPath());
} catch (FileNotFoundException fnfe) {
LOGGER.warn(fnfe.getMessage());
}
} else {
LOGGER.error("Please provide an input folder with --in-folder \"in/folder\"");
return;
}
cssAnalyser.analyse(params.getFPGrowthMinsup());
}
private static void doCloneRefactoringInFolderMode(ParametersParser params) throws IOException {
List<String> folders = getFolderPathsFromParameters(params);
if (folders.size() == 0) {
LOGGER.error("Please provide an input folder with --in-folder \"in/folder\" or list of folders using --folders-file \"path/to/file\".");
} else {
for (String folder : folders) {
List<File> allStatesFiles = IOHelper.searchForFiles(folder + "crawljax/doms", "html");
if (allStatesFiles.size() == 0) {
LOGGER.warn("No HTML file found in " + folder + "crawljax/doms, skipping this folder");
} else {
for (File domStateHtml : allStatesFiles) {
String stateName = domStateHtml.getName();
// Remove .html
String correspondingCSSFolderName = stateName.substring(0, stateName.length() - 5);
try {
CSSAnalyser cssAnalyser = new CSSAnalyser(domStateHtml.getAbsolutePath(), folder + "css/" + correspondingCSSFolderName);
cssAnalyser.analyse(params.getFPGrowthMinsup());
} catch (FileNotFoundException fnfe) {
LOGGER.warn(fnfe.getMessage());
}
}
}
}
}
}
private static void doCloneRefactoringInCrawlMode(ParametersParser params) throws IOException {
if (params.getOutputFolderPath() == null) {
LOGGER.error("Please provide an output folder using --out-folder \"out/folder\".");
return;
} else if (params.getUrl() == null && params.getListOfURLsToAnalyzeFilePath() == null) {
LOGGER.error("Please provide a url using --url \"http://url/to/site\" or the file containing list of urls using --urls-file \"path/to/url\"");
return;
}
List<String> urls = new ArrayList<>();
if (params.getListOfURLsToAnalyzeFilePath() != null) {
urls.addAll(params.getURLs());
} else {
urls.add(params.getUrl());
}
for (String currentUrl : urls) {
String outputFolderPath = params.getOutputFolderPath() + currentUrl.replaceFirst("http[s]?://", "").replaceFirst("file://", "").replace("/", "_").replace(":", "_") + "/";
// Make sure to configure ca.concordia.cssanalyser.crawler in Crawler class
Crawler crawler = new Crawler(currentUrl, outputFolderPath);
crawler.start();
// Get all ca.concordia.cssanalyser.dom states in outputFolder/crawljax/doms
List<File> allStatesFiles = IOHelper.searchForFiles(outputFolderPath + "crawljax/doms", "html");
for (File domStateHtml : allStatesFiles) {
String stateName = domStateHtml.getName();
// Remove .html
String correspondingCSSFolderName = stateName.substring(0, stateName.length() - 5);
try {
CSSAnalyser cssAnalyser = new CSSAnalyser(domStateHtml.getAbsolutePath(), outputFolderPath + "css/" + correspondingCSSFolderName);
cssAnalyser.analyse(params.getFPGrowthMinsup());
} catch (FileNotFoundException fnfe) {
LOGGER.warn(fnfe.getMessage());
}
}
}
}
private static void doPreprocessorMigration(ParametersParser params) {
List<String> folders = getFolderPathsFromParameters(params);
String outFolder = params.getOutputFolderPath();
LessPrinter lessPrinter = new LessPrinter();
if (folders.size() > 0) {
for (String folder : folders) {
List<File> allStatesFiles = IOHelper.searchForFiles(folder + "crawljax/doms", "html");
if (allStatesFiles.size() == 0) {
LOGGER.warn("No HTML file found in " + folder + "crawljax/doms, skipping this folder");
} else {
for (File domStateHtml : allStatesFiles) {
String stateName = domStateHtml.getName();
// Remove .html
String correspondingCSSFolderName = stateName.substring(0, stateName.length() - 5);
FileLogger.addFileAppender(folder + "css/log.log", false);
List<File> cssFiles = IOHelper.searchForFiles(folder + "css/" + correspondingCSSFolderName, "css");
for (File f : cssFiles) {
try {
CSSParser parser = CSSParserFactory.getCSSParser(CSSParserType.LESS);
StyleSheet styleSheet = parser.parseExternalCSS(f.getAbsolutePath());
LessMigrationOpportunitiesDetector preprocessorOpportunities = new LessMigrationOpportunitiesDetector(styleSheet);
List<LessMixinMigrationOpportunity> migrationOpportunities = preprocessorOpportunities.findMixinOpportunities(true);
Collections.sort(migrationOpportunities, new Comparator<LessMixinMigrationOpportunity>() {
@Override
public int compare(LessMixinMigrationOpportunity o1, LessMixinMigrationOpportunity o2) {
if (o1.getRank() == o2.getRank()) {
return 1;
}
return Double.compare(o1.getRank(), o2.getRank());
}
});
int i = 0;
for (MixinMigrationOpportunity<com.github.sommeri.less4j.core.ast.StyleSheet> migrationOpportunity : migrationOpportunities) {
boolean preservesPresentation = migrationOpportunity.preservesPresentation().isOK();
if (!preservesPresentation) {
LOGGER.warn("The following migration opportunity do not preserve the presentation:");
}
String path = outFolder + f.getName() + "migrated" + ++i + ".less";
IOHelper.writeStringToFile(lessPrinter.getString(migrationOpportunity.apply()), path);
LOGGER.info("Created Mixin {}, new file has been written to {}", migrationOpportunity.getMixinName(), path);
}
}
catch (ParseException e) {
LOGGER.warn("Parse exception in parsing " + f.getAbsolutePath());
}
}
}
}
}
} else if (null != params.getFilePath() && !"".equals(params.getFilePath())) {
try {
CSSParser parser = CSSParserFactory.getCSSParser(CSSParserType.LESS);
StyleSheet styleSheet = parser.parseExternalCSS(params.getFilePath());
LessMigrationOpportunitiesDetector preprocessorOpportunities = new LessMigrationOpportunitiesDetector(styleSheet);
Iterable<LessMixinMigrationOpportunity> refactoringOpportunities = preprocessorOpportunities.findMixinOpportunities(true);
System.out.println(refactoringOpportunities);
} catch (ParseException e) {
e.printStackTrace();
}
}
else
LOGGER.error("No CSS file is provided.");
}
private static void doMixinsExtractionEmpiricalStudy(ParametersParser params) throws IOException {
String outfolder = params.getOutputFolderPath();
int maxDeclarations = params.getMaxDeclarations();
int maxParameters = params.getMaxParameters();
int maxCalls = params.getMaxCalls();
LOGGER.info("Max Mixin calls {}, Max Declarations {}, Max Parameters {}", maxCalls, maxDeclarations, maxParameters);
String opportunitiesCsvOutputPath = outfolder + String.format("/migrationOpportunities[maxDecls%s, maxParams%s, maxCalls%s].csv", maxDeclarations, maxParameters, maxCalls);
CSVColumns fileColumns = new CSVColumns("WebSite", "File", "Parameters", "Declarations", "DeclarationsUsingParams",
"CrossBrowserDeclarations", "NonCrossBrowserDeclarations", "UniqueParametersUsedInMoreThanOneKindOfDeclaration",
"DeclarationsHavingOnlyHardCoded", "ParametersReusedInVendorSpecific", "VendorSpecificSharingParam",
/*"GlobalVarsAccessed",*/ "NameOfTheMappedMixinName", "MappedMixinFile", /*"PreservesPresentation",*/
"Support", "ExactProperties", "ExactSelectors", "NumbefOfInvolvedSelectors", "NumberOfDependenciesInMixin", "NumberOfDependenciesAffectingMixinCallPosition");
IOHelper.writeStringToFile(fileColumns.getHeader(true), opportunitiesCsvOutputPath);
String cssFilesCSVOutputPath = outfolder + String.format("/cssFiles[maxDecls%s, maxParams%s, maxCalls%s].csv", maxDeclarations, maxParameters, maxCalls);
CSVColumns cssFileColumns = new CSVColumns("WebSite", "File", "MixinsToConsider", "Selectors", "Declarations", "MigrationOpportunities", "NonOverlapping");
IOHelper.writeStringToFile(cssFileColumns.getHeader(true), cssFilesCSVOutputPath);
String mixinsToConsiderCSVPath = outfolder + "/mixinsToConsider.csv";
CSVColumns mixinsCSVColumns = new CSVColumns("WebSite", "File", "MixinName", "Parameters", "Declarations", "DeclarationsUsingParams",
"CrossBrowserDeclarations", "NonCrossBrowserDeclarations", "UniqueParametersUsedInMoreThanOneKindOfDeclaration",
"DeclarationsHavingOnlyHardCoded", "ParametersReusedInVendorSpecific", "VendorSpecificSharingParam",
"GlobalVarsAccessed", "MixinCalls");
IOHelper.writeStringToFile(mixinsCSVColumns.getHeader(true), mixinsToConsiderCSVPath, false);
String differencesPath = outfolder + String.format("/differences.txt[maxDecls%s, maxParams%s, maxCalls%s].csv", maxDeclarations, maxParameters, maxCalls);
processLessFiles(params, (percentage, website, pathToLessFile) -> {
LOGGER.info(String.format("%3s%%: Parsing %s", percentage, pathToLessFile));
try {
// Get the less style sheet
com.github.sommeri.less4j.core.ast.StyleSheet styleSheet = LessCSSParser.getLessStyleSheet(new ModifiedLessFileSource(new File(pathToLessFile)));
LOGGER.info(String.format("%3s%%: Finding mixins in %s", percentage, pathToLessFile));
LessASTQueryHandler lessASTQueryHandler = new LessASTQueryHandler(styleSheet);
Map<LessMixinDeclaration, Set<Selector>> mixinCallsMap = lessASTQueryHandler.getMixinDeclarationsAndSelectorsTheyWereCalledIn();
List<LessMixinDeclaration> mixinsToConsider = mixinCallsMap.keySet().stream()
.filter(mixinDeclaration -> mixinDeclaration.getPropertiesAtTheDeepestLevel(false).size() > 0 && mixinCallsMap.get(mixinDeclaration).size() >= 2)
.collect(Collectors.toList());
if (mixinsToConsider.size() == 0) {
LOGGER.warn("No mixin found in {} that was called more than once", website);
} else {
int numberOfMixinsToConsider = mixinsToConsider.size();
writeMixinsToFile(mixinsToConsiderCSVPath, website, mixinCallsMap, mixinsCSVColumns);
LOGGER.info(String.format("%3s%%: Compiling %s", percentage, pathToLessFile));
// Compile the less style sheet to CSS
StyleSheet compiled = LessHelper.compileLESSStyleSheet(styleSheet);
/*
* Format the style sheet.
* This is really important to keep the location information consistent,
* as we will use the toString of the StyleSheet object
* in order to re-parse the style sheet in the later phases.
*/
LOGGER.info(String.format("%3s%%: Parsing the resuling style sheet compiled from %s", percentage, pathToLessFile));
compiled = CSSParserFactory.getCSSParser(CSSParserType.LESS).parseCSSString(compiled.toString());
LOGGER.info(String.format("%3s%%: Getting migration opportunities for %s", percentage, pathToLessFile));
LessMigrationOpportunitiesDetector preprocessorOpportunityDetector = new LessMigrationOpportunitiesDetector(compiled);
// Get mixin opportunities, without subsumed
List<LessMixinMigrationOpportunity> migrationOpportunities = preprocessorOpportunityDetector.findMixinOpportunities(true);
if (maxDeclarations > 1) {
migrationOpportunities = migrationOpportunities.stream()
.filter(migrationOpportunity -> ((Collection<MixinDeclaration>)migrationOpportunity.getAllMixinDeclarations()).size() <= maxDeclarations)
.collect(Collectors.toList());
}
if (maxParameters >= 0) {
migrationOpportunities = migrationOpportunities.stream()
.filter(migrationOpportunity -> migrationOpportunity.getNumberOfParameters() <= maxParameters)
.collect(Collectors.toList());
}
if (maxCalls > 2) {
migrationOpportunities = migrationOpportunities.stream()
.filter(migrationOpportunity -> ((Collection<Selector>)migrationOpportunity.getInvolvedSelectors()).size() <= maxCalls)
.collect(Collectors.toList());
}
int numberOfMigrationOpportunities = migrationOpportunities.size();
int numberOfSelectors = ((Set<Selector>)compiled.getAllSelectors()).size();
int numberOfDeclarations = (compiled.getAllDeclarations()).size();
LOGGER.info(String.format("Found %s migration opportunities", numberOfMigrationOpportunities)) ;
Set<LessMixinDeclaration> foundRealMixins = new HashSet<>();
if (maxParameters >= 0) {
numberOfMigrationOpportunities = 0;
}
int numberOfNonOverlappingOpportunities = 0;
List<Set<String>> alreadySeenProperties = new ArrayList<>();
List<Set<Selector>> alreadySeenSelectors = new ArrayList<>();
for (LessMixinMigrationOpportunity migrationOpportunity : migrationOpportunities) {
if (maxParameters >= 0 && migrationOpportunity.getNumberOfParameters() <= maxParameters) {
numberOfMigrationOpportunities++;
}
Set<String> propertiesInOpportunity = migrationOpportunity.getPropertiesAtTheDeepestLevel();
Set<Selector> involvedSelectors = (Set<Selector>)migrationOpportunity.getInvolvedSelectors();
boolean hasOverlap = false;
for (int i = 0; i < alreadySeenProperties.size(); i++) {
Set<String> properties = new HashSet<>(propertiesInOpportunity);
Set<Selector> selectors = new HashSet<>(involvedSelectors);
properties.retainAll(alreadySeenProperties.get(i));
selectors.retainAll(alreadySeenSelectors.get(i));
if (properties.size() > 0 && selectors.size() > 0) {
hasOverlap = true;
break;
}
}
if (!hasOverlap) {
numberOfNonOverlappingOpportunities++;
}
alreadySeenProperties.add(propertiesInOpportunity);
alreadySeenSelectors.add(involvedSelectors);
String mixinMappedToName = "";
String mixinMappedToFile = "";
boolean exactProperties = false;
boolean exactSelectors = false;
for (LessMixinDeclaration realLessMixinDeclaration : mixinsToConsider) {
exactProperties = false;
exactSelectors = false;
Set<String> propertiesInRealMixin = realLessMixinDeclaration.getPropertiesAtTheDeepestLevel(false);
if (propertiesInOpportunity.containsAll(propertiesInRealMixin)) {
Set<BaseSelector> realMixinCalledInSelectors = getBaseSelectorsFromSelectors(mixinCallsMap.get(realLessMixinDeclaration));
Set<BaseSelector> opportunityCalledInSelectors = getBaseSelectorsFromSelectors(migrationOpportunity.getInvolvedSelectors());
Set<Selector> selectorsIntersection = new HashSet<>();
for (Selector realMixinCalledIn : realMixinCalledInSelectors) {
boolean selectorFound = false;
for (Selector selectorInvolvedForOpportunity : opportunityCalledInSelectors) {
if (!selectorsIntersection.contains(selectorInvolvedForOpportunity) &&
realMixinCalledIn.selectorEquals(selectorInvolvedForOpportunity, false)) {
selectorsIntersection.add(selectorInvolvedForOpportunity);
selectorFound = true;
break;
}
if (selectorFound) {
break;
}
}
}
if (selectorsIntersection.size() == realMixinCalledInSelectors.size()) {
exactProperties = propertiesInRealMixin.equals(propertiesInOpportunity);
exactSelectors = selectorsIntersection.size() == involvedSelectors.size();
mixinMappedToName = realLessMixinDeclaration.toString();
mixinMappedToFile = realLessMixinDeclaration.getStyleSheetPath();
foundRealMixins.add(realLessMixinDeclaration);
}
}
}
TransformationStatus transformationStatus = migrationOpportunity.preservesPresentation();
if (!transformationStatus.isOK()) {
StringBuilder builder = new StringBuilder();
builder.append(pathToLessFile).append(System.lineSeparator());
builder.append(migrationOpportunity.getInvolvedSelectors()).append(System.lineSeparator());
builder.append(migrationOpportunity.toString()).append(System.lineSeparator());
List<TransformationStatusEntry> statusEntries = transformationStatus.getStatusEntries();
for (TransformationStatusEntry entry : statusEntries) {
builder.append(entry.toString()).append(System.lineSeparator());
}
builder.append("-------------").append(System.lineSeparator()).append(System.lineSeparator());
IOHelper.writeStringToFile(builder.toString(), outfolder + "/notpreserving.txt", true);
}
String row = String.format(fileColumns.getRowFormat(true),
website,
pathToLessFile,
migrationOpportunity.getNumberOfParameters(),
migrationOpportunity.getNumberOfMixinDeclarations(),
migrationOpportunity.getNumberOfDeclarationsUsingParameters(),
migrationOpportunity.getNumberOfUniqueCrossBrowserDeclarations(),
migrationOpportunity.getNumberOfNonCrossBrowserDeclarations(),
migrationOpportunity.getNumberOfUniqueParametersUsedInMoreThanOneKindOfDeclaration(),
migrationOpportunity.getNumberOfDeclarationsHavingOnlyHardCodedValues(),
migrationOpportunity.getNumberOfUniqueParametersUsedInVendorSpecific(),
migrationOpportunity.getNumberOfVendorSpecificSharingParameter(),
/*migrationOpportunity.getNumberOfVariablesOutOfScopeAccessed(),*/
mixinMappedToName,
mixinMappedToFile,
/*preservesPresentation,*/
migrationOpportunity.getRank(),
exactProperties,
exactSelectors,
((Set<Selector>)migrationOpportunity.getInvolvedSelectors()).size(),
migrationOpportunity.getNumberOfIntraSelectorDependenciesInMixin(),
migrationOpportunity.getNumberOfIntraSelectorDependenciesAffectingMixinCallPosition()
);
IOHelper.writeStringToFile(row.replace("#", "\\#"), opportunitiesCsvOutputPath, true);
}
mixinsToConsider.removeAll(foundRealMixins);
if (mixinsToConsider.size() > 0) {
IOHelper.writeStringToFile(pathToLessFile + ":" + mixinsToConsider.toString() + "\n", differencesPath, true);
}
String cssRow = String.format(cssFileColumns.getRowFormat(true),
website,
pathToLessFile,
numberOfMixinsToConsider,
numberOfSelectors,
numberOfDeclarations,
numberOfMigrationOpportunities,
numberOfNonOverlappingOpportunities);
IOHelper.writeStringToFile(cssRow.replace("#", "\\#"), cssFilesCSVOutputPath, true);
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
private static void writeMixinsToFile(String path, String website, Map<LessMixinDeclaration, Set<Selector>> mixinsToConsider, CSVColumns columns) {
for (LessMixinDeclaration mixinDeclarationInfo : mixinsToConsider.keySet()) {
Set<Selector> mixinCalls = mixinsToConsider.get(mixinDeclarationInfo);
if (mixinCalls.size() >= 2 && mixinDeclarationInfo.getPropertiesAtTheDeepestLevel(false).size() > 0) {
IOHelper.writeStringToFile(
String.format(columns.getRowFormat(true),
website,
mixinDeclarationInfo.getStyleSheetPath(),
mixinDeclarationInfo.getMixinName(),
mixinDeclarationInfo.getNumberOfParams(),
mixinDeclarationInfo.getNumberOfDeclarations(),
mixinDeclarationInfo.getNumberOfDeclarationsUsingParameters(),
mixinDeclarationInfo.getNumberOfUniqueCrossBrowserDeclarations(),
mixinDeclarationInfo.getNumberOfNonCrossBrowserDeclarations(),
mixinDeclarationInfo.getNumberOfUniqueParametersUsedInMoreThanOneKindOfDeclaration(),
mixinDeclarationInfo.getNumberOfDeclarationsHavingOnlyHardCodedValues(),
mixinDeclarationInfo.getNumberOfUniqueParametersUsedInVendorSpecific(),
mixinDeclarationInfo.getNumberOfVendorSpecificSharingParameter(),
mixinDeclarationInfo.getNumberOfVariablesOutOfScopeAccessed(),
mixinCalls.size()
)
, path, true);
}
}
}
private static Set<BaseSelector> getBaseSelectorsFromSelectors(Iterable<Selector> setOfSelectors) {
Set<BaseSelector> baseSelectorsToReturn = new HashSet<>();
for (Selector selector : setOfSelectors) {
if (selector instanceof GroupingSelector) {
baseSelectorsToReturn.addAll((Set<BaseSelector>)((GroupingSelector)selector).getBaseSelectors());
} else if (selector instanceof BaseSelector) {
baseSelectorsToReturn.add((BaseSelector)selector);
}
}
return baseSelectorsToReturn;
}
private static List<String> getFolderPathsFromParameters(ParametersParser params) {
List<String> folders = new ArrayList<>();
if (params.getInputFolderPath() != null)
folders.add(params.getInputFolderPath());
else if (params.getListOfFoldersPathsToBeAnayzedFile() != null) {
folders.addAll(params.getFoldersListToBeAnalyzed());
} else {
return new ArrayList<>();
}
return folders;
}
}