Skip to content

Commit

Permalink
Merge pull request #210 from dyadix/master
Browse files Browse the repository at this point in the history
Checkstyle code style importer: initial implementation
  • Loading branch information
jshiell committed Jan 30, 2016
2 parents f180e59 + 18d9ec6 commit 8cba26c
Show file tree
Hide file tree
Showing 15 changed files with 926 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.infernus.idea.checkstyle.importer;

import com.intellij.openapi.options.SchemeFactory;
import com.intellij.openapi.options.SchemeImportException;
import com.intellij.openapi.options.SchemeImporter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.CodeStyleScheme;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.InputSource;

import java.io.InputStream;

/**
* Imports code style settings from check style configuration file.
*/
public class CheckStyleCodeStyleImporter implements SchemeImporter<CodeStyleScheme> {
@NotNull
@Override
public String[] getSourceExtensions() {
return new String[]{"xml"};
}

@Nullable
@Override
public CodeStyleScheme importScheme(@NotNull final Project project,
@NotNull final VirtualFile selectedFile,
@NotNull final CodeStyleScheme currentScheme,
@NotNull final SchemeFactory<CodeStyleScheme> schemeFactory) throws SchemeImportException {
try {
CodeStyleScheme targetScheme = currentScheme;
if (currentScheme.isDefault()) {
targetScheme = schemeFactory.createNewScheme(currentScheme.getName());
}
Configuration configuration = loadConfiguration(selectedFile);
if (configuration != null) {
importConfiguration(configuration, targetScheme.getCodeStyleSettings());
return targetScheme;
}
} catch (Exception e) {
throw new SchemeImportException(e);
}
return null;
}

@Nullable
@Override
public String getAdditionalImportInfo(@NotNull final CodeStyleScheme scheme) {
return null;
}

@Nullable
private Configuration loadConfiguration(@NotNull VirtualFile selectedFile) throws Exception {
InputStream inputStream = null;
try {
inputStream = selectedFile.getInputStream();
InputSource inputSource = new InputSource(inputStream);
return ConfigurationLoader.loadConfiguration(inputSource, null, false);
}
finally {
if (inputStream != null) //noinspection ThrowFromFinallyBlock
inputStream.close();
}
}

static void importConfiguration(@NotNull Configuration configuration, @NotNull CodeStyleSettings settings)
throws IllegalAccessException, InstantiationException {
ModuleImporter moduleImporter =
ModuleImporterFactory.getModuleImporter(configuration);
if (moduleImporter != null) moduleImporter.importTo(settings);
for (Configuration childConfig : configuration.getChildren()) {
importConfiguration(childConfig, settings);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.infernus.idea.checkstyle.importer;

import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public abstract class ModuleImporter {
private final static String TOKENS_PROP = "tokens";
private int[] tokens;

@NotNull
protected CommonCodeStyleSettings getJavaSettings(@NotNull CodeStyleSettings settings) {
return settings.getCommonSettings(JavaLanguage.INSTANCE);
}

public void setFrom(@NotNull Configuration moduleConfig) {
for (String attrName : moduleConfig.getAttributeNames()) {
try {
handleAttribute(attrName, moduleConfig.getAttribute(attrName));
} catch (CheckstyleException e) {
// Ignore, shouldn't happen
}
}
}

protected boolean handleAttribute(@NotNull String attrName, @NotNull String attrValue) {
if (TOKENS_PROP.equals(attrName)) {
tokens = TokenSetUtil.getTokens(attrValue);
}
return false;
}

protected boolean appliesTo(int tokenId) {
if (tokens != null) {
for (int token : tokens) {
if (token == tokenId) {
return true;
}
}
return false;
}
return true;
}

protected boolean appliesToOneOf(Set<Integer> tokenSet) {
if (tokens != null) {
for (int token : tokens) {
if (tokenSet.contains(token)) return true;
}
return false;
}
return true;
}

protected static Set<Integer> setOf(int... ids) {
Set<Integer> tokenSet = new HashSet<>(ids.length);
for (int id : ids) {
tokenSet.add(id);
}
return tokenSet;
}

public abstract void importTo(@NotNull CodeStyleSettings settings);

protected int getIntOrDefault(@NotNull String intStr, int defaultValue) {
try {
return Integer.parseInt(intStr);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.infernus.idea.checkstyle.importer;

import com.puppycrawl.tools.checkstyle.api.Configuration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

class ModuleImporterFactory {

@Nullable
static ModuleImporter getModuleImporter(@NotNull Configuration configuration)
throws InstantiationException, IllegalAccessException {
String name = configuration.getName();
ModuleImporter moduleImporter = createImporter(name);
if (moduleImporter != null) {
moduleImporter.setFrom(configuration);
}
return moduleImporter;
}

@Nullable
private static ModuleImporter createImporter(@NotNull String name)
throws IllegalAccessException, InstantiationException {
String fqn = getFullyQualifiedClassName(name);
try {
Class c = Class.forName(fqn);
Object o = c.newInstance();
return o instanceof ModuleImporter ? (ModuleImporter)o : null;
} catch (ClassNotFoundException e) {
return null;
}
}

private static String getFullyQualifiedClassName(@NotNull String moduleName) {
return ModuleImporterFactory.class.getPackage().getName() + ".modules." + moduleName + "Importer";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.infernus.idea.checkstyle.importer;

import com.puppycrawl.tools.checkstyle.api.TokenTypes;

import java.lang.reflect.Field;
import java.util.Arrays;

class TokenSetUtil {
private TokenSetUtil() {
}

static int[] getTokens(String tokenString) {
String[] tokenStrings = tokenString.split("\\s*,\\s*");
int[] tokenIds = new int[tokenStrings.length];
int i = 0;
for (String tokenStr : tokenStrings) {
try {
Field f = TokenTypes.class.getDeclaredField(tokenStr);
tokenIds[i] = f.getInt(null);
i ++;
} catch (NoSuchFieldException | IllegalAccessException e) {
// Ignore
}
}
return Arrays.copyOf(tokenIds, i);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.infernus.idea.checkstyle.importer.modules;

import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import org.infernus.idea.checkstyle.importer.ModuleImporter;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings("unused")
public class EmptyLineSeparatorImporter extends ModuleImporter {
private boolean noEmptyLinesBetweenFields = false;
private final static String NO_EMPTY_LINES_BETWEEN_FIELDS_PROP = "allowNoEmptyLineBetweenFields";

@Override
protected boolean handleAttribute(@NotNull final String attrName, @NotNull final String attrValue) {
if (!super.handleAttribute(attrName, attrValue)) {
if (NO_EMPTY_LINES_BETWEEN_FIELDS_PROP.equals(attrName)) {
noEmptyLinesBetweenFields = Boolean.parseBoolean(attrValue);
}
}
return false;
}

@Override
public void importTo(@NotNull final CodeStyleSettings settings) {
CommonCodeStyleSettings javaSettings = getJavaSettings(settings);
if (noEmptyLinesBetweenFields) {
javaSettings.BLANK_LINES_AROUND_FIELD = 0;
}
else if (appliesTo(TokenTypes.VARIABLE_DEF)) {
javaSettings.BLANK_LINES_AROUND_FIELD = 1;
}
if (appliesTo(TokenTypes.PACKAGE_DEF)) {
javaSettings.BLANK_LINES_AFTER_PACKAGE = 1;
}
if (appliesTo(TokenTypes.IMPORT)) {
javaSettings.BLANK_LINES_AFTER_IMPORTS = 1;
}
if (appliesTo(TokenTypes.METHOD_DEF)) {
javaSettings.BLANK_LINES_AROUND_METHOD = 1;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.infernus.idea.checkstyle.importer.modules;

import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import org.infernus.idea.checkstyle.importer.ModuleImporter;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings("unused")
public class FileTabCharacterImporter extends ModuleImporter {
private final static String FILE_EXTENSIONS_PROP = "fileExtensions";
private String[] extensions;

@Override
protected boolean handleAttribute(@NotNull final String attrName, @NotNull final String attrValue) {
if (FILE_EXTENSIONS_PROP.equals(attrName)) {
extensions = attrValue.split("\\s*,\\s*");
return true;
}
return false;
}

@Override
public void importTo(@NotNull final CodeStyleSettings settings) {
if (extensions != null) {
for (String extension : extensions) {
if (!extension.isEmpty()) {
FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(extension);
setNoTabChar(settings, fileType);
}
}
}
else {
for (FileType fileType : FileTypeManager.getInstance().getRegisteredFileTypes()) {
setNoTabChar(settings, fileType);
}
}
}

private void setNoTabChar(final @NotNull CodeStyleSettings settings, final FileType fileType) {
CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptions(fileType);
if (indentOptions != null) {
indentOptions.USE_TAB_CHARACTER = false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.infernus.idea.checkstyle.importer.modules;

import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import org.infernus.idea.checkstyle.importer.ModuleImporter;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings("unused")
public class IndentationImporter extends ModuleImporter {
private final static String BASIC_OFFSET_PROP = "basicOffset";
private final static String CASE_INDENT_PROP = "caseIndent";
private final static String LINE_WRAP_INDENT_PROP = "lineWrappingIndentation";

private final static int DEFAULT_BASIC_OFFSET = 4;
private final static int DEFAULT_LINE_WRAP_INDENT = 4;
private final static boolean DEFAULT_INDENT_CASE = true;

private int basicIndent = DEFAULT_BASIC_OFFSET;
private int continuationIndent = DEFAULT_LINE_WRAP_INDENT;
private boolean indentCase = DEFAULT_INDENT_CASE;

@Override
protected boolean handleAttribute(@NotNull final String attrName, @NotNull final String attrValue) {
switch (attrName) {
case BASIC_OFFSET_PROP:
basicIndent = getIntOrDefault(attrValue, DEFAULT_BASIC_OFFSET);
return true;
case CASE_INDENT_PROP:
int caseIndent = getIntOrDefault(attrValue, 0);
indentCase = caseIndent > 0;
return true;
case LINE_WRAP_INDENT_PROP:
continuationIndent = getIntOrDefault(attrValue, DEFAULT_LINE_WRAP_INDENT);
return true;
}
return false;
}

@Override
public void importTo(@NotNull final CodeStyleSettings settings) {
CommonCodeStyleSettings javaSettings = getJavaSettings(settings);
CommonCodeStyleSettings.IndentOptions indentOptions = javaSettings.getIndentOptions();
if (indentOptions != null) {
indentOptions.INDENT_SIZE = basicIndent;
indentOptions.CONTINUATION_INDENT_SIZE = continuationIndent;
}
javaSettings.INDENT_CASE_FROM_SWITCH = indentCase;
}
}
Loading

0 comments on commit 8cba26c

Please sign in to comment.