/** * 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.gradle.plugins.defaults.tasks; import com.liferay.gradle.plugins.defaults.internal.util.GradleUtil; import groovy.lang.Closure; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.gradle.api.DefaultTask; import org.gradle.api.Project; import org.gradle.api.file.FileCollection; import org.gradle.api.logging.Logger; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; import org.gradle.util.GUtil; /** * @author Andrea Di Giorgi */ public class ReplaceRegexTask extends DefaultTask { @Input @SkipWhenEmpty public Map<String, FileCollection> getMatches() { return _matches; } public List<Closure<String>> getPre() { return _preClosures; } @Input public Object getReplacement() { return _replacement; } public List<Closure<Boolean>> getReplaceOnlyIf() { return _replaceOnlyIfClosures; } public ReplaceRegexTask match(String regex, Iterable<Object> files) { Project project = getProject(); FileCollection fileCollection = _matches.get(regex); FileCollection filesFileCollection = project.files(files); if (fileCollection == null) { fileCollection = filesFileCollection; } else { fileCollection = fileCollection.plus(filesFileCollection); } _matches.put(regex, fileCollection); return this; } public ReplaceRegexTask match(String regex, Object... files) { return match(regex, Arrays.asList(files)); } public ReplaceRegexTask pre(Closure<String>... preClosures) { return pre(Arrays.asList(preClosures)); } public ReplaceRegexTask pre(Iterable<Closure<String>> preClosures) { GUtil.addToCollection(_preClosures, preClosures); return this; } public ReplaceRegexTask replaceOnlyIf( Closure<Boolean>... replaceOnlyIfClosures) { return replaceOnlyIf(Arrays.asList(replaceOnlyIfClosures)); } public ReplaceRegexTask replaceOnlyIf( Iterable<Closure<Boolean>> replaceOnlyIfClosures) { GUtil.addToCollection(_replaceOnlyIfClosures, replaceOnlyIfClosures); return this; } @TaskAction public void replaceRegex() throws IOException { Map<String, FileCollection> matches = getMatches(); Object replacementObj = _getReplacementObj(); for (Map.Entry<String, FileCollection> entry : matches.entrySet()) { Pattern pattern = Pattern.compile(entry.getKey()); FileCollection fileCollection = entry.getValue(); for (File file : fileCollection) { _replaceRegex(file, pattern, replacementObj); } } } public void setMatches(Map<String, FileCollection> matches) { _matches.clear(); _matches.putAll(matches); } public void setPre(Closure<String>... preClosures) { setPre(Arrays.asList(preClosures)); } public void setPre(Iterable<Closure<String>> preClosures) { _preClosures.clear(); pre(preClosures); } public void setReplacement(Object replacement) { _replacement = replacement; } public void setReplaceOnlyIf(Closure<Boolean>... replaceOnlyIfClosures) { setReplaceOnlyIf(Arrays.asList(replaceOnlyIfClosures)); } public void setReplaceOnlyIf( Iterable<Closure<Boolean>> replaceOnlyIfClosures) { _replaceOnlyIfClosures.clear(); replaceOnlyIf(replaceOnlyIfClosures); } private Object _getReplacementObj() { Object replacementObj = getReplacement(); if ((replacementObj instanceof Callable<?>) && !(replacementObj instanceof Closure<?>)) { replacementObj = GradleUtil.toString(replacementObj); } return replacementObj; } private void _replaceRegex( File file, Pattern pattern, Object replacementObj) throws IOException { Logger logger = getLogger(); Path path = file.toPath(); String content = new String( Files.readAllBytes(path), StandardCharsets.UTF_8); String newContent = content; for (Closure<String> closure : getPre()) { newContent = closure.call(newContent, file); } Matcher matcher = pattern.matcher(newContent); while (matcher.find()) { boolean replace = true; int groupCount = matcher.groupCount(); String group = matcher.group(groupCount); String replacement; if (replacementObj instanceof Closure<?>) { Closure<String> replacementClosure = (Closure<String>)replacementObj; replacement = replacementClosure.call(group); } else { replacement = GradleUtil.toString(replacementObj); } for (Closure<Boolean> closure : getReplaceOnlyIf()) { if (!closure.call(group, replacement, newContent, file)) { replace = false; break; } } if (replace) { newContent = newContent.substring(0, matcher.start(groupCount)) + replacement + newContent.substring(matcher.end(groupCount)); } else if (logger.isInfoEnabled()) { logger.info( "Skipped replacement of {} to {} in {}", group, replacement, file); } } if (!content.equals(newContent)) { Files.write(path, newContent.getBytes(StandardCharsets.UTF_8)); if (logger.isLifecycleEnabled()) { Project project = getProject(); logger.lifecycle("Updated {}", project.relativePath(file)); } } } private final Map<String, FileCollection> _matches = new LinkedHashMap<>(); private final List<Closure<String>> _preClosures = new ArrayList<>(); private Object _replacement; private final List<Closure<Boolean>> _replaceOnlyIfClosures = new ArrayList<>(); }