/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.source.formatter; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.tools.ArgumentsUtil; import com.liferay.portal.tools.GitException; import com.liferay.portal.tools.GitUtil; import com.liferay.portal.tools.ToolsUtil; import com.liferay.source.formatter.util.SourceFormatterUtil; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * @author Hugo Huijser */ public class SourceFormatter { public static void main(String[] args) throws Exception { Map<String, String> arguments = ArgumentsUtil.parseArguments(args); try { SourceFormatterArgs sourceFormatterArgs = new SourceFormatterArgs(); boolean autoFix = ArgumentsUtil.getBoolean( arguments, "source.auto.fix", SourceFormatterArgs.AUTO_FIX); sourceFormatterArgs.setAutoFix(autoFix); String baseDirName = ArgumentsUtil.getString( arguments, "source.base.dir", SourceFormatterArgs.BASE_DIR_NAME); sourceFormatterArgs.setBaseDirName(baseDirName); boolean formatCurrentBranch = ArgumentsUtil.getBoolean( arguments, "format.current.branch", SourceFormatterArgs.FORMAT_CURRENT_BRANCH); sourceFormatterArgs.setFormatCurrentBranch(formatCurrentBranch); boolean formatLatestAuthor = ArgumentsUtil.getBoolean( arguments, "format.latest.author", SourceFormatterArgs.FORMAT_LATEST_AUTHOR); sourceFormatterArgs.setFormatLatestAuthor(formatLatestAuthor); boolean formatLocalChanges = ArgumentsUtil.getBoolean( arguments, "format.local.changes", SourceFormatterArgs.FORMAT_LOCAL_CHANGES); sourceFormatterArgs.setFormatLocalChanges(formatLocalChanges); if (formatCurrentBranch) { String gitWorkingBranchName = ArgumentsUtil.getString( arguments, "git.working.branch.name", SourceFormatterArgs.GIT_WORKING_BRANCH_NAME); sourceFormatterArgs.setGitWorkingBranchName( gitWorkingBranchName); sourceFormatterArgs.setRecentChangesFileNames( GitUtil.getCurrentBranchFileNames( baseDirName, gitWorkingBranchName)); } else if (formatLatestAuthor) { sourceFormatterArgs.setRecentChangesFileNames( GitUtil.getLatestAuthorFileNames(baseDirName)); } else if (formatLocalChanges) { sourceFormatterArgs.setRecentChangesFileNames( GitUtil.getLocalChangesFileNames(baseDirName)); } String fileNamesString = ArgumentsUtil.getString( arguments, "source.files", StringPool.BLANK); String[] fileNames = StringUtil.split( fileNamesString, StringPool.COMMA); if (ArrayUtil.isNotEmpty(fileNames)) { sourceFormatterArgs.setFileNames(Arrays.asList(fileNames)); } else { String fileExtensionsString = ArgumentsUtil.getString( arguments, "source.file.extensions", StringPool.BLANK); String[] fileExtensions = StringUtil.split( fileExtensionsString, StringPool.COMMA); sourceFormatterArgs.setFileExtensions( Arrays.asList(fileExtensions)); } boolean includeSubrepositories = ArgumentsUtil.getBoolean( arguments, "include.subrepositories", SourceFormatterArgs.INCLUDE_SUBREPOSITORIES); sourceFormatterArgs.setIncludeSubrepositories( includeSubrepositories); int maxLineLength = ArgumentsUtil.getInteger( arguments, "max.line.length", SourceFormatterArgs.MAX_LINE_LENGTH); sourceFormatterArgs.setMaxLineLength(maxLineLength); boolean printErrors = ArgumentsUtil.getBoolean( arguments, "source.print.errors", SourceFormatterArgs.PRINT_ERRORS); sourceFormatterArgs.setPrintErrors(printErrors); int processorThreadCount = ArgumentsUtil.getInteger( arguments, "processor.thread.count", SourceFormatterArgs.PROCESSOR_THREAD_COUNT); sourceFormatterArgs.setProcessorThreadCount(processorThreadCount); boolean showDocumentation = ArgumentsUtil.getBoolean( arguments, "show.documentation", SourceFormatterArgs.SHOW_DOCUMENTATION); sourceFormatterArgs.setShowDocumentation(showDocumentation); boolean throwException = ArgumentsUtil.getBoolean( arguments, "source.throw.exception", SourceFormatterArgs.THROW_EXCEPTION); sourceFormatterArgs.setThrowException(throwException); SourceFormatter sourceFormatter = new SourceFormatter( sourceFormatterArgs); sourceFormatter.format(); } catch (GitException ge) { System.out.println(ge.getMessage()); System.exit(0); } catch (Exception e) { ArgumentsUtil.processMainException(arguments, e); } } public SourceFormatter(SourceFormatterArgs sourceFormatterArgs) { _sourceFormatterArgs = sourceFormatterArgs; } public void format() throws Exception { if (_isPortalSource()) { _populatePortalImplProperties(); _addDefaultExcludes(); _populateAllFileNames(); _populateModulesProperties(); } else { _populateProperties(); _addDefaultExcludes(); _populateAllFileNames(); } List<SourceProcessor> sourceProcessors = new ArrayList<>(); sourceProcessors.add(new BNDSourceProcessor()); sourceProcessors.add(new CSSSourceProcessor()); sourceProcessors.add(new FTLSourceProcessor()); sourceProcessors.add(new GradleSourceProcessor()); sourceProcessors.add(new GroovySourceProcessor()); sourceProcessors.add(new JavaSourceProcessor()); sourceProcessors.add(new JSONSourceProcessor()); sourceProcessors.add(new JSPSourceProcessor()); sourceProcessors.add(new JSSourceProcessor()); sourceProcessors.add(new MarkdownSourceProcessor()); sourceProcessors.add(new PropertiesSourceProcessor()); sourceProcessors.add(new SHSourceProcessor()); sourceProcessors.add(new SoySourceProcessor()); sourceProcessors.add(new SQLSourceProcessor()); sourceProcessors.add(new TLDSourceProcessor()); sourceProcessors.add(new XMLSourceProcessor()); ExecutorService executorService = Executors.newFixedThreadPool( sourceProcessors.size()); List<Future<Void>> futures = new ArrayList<>(sourceProcessors.size()); for (final SourceProcessor sourceProcessor : sourceProcessors) { Future<Void> future = executorService.submit( new Callable<Void>() { @Override public Void call() throws Exception { _runSourceProcessor(sourceProcessor); return null; } }); futures.add(future); } ExecutionException ee1 = null; for (Future<Void> future : futures) { try { future.get(); } catch (ExecutionException ee) { if (ee1 == null) { ee1 = ee; } else { ee1.addSuppressed(ee); } } } executorService.shutdown(); while (!executorService.isTerminated()) { Thread.sleep(20); } if (ee1 != null) { throw ee1; } if (_sourceFormatterArgs.isThrowException()) { if (!_sourceFormatterMessages.isEmpty()) { StringBundler sb = new StringBundler( _sourceFormatterMessages.size() * 2); for (SourceFormatterMessage sourceFormatterMessage : _sourceFormatterMessages) { sb.append(sourceFormatterMessage.toString()); sb.append("\n"); } throw new Exception(sb.toString()); } if (_firstSourceMismatchException != null) { throw _firstSourceMismatchException; } } } public List<String> getModifiedFileNames() { return _modifiedFileNames; } public SourceFormatterArgs getSourceFormatterArgs() { return _sourceFormatterArgs; } public Set<SourceFormatterMessage> getSourceFormatterMessages() { return _sourceFormatterMessages; } public SourceMismatchException getSourceMismatchException() { return _firstSourceMismatchException; } private void _addDefaultExcludes() { String excludesValue = _properties.getProperty( "source.formatter.excludes"); if (Validator.isNull(excludesValue)) { excludesValue = StringUtil.merge(_defaultExcludes); } else { excludesValue += StringPool.COMMA + StringUtil.merge(_defaultExcludes); } _properties.setProperty("source.formatter.excludes", excludesValue); } private boolean _isPortalSource() { File portalImplDir = SourceFormatterUtil.getFile( _sourceFormatterArgs.getBaseDirName(), "portal-impl", ToolsUtil.PORTAL_MAX_DIR_LEVEL); if (portalImplDir != null) { return true; } return false; } private void _populateAllFileNames() throws Exception { String excludesValue = _properties.getProperty( "source.formatter.excludes"); List<String> excludesList = ListUtil.fromString( GetterUtil.getString(excludesValue), StringPool.COMMA); _allFileNames = SourceFormatterUtil.scanForFiles( _sourceFormatterArgs.getBaseDirName(), excludesList.toArray(new String[excludesList.size()]), new String[] {"**/*.*"}, _sourceFormatterArgs.isIncludeSubrepositories()); } private void _populateModulesProperties() throws Exception { List<Properties> propertiesList = new ArrayList<>(); propertiesList.add(_properties); // Find properties files in any parent directory String parentDirName = _sourceFormatterArgs.getBaseDirName(); for (int i = 0; i < ToolsUtil.PORTAL_MAX_DIR_LEVEL; i++) { try { InputStream inputStream = new FileInputStream( parentDirName + _PROPERTIES_FILE_NAME); Properties properties = new Properties(); properties.load(inputStream); propertiesList.add(properties); } catch (FileNotFoundException fnfe) { } parentDirName += "../"; } // Find properties file in any child directory String excludesValue = _properties.getProperty( "source.formatter.excludes"); List<String> excludesList = ListUtil.fromString( GetterUtil.getString(excludesValue), StringPool.COMMA); List<String> modulePropertiesFileNames = SourceFormatterUtil.filterFileNames( _allFileNames, excludesList.toArray(new String[excludesList.size()]), new String[] {"**/" + _PROPERTIES_FILE_NAME}); for (String modulePropertiesFileName : modulePropertiesFileNames) { InputStream inputStream = new FileInputStream( modulePropertiesFileName); Properties properties = new Properties(); properties.load(inputStream); propertiesList.add(properties); } // Merge all properties files _properties = new Properties(); for (int i = 0; i < propertiesList.size(); i++) { Properties properties = propertiesList.get(i); Enumeration<String> enu = (Enumeration<String>)properties.propertyNames(); while (enu.hasMoreElements()) { String key = enu.nextElement(); String value = properties.getProperty(key); if (Validator.isNull(value)) { continue; } if (key.contains("excludes")) { String existingValue = _properties.getProperty(key); if (Validator.isNotNull(existingValue)) { value = existingValue + StringPool.COMMA + value; } _properties.put(key, value); } else if (!_properties.containsKey(key)) { _properties.put(key, value); } } } } private void _populatePortalImplProperties() throws Exception { File propertiesFile = SourceFormatterUtil.getFile( _sourceFormatterArgs.getBaseDirName(), "portal-impl/src/" + _PROPERTIES_FILE_NAME, ToolsUtil.PORTAL_MAX_DIR_LEVEL); if (propertiesFile != null) { InputStream inputStream = new FileInputStream(propertiesFile); _properties.load(inputStream); } } private void _populateProperties() throws Exception { String fileName = _PROPERTIES_FILE_NAME; for (int i = 0; i <= ToolsUtil.PLUGINS_MAX_DIR_LEVEL; i++) { try { InputStream inputStream = new FileInputStream( _sourceFormatterArgs.getBaseDirName() + fileName); _properties.load(inputStream); return; } catch (FileNotFoundException fnfe) { } fileName = "../" + fileName; } } private void _runSourceProcessor(SourceProcessor sourceProcessor) throws Exception { sourceProcessor.setAllFileNames(_allFileNames); sourceProcessor.setProperties(_properties); sourceProcessor.setSourceFormatterArgs(_sourceFormatterArgs); sourceProcessor.format(); _sourceFormatterMessages.addAll( sourceProcessor.getSourceFormatterMessages()); _modifiedFileNames.addAll(sourceProcessor.getModifiedFileNames()); if (_firstSourceMismatchException == null) { _firstSourceMismatchException = sourceProcessor.getFirstSourceMismatchException(); } } private static final String _PROPERTIES_FILE_NAME = "source-formatter.properties"; private static final List<String> _defaultExcludes = Arrays.asList( "**/.git/**", "**/.gradle/**", "**/bin/**", "**/build/**", "**/classes/**", "**/node_modules/**", "**/npm-shrinkwrap.json", "**/test-classes/**", "**/test-coverage/**", "**/test-results/**", "**/tmp/**"); private List<String> _allFileNames; private volatile SourceMismatchException _firstSourceMismatchException; private final List<String> _modifiedFileNames = new CopyOnWriteArrayList<>(); private Properties _properties = new Properties(); private final SourceFormatterArgs _sourceFormatterArgs; private final Set<SourceFormatterMessage> _sourceFormatterMessages = new ConcurrentSkipListSet<>(); }