/******************************************************************************* * Copyright (c) 2016 ARM Ltd. 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: * ARM Ltd and ARM Germany GmbH - Initial API and implementation *******************************************************************************/ package com.arm.cmsis.parser; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.rules.IToken; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import com.arm.cmsis.config.Messages; import com.arm.cmsis.config.model.ConfigWizardItem; import com.arm.cmsis.config.model.IConfigWizardItem; import com.arm.cmsis.config.model.IConfigWizardItem.EItemErrorType; import com.arm.cmsis.config.model.IConfigWizardItem.EItemType; import com.arm.cmsis.parser.ConfigWizardScanner.ETokenType; import com.arm.cmsis.utils.Utils; /** * Parser for the config wizard */ public class ConfigWizardParser { private static final int LAST_CONFIG_WIZARD_START_LINE = 100; private ConfigWizardScanner fScanner; private IDocument fDocument; private boolean fContainWizard; private int fParsingErrorOffset; private IConfigWizardItem fRoot; private int fStartParseOffset; private int fEndParseOffset; private IToken cToken; // current token private ETokenType cType; // current token type private TreeMap<Integer, String> fNumberContainer; // offset->number string private TreeMap<Integer, String> fStringContainer; // offset->string private TreeMap<Integer, String> fCommentContainer; // offset->string String fParsingErrorMessage; /** * Parser for the config wizard */ public ConfigWizardParser(ConfigWizardScanner scanner, IDocument document) { fScanner = scanner; fDocument = document; fContainWizard = false; fNumberContainer = new TreeMap<>(); fStringContainer = new TreeMap<>(); fCommentContainer = new TreeMap<>(); initialize(); } public void clear() { fScanner.clear(); fParsingErrorOffset = -1; fContainWizard = false; fRoot = null; fStartParseOffset = fEndParseOffset = fDocument.getLength(); cToken = null; cType = null; fNumberContainer.clear(); fStringContainer.clear(); fCommentContainer.clear(); } protected void initialize() { fStartParseOffset = fEndParseOffset = fDocument.getLength(); findConfigurationWizard(); } public IConfigWizardItem parse() { clear(); setParseRange(0, fDocument.getLength()); return doParse(); } public boolean findConfigurationWizard() { int offset = fDocument.getLength(); try { offset = fDocument.getLineOffset(LAST_CONFIG_WIZARD_START_LINE); } catch (BadLocationException e) { } clear(); setParseRange(0, offset); IToken token = fScanner.nextToken(); while (!token.isEOF() && fScanner.getTokenType(token) != ETokenType.START) { token = fScanner.nextToken(); } if (fScanner.getTokenType(token) != ETokenType.START) { fContainWizard = false; } else { fContainWizard = true; } return fContainWizard; } private void setParseRange(int start, int end) { fScanner.setRange(fDocument, start, end); } private IConfigWizardItem doParse() { fRoot = new ConfigWizardItem(EItemType.ROOT, 0, null); fRoot.setName("Root"); //$NON-NLS-1$ IToken token = fScanner.nextToken(); while (!token.isEOF() && fScanner.getTokenType(token) != ETokenType.START) { token = fScanner.nextToken(); } if (token.isEOF()) { return null; } // Start parsing!!! fContainWizard = true; fStartParseOffset = fScanner.getTokenOffset(); fRoot = parseItems(fRoot, ETokenType.EOC); if (fRoot != null) { setValuesAndStrings(); } return fRoot; } protected IConfigWizardItem parseItems(IConfigWizardItem parent, ETokenType endToken) { getNextToken(); while (cType != ETokenType.EOC && cType != ETokenType.HEADING_END && cType != ETokenType.HEADING_ENABLE_END && cType != ETokenType.CODE_END) { ETokenType type = cType; IConfigWizardItem child = parseItem(parent); if (child == null || cType == ETokenType.UNKNOWN || cType == ETokenType.COMMENT || cType == ETokenType.BLOCK_COMMENT) { if (fParsingErrorOffset == -1) { fParsingErrorMessage = NLS.bind(Messages.ConfigWizardParser_WrongTokenFormat, fScanner.getTokenContent(cToken)); syntaxError(); } return null; } if (type != ETokenType.TOOLTIP && type != ETokenType.NUMBER && type != ETokenType.STRING && type != ETokenType.START) { parent.addChild(child); } } if (cType == endToken) { parent.setEndLine(fScanner.getCurrentLineNumber()); if (cType == ETokenType.CODE_END) { parent = analyseCodeContent(parent); } return parent; } if (endToken == ETokenType.EOC) { fEndParseOffset = fScanner.getTokenOffset(); } fParsingErrorMessage = Messages.ConfigWizardParser_WrongEndingToken + getTokenTypeString(endToken); fParsingErrorMessage += Messages.ConfigWizardParser_RealIs + getTokenTypeString(cType); syntaxError(); return null; } protected IConfigWizardItem parseItem(IConfigWizardItem parent) { IConfigWizardItem item; switch (cType) { case HEADING: return parseHeading(parent); case HEADING_ENABLE: return parseHeadingEnable(parent); case CODE_ENABLE: case CODE_DISABLE: return parseCodeComment(parent); case OPTION: item = new ConfigWizardItem(EItemType.OPTION, fScanner.getCurrentLineNumber(), parent); return parseOption(item); case OPTION_CHECK: item = new ConfigWizardItem(EItemType.OPTION_CHECK, fScanner.getCurrentLineNumber(), parent); return parseOption(item); case OPTION_STRING: item = new ConfigWizardItem(EItemType.OPTION_STRING, fScanner.getCurrentLineNumber(), parent); return parseOptionString(item); case NOTIFICATION: item = new ConfigWizardItem(EItemType.NOTIFICATION, fScanner.getCurrentLineNumber(), parent); return parseNotification(item); case TOOLTIP: item = parent.getLastChild(); if (item == null) { item = parent; } return parseTooltip(item); case NUMBER: parseNumber(); return parent; case STRING: parseString(); return parent; case START: getNextToken(); return parent; default: fParsingErrorMessage = NLS.bind(Messages.ConfigWizardParser_UnknownTokenType, fScanner.getTokenContent(cToken)); syntaxError(); break; } return null; } protected IConfigWizardItem parseHeading(IConfigWizardItem parent) { Assert.isTrue(cType == ETokenType.HEADING); String tokenContent = fScanner.getTokenContent(cToken); if (!tokenContent.toLowerCase().equals("h")) { //$NON-NLS-1$ fParsingErrorMessage = Messages.ConfigWizardParser_WrongHeadTokenFormat + tokenContent; fParsingErrorMessage += Messages.ConfigWizardParser_CorrectTokenFormat + "<h>"; //$NON-NLS-1$ syntaxError(); return null; } IConfigWizardItem heading = new ConfigWizardItem(EItemType.HEADING, fScanner.getCurrentLineNumber(), parent); heading.setName(fScanner.readLine()); heading = parseItems(heading, ETokenType.HEADING_END); getNextToken(); return heading; } protected IConfigWizardItem parseHeadingEnable(IConfigWizardItem parent) { Assert.isTrue(cType == ETokenType.HEADING_ENABLE); IConfigWizardItem headingEnable = new ConfigWizardItem(EItemType.HEADING_ENABLE, fScanner.getCurrentLineNumber(), parent); String tokenContent = fScanner.getTokenContent(cToken); headingEnable = parseModifier(headingEnable, tokenContent.substring(1)); if (headingEnable != null) { headingEnable.setName(fScanner.readLine()); headingEnable = parseItems(headingEnable, ETokenType.HEADING_ENABLE_END); } getNextToken(); return headingEnable; } protected IConfigWizardItem parseCodeComment(IConfigWizardItem parent) { Assert.isTrue(cType == ETokenType.CODE_ENABLE || cType == ETokenType.CODE_DISABLE); EItemType itemType = EItemType.CODE_ENABLE; if (cType == ETokenType.CODE_DISABLE) { itemType = EItemType.CODE_DISABLE; } IConfigWizardItem codeComment = new ConfigWizardItem(itemType, fScanner.getCurrentLineNumber(), parent); String tokenContent = fScanner.getTokenContent(cToken); String modifier = tokenContent.substring(1); if (itemType == EItemType.CODE_DISABLE) { codeComment.setInvertValue(true); modifier = tokenContent.substring(2); } codeComment = parseModifier(codeComment, modifier); if (codeComment != null) { codeComment.setName(fScanner.readLine()); codeComment = parseItems(codeComment, ETokenType.CODE_END); } getNextToken(); return codeComment; } protected IConfigWizardItem parseModifier(IConfigWizardItem item, String modifier) { item.setMinBit(0); item.setMaxBit(Long.SIZE - 1); item.setSkipNumber(0); item.setMinValue(Long.MIN_VALUE); item.setMaxValue(Long.MAX_VALUE); item.setBase(16); if (modifier.length() == 0) { return item; } if (modifier.startsWith(".")) { //$NON-NLS-1$ modifier = "0" + modifier; //$NON-NLS-1$ } if (!modifier.matches("\\d+((\\.\\d+)?|(\\.\\d+(\\.\\.\\d+)?))")) { //$NON-NLS-1$ fParsingErrorMessage = Messages.ConfigWizardParser_WrongModificationFormat + modifier; fParsingErrorMessage += Messages.ConfigWizardParser_CorrectTokenFormat + "\\d+((\\.\\d+)?|(\\.\\d+(\\.\\.\\d+)?))"; //$NON-NLS-1$ syntaxError(); return null; } try { int dotPos = modifier.indexOf('.'); if (dotPos == -1) { dotPos = modifier.length(); } int skipNumber = Integer.parseInt(modifier.substring(0, dotPos)); item.setSkipNumber(skipNumber); if (dotPos == modifier.length()) { return item; } int ddotPos = modifier.indexOf(".."); //$NON-NLS-1$ if (ddotPos == -1) { ddotPos = modifier.length(); } int minBit = Integer.parseInt(modifier.substring(dotPos + 1, ddotPos)); item.setMinBit(minBit); item.setMaxBit(minBit); if (ddotPos == modifier.length()) { if (item.getItemType() == EItemType.OPTION) { item.setItemType(EItemType.OPTION_CHECK); } return item; } int maxBit = Integer.parseInt(modifier.substring(ddotPos + 2, modifier.length())); if (maxBit < minBit) { maxBit = minBit; } item.setMaxBit(maxBit); if (item.getMinBit() == item.getMaxBit() && item.getItemType() == EItemType.OPTION) { item.setItemType(EItemType.OPTION_CHECK); } } catch (NumberFormatException e) { fParsingErrorMessage = Messages.ConfigWizardParser_FailToParseModifierNumber + modifier; syntaxError(); return null; } return item; } protected IConfigWizardItem parseOption(IConfigWizardItem item) { Assert.isTrue(cType == ETokenType.OPTION || cType == ETokenType.OPTION_CHECK); String tokenContent = fScanner.getTokenContent(cToken); item = parseModifier(item, tokenContent.substring(1)); if (item != null) { String name = fScanner.readString(); item.setName(name); getNextToken(); while (cType == ETokenType.TOOLTIP) { parseTooltip(item); } if (cType != ETokenType.EOC) { tokenContent = fScanner.getTokenContent(cToken); if (parseRangeOrSelection(item, tokenContent) == null) { return null; } if (!item.isSelection()) { tokenContent = fScanner.getTokenContent(cToken); if (parseModification(item, tokenContent) == null) { return null; } } else { item.setItemType(EItemType.OPTION_SELECT); } } } return item; } // jump to next token afterwards if cToken is a range or selection token protected IConfigWizardItem parseRangeOrSelection(IConfigWizardItem item, String rangeOrSelection) { item.setBase(16); int radix = 16; String numRegex = "(0[xX][0-9a-fA-F]+|\\d+)"; //$NON-NLS-1$ String rangeRegex = numRegex + "-" + numRegex + "(:" + numRegex + ")?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ String selectRegex = numRegex + "="; //$NON-NLS-1$ if (!rangeOrSelection.matches(rangeRegex) && !rangeOrSelection.matches(selectRegex)) { return item; } if (!rangeOrSelection.toLowerCase().contains("0x")) { //$NON-NLS-1$ radix = 10; item.setBase(10); item.setMinValue(Long.MIN_VALUE); } try { if (rangeOrSelection.matches(rangeRegex)) { rangeOrSelection = rangeOrSelection.toLowerCase().replace("0x", ""); //$NON-NLS-1$ //$NON-NLS-2$ int hypPos = rangeOrSelection.indexOf('-'); long minValue = Long.parseLong(rangeOrSelection.substring(0, hypPos), radix); item.setMinValue(minValue); item.setMaxValue(minValue); int colPos = rangeOrSelection.indexOf(':'); if (colPos == -1) { colPos = rangeOrSelection.length(); } long maxValue = Long.parseLong(rangeOrSelection.substring(hypPos + 1, colPos), radix); item.setMaxValue(maxValue); if (colPos < rangeOrSelection.length()) { long spinStep = Long.parseLong(rangeOrSelection.substring(colPos + 1), radix); item.setSpinStep(spinStep); } // The next line added just to be consistent with the // selection part, so that // after this method the token is already the next one getNextToken(); } else { item = parseSelection(item); return item; } return item; } catch (NumberFormatException e) { fParsingErrorMessage = Messages.ConfigWizardParser_WrongRangeSelectionFormat + rangeOrSelection; fParsingErrorMessage += Messages.ConfigWizardParser_CorrectTokenFormat + rangeRegex + Messages.ConfigWizardParser_Range + selectRegex + Messages.ConfigWizardParser_Selection; syntaxError(); return null; } } // jump to next token afterwards if cToken is a selection token protected IConfigWizardItem parseSelection(IConfigWizardItem parent) { Assert.isTrue(cType == ETokenType.VALUE); parent.setIsSelection(true); while (cType == ETokenType.VALUE) { String value = fScanner.getTokenContent(cToken); int equPos = value.indexOf('='); int radix = 10; String number = value.substring(0, equPos).toLowerCase(); if (number.startsWith("0x")) { //$NON-NLS-1$ radix = 16; number = number.substring(2); } long key = Long.parseLong(number, radix); String name = fScanner.readString(); parent.addItem(key, name); getNextToken(); } if (cType == ETokenType.UNKNOWN) { fParsingErrorMessage = NLS.bind(Messages.ConfigWizardParser_WrongSelectionToken, fScanner.getTokenContent(cToken)); fParsingErrorMessage += Messages.ConfigWizardParser_CorrectTokenFormat + getTokenTypeString(ETokenType.VALUE); syntaxError(); return null; } return parent; } protected IConfigWizardItem parseModification(IConfigWizardItem item, String modification) { String regex = "#(\\+|-|\\*|/)(0[xX]|)\\d+"; //$NON-NLS-1$ if (modification.matches(regex)) { try { char m = modification.charAt(1); item.setModification(m); int radix = 10; String number = modification.substring(2).toLowerCase(); if (number.startsWith("0x")) { //$NON-NLS-1$ radix = 16; number = number.substring(2); } long modifier = Long.parseLong(number, radix); if ((m == '/' || m == '*') && modifier == 0) { modifier = 1; } item.setModifier(modifier); getNextToken(); } catch (NumberFormatException e) { fParsingErrorMessage = Messages.ConfigWizardParser_WrongModificationFormat + modification; fParsingErrorMessage += Messages.ConfigWizardParser_CorrectTokenFormat + regex; syntaxError(); return null; } } return item; } protected IConfigWizardItem parseTooltip(IConfigWizardItem item) { Assert.isTrue(cType == ETokenType.TOOLTIP); if (item == null) { fParsingErrorMessage = Messages.ConfigWizardParser_TooltipForUnknownConfigItem; syntaxError(); return null; } String moreTooltip = fScanner.readLine(); item.extendTooltip(moreTooltip); getNextToken(); return item; } protected IConfigWizardItem parseOptionString(IConfigWizardItem item) { Assert.isTrue(cType == ETokenType.OPTION_STRING); String tokenContent = fScanner.getTokenContent(cToken); item = parseStringModifier(item, tokenContent.substring(1)); if (item != null) { String name = fScanner.readString(); item.setName(name); getNextToken(); while (cType == ETokenType.TOOLTIP) { parseTooltip(item); } } return item; } protected IConfigWizardItem parseStringModifier(IConfigWizardItem item, String modifier) { if (modifier.length() == 0) { return item; } if (modifier.startsWith(".")) { //$NON-NLS-1$ modifier = "0" + modifier; //$NON-NLS-1$ } if (!modifier.matches("\\d+(\\.\\d+)?")) { //$NON-NLS-1$ fParsingErrorMessage = Messages.ConfigWizardParser_WrongModifierFormat + modifier; fParsingErrorMessage += Messages.ConfigWizardParser_CorrectTokenFormat + "\\d+(\\.\\d+)?"; //$NON-NLS-1$ syntaxError(); return null; } try { int dotPos = modifier.indexOf('.'); if (dotPos == -1) { dotPos = modifier.length(); } int skipNumber = Integer.parseInt(modifier.substring(0, dotPos)); item.setSkipNumber(skipNumber); if (dotPos == modifier.length()) { return item; } int length = Integer.parseInt(modifier.substring(dotPos + 1)); item.setStringLength(length); } catch (NumberFormatException e) { fParsingErrorMessage = Messages.ConfigWizardParser_FailToParseModifierNumber + modifier; syntaxError(); return null; } return item; } protected IConfigWizardItem parseNotification(IConfigWizardItem item) { item.setName(fScanner.readLine()); getNextToken(); while (cType == ETokenType.TOOLTIP) { parseTooltip(item); } return item; } protected void parseNumber() { String tokenContent = fScanner.getTokenContent(cToken); fNumberContainer.put(fScanner.getTokenOffset(), tokenContent); getNextToken(); } protected void parseString() { String tokenContent = fScanner.getTokenContent(cToken); fStringContainer.put(fScanner.getTokenOffset() + 1, tokenContent.substring(1, tokenContent.length() - 1)); getNextToken(); } private long parseNumber(IConfigWizardItem item, String number) { int radix = 10; number = number.toLowerCase(); if (number.startsWith("0x")) { //$NON-NLS-1$ radix = 16; number = number.substring(2); } else if (number.startsWith("0b")) { //$NON-NLS-1$ radix = 2; number = number.substring(2); } else if (number.startsWith("0") && number.length() > 1 && //$NON-NLS-1$ Character.isDigit(number.charAt(number.length() - 1))) { radix = 8; number = number.substring(1); } char lastChar = number.charAt(number.length() - 1); if (radix != 16 && radix != 2 && radix != 8 && !Character.isDigit(lastChar)) { switch (lastChar) { case 'b': case 'B': radix = 2; break; case 'o': case 'O': case 'q': case 'Q': radix = 8; break; case 'd': case 'D': radix = 10; break; case 'h': case 'H': radix = 16; break; default: item.setItemErrorType(EItemErrorType.NUMBER_PARSE_ERROR); return 0; } number = number.substring(0, number.length() - 1); } // For 2,8,16 based numbers, min value could not be negative if (radix == 2 || radix == 8 || radix == 16) { if (item.getMinValue() <= 0) { item.setMinValue(0); } } try { long i = Long.parseLong(number, radix); item.setItemErrorType(EItemErrorType.NO_ERROR); return i; } catch (NumberFormatException e) { item.setItemErrorType(EItemErrorType.NUMBER_PARSE_ERROR); return 0; } } protected void syntaxError() { fParsingErrorOffset = fScanner.getTokenOffset(); int line = fScanner.getCurrentLineNumber(); Display.getDefault().asyncExec(() -> MessageDialog.openError(Display.getDefault().getActiveShell(), Messages.ConfigWizardParser_ErrorInConfigWizard, Messages.ConfigWizardParser_SyntaxErrorAtLine + (line + 1) + ": " + fParsingErrorMessage)); //$NON-NLS-1$ } private IConfigWizardItem analyseCodeContent(IConfigWizardItem item) { try { int skip = item.getSkipNumber() + 1; int startLine = item.getStartLine() + skip; int startOffset = fDocument.getLineOffset(startLine); int endLine = item.getEndLine(); int endOffset = fDocument.getLineOffset(endLine); if (startLine >= endLine) { fParsingErrorMessage = Messages.ConfigWizardParser_SkipTooManyLines; syntaxError(); return null; } String codeContent = fDocument.get(startOffset, endOffset - startOffset); String[] lines = codeContent.split("\\r?\\n"); //$NON-NLS-1$ Set<Boolean> lineCommented = new HashSet<>(); for (String line : lines) { if (line.trim().startsWith("//") || line.trim().startsWith("/*")) { //$NON-NLS-1$ //$NON-NLS-2$ lineCommented.add(true); if (line.trim().startsWith("//")) { //$NON-NLS-1$ int moreOffset = line.indexOf("//"); //$NON-NLS-1$ int slashLength = 2; while (line.length() > moreOffset + slashLength && line.charAt(moreOffset + slashLength) == '/') { slashLength++; } int slashBegin = startOffset + moreOffset; fCommentContainer.put(slashBegin, fDocument.get(slashBegin, slashLength)); } } else { int spaceCount = 0; for (char c : line.toCharArray()) { if (c == ' ') { spaceCount++; } else { break; } } fCommentContainer.put(startOffset + spaceCount, ""); //$NON-NLS-1$ lineCommented.add(false); } startLine++; startOffset = fDocument.getLineOffset(startLine); } if (lineCommented.size() > 1) { item.setInconsistent(true); } if (lineCommented.contains(false)) { item.setValue(1); } else { item.setValue(0); } if (item.invertValue()) { item.setValue(1 - item.getValue()); } } catch (BadLocationException e) { } return item; } private String getTokenTypeString(ETokenType type) { switch (type) { case HEADING: return "<h>"; //$NON-NLS-1$ case HEADING_ENABLE: return "<e>"; //$NON-NLS-1$ case HEADING_ENABLE_END: return "</e>"; //$NON-NLS-1$ case HEADING_END: return "</h>"; //$NON-NLS-1$ case OPTION: return "<o>"; //$NON-NLS-1$ case OPTION_CHECK: return "<q>"; //$NON-NLS-1$ case OPTION_STRING: return "<s>"; //$NON-NLS-1$ case NOTIFICATION: return "<n>"; //$NON-NLS-1$ case TOOLTIP: return "<i>"; //$NON-NLS-1$ case VALUE: return "<\\d+=>"; //$NON-NLS-1$ case EOC: return Messages.ConfigWizardParser_EndOfConfiguration; case BLOCK_COMMENT: return Messages.ConfigWizardParser_BlockComment; case COMMENT: return Messages.ConfigWizardParser_Comment; case DEFAULT: return Messages.ConfigWizardParser_Unknown; case NUMBER: return Messages.ConfigWizardParser_Number; case STRING: return Messages.ConfigWizardParser_String; default: return Messages.ConfigWizardParser_Unknown; } } private void setValuesAndStrings() { try { if (fRoot.hasChildren()) { for (IConfigWizardItem child : fRoot.getChildren()) { setValuesAndStrings(child); } } } catch (BadLocationException e) { e.printStackTrace(); } } private void setValuesAndStrings(IConfigWizardItem item) throws BadLocationException { int line = item.getStartLine(); int offset = fDocument.getLineOffset(line); int skip = item.getSkipNumber(); int minBit = item.getMinBit(); int maxBit = item.getMaxBit(); long mask = buildMask(minBit, maxBit); EItemType type = item.getItemType(); switch (type) { case HEADING_ENABLE: case OPTION: case OPTION_CHECK: case OPTION_SELECT: Collection<String> numbers = fNumberContainer.tailMap(offset).values(); Iterator<String> iter = numbers.iterator(); String valueText = ""; //$NON-NLS-1$ long value = 0; while (iter.hasNext() && skip >= 0) { valueText = iter.next(); skip--; } if (skip >= 0) { item.setItemErrorType(EItemErrorType.LOCATE_POSITION_ERROR); return; } value = parseNumber(item, valueText); if (item.getItemErrorType() != EItemErrorType.NO_ERROR) { return; } int base = getBaseFromValue(valueText); item.setBase(base); long realValue = value; if (value >= 0) { realValue = (value >> minBit) & mask; } item.setValue(realValue); break; case OPTION_STRING: Collection<String> strings = fStringContainer.tailMap(offset).values(); Iterator<String> siter = strings.iterator(); String str = ""; //$NON-NLS-1$ while (siter.hasNext() && skip >= 0) { str = siter.next(); skip--; } if (skip >= 0) { item.setItemErrorType(EItemErrorType.LOCATE_POSITION_ERROR); return; } item.setItemErrorType(EItemErrorType.NO_ERROR); item.setString(str); break; default: break; } if (item.hasChildren()) { for (IConfigWizardItem child : item.getChildren()) { setValuesAndStrings(child); } } } private long buildMask(int minBit, int maxBit) { long mask = 1; for (int i = 0; i < maxBit - minBit; i++) { mask <<= 1; mask |= 1; } return mask; } private int getBaseFromValue(String valueText) { valueText = valueText.toLowerCase(); if (valueText.startsWith("0x")) { //$NON-NLS-1$ return 16; } else if (valueText.startsWith("0b")) { //$NON-NLS-1$ return 2; } else if (valueText.startsWith("0") && valueText.length() > 1 && //$NON-NLS-1$ Character.isDigit(valueText.charAt(valueText.length() - 1))) { return 8; } switch (valueText.charAt(valueText.length() - 1)) { case 'b': return 2; case 'o': case 'q': return 8; case 'd': return 10; case 'h': return 16; default: return 10; } } private void getNextToken() { cToken = fScanner.nextToken(); cType = fScanner.getTokenType(cToken); } public IConfigWizardItem getConfigWizardRoot() { return fRoot; } public boolean containWizard() { return fContainWizard; } public int getStartParseOffset() { return fStartParseOffset; } public int getEndParseOffset() { return fEndParseOffset; } public int getParsingErrorOffset() { return fParsingErrorOffset; } public void updateModel(IConfigWizardItem item, Object newVal) { if (newVal instanceof Boolean) { updateBooleanValue(item, (Boolean) newVal); } else if (newVal instanceof Integer || newVal instanceof Long) { updateNumberValue(item, (Long) newVal); } else if (newVal instanceof String) { updateStringValue(item, (String) newVal); } } private void updateBooleanValue(IConfigWizardItem item, Boolean newVal) { long value = newVal.booleanValue() ? 1 : 0; if (item.getValue() == value) { return; } item.setValue(value); if (item.invertValue()) { newVal = !newVal.booleanValue(); } updateDocument(item, newVal); } private void updateNumberValue(IConfigWizardItem item, Long newVal) { long value = newVal.longValue(); if (item.getItemType() == EItemType.OPTION_SELECT) { // For Menu Item int i = 0; for (Long k : item.getItems().keySet()) { if (i == value) { if (item.getValue() == k) { return; } item.setValue(k); updateDocument(item, k); return; } i++; } } else { // For Spinner Item long min = item.getMinValue(); long modifier = item.getModifier(); char op = item.getModification(); value = Utils.modifyValue(value, op, modifier, item.getMaxValue(), item.getMinValue()); long spinStep = Utils.modifyValue(item.getSpinStep(), op, modifier, Long.MAX_VALUE, 1); if ((value - min) % spinStep != 0) { value = min + (value - min) / spinStep * spinStep; } if (item.getValue() == value) { return; } item.setValue(value); updateDocument(item, value); return; } } private void updateStringValue(IConfigWizardItem item, String newVal) { EItemType type = item.getItemType(); String value = newVal; switch (type) { case OPTION_STRING: value = value.replace("\"", "\\\""); //$NON-NLS-1$ //$NON-NLS-2$ if (item.getString().equals(value)) { return; } item.setString(value); updateDocument(item, value); return; case OPTION_SELECT: if (item.getItems().get(item.getValue()) != null && item.getItems().get(item.getValue()).equals(value)) { return; } for (Entry<Long, String> entry : item.getItems().entrySet()) { if (entry.getValue().equals(value)) { item.setValue(entry.getKey()); updateDocument(item, entry.getKey()); return; } } break; case OPTION: int radix = item.getBase(); if (value.toLowerCase().startsWith("0x")) { //$NON-NLS-1$ value = value.substring(2); radix = 16; } value = value.replaceAll("[\\s<>]",""); //$NON-NLS-1$ //$NON-NLS-2$ try { long realValue = Utils.modifyValue(Long.parseLong(value, radix), item.getModification(), item.getModifier(), item.getMaxValue(), item.getMinValue()); item.setItemErrorType(EItemErrorType.NO_ERROR); if (item.getValue() == realValue) { return; } item.setValue(realValue); updateDocument(item, realValue); return; } catch (NumberFormatException e) { item.setItemErrorType(EItemErrorType.NUMBER_PARSE_ERROR); } break; default: break; } } private void updateDocument(IConfigWizardItem item, Object newVal) { try { int startLine = item.getStartLine(); int endLine = item.getEndLine(); int startOffset = fDocument.getLineOffset(startLine); int endOffset = fDocument.getLineOffset(endLine); int skip = item.getSkipNumber(); // Comment Check Box if (item.getItemType() == EItemType.CODE_ENABLE || item.getItemType() == EItemType.CODE_DISABLE) { // newVal == true -> uncomment, newVal == false -> comment Assert.isTrue(newVal instanceof Boolean); boolean useCode = ((Boolean) newVal).booleanValue(); startOffset = fDocument.getLineOffset(startLine + skip + 1); Iterator<Entry<Integer, String>> iter = fCommentContainer.subMap(startOffset, endOffset).entrySet() .iterator(); TreeMap<Integer, String> newMap = new TreeMap<>(); int shift = 0; while (iter.hasNext()) { Entry<Integer, String> e = iter.next(); int offset = e.getKey() + shift; String oldComment = e.getValue(); iter.remove(); if (useCode) { // should uncomment fDocument.replace(offset, oldComment.length(), ""); //$NON-NLS-1$ shift -= oldComment.length(); newMap.put(offset, ""); //$NON-NLS-1$ } else { fDocument.replace(offset, oldComment.length(), "//"); //$NON-NLS-1$ shift -= oldComment.length() - 2; newMap.put(offset, "//"); //$NON-NLS-1$ } } fCommentContainer.putAll(newMap); if (shift != 0) { updateIndex(endOffset, shift); } } // Heading_enable, Option, Option_enable, Option_select else if (newVal instanceof Integer || newVal instanceof Long || newVal instanceof Boolean) { // find the offset of the value corresponding to the item Set<Entry<Integer, String>> map = fNumberContainer.tailMap(startOffset).entrySet(); int i = 0; int valueOffset = -1; String oldValue = ""; //$NON-NLS-1$ int oldValueLength = 0; for (Entry<Integer, String> e : map) { if (i == skip) { valueOffset = e.getKey(); oldValue = e.getValue(); oldValueLength = oldValue.length(); break; } i++; } Assert.isTrue(valueOffset != -1 && !oldValue.isEmpty()); int minBit = item.getMinBit(); int maxBit = item.getMaxBit(); long mask = ~(buildMask(minBit, maxBit) << minBit); long value; if (newVal instanceof Boolean) { value = (Boolean) newVal ? 1 : 0; } else { value = (Long.valueOf(newVal.toString())); } value <<= minBit; long newValue = parseNumber(item, oldValue); if (item.getItemErrorType() != EItemErrorType.NO_ERROR) { return; } newValue &= mask; newValue |= value; String newText = String.valueOf(newValue); if (item.getBase() == 16) { newText = Long.toHexString(newValue).toUpperCase(); StringBuilder sb = new StringBuilder(newText); sb.insert(0, "0x"); //$NON-NLS-1$ while (sb.length() < oldValueLength) { sb.insert(2, '0'); } newText = sb.toString(); } else if (item.getBase() == 8) { newText = Long.toOctalString(newValue); newText = "0" + newText; //$NON-NLS-1$ } else if (item.getBase() == 2) { newText = Long.toBinaryString(newValue); StringBuilder sb = new StringBuilder(newText); sb.insert(0, "0b"); //$NON-NLS-1$ while (sb.length() < oldValueLength) { sb.insert(2, '0'); } newText = sb.toString(); } updateIndex(valueOffset, newText.length() - oldValueLength); fNumberContainer.remove(valueOffset); fNumberContainer.put(valueOffset, newText); fDocument.replace(valueOffset, oldValueLength, newText); } // Option_string else if (newVal instanceof String) { // find the offset of the value corresponding to the item Set<Entry<Integer, String>> map = fStringContainer.tailMap(startOffset).entrySet(); int i = 0; int valueOffset = -1; String oldValue = ""; //$NON-NLS-1$ int oldValueLength = 0; for (Entry<Integer, String> e : map) { if (i == skip) { valueOffset = e.getKey(); oldValue = e.getValue(); oldValueLength = oldValue.length(); break; } i++; } Assert.isTrue(valueOffset != -1 && !oldValue.isEmpty()); String newText = (String) newVal; updateIndex(valueOffset, newText.length() - oldValueLength); fStringContainer.remove(valueOffset); fStringContainer.put(valueOffset, newText); // consider the double quote here fDocument.replace(valueOffset - 1, oldValueLength + 2, "\"" + newText + "\""); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (BadLocationException e) { e.printStackTrace(); } } /** * update the index that is > startOffset by shift characters * * @param startOffset * starting offset * @param shift * number of characters to shift */ private void updateIndex(int startOffset, int shift) { if (shift == 0) { return; } updateContainer(fNumberContainer, startOffset, shift); updateContainer(fStringContainer, startOffset, shift); updateContainer(fCommentContainer, startOffset, shift); } private void updateContainer(TreeMap<Integer, String> container, int startOffset, int shift) { SortedMap<Integer, String> behind = container.tailMap(startOffset, false); TreeMap<Integer, String> newMap = new TreeMap<>(); Iterator<Entry<Integer, String>> iter = behind.entrySet().iterator(); while (iter.hasNext()) { Entry<Integer, String> entry = iter.next(); int key = entry.getKey(); String value = entry.getValue(); newMap.put(key + shift, value); iter.remove(); } container.putAll(newMap); } }