| /******************************************************************************* |
| * Copyright (c) 2004, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.AbstractCSSSourceFormatter |
| * modified in order to process JSON Objects. |
| *******************************************************************************/ |
| package org.eclipse.wst.json.core.internal.format; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| |
| import org.eclipse.core.runtime.Preferences; |
| import org.eclipse.jface.text.DefaultLineTracker; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.wst.json.core.JSONCorePlugin; |
| import org.eclipse.wst.json.core.cleanup.IJSONCleanupStrategy; |
| import org.eclipse.wst.json.core.cleanup.JSONCleanupStrategyImpl; |
| import org.eclipse.wst.json.core.document.IJSONArray; |
| import org.eclipse.wst.json.core.document.IJSONDocument; |
| import org.eclipse.wst.json.core.document.IJSONModel; |
| import org.eclipse.wst.json.core.document.IJSONNode; |
| import org.eclipse.wst.json.core.document.IJSONObject; |
| import org.eclipse.wst.json.core.internal.util.RegionIterator; |
| import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames; |
| import org.eclipse.wst.json.core.regions.JSONRegionContexts; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; |
| |
| public abstract class AbstractJSONSourceFormatter implements |
| IJSONSourceFormatter { |
| |
| protected final static short GENERATE = 0; |
| protected final static short FORMAT = 1; |
| protected final static short CLEANUP = 2; |
| protected static short strategy; |
| |
| AbstractJSONSourceFormatter() { |
| super(); |
| } |
| |
| protected void appendDelimBefore(IJSONNode node, CompoundRegion toAppend, |
| StringBuilder source) { |
| if (node == null || source == null) |
| return; |
| if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) |
| return; // for not formatting case on cleanup action |
| String delim = getLineDelimiter(node); |
| |
| boolean needIndent = !(node instanceof IJSONDocument); |
| if (toAppend == null) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else { |
| String type = toAppend.getType(); |
| if (type == JSONRegionContexts.JSON_COMMENT) { |
| RegionIterator it = new RegionIterator( |
| toAppend.getDocumentRegion(), toAppend.getTextRegion()); |
| it.prev(); |
| ITextRegion prev = it.prev(); |
| int[] result = null; |
| if (prev == null |
| || (prev.getType() == JSONRegionContexts.WHITE_SPACE && (result = TextUtilities |
| .indexOf(DefaultLineTracker.DELIMITERS, |
| it.getStructuredDocumentRegion() |
| .getText(prev), 0))[0] >= 0)) { |
| // Collapse to one empty line if there's more than one. |
| if (result != null) { |
| int offset = result[0] |
| + DefaultLineTracker.DELIMITERS[result[1]] |
| .length(); |
| if (offset < it.getStructuredDocumentRegion() |
| .getText(prev).length()) { |
| if (TextUtilities.indexOf( |
| DefaultLineTracker.DELIMITERS, it |
| .getStructuredDocumentRegion() |
| .getText(prev), offset)[0] >= 0) { |
| source.append(delim); |
| } |
| } |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } else if (prev.getType() == JSONRegionContexts.JSON_COMMENT) { |
| String fullText = toAppend.getDocumentRegion().getFullText( |
| prev); |
| String trimmedText = toAppend.getDocumentRegion().getText( |
| prev); |
| String whiteSpaces = "";//$NON-NLS-1$ |
| if (fullText != null && trimmedText != null) |
| whiteSpaces = fullText.substring(trimmedText.length()); |
| int[] delimiterFound = TextUtilities.indexOf( |
| DefaultLineTracker.DELIMITERS, whiteSpaces, 0); |
| if (delimiterFound[0] != -1) { |
| source.append(delim); |
| } else { |
| appendSpaceBefore(node, toAppend.getText(), source); |
| |
| /* |
| * If two comments can't be adjusted in one |
| * line(combined length exceeds line width), a tab is |
| * also appended along with next line delimiter , we |
| * need to remove that. |
| */ |
| if (source.toString().endsWith(getIndentString())) { |
| source.delete((source.length() - getIndentString() |
| .length()), source.length()); |
| } |
| } |
| } else { |
| appendSpaceBefore(node, toAppend.getText(), source); |
| } |
| } else if (type == JSONRegionContexts.JSON_COMMA) { |
| RegionIterator it = new RegionIterator( |
| toAppend.getDocumentRegion(), toAppend.getTextRegion()); |
| it.prev(); |
| ITextRegion prev = it.prev(); |
| |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| |
| if (prev.getType() == JSONRegionContexts.WHITE_SPACE |
| && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, |
| it.getStructuredDocumentRegion().getText(prev), |
| 0)[0] >= 0) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else if (preferences |
| .getInt(JSONCorePreferenceNames.LINE_WIDTH) > 0 |
| && (!preferences |
| .getBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node |
| .getOwnerDocument().getNodeType() != IJSONNode.PAIR_NODE)) { |
| int length = getLastLineLength(node, source); |
| int append = 1; |
| if (length + append > preferences |
| .getInt(JSONCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } |
| } else |
| if (type == JSONRegionContexts.JSON_OBJECT_OPEN |
| || type == JSONRegionContexts.JSON_OBJECT_CLOSE |
| || type == JSONRegionContexts.JSON_ARRAY_OPEN |
| || type == JSONRegionContexts.JSON_ARRAY_CLOSE) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| } else { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } |
| } |
| |
| protected void appendSpaceBefore(IJSONNode node, CompoundRegion toAppend, |
| StringBuilder source) { |
| if (node == null || toAppend == null || source == null) |
| return; |
| if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) |
| return; // for not formatting case on cleanup action |
| String type = toAppend.getType(); |
| |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| |
| boolean needIndent = !(node instanceof IJSONDocument); |
| /*if (type == JSONRegionContexts.JSON_COMMENT) { |
| // check whether previous region is 'S' and has CR-LF |
| String delim = getLineDelimiter(node); |
| RegionIterator it = new RegionIterator( |
| toAppend.getDocumentRegion(), toAppend.getTextRegion()); |
| it.prev(); |
| ITextRegion prev = it.prev(); |
| // bug390904 |
| if (prev.getType() == JSONRegionContexts.JSON_LBRACE |
| && TextUtilities |
| .indexOf(DefaultLineTracker.DELIMITERS, |
| it.getStructuredDocumentRegion() |
| .getFullText(prev), 0)[0] > 0) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| source.append(getIndentString()); |
| } else if (prev.getType() == JSONRegionContexts.WHITE_SPACE |
| && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, it |
| .getStructuredDocumentRegion().getText(prev), 0)[0] >= 0) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else { |
| appendSpaceBefore(node, toAppend.getText(), source); |
| } |
| }*/ |
| if ((type == JSONRegionContexts.JSON_OBJECT_OPEN || type == JSONRegionContexts.JSON_ARRAY_OPEN) |
| && preferences |
| .getBoolean(JSONCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE)) { |
| String delim = getLineDelimiter(node); |
| source.append(delim); |
| source.append(getIndent(node)); |
| // } else if (type == JSONRegionContexts.JSON_CURLY_BRACE_CLOSE) { |
| // } else if (type == JSONRegionContexts.JSON_INCLUDES || type == |
| // JSONRegionContexts.JSON_DASHMATCH) { |
| /*} else if (type == JSONRegionContexts.JSON_DECLARATION_SEPARATOR |
| && node instanceof IJSONStyleDeclItem) { |
| int n = preferences |
| .getInt(JSONCorePreferenceNames.FORMAT_PROP_PRE_DELIM); |
| // no delimiter case |
| while (n-- > 0) |
| source.append(" ");//$NON-NLS-1$ |
| } else if (type == JSONRegionContexts.JSON_DECLARATION_VALUE_OPERATOR |
| || type == JSONRegionContexts.JSON_DECLARATION_VALUE_PARENTHESIS_CLOSE) { |
| if (preferences.getInt(JSONCorePreferenceNames.LINE_WIDTH) > 0 |
| && (!preferences |
| .getBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node |
| .getOwnerDocument().getNodeType() != IJSONNode.STYLEDECLARATION_NODE)) { |
| int length = getLastLineLength(node, source); |
| int append = 1; |
| if (length + append > preferences |
| .getInt(JSONCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } |
| } else if (JSONRegionContexts.JSON_FOREIGN_ELEMENT == type |
| || JSONRegionContexts.JSON_DECLARATION_DELIMITER == type) { |
| return; |
| */ |
| } else |
| appendSpaceBefore(node, toAppend.getText(), source); |
| } |
| |
| protected void appendSpaceBefore(IJSONNode node, String toAppend, |
| StringBuilder source) { |
| if (node == null || source == null) |
| return; |
| if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) |
| return; // for not formatting case on cleanup action |
| |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| if (toAppend != null |
| && toAppend.startsWith("{") && preferences.getBoolean(JSONCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE)) {//$NON-NLS-1$ |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| return; |
| } else if (/* ! mgr.isOnePropertyPerLine() && */preferences |
| .getInt(JSONCorePreferenceNames.LINE_WIDTH) > 0 |
| && (!preferences |
| .getBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) |
| /*|| node |
| .getOwnerDocument().getNodeType() != IJSONNode.STYLEDECLARATION_NODE*/)) { |
| int n = getLastLineLength(node, source); |
| int append = (toAppend != null) ? TextUtilities.indexOf( |
| DefaultLineTracker.DELIMITERS, toAppend, 0)[0] : 0; |
| if (toAppend != null) |
| append = (append < 0) ? toAppend.length() : append; |
| if (n + append + 1 > preferences |
| .getInt(JSONCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| source.append(getIndentString()); |
| return; |
| } |
| } |
| // bug412395 |
| // just verify if the source and the toAppend strings do not end with a |
| // whitespace to avoid the whitespace duplication. |
| if (!(source.length() > 0 && source.toString().charAt( |
| source.length() - 1) == ' ') |
| && !(toAppend.length() > 0 && toAppend |
| .charAt(toAppend.length() - 1) == ' ')) { |
| source.append(" ");//$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public final StringBuilder cleanup(IJSONNode node) { |
| short oldStrategy = strategy; |
| strategy = CLEANUP; |
| StringBuilder source = formatProc(node); |
| strategy = oldStrategy; |
| return source; |
| } |
| |
| @Override |
| public final StringBuilder cleanup(IJSONNode node, IRegion region) { |
| short oldStrategy = strategy; |
| strategy = CLEANUP; |
| StringBuilder source = formatProc(node, region); |
| strategy = oldStrategy; |
| |
| return source; |
| } |
| |
| protected String decoratedIdentRegion(CompoundRegion region, |
| IJSONCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| String text = null; |
| if (!stgy.isFormatSource()) |
| text = region.getFullText(); |
| else |
| text = region.getText(); |
| |
| // if (region.getType() == JSONRegionContexts.WHITE_SPACETRING |
| // || region.getType() == JSONRegionContexts.JSON_URI) |
| // return decoratedRegion(region, 0, stgy); |
| |
| if (isCleanup()) { |
| if (stgy.getIdentCase() == IJSONCleanupStrategy.ASIS |
| /*|| region.getType() == JSONRegionContexts.JSON_COMMENT*/) |
| return text; |
| else if (stgy.getIdentCase() == IJSONCleanupStrategy.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| // if (region.getType() == JSONRegionContexts.JSON_COMMENT) |
| // return text; |
| // else if (preferences.getInt(JSONCorePreferenceNames.CASE_IDENTIFIER) == JSONCorePreferenceNames.UPPER) |
| // return text.toUpperCase(); |
| // else |
| return text.toLowerCase(); |
| } |
| |
| protected String decoratedPropNameRegion(CompoundRegion region, |
| IJSONCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| String text = null; |
| if (!stgy.isFormatSource()) |
| text = region.getFullText(); |
| else |
| text = region.getText(); |
| |
| // if (region.getType() == JSONRegionContexts.WHITE_SPACETRING |
| // || region.getType() == JSONRegionContexts.JSON_URI) |
| // return decoratedRegion(region, 1, stgy); |
| if (isCleanup()) { |
| /*if (stgy.getPropNameCase() == JSONCleanupStrategy.ASIS |
| || region.getType() != JSONRegionContexts.JSON_DECLARATION_PROPERTY) |
| return text; |
| else*/ |
| if (stgy.getPropNameCase() == IJSONCleanupStrategy.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| |
| // if (region.getType() != JSONRegionContexts.JSON_DECLARATION_PROPERTY) |
| // return text; |
| //else |
| if (preferences.getInt(JSONCorePreferenceNames.CASE_PROPERTY_NAME) == JSONCorePreferenceNames.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| |
| protected String decoratedPropValueRegion(CompoundRegion region, |
| IJSONCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| String text = null; |
| if (!stgy.isFormatSource()) |
| text = region.getFullText(); |
| else |
| text = region.getText(); |
| |
| String type = region.getType(); |
| // if (type == JSONRegionContexts.WHITE_SPACETRING |
| // || type == JSONRegionContexts.JSON_URI |
| // || type == JSONRegionContexts.JSON_DECLARATION_VALUE_URI) |
| // return decoratedRegion(region, 2, stgy); |
| if (isCleanup()) { |
| if (stgy.getPropValueCase() != IJSONCleanupStrategy.ASIS) { |
| //if (type == JSONRegionContexts.JSON_COMMENT) { |
| //} else { |
| if (stgy.getPropValueCase() == IJSONCleanupStrategy.UPPER) |
| text = text.toUpperCase(); |
| else |
| text = text.toLowerCase(); |
| //} |
| } |
| } |
| return text; |
| } |
| |
| protected String decoratedRegion(CompoundRegion region, int type, |
| IJSONCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| |
| String text = null; |
| if (!stgy.isFormatSource()) |
| text = region.getFullText(); |
| else |
| text = region.getText(); |
| return text; |
| } |
| |
| @Override |
| public final StringBuilder format(IJSONNode node) { |
| short oldStrategy = strategy; |
| strategy = FORMAT; |
| StringBuilder source = formatProc(node); |
| strategy = oldStrategy; |
| |
| return source; |
| } |
| |
| @Override |
| public final StringBuilder format(IJSONNode node, IRegion region) { |
| short oldStrategy = strategy; |
| strategy = FORMAT; |
| StringBuilder source = formatProc(node, region); |
| strategy = oldStrategy; |
| return source; |
| } |
| |
| protected void formatChildren(IJSONNode node, StringBuilder source) { |
| IJSONNode child = node.getFirstChild(); |
| IJSONNode last = null; |
| while (child != null) { |
| // append child |
| IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) child) |
| .getAdapterFor(IJSONSourceFormatter.class); |
| if (formatter == null) { |
| formatter = JSONSourceFormatterFactory.getInstance() |
| .getSourceFormatter(child); |
| } |
| StringBuilder childSource = ((AbstractJSONSourceFormatter) formatter) |
| .formatProc(child); |
| source.append(childSource); |
| last = child; |
| child = child.getNextSibling(); |
| } |
| } |
| |
| protected void formatChildren(IJSONNode node, IRegion region, |
| StringBuilder source) { |
| IJSONNode child = node.getFirstChild(); |
| int start = region.getOffset(); |
| int end = region.getOffset() + region.getLength(); |
| while (child != null) { |
| int curEnd = child.getEndOffset(); |
| StringBuilder childSource = null; |
| boolean toFinish = false; |
| if (start < curEnd) { |
| int curStart = child.getStartOffset(); |
| if (curStart < end) { |
| // append child |
| IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) child) |
| .getAdapterFor(IJSONSourceFormatter.class); |
| if (formatter == null) { |
| formatter = JSONSourceFormatterFactory.getInstance() |
| .getSourceFormatter(child); |
| } |
| if (includes(region, curStart, curEnd)) |
| childSource = ((AbstractJSONSourceFormatter) formatter) |
| .formatProc(child); |
| else |
| childSource = ((AbstractJSONSourceFormatter) formatter) |
| .formatProc( |
| child, |
| overlappedRegion(region, curStart, |
| curEnd)); |
| } else |
| toFinish = true; |
| } |
| if (childSource != null) { |
| source.append(childSource); |
| } |
| if (toFinish) |
| break; |
| child = child.getNextSibling(); |
| } |
| } |
| |
| /** |
| * Generate or format source after the last child and append to string |
| * buffer |
| */ |
| protected abstract void formatPost(IJSONNode node, StringBuilder source); |
| |
| /** |
| * Generate or format source after the last child and append to string |
| * buffer |
| */ |
| protected abstract void formatPost(IJSONNode node, IRegion region, |
| StringBuilder source); |
| |
| /** |
| * Generate or format source before the first child and append to string |
| * buffer |
| */ |
| protected abstract void formatPre(IJSONNode node, StringBuilder source); |
| |
| /** |
| * Generate or format source before the first child and append to string |
| * buffer |
| */ |
| abstract protected void formatPre(IJSONNode node, IRegion region, |
| StringBuilder source); |
| |
| /** |
| * |
| * @return java.lang.StringBuilder |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.IJSONNode |
| */ |
| protected final StringBuilder formatProc(IJSONNode node) { |
| StringBuilder source = new StringBuilder(); |
| formatPre(node, source); |
| formatChildren(node, source); |
| formatPost(node, source); |
| return source; |
| } |
| |
| /** |
| * |
| * @return java.lang.StringBuilder |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.IJSONNode |
| * @param region |
| * org.eclipse.jface.text.IRegion |
| */ |
| protected StringBuilder formatProc(IJSONNode node, IRegion region) { |
| StringBuilder source = new StringBuilder(); |
| int curStart = node.getStartOffset(); |
| int curEnd = node.getEndOffset(); |
| if (node.hasChildNodes()) { |
| curEnd = node.getFirstChild().getStartOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPre(node, source); |
| else |
| formatPre(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| curStart = curEnd; |
| curEnd = node.getLastChild().getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatChildren(node, source); |
| else |
| formatChildren(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| curStart = curEnd; |
| curEnd = node.getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPost(node, source); |
| else |
| formatPost(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| } else if (node instanceof IJSONArray || node instanceof IJSONObject) { |
| curStart = node.getStartOffset(); |
| curEnd = node.getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) { |
| formatPre(node, source); |
| formatPost(node, source); |
| } else { |
| formatPre(node, overlappedRegion(region, curStart, curEnd), source); |
| formatPost(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| } |
| } else { |
| // curEnd = getChildInsertPos(node); |
| curEnd = node.getEndOffset() > 0 ? node.getEndOffset() : -1; |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPre(node, source); |
| else |
| formatPre(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| curStart = curEnd; |
| curEnd = node.getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPost(node, source); |
| else |
| formatPost(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| } |
| return source; |
| } |
| |
| /** |
| * Insert the method's description here. |
| * |
| * @return org.eclipse.wst.css.core.internal.cleanup.JSONCleanupStrategy |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.IJSONNode |
| */ |
| protected IJSONCleanupStrategy getCleanupStrategy(IJSONNode node) { |
| IJSONCleanupStrategy currentStrategy = JSONCleanupStrategyImpl |
| .getInstance(); |
| IJSONDocument doc = node.getOwnerDocument(); |
| if (doc == null) |
| return currentStrategy; |
| IJSONModel model = doc.getModel(); |
| if (model == null) |
| return currentStrategy; |
| return currentStrategy; |
| } |
| |
| protected String getIndent(IJSONNode node) { |
| if (node == null) |
| return "";//$NON-NLS-1$ |
| IJSONNode parent = node.getParentNode(); |
| if (parent == null || parent instanceof IJSONDocument) |
| return "";//$NON-NLS-1$ |
| String parentIndent = getIndent(parent); |
| return parentIndent + getIndentString(); |
| } |
| |
| protected int getLastLineLength(IJSONNode node, StringBuilder source) { |
| if (node == null || source == null) |
| return 0; |
| String delim = getLineDelimiter(node); |
| String str = new String(source); |
| int n = str.lastIndexOf(delim); |
| if (n < 0) |
| return str.length(); |
| |
| return str.length() - n - delim.length(); |
| } |
| |
| String getLineDelimiter(IJSONNode node) { |
| IJSONModel model = node != null ? |
| node.getOwnerDocument().getModel() : |
| null; |
| IStructuredDocument structuredDocument = model != null ? |
| model.getStructuredDocument() : |
| null; |
| return structuredDocument != null ? |
| structuredDocument.getLineDelimiter() : |
| "\n"; //$NON-NLS-1$ |
| } |
| |
| protected CompoundRegion[] getOutsideRegions(IStructuredDocument model, |
| IRegion reg) { |
| CompoundRegion[] ret = new CompoundRegion[2]; |
| RegionIterator it = new RegionIterator(model, reg.getOffset()); |
| it.prev(); |
| if (it.hasPrev()) { |
| ITextRegion textRegion = it.prev(); |
| IStructuredDocumentRegion documentRegion = it |
| .getStructuredDocumentRegion(); |
| ret[0] = new CompoundRegion(documentRegion, textRegion); |
| } else { |
| ret[0] = null; |
| } |
| it.reset(model, reg.getOffset() + reg.getLength()); |
| if (it.hasNext()) { |
| ITextRegion textRegion = it.next(); |
| IStructuredDocumentRegion documentRegion = it |
| .getStructuredDocumentRegion(); |
| ret[1] = new CompoundRegion(documentRegion, textRegion); |
| } else { |
| ret[1] = null; |
| } |
| return ret; |
| } |
| |
| protected IJSONSourceFormatter getParentFormatter(IJSONNode node) { |
| IJSONNode parent = node.getParentNode(); |
| if (parent != null) { |
| IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) parent) |
| .getAdapterFor(IJSONSourceFormatter.class); |
| if (formatter == null) { |
| formatter = JSONSourceFormatterFactory.getInstance() |
| .getSourceFormatter(parent); |
| } |
| return formatter; |
| } |
| return null; |
| } |
| |
| protected CompoundRegion[] getRegions(IStructuredDocument model, |
| IRegion reg, IRegion exceptFor, String pickupType) { |
| int start = reg.getOffset(); |
| int end = reg.getOffset() + reg.getLength(); |
| int startE = (exceptFor != null) ? exceptFor.getOffset() : -1; |
| int endE = (exceptFor != null) ? exceptFor.getOffset() |
| + exceptFor.getLength() : 0; |
| |
| ArrayList list = new ArrayList(); |
| IStructuredDocumentRegion flatNode = model |
| .getRegionAtCharacterOffset(start); |
| boolean pickuped = false; |
| while (flatNode != null && flatNode.getStartOffset() < end) { |
| ITextRegionList regionList = flatNode.getRegions(); |
| Iterator it = regionList.iterator(); |
| while (it.hasNext()) { |
| ITextRegion region = (ITextRegion) it.next(); |
| if (flatNode.getStartOffset(region) < start) |
| continue; |
| if (end <= flatNode.getStartOffset(region)) |
| break; |
| if (startE >= 0 && startE <= flatNode.getStartOffset(region) |
| && flatNode.getEndOffset(region) <= endE) |
| continue; |
| // if (region.getType() == JSONRegionContexts.JSON_COMMENT |
| // || region.getType() == JSONRegionContexts.JSON_CDC |
| // || region.getType() == JSONRegionContexts.JSON_CDO) |
| // list.add(new CompoundRegion(flatNode, region)); |
| // else |
| if (!pickuped && region.getType() == pickupType) { |
| list.add(new CompoundRegion(flatNode, region)); |
| pickuped = true; |
| } |
| } |
| flatNode = flatNode.getNext(); |
| } |
| if (list.size() > 0) { |
| CompoundRegion[] regions = new CompoundRegion[list.size()]; |
| list.toArray(regions); |
| return regions; |
| } |
| return new CompoundRegion[0]; |
| } |
| |
| protected CompoundRegion[] getRegionsWithoutWhiteSpaces( |
| IStructuredDocument model, IRegion reg, IJSONCleanupStrategy stgy) { |
| int start = reg.getOffset(); |
| int end = reg.getOffset() + reg.getLength() - 1; |
| ArrayList list = new ArrayList(); |
| IStructuredDocumentRegion flatNode = model |
| .getRegionAtCharacterOffset(start); |
| while (flatNode != null && flatNode.getStartOffset() <= end) { |
| ITextRegionList regionList = flatNode.getRegions(); |
| Iterator it = regionList.iterator(); |
| while (it.hasNext()) { |
| ITextRegion region = (ITextRegion) it.next(); |
| if (flatNode.getStartOffset(region) < start) |
| continue; |
| if (end < flatNode.getStartOffset(region)) |
| break; |
| if (region.getType() != JSONRegionContexts.WHITE_SPACE |
| || (isCleanup() && !stgy.isFormatSource())) // for |
| // not |
| // formatting |
| // case |
| // on |
| // cleanup |
| // action |
| list.add(new CompoundRegion(flatNode, region)); |
| } |
| flatNode = flatNode.getNext(); |
| } |
| if (list.size() > 0) { |
| CompoundRegion[] regions = new CompoundRegion[list.size()]; |
| list.toArray(regions); |
| return regions; |
| } |
| return new CompoundRegion[0]; |
| } |
| |
| public static boolean includes(IRegion region, int start, int end) { |
| if (region == null) |
| return false; |
| |
| return (region.getOffset() <= start) |
| && (end <= region.getOffset() + region.getLength()); |
| } |
| |
| /** |
| * |
| * @return boolean |
| */ |
| protected static boolean isCleanup() { |
| return strategy == CLEANUP; |
| } |
| |
| /** |
| * |
| * @return boolean |
| */ |
| protected static boolean isFormat() { |
| return strategy == FORMAT; |
| } |
| |
| protected boolean isIncludesPreEnd(IJSONNode node, IRegion region) { |
| return (node.getFirstChild() != null && ((IndexedRegion) node |
| .getFirstChild()).getStartOffset() == (region.getOffset() + region |
| .getLength())); |
| } |
| |
| static protected boolean needS(CompoundRegion region) { |
| return (region != null && region.getType() != JSONRegionContexts.WHITE_SPACE); |
| } |
| |
| public static IRegion overlappedRegion(IRegion region, int start, int end) { |
| if (overlaps(region, start, end)) { |
| int offset = (region.getOffset() <= start) ? start : region |
| .getOffset(); |
| int length = ((end <= region.getOffset() + region.getLength()) ? end |
| : region.getOffset() + region.getLength()) |
| - offset; |
| return new FormatRegion(offset, length); |
| } |
| return null; |
| } |
| |
| public static boolean overlaps(IRegion region, int start, int end) { |
| if (region == null) |
| return false; |
| |
| return (start < region.getOffset() + region.getLength()) |
| && (region.getOffset() < end); |
| } |
| |
| protected String getIndentString() { |
| StringBuilder indent = new StringBuilder(); |
| |
| Preferences preferences = JSONCorePlugin.getDefault() |
| .getPluginPreferences(); |
| if (preferences != null) { |
| char indentChar = ' '; |
| String indentCharPref = preferences |
| .getString(JSONCorePreferenceNames.INDENTATION_CHAR); |
| if (JSONCorePreferenceNames.TAB.equals(indentCharPref)) { |
| indentChar = '\t'; |
| } |
| int indentationWidth = preferences |
| .getInt(JSONCorePreferenceNames.INDENTATION_SIZE); |
| |
| for (int i = 0; i < indentationWidth; i++) { |
| indent.append(indentChar); |
| } |
| } |
| return indent.toString(); |
| } |
| |
| protected void formatValue(IJSONNode node, StringBuilder source, IJSONNode value) { |
| IJSONCleanupStrategy stgy = getCleanupStrategy(node); |
| IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); |
| int start = node.getStartOffset(); |
| int end = node.getEndOffset(); |
| CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, |
| new FormatRegion(start, end - start), stgy); |
| if (regions.length > 2) { |
| for (int i = 2; i < regions.length; i++) { |
| source.append(decoratedRegion(regions[i], 0, stgy)); |
| } |
| } |
| } |
| |
| protected void formatObject(IJSONNode node, StringBuilder source, IJSONNode jsonObject) { |
| IJSONCleanupStrategy stgy = getCleanupStrategy(node); |
| IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); |
| IStructuredDocumentRegion[] structuredRegions = structuredDocument |
| .getStructuredDocumentRegions(node.getStartOffset(), node.getEndOffset()); |
| if (structuredRegions.length >= 2) { |
| int start = structuredRegions[1].getStartOffset(); |
| int end = node.getEndOffset(); |
| IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) jsonObject) |
| .getAdapterFor(IJSONSourceFormatter.class); |
| if (formatter == null) { |
| formatter = JSONSourceFormatterFactory.getInstance().getSourceFormatter(jsonObject); |
| } |
| StringBuilder objectSource = formatter.format(jsonObject, new FormatRegion(start, end - start)); |
| if (objectSource != null) { |
| source.append(objectSource); |
| } |
| } |
| } |
| |
| } |