| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed 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.intellij.openapi.fileTypes.impl; |
| |
| import com.intellij.ide.highlighter.FileTypeRegistrator; |
| import com.intellij.ide.highlighter.custom.SyntaxTable; |
| import com.intellij.ide.highlighter.custom.impl.CustomFileTypeEditor; |
| import com.intellij.lang.Commenter; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.fileTypes.*; |
| import com.intellij.openapi.fileTypes.ex.ExternalizableFileType; |
| import com.intellij.openapi.options.ExternalInfo; |
| import com.intellij.openapi.options.ExternalizableScheme; |
| import com.intellij.openapi.options.SettingsEditor; |
| import com.intellij.openapi.util.*; |
| import com.intellij.util.text.StringTokenizer; |
| import org.jdom.Element; |
| import org.jdom.output.XMLOutputter; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Set; |
| |
| |
| public class AbstractFileType extends UserFileType<AbstractFileType> implements ExternalizableFileType, ExternalizableScheme, |
| CustomSyntaxTableFileType { |
| private static final String SEMICOLON = ";"; |
| protected SyntaxTable mySyntaxTable; |
| private SyntaxTable myDefaultSyntaxTable; |
| protected Commenter myCommenter = null; |
| @NonNls public static final String ELEMENT_HIGHLIGHTING = "highlighting"; |
| @NonNls private static final String ELEMENT_OPTIONS = "options"; |
| @NonNls private static final String ELEMENT_OPTION = "option"; |
| @NonNls private static final String ATTRIBUTE_VALUE = "value"; |
| @NonNls private static final String VALUE_LINE_COMMENT = "LINE_COMMENT"; |
| @NonNls private static final String VALUE_COMMENT_START = "COMMENT_START"; |
| @NonNls private static final String VALUE_COMMENT_END = "COMMENT_END"; |
| @NonNls private static final String VALUE_HEX_PREFIX = "HEX_PREFIX"; |
| @NonNls private static final String VALUE_NUM_POSTFIXES = "NUM_POSTFIXES"; |
| @NonNls private static final String VALUE_HAS_BRACES = "HAS_BRACES"; |
| @NonNls private static final String VALUE_HAS_BRACKETS = "HAS_BRACKETS"; |
| @NonNls private static final String VALUE_HAS_PARENS = "HAS_PARENS"; |
| @NonNls private static final String VALUE_HAS_STRING_ESCAPES = "HAS_STRING_ESCAPES"; |
| @NonNls private static final String VALUE_LINE_COMMENT_AT_START = "LINE_COMMENT_AT_START"; |
| @NonNls private static final String ELEMENT_KEYWORDS = "keywords"; |
| @NonNls private static final String ATTRIBUTE_IGNORE_CASE = "ignore_case"; |
| @NonNls private static final String ELEMENT_KEYWORD = "keyword"; |
| @NonNls private static final String ELEMENT_KEYWORDS2 = "keywords2"; |
| @NonNls private static final String ELEMENT_KEYWORDS3 = "keywords3"; |
| @NonNls private static final String ELEMENT_KEYWORDS4 = "keywords4"; |
| @NonNls private static final String ATTRIBUTE_NAME = "name"; |
| @NonNls public static final String ELEMENT_EXTENSIONMAP = "extensionMap"; |
| private final ExternalInfo myExternalInfo = new ExternalInfo(); |
| |
| public AbstractFileType(SyntaxTable syntaxTable) { |
| mySyntaxTable = syntaxTable; |
| } |
| |
| public void initSupport() { |
| for (FileTypeRegistrator registrator : Extensions.getRootArea().getExtensionPoint(FileTypeRegistrator.EP_NAME).getExtensions()) { |
| registrator.initFileType(this); |
| } |
| } |
| |
| @Override |
| public SyntaxTable getSyntaxTable() { |
| return mySyntaxTable; |
| } |
| |
| public Commenter getCommenter() { |
| return myCommenter; |
| } |
| |
| public void setSyntaxTable(SyntaxTable syntaxTable) { |
| mySyntaxTable = syntaxTable; |
| } |
| |
| public AbstractFileType clone() { |
| return (AbstractFileType)super.clone(); |
| } |
| |
| public void copyFrom(UserFileType newType) { |
| super.copyFrom(newType); |
| if (newType instanceof AbstractFileType) { |
| mySyntaxTable = ((CustomSyntaxTableFileType)newType).getSyntaxTable(); |
| myExternalInfo.copy(((AbstractFileType)newType).myExternalInfo); |
| } |
| } |
| |
| public boolean isBinary() { |
| return false; |
| } |
| |
| public void readExternal(final Element typeElement) throws InvalidDataException { |
| Element element = typeElement.getChild(ELEMENT_HIGHLIGHTING); |
| if (element != null) { |
| SyntaxTable table = readSyntaxTable(element); |
| if (table != null) { |
| setSyntaxTable(table); |
| } |
| } |
| } |
| |
| public static SyntaxTable readSyntaxTable(Element root) { |
| SyntaxTable table = new SyntaxTable(); |
| |
| for (final Object o : root.getChildren()) { |
| Element element = (Element)o; |
| |
| if (ELEMENT_OPTIONS.equals(element.getName())) { |
| for (final Object o1 : element.getChildren(ELEMENT_OPTION)) { |
| Element e = (Element)o1; |
| String name = e.getAttributeValue(ATTRIBUTE_NAME); |
| String value = e.getAttributeValue(ATTRIBUTE_VALUE); |
| if (VALUE_LINE_COMMENT.equals(name)) { |
| table.setLineComment(value); |
| } |
| else if (VALUE_COMMENT_START.equals(name)) { |
| table.setStartComment(value); |
| } |
| else if (VALUE_COMMENT_END.equals(name)) { |
| table.setEndComment(value); |
| } |
| else if (VALUE_HEX_PREFIX.equals(name)) { |
| table.setHexPrefix(value); |
| } |
| else if (VALUE_NUM_POSTFIXES.equals(name)) { |
| table.setNumPostfixChars(value); |
| } |
| else if (VALUE_LINE_COMMENT_AT_START.equals(name)) { |
| table.lineCommentOnlyAtStart = Boolean.valueOf(value).booleanValue(); |
| } |
| else if (VALUE_HAS_BRACES.equals(name)) { |
| table.setHasBraces(Boolean.valueOf(value).booleanValue()); |
| } |
| else if (VALUE_HAS_BRACKETS.equals(name)) { |
| table.setHasBrackets(Boolean.valueOf(value).booleanValue()); |
| } |
| else if (VALUE_HAS_PARENS.equals(name)) { |
| table.setHasParens(Boolean.valueOf(value).booleanValue()); |
| } else if (VALUE_HAS_STRING_ESCAPES.equals(name)) { |
| table.setHasStringEscapes(Boolean.valueOf(value).booleanValue()); |
| } |
| } |
| } |
| else if (ELEMENT_KEYWORDS.equals(element.getName())) { |
| boolean ignoreCase = Boolean.valueOf(element.getAttributeValue(ATTRIBUTE_IGNORE_CASE)).booleanValue(); |
| table.setIgnoreCase(ignoreCase); |
| loadKeywords(element, table.getKeywords1()); |
| } |
| else if (ELEMENT_KEYWORDS2.equals(element.getName())) { |
| loadKeywords(element, table.getKeywords2()); |
| } |
| else if (ELEMENT_KEYWORDS3.equals(element.getName())) { |
| loadKeywords(element, table.getKeywords3()); |
| } |
| else if (ELEMENT_KEYWORDS4.equals(element.getName())) { |
| loadKeywords(element, table.getKeywords4()); |
| } |
| } |
| |
| boolean DUMP_TABLE = false; |
| if (DUMP_TABLE) { |
| Element element = new Element("temp"); |
| writeTable(element, table); |
| XMLOutputter outputter = JDOMUtil.createOutputter("\n"); |
| try { |
| outputter.output((Element)element.getContent().get(0), System.out); |
| } catch (IOException ex) {} |
| } |
| return table; |
| } |
| |
| private static void loadKeywords(Element element, Set<String> keywords) { |
| String value = element.getAttributeValue(ELEMENT_KEYWORDS); |
| if (value != null) { |
| StringTokenizer tokenizer = new StringTokenizer(value, SEMICOLON); |
| while(tokenizer.hasMoreElements()) { |
| String keyword = tokenizer.nextToken().trim(); |
| if (keyword.length() != 0) keywords.add(keyword); |
| } |
| } |
| for (final Object o1 : element.getChildren(ELEMENT_KEYWORD)) { |
| keywords.add(((Element)o1).getAttributeValue(ATTRIBUTE_NAME)); |
| } |
| } |
| |
| public void writeExternal(final Element element) throws WriteExternalException { |
| SyntaxTable table = getSyntaxTable(); |
| writeTable(element, table); |
| } |
| |
| private static void writeTable(Element element, SyntaxTable table) { |
| Element highlightingElement = new Element(ELEMENT_HIGHLIGHTING); |
| |
| Element optionsElement = new Element(ELEMENT_OPTIONS); |
| |
| Element lineComment = new Element(ELEMENT_OPTION); |
| lineComment.setAttribute(ATTRIBUTE_NAME, VALUE_LINE_COMMENT); |
| lineComment.setAttribute(ATTRIBUTE_VALUE, table.getLineComment()); |
| optionsElement.addContent(lineComment); |
| |
| String commentStart = table.getStartComment(); |
| if (commentStart != null) { |
| Element commentStartElement = new Element(ELEMENT_OPTION); |
| commentStartElement.setAttribute(ATTRIBUTE_NAME, VALUE_COMMENT_START); |
| commentStartElement.setAttribute(ATTRIBUTE_VALUE, commentStart); |
| optionsElement.addContent(commentStartElement); |
| } |
| |
| String endComment = table.getEndComment(); |
| |
| if (endComment != null) { |
| Element commentEndElement = new Element(ELEMENT_OPTION); |
| commentEndElement.setAttribute(ATTRIBUTE_NAME, VALUE_COMMENT_END); |
| commentEndElement.setAttribute(ATTRIBUTE_VALUE, endComment); |
| optionsElement.addContent(commentEndElement); |
| } |
| |
| String prefix = table.getHexPrefix(); |
| |
| if (prefix != null) { |
| Element hexPrefix = new Element(ELEMENT_OPTION); |
| hexPrefix.setAttribute(ATTRIBUTE_NAME, VALUE_HEX_PREFIX); |
| hexPrefix.setAttribute(ATTRIBUTE_VALUE, prefix); |
| optionsElement.addContent(hexPrefix); |
| } |
| |
| String chars = table.getNumPostfixChars(); |
| |
| if (chars != null) { |
| Element numPostfixes = new Element(ELEMENT_OPTION); |
| numPostfixes.setAttribute(ATTRIBUTE_NAME, VALUE_NUM_POSTFIXES); |
| numPostfixes.setAttribute(ATTRIBUTE_VALUE, chars); |
| optionsElement.addContent(numPostfixes); |
| } |
| |
| addElementOption(optionsElement, VALUE_HAS_BRACES, table.isHasBraces()); |
| addElementOption(optionsElement, VALUE_HAS_BRACKETS, table.isHasBrackets()); |
| addElementOption(optionsElement, VALUE_HAS_PARENS, table.isHasParens()); |
| addElementOption(optionsElement, VALUE_HAS_STRING_ESCAPES, table.isHasStringEscapes()); |
| addElementOption(optionsElement, VALUE_LINE_COMMENT_AT_START, table.lineCommentOnlyAtStart); |
| |
| highlightingElement.addContent(optionsElement); |
| |
| writeKeywords(table.getKeywords1(), ELEMENT_KEYWORDS, highlightingElement).setAttribute(ATTRIBUTE_IGNORE_CASE, String.valueOf(table.isIgnoreCase())); |
| writeKeywords(table.getKeywords2(), ELEMENT_KEYWORDS2, highlightingElement); |
| writeKeywords(table.getKeywords3(), ELEMENT_KEYWORDS3, highlightingElement); |
| writeKeywords(table.getKeywords4(), ELEMENT_KEYWORDS4, highlightingElement); |
| |
| element.addContent(highlightingElement); |
| } |
| |
| private static void addElementOption(final Element optionsElement, final String valueHasParens, final boolean hasParens) { |
| if (!hasParens) return; |
| Element supportParens = new Element(ELEMENT_OPTION); |
| supportParens.setAttribute(ATTRIBUTE_NAME, valueHasParens); |
| supportParens.setAttribute(ATTRIBUTE_VALUE, String.valueOf(hasParens)); |
| optionsElement.addContent(supportParens); |
| } |
| |
| private static Element writeKeywords(Set<String> keywords, String tagName, Element highlightingElement) { |
| if (keywords.size() == 0 && !ELEMENT_KEYWORDS.equals(tagName)) return null; |
| Element keywordsElement = new Element(tagName); |
| String[] strings = keywords.toArray(new String[keywords.size()]); |
| Arrays.sort(strings); |
| StringBuilder keywordsAttribute = new StringBuilder(); |
| |
| for (final String keyword : strings) { |
| if (keyword.indexOf(SEMICOLON) == -1) { |
| if (keywordsAttribute.length() != 0) keywordsAttribute.append(SEMICOLON); |
| keywordsAttribute.append(keyword); |
| } else { |
| Element e = new Element(ELEMENT_KEYWORD); |
| e.setAttribute(ATTRIBUTE_NAME, keyword); |
| keywordsElement.addContent(e); |
| } |
| } |
| if (keywordsAttribute.length() != 0) { |
| keywordsElement.setAttribute(ELEMENT_KEYWORDS, keywordsAttribute.toString()); |
| } |
| highlightingElement.addContent(keywordsElement); |
| return keywordsElement; |
| } |
| |
| public void markDefaultSettings() { |
| myDefaultSyntaxTable = mySyntaxTable; |
| } |
| |
| public boolean isModified() { |
| return !Comparing.equal(myDefaultSyntaxTable, getSyntaxTable()); |
| } |
| |
| @NonNls private static final String ELEMENT_MAPPING = "mapping"; |
| @NonNls private static final String ATTRIBUTE_EXT = "ext"; |
| @NonNls private static final String ATTRIBUTE_PATTERN = "pattern"; |
| /** Applied for removed mappings approved by user */ |
| @NonNls private static final String ATTRIBUTE_APPROVED = "approved"; |
| |
| @NonNls private static final String ELEMENT_REMOVED_MAPPING = "removed_mapping"; |
| @NonNls private static final String ATTRIBUTE_TYPE = "type"; |
| |
| @NotNull |
| public static List<Pair<FileNameMatcher, String>> readAssociations(@NotNull Element e) { |
| ArrayList<Pair<FileNameMatcher, String>> result = new ArrayList<Pair<FileNameMatcher, String>>(); |
| List mappings = e.getChildren(ELEMENT_MAPPING); |
| |
| for (Object mapping1 : mappings) { |
| Element mapping = (Element)mapping1; |
| String ext = mapping.getAttributeValue(ATTRIBUTE_EXT); |
| String pattern = mapping.getAttributeValue(ATTRIBUTE_PATTERN); |
| |
| FileNameMatcher matcher = ext != null ? new ExtensionFileNameMatcher(ext) : FileTypeManager.parseFromString(pattern); |
| result.add(Pair.create(matcher, mapping.getAttributeValue(ATTRIBUTE_TYPE))); |
| } |
| |
| return result; |
| } |
| |
| @NotNull |
| public static List<Trinity<FileNameMatcher, String, Boolean>> readRemovedAssociations(@NotNull Element e) { |
| ArrayList<Trinity<FileNameMatcher, String, Boolean>> result = new ArrayList<Trinity<FileNameMatcher, String, Boolean>>(); |
| List removedMappings = e.getChildren(ELEMENT_REMOVED_MAPPING); |
| for (Object removedMapping : removedMappings) { |
| Element mapping = (Element)removedMapping; |
| String ext = mapping.getAttributeValue(ATTRIBUTE_EXT); |
| String pattern = mapping.getAttributeValue(ATTRIBUTE_PATTERN); |
| String approved = mapping.getAttributeValue(ATTRIBUTE_APPROVED); |
| |
| FileNameMatcher matcher = ext != null ? new ExtensionFileNameMatcher(ext) : FileTypeManager.parseFromString(pattern); |
| result.add(new Trinity<FileNameMatcher, String, Boolean>(matcher, mapping.getAttributeValue(ATTRIBUTE_TYPE), Boolean.parseBoolean(approved))); |
| } |
| |
| return result; |
| } |
| |
| public static Element writeMapping(@NotNull FileType type, final FileNameMatcher matcher, boolean specifyTypeName) { |
| Element mapping = new Element(ELEMENT_MAPPING); |
| if (matcher instanceof ExtensionFileNameMatcher) { |
| mapping.setAttribute(ATTRIBUTE_EXT, ((ExtensionFileNameMatcher)matcher).getExtension()); |
| } |
| else if (matcher instanceof WildcardFileNameMatcher) { |
| mapping.setAttribute(ATTRIBUTE_PATTERN, ((WildcardFileNameMatcher)matcher).getPattern()); |
| } |
| else if (matcher instanceof ExactFileNameMatcher) { |
| mapping.setAttribute(ATTRIBUTE_PATTERN, ((ExactFileNameMatcher)matcher).getFileName()); |
| } |
| else { |
| return null; |
| } |
| |
| if (specifyTypeName) { |
| mapping.setAttribute(ATTRIBUTE_TYPE, type.getName()); |
| } |
| |
| return mapping; |
| |
| } |
| |
| static Element writeRemovedMapping(final FileType type, final FileNameMatcher matcher, final boolean specifyTypeName, boolean approved) { |
| Element mapping = new Element(ELEMENT_REMOVED_MAPPING); |
| if (matcher instanceof ExtensionFileNameMatcher) { |
| mapping.setAttribute(ATTRIBUTE_EXT, ((ExtensionFileNameMatcher)matcher).getExtension()); |
| if (approved) { |
| mapping.setAttribute(ATTRIBUTE_APPROVED, "true"); |
| } |
| } |
| else if (matcher instanceof WildcardFileNameMatcher) { |
| mapping.setAttribute(ATTRIBUTE_PATTERN, ((WildcardFileNameMatcher)matcher).getPattern()); |
| } |
| else if (matcher instanceof ExactFileNameMatcher) { |
| mapping.setAttribute(ATTRIBUTE_PATTERN, ((ExactFileNameMatcher)matcher).getFileName()); |
| } |
| else { |
| return null; |
| } |
| if (specifyTypeName) { |
| mapping.setAttribute(ATTRIBUTE_TYPE, type.getName()); |
| } |
| |
| return mapping; |
| } |
| |
| public SettingsEditor<AbstractFileType> getEditor() { |
| return new CustomFileTypeEditor(); |
| } |
| |
| public void setCommenter(final Commenter commenter) { |
| myCommenter = commenter; |
| } |
| |
| @NotNull |
| public ExternalInfo getExternalInfo() { |
| return myExternalInfo; |
| } |
| } |