/*
* Copyright (c) 2015-2015 Vladimir Schneider <vladimir.schneider@gmail.com>
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.vladsch.idea.multimarkdown.spellchecking;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.NameUtil;
import com.intellij.spellchecker.SpellCheckerManager;
import com.vladsch.idea.multimarkdown.util.FileRef;
import com.vladsch.idea.multimarkdown.util.PathInfo;
import com.vladsch.idea.multimarkdown.util.ProjectFileRef;
import com.vladsch.idea.multimarkdown.util.WikiLinkRef;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SuggestionFixers {
public static final CleanSpacedWordsFixer SuggestCleanSpacedWords = new CleanSpacedWordsFixer();
public static final CapSpacedWordsFixer SuggestCapSpacedWords = new CapSpacedWordsFixer();
public static final LowerSpacedWordsFixer SuggestLowerSpacedWords = new LowerSpacedWordsFixer();
public static final UpperSpacedWordsFixer SuggestUpperSpacedWords = new UpperSpacedWordsFixer();
public static final CleanSplicedWordsFixer SuggestCleanSplicedWords = new CleanSplicedWordsFixer();
public static final CapSplicedWordsFixer SuggestCapSplicedWords = new CapSplicedWordsFixer();
public static final LowerSplicedWordsFixer SuggestLowerSplicedWords = new LowerSplicedWordsFixer();
public static final UpperSplicedWordsFixer SuggestUpperSplicedWords = new UpperSplicedWordsFixer();
public static final CleanDashedWordsFixer SuggestCleanDashedWords = new CleanDashedWordsFixer();
public static final CapDashedWordsFixer SuggestCapDashedWords = new CapDashedWordsFixer();
public static final LowerDashedWordsFixer SuggestLowerDashedWords = new LowerDashedWordsFixer();
public static final UpperDashedWordsFixer SuggestUpperDashedWords = new UpperDashedWordsFixer();
public static final CleanSnakedWordsFixer SuggestCleanSnakedWords = new CleanSnakedWordsFixer();
public static final CapSnakedWordsFixer SuggestCapSnakedWords = new CapSnakedWordsFixer();
public static final LowerSnakedWordsFixer SuggestLowerSnakedWords = new LowerSnakedWordsFixer();
public static final UpperSnakedWordsFixer SuggestUpperSnakedWords = new UpperSnakedWordsFixer();
public static final SpellingFixer SuggestSpelling = new SpellingFixer();
public static final WikiRefAsFilNameWithExtFixer SuggestWikiRefAsFilNameWithExt = new WikiRefAsFilNameWithExtFixer();
public static final FileNameWithExtFixer SuggestFileNameWithExt = new FileNameWithExtFixer();
public static final RemoveInvalidFileNamesFixer SuggestRemoveInvalidFileNames = new RemoveInvalidFileNamesFixer();
//public static final SpaceCamelCaseWordsFixer SuggestSpaceCamelCaseWords = new SpaceCamelCaseWordsFixer();
public static abstract class FixerBase implements Suggestion.Fixer {
private SuggestionList suggestionList;
private Suggestion sourceSuggestion;
private void setFields(SuggestionList suggestionList, Suggestion sourceSuggestion) {
this.suggestionList = suggestionList;
this.sourceSuggestion = sourceSuggestion;
}
private void clearFields() {
setFields(null, null);
}
@Nullable
@Override
final public SuggestionList fix(@NotNull Suggestion suggestion, Project project) {
SuggestionList suggestedNames = new SuggestionList();
setFields(suggestedNames, suggestion);
makeSuggestions(suggestion.getText(), suggestion, project);
clearFields();
return suggestedNames;
}
protected void addSuggestion(@NotNull Suggestion suggestion) {
suggestionList.add(suggestion.getText(), suggestion, sourceSuggestion);
}
protected void addSuggestion(@NotNull String suggestion) {
suggestionList.add(suggestion, sourceSuggestion);
}
public abstract void makeSuggestions(@NotNull String text, @NotNull Suggestion suggestion, Project project);
}
public static class WikiRefAsFilNameWithExtFixer extends FixerBase {
@Override
public void makeSuggestions(@NotNull String text, @NotNull Suggestion suggestion, Project project) {
String fileName = WikiLinkRef.linkAsFile(new PathInfo(text).getFileNameNoExt()) + PathInfo.WIKI_PAGE_EXTENSION;
addSuggestion(fileName);
}
}
// not really needed CleanSpacedWords does it already
//public static class SpaceCamelCaseWordsFixer extends FixerBase {
// @Override
// public void makeSuggestions(@NotNull String text, @NotNull Suggestion suggestion, Project project) {
// // if text is camel case we convert it to spaced words
// int iMax = text.length();
// boolean prevWasLower = false;
// StringBuilder stringBuilder = new StringBuilder(iMax + 10);
//
// for (int i = 0; i < iMax; i++) {
// Character c = text.charAt(i);
// if (Character.isLowerCase(c)) {
// prevWasLower = true;
// } else {
// if (Character.isUpperCase(c)) {
// if (prevWasLower) {
// stringBuilder.append(' ');
// }
// }
// prevWasLower = false;
// }
// stringBuilder.append(c);
// }
// addSuggestion(stringBuilder.toString());
// }
//}
public static class RemoveInvalidFileNamesFixer extends FixerBase {
private static final Logger logger = Logger.getLogger(RemoveInvalidFileNamesFixer.class);
@Override
public void makeSuggestions(final @NotNull String text, @NotNull final Suggestion suggestion, Project project) {
if (project != null && suggestion.hasParam(FILE_PATH)) {
FileRef fileReference = new FileRef(suggestion.stringParam(FILE_PATH));
FileRef renamedFileReference = fileReference.append("..", text);
ProjectFileRef projectFileRef = renamedFileReference.projectFileRef(project);
if (projectFileRef == null || projectFileRef.getExists()) return;
}
addSuggestion(suggestion);
}
}
public static class FileNameWithExtFixer extends FixerBase {
@Override
public void makeSuggestions(@NotNull String text, @NotNull Suggestion suggestion, Project project) {
String wikiRef = new PathInfo(text).getFileNameNoExt();
addSuggestion(wikiRef + PathInfo.WIKI_PAGE_EXTENSION);
}
}
public static class SpellingFixer implements Suggestion.Fixer {
@Nullable
@Override
public SuggestionList fix(@NotNull Suggestion suggestion, Project project) {
if (!suggestion.boolParam(NEEDS_SPELLING_FIXER) || suggestion.boolParam(HAD_SPELLING_FIXER)) return null;
SuggestionList suggestionList = new SuggestionList();
SpellCheckerManager manager = SpellCheckerManager.getInstance(project);
List<String> spellingSuggestions = getSuggestions(manager, suggestion.getText());
Suggestion.Param<Boolean> param = new Suggestion.Param<Boolean>(HAD_SPELLING_FIXER, true);
for (String text : spellingSuggestions) {
suggestionList.add(text, param, suggestion);
}
return suggestionList;
}
}
public static abstract class WordsFixerBase extends FixerBase {
private Suggestion.Param<Boolean> param;
private void setFields(Suggestion.Param<Boolean> param) {
this.param = param;
}
private void clearFields() {
setFields(null);
}
@Override
protected void addSuggestion(@NotNull Suggestion suggestion) {
super.addSuggestion(new Suggestion(suggestion.getText(), param, suggestion));
}
@Override
protected void addSuggestion(@NotNull String suggestion) {
super.addSuggestion(new Suggestion(suggestion, param));
}
@Override
final public void makeSuggestions(@NotNull String text, @NotNull Suggestion suggestion, Project project) {
String[] words = suggestion.getWords();
String cleanedSuggestion = "";
boolean needSpellingSuggestions = false;
boolean prevWasAlphaNum = false;
SpellCheckerManager manager = project != null ? SpellCheckerManager.getInstance(project) : null;
for (String word : words) {
boolean isAlphaNum = isAlphaNum(word);
if (manager != null && manager.hasProblem(word)) needSpellingSuggestions = true;
if (isAlphaNum && prevWasAlphaNum) {
cleanedSuggestion += getWordSpacer();
}
cleanedSuggestion += fixWord(word);
prevWasAlphaNum = isAlphaNum;
}
cleanedSuggestion = fixSuggestion(cleanedSuggestion, getRemoveChars(), getWordSpacer());
setFields(new Suggestion.Param<Boolean>(Suggestion.Fixer.NEEDS_SPELLING_FIXER, true));
makeSuggestions(cleanedSuggestion);
clearFields();
}
@NotNull
protected String getRemoveChars() {
return " -_.'/\\#\"";
}
@NotNull
abstract public String getWordSpacer();
@NotNull
public String fixWord(@NotNull String word) {
return word;
}
public void makeSuggestions(@NotNull String cleanedWord) {
addSuggestion(cleanedWord);
}
}
public static class CleanSpacedWordsFixer extends WordsFixerBase {
@Override
@NotNull
public String getWordSpacer() {
return " ";
}
}
public static class CapSpacedWordsFixer extends CleanSpacedWordsFixer {
@NotNull
@Override
public String fixWord(@NotNull String word) {
return StringUtil.capitalize(word.toLowerCase());
}
}
public static class LowerSpacedWordsFixer extends CleanSpacedWordsFixer {
@NotNull
@Override
public String fixWord(@NotNull String word) {
return word.toLowerCase();
}
}
public static class UpperSpacedWordsFixer extends CleanSpacedWordsFixer {
@NotNull
@Override
public String fixWord(@NotNull String word) {
return word.toUpperCase();
}
}
public static class CleanSplicedWordsFixer extends CleanSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "";
}
}
public static class CapSplicedWordsFixer extends CapSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "";
}
}
public static class LowerSplicedWordsFixer extends LowerSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "";
}
}
public static class UpperSplicedWordsFixer extends UpperSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "";
}
}
public static class CleanDashedWordsFixer extends CleanSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "-";
}
}
public static class CapDashedWordsFixer extends CapSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "-";
}
}
public static class LowerDashedWordsFixer extends LowerSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "-";
}
}
public static class UpperDashedWordsFixer extends UpperSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "-";
}
}
public static class CleanSnakedWordsFixer extends CleanSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "_";
}
}
public static class CapSnakedWordsFixer extends CapSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "_";
}
}
public static class LowerSnakedWordsFixer extends LowerSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "_";
}
}
public static class UpperSnakedWordsFixer extends UpperSpacedWordsFixer {
@NotNull
@Override
public String getWordSpacer() {
return "_";
}
}
public static boolean isAlphaNum(@NotNull String word) {
int iMax = word.length();
for (int i = 0; i < iMax; i++) {
if (!Character.isLetterOrDigit(word.charAt(i))) {
return false;
}
}
return true;
}
@NotNull
public static String fixSuggestion(@NotNull String suggestion, @NotNull String remove, @NotNull String pad) {// replace all unacceptables with a space
int iMax = suggestion.length();
remove += pad;
StringBuilder newSuggestion = new StringBuilder(suggestion.length());
boolean lastWasPad = false;
for (int i = 0; i < iMax; i++) {
if (remove.indexOf(suggestion.charAt(i)) >= 0) {
if (newSuggestion.length() > 0 && newSuggestion.charAt(newSuggestion.length() - 1) != ' ') {
newSuggestion.append(pad);
lastWasPad = true;
}
continue;
}
newSuggestion.append(suggestion.charAt(i));
lastWasPad = false;
}
if (lastWasPad && pad.length() > 0) {
newSuggestion.setLength(newSuggestion.length() - pad.length());
}
suggestion = newSuggestion.toString();
return suggestion;
}
@NotNull
protected static List<String> getSuggestions(SpellCheckerManager manager, @NotNull String text) {
String[] words = NameUtil.nameToWords(text);
int index = 0;
List[] res = new List[words.length];
int i = 0;
for (String word : words) {
int start = text.indexOf(word, index);
int end = start + word.length();
if (manager.hasProblem(word)) {
List<String> variants = manager.getSuggestions(word);
res[i++] = variants;
} else {
List<String> variants = new ArrayList<String>();
variants.add(word);
res[i++] = variants;
}
index = end;
}
int[] counter = new int[i];
int size = 1;
for (int j = 0; j < i; j++) {
size *= res[j].size();
}
String[] all = new String[size];
for (int k = 0; k < size; k++) {
boolean prevAlnum = false;
for (int j = 0; j < i; j++) {
boolean isAlnum = isAlphaNum((String) res[j].get(counter[j]));
if (all[k] == null) {
all[k] = "";
} else if (isAlnum && prevAlnum) {
all[k] += " ";
}
all[k] += res[j].get(counter[j]);
prevAlnum = isAlnum;
counter[j]++;
if (counter[j] >= res[j].size()) {
counter[j] = 0;
}
}
}
return Arrays.asList(all);
}
}