/*******************************************************************************
* Copyright (c) 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.ui.preferences.PreferencesMessages;
/**
* Style preferences for a category of include statements.
*/
public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
public enum IncludeKind {
RELATED(PreferencesMessages.IncludeCategoriesBlock_related_headers_node,
PreferencesMessages.IncludeCategoriesBlock_related_headers_node_description,
null),
PARTNER(PreferencesMessages.IncludeCategoriesBlock_partner_header_node,
PreferencesMessages.IncludeCategoriesBlock_partner_header_node_description,
RELATED),
IN_SAME_FOLDER(PreferencesMessages.IncludeCategoriesBlock_same_folder_header_node,
PreferencesMessages.IncludeCategoriesBlock_same_folder_header_node_description,
RELATED),
IN_SUBFOLDER(PreferencesMessages.IncludeCategoriesBlock_subfolder_header_node,
PreferencesMessages.IncludeCategoriesBlock_subfolder_header_node_description,
RELATED),
SYSTEM(PreferencesMessages.IncludeCategoriesBlock_system_headers_node,
PreferencesMessages.IncludeCategoriesBlock_system_headers_node_description,
null),
SYSTEM_WITH_EXTENSION(PreferencesMessages.IncludeCategoriesBlock_system_headers_with_extension_node,
PreferencesMessages.IncludeCategoriesBlock_system_headers_with_extension_node_description,
SYSTEM),
SYSTEM_WITHOUT_EXTENSION(PreferencesMessages.IncludeCategoriesBlock_system_headers_without_extension_node,
PreferencesMessages.IncludeCategoriesBlock_system_headers_without_extension_node_description,
SYSTEM),
OTHER(PreferencesMessages.IncludeCategoriesBlock_unrelated_headers_node,
PreferencesMessages.IncludeCategoriesBlock_unrelated_headers_node_description,
null),
IN_SAME_PROJECT(PreferencesMessages.IncludeCategoriesBlock_same_project_headers_node,
PreferencesMessages.IncludeCategoriesBlock_same_project_headers_node_description,
OTHER),
IN_OTHER_PROJECT(PreferencesMessages.IncludeCategoriesBlock_other_project_headers_node,
PreferencesMessages.IncludeCategoriesBlock_other_project_headers_node_description,
OTHER),
EXTERNAL(PreferencesMessages.IncludeCategoriesBlock_external_headers_node,
PreferencesMessages.IncludeCategoriesBlock_external_headers_node_description,
OTHER),
MATCHING_PATTERN(PreferencesMessages.IncludeCategoriesBlock_user_defined_categories_node,
PreferencesMessages.IncludeCategoriesBlock_user_defined_categories_node_description,
null);
public final String name;
public final String description;
public final IncludeKind parent;
public final List<IncludeKind> children = new ArrayList<IncludeKind>();
private IncludeKind(String name, String description, IncludeKind parent) {
this.name = name;
this.description = description;
this.parent = parent;
if (parent != null)
parent.children.add(this);
}
public boolean hasChildren() {
return !children.isEmpty();
}
}
private static final String TAG_STYLE = "style"; //$NON-NLS-1$
private static final String TAG_NAME = "name"; //$NON-NLS-1$
private static final String TAG_PATTERN = "pattern"; //$NON-NLS-1$
private static final String TAG_KEEP_TOGETHER = "keep_together"; //$NON-NLS-1$
private static final String TAG_BLANK_LINE_BEFORE = "blank_line_before"; //$NON-NLS-1$
private static final String TAG_RELATIVE_PATH = "relative_path"; //$NON-NLS-1$
private static final String TAG_ANGLE_BRACKETS = "angle_brackets"; //$NON-NLS-1$
private static final String TAG_ORDER = "order"; //$NON-NLS-1$
private final IncludeKind includeKind;
private boolean keepTogether;
private boolean blankLineBefore;
private boolean relativePath;
private boolean angleBrackets;
private Pattern headerNamePattern;
private String name;
private int order; // Relative position of the include group. Ignored if keepTogether is false.
public IncludeGroupStyle(IncludeKind includeKind) {
if (includeKind == null || includeKind == IncludeKind.MATCHING_PATTERN)
throw new IllegalArgumentException();
this.includeKind = includeKind;
}
public IncludeGroupStyle(String name, Pattern headerNamePattern) {
if (name == null)
throw new IllegalArgumentException();
if (headerNamePattern == null)
throw new IllegalArgumentException();
this.includeKind = IncludeKind.MATCHING_PATTERN;
this.name = name;
this.headerNamePattern = headerNamePattern;
}
public boolean isKeepTogether() {
return keepTogether;
}
public void setKeepTogether(boolean value) {
this.keepTogether = value;
}
public boolean isBlankLineBefore() {
return blankLineBefore;
}
public void setBlankLineBefore(boolean value) {
this.blankLineBefore = value;
}
public boolean isRelativePath() {
return relativePath;
}
public void setRelativePath(boolean value) {
assert !includeKind.hasChildren();
this.relativePath = value;
}
public boolean isAngleBrackets() {
return angleBrackets;
}
public void setAngleBrackets(boolean value) {
assert !includeKind.hasChildren();
this.angleBrackets = value;
}
public Pattern getHeaderNamePattern() {
return headerNamePattern;
}
public void setHeaderNamePattern(Pattern headerNamePattern) {
this.headerNamePattern = headerNamePattern;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IncludeKind getIncludeKind() {
return includeKind;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public static IncludeGroupStyle fromMemento(IMemento memento, IncludeKind includeKind) {
IncludeGroupStyle style;
if (includeKind == IncludeKind.MATCHING_PATTERN) {
String name = nullToEmpty(memento.getString(TAG_NAME));
String pattern = nullToEmpty(memento.getString(TAG_PATTERN));
style = new IncludeGroupStyle(name, Pattern.compile(pattern));
} else {
style = new IncludeGroupStyle(includeKind);
}
style.setKeepTogether(nullToFalse(memento.getBoolean(TAG_KEEP_TOGETHER)));
style.setBlankLineBefore(nullToFalse(memento.getBoolean(TAG_BLANK_LINE_BEFORE)));
if (!includeKind.hasChildren()) {
style.setRelativePath(nullToFalse(memento.getBoolean(TAG_RELATIVE_PATH)));
style.setAngleBrackets(nullToFalse(memento.getBoolean(TAG_ANGLE_BRACKETS)));
}
Integer order = memento.getInteger(TAG_ORDER);
if (order != null)
style.setOrder(order.intValue());
return style;
}
private static boolean nullToFalse(Boolean val) {
return val != null && val.booleanValue();
}
private static String nullToEmpty(String val) {
return val != null ? val : ""; //$NON-NLS-1$
}
public void saveToMemento(IMemento memento) {
if (includeKind == IncludeKind.MATCHING_PATTERN) {
memento.putString(TAG_NAME, name);
memento.putString(TAG_PATTERN, headerNamePattern.toString());
}
memento.putBoolean(TAG_KEEP_TOGETHER, keepTogether);
memento.putBoolean(TAG_BLANK_LINE_BEFORE, blankLineBefore);
if (!includeKind.hasChildren()) {
memento.putBoolean(TAG_RELATIVE_PATH, relativePath);
memento.putBoolean(TAG_ANGLE_BRACKETS, angleBrackets);
}
if (keepTogether)
memento.putInteger(TAG_ORDER, order);
}
public String toXmlString() {
XMLMemento memento = XMLMemento.createWriteRoot(TAG_STYLE);
saveToMemento(memento);
StringWriter writer = new StringWriter();
try {
memento.save(writer);
} catch (IOException e) {
CUIPlugin.log(e);
}
return writer.toString();
}
public static IncludeGroupStyle fromXmlString(String str, IncludeKind includeKind) {
StringReader reader = new StringReader(str);
XMLMemento memento;
try {
memento = XMLMemento.createReadRoot(reader);
} catch (WorkbenchException e) {
return null;
}
return fromMemento(memento, includeKind);
}
/** For debugging only */
@Override
public String toString() {
return includeKind.toString();
}
/**
* Compares styles according to their sorting order.
*/
@Override
public int compareTo(IncludeGroupStyle other) {
if (keepTogether != other.keepTogether)
return keepTogether ? -1 : 1;
int c = order - other.order;
if (c != 0)
return c;
return includeKind.ordinal() - other.includeKind.ordinal();
}
public IncludeGroupStyle getGroupingStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
if (keepTogether)
return this;
IncludeGroupStyle parent = getParentStyle(stylesMap);
if (parent != null && (parent.keepTogether || parent.includeKind == IncludeKind.OTHER))
return parent;
return stylesMap.get(IncludeKind.OTHER);
}
private IncludeGroupStyle getParentStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
IncludeKind kind = includeKind.parent;
if (kind == null)
return null;
return stylesMap.get(kind);
}
public boolean isBlankLineNeededAfter(IncludeGroupStyle previousIncludeStyle,
Map<IncludeKind, IncludeGroupStyle> stylesMap) {
if (previousIncludeStyle == null)
return false;
IncludeGroupStyle groupingStyle = getGroupingStyle(stylesMap);
IncludeGroupStyle previousGroupingStyle = previousIncludeStyle.getGroupingStyle(stylesMap);
if (groupingStyle != previousGroupingStyle && groupingStyle.isBlankLineBefore())
return true;
IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(stylesMap);
IncludeGroupStyle previousParentStyle = previousGroupingStyle.getParentStyle(stylesMap);
return parentStyle != null && previousParentStyle != null &&
parentStyle != previousParentStyle && parentStyle.isKeepTogether() &&
parentStyle.isBlankLineBefore();
}
}