/** * Copyright (c) 2013-2016 Angelo ZERR. * 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: * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation */ package org.eclipse.wst.json.ui.internal.contentassist; import java.io.IOException; import java.util.HashSet; import java.util.Set; import org.eclipse.json.jsonpath.IJSONPath; import org.eclipse.json.jsonpath.JSONPath; import org.eclipse.json.schema.IJSONSchemaDocument; import org.eclipse.json.schema.IJSONSchemaProperty; import org.eclipse.json.schema.JSONSchemaType; import org.eclipse.swt.graphics.Image; import org.eclipse.wst.json.core.JSONCorePlugin; import org.eclipse.wst.json.core.document.IJSONNode; import org.eclipse.wst.json.core.document.IJSONObject; import org.eclipse.wst.json.core.document.IJSONPair; import org.eclipse.wst.json.core.regions.JSONRegionContexts; import org.eclipse.wst.json.ui.contentassist.AbstractJSONCompletionProposalComputer; import org.eclipse.wst.json.ui.contentassist.ContentAssistHelper; import org.eclipse.wst.json.ui.contentassist.ContentAssistRequest; import org.eclipse.wst.json.ui.contentassist.ICompletionProposalCollector.TargetType; import org.eclipse.wst.json.ui.contentassist.JSONKeyCompletionProposal; import org.eclipse.wst.json.ui.contentassist.JSONRelevanceConstants; import org.eclipse.wst.json.ui.internal.Logger; import org.eclipse.wst.json.ui.internal.editor.JSONEditorPluginImageHelper; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext; public class JSONCompletionProposalComputer extends AbstractJSONCompletionProposalComputer { @Override public void sessionStarted() { // default is to do nothing } @Override protected void addObjectKeyProposals( ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context) { collectProposalsFromSchema(contentAssistRequest, context); collectProposalsFromExtensionPoint(contentAssistRequest, context); } /** * Collect completion proposals from * "org.eclipse.wst.json.ui.completionProposalCollectors" extension point. * * @param contentAssistRequest * @param context */ private void collectProposalsFromExtensionPoint( ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context) { CompletionProposalCollectorsRegistryReader.getInstance().addProposals( contentAssistRequest, context, TargetType.key); } /** * Collect completion proposals from JSON Schema. * * @param contentAssistRequest * @param context */ private void collectProposalsFromSchema( ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context) { try { IJSONNode node = contentAssistRequest.getNode(); IJSONSchemaDocument schemaDocument = JSONCorePlugin.getDefault() .getSchemaDocument(node); if (schemaDocument != null) { String matchString = contentAssistRequest.getMatchString(); if (matchString == null) { matchString = ""; //$NON-NLS-1$ } if ((matchString.length() > 0) && (matchString.startsWith(QUOTE))) { matchString = matchString.substring(1); } // Loop for each properties of the JSON Schema. IJSONPath path = node.getPath(); if (node instanceof IJSONPair) { IJSONSchemaProperty thisProperty = schemaDocument.getProperty(path); ITextRegion region = contentAssistRequest.getRegion(); boolean isValue = isPairValue(context, node); if (thisProperty != null && isValue) { if (thisProperty.getFirstType() == JSONSchemaType.Boolean) { if (beginsWith(FALSE, matchString.trim())) { addStringProposal(contentAssistRequest, FALSE, false); } if (beginsWith(TRUE, matchString.trim())) { addStringProposal(contentAssistRequest, TRUE, false); } return; } if (thisProperty.getFirstType() == JSONSchemaType.String) { if (thisProperty.getEnumList() != null && thisProperty.getEnumList().size() > 0) { for (String prop : thisProperty.getEnumList()) { boolean showProperty = beginsWith(prop, matchString.trim()); if (showProperty) { addStringProposal(contentAssistRequest, prop, !(region.getType() == JSONRegionContexts.JSON_VALUE_STRING)); } } } else { if (thisProperty.getDefaultValue() != null) { boolean showProperty = beginsWith(thisProperty.getDefaultValue(), matchString.trim()); if (showProperty) { addStringProposal(contentAssistRequest, thisProperty.getDefaultValue(), !(region.getType() == JSONRegionContexts.JSON_VALUE_STRING)); } } } return; } } } if ( !(node instanceof IJSONObject && node.getOwnerPairNode() != null) ) { if (path.getSegments().length > 0) { String[] segments = new String[path.getSegments().length - 1]; System.arraycopy(path.getSegments(), 0, segments, 0, path.getSegments().length-1); path = new JSONPath(segments); } } IJSONSchemaProperty parentProperty = schemaDocument .getProperty(path); Set<String> existing = new HashSet<String>(); boolean addComma = false; if (node instanceof IJSONObject) { addExisting(existing, node); addComma = addComma(context, node); } else if (node instanceof IJSONPair && node.getParentNode() instanceof IJSONObject) { addExisting(existing, node.getParentNode()); } if (parentProperty != null) { for (IJSONSchemaProperty property : parentProperty .getPropertyValues()) { boolean showProperty = !existing.contains(property.getName()) && beginsWith(property.getName(), matchString.trim()); if (showProperty) { String replacementString; if (node instanceof IJSONPair) { replacementString = property.getName(); } else { replacementString = ContentAssistHelper .getRequiredName(node, property); if (addComma) { replacementString = replacementString + ","; } } String additionalProposalInfo = property .getDescription(); Image icon = JSONEditorPluginImageHelper .getInstance().getImage( property.getFirstType()); String displayString = property.getName(); JSONKeyCompletionProposal proposal = new JSONKeyCompletionProposal( replacementString, contentAssistRequest .getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), replacementString.length() - 2, icon, displayString, null, additionalProposalInfo, JSONRelevanceConstants.R_OBJECT_KEY); contentAssistRequest.addProposal(proposal); } } } } } catch (IOException e) { Logger.logException(e); } } private boolean addComma(CompletionProposalInvocationContext context, IJSONNode node) { IJSONNode child = node.getFirstChild(); int documentPosition = context.getInvocationOffset(); while (child != null) { if (documentPosition > child.getStartOffset()) { child = child.getNextSibling(); } else { break; } } return child != null; } private void addExisting(Set<String> existing, IJSONNode node) { IJSONNode child = node.getFirstChild(); while (child != null) { if (child instanceof IJSONPair) { String name = ((IJSONPair) child).getName(); if (name != null && !name.isEmpty()) { existing.add(name); } } child = child.getNextSibling(); } } private void addStringProposal(ContentAssistRequest contentAssistRequest, String replacementString, boolean addQuote) { String additionalProposalInfo = null; Image icon = null; String displayString = replacementString; if (addQuote) { replacementString = QUOTE + replacementString + QUOTE; } String matchString = contentAssistRequest.getMatchString(); if (matchString != null) { matchString = matchString.replaceAll(QUOTE, ""); //$NON-NLS-1$ } JSONKeyCompletionProposal proposal = new JSONKeyCompletionProposal( replacementString, contentAssistRequest .getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), replacementString.length() - 2, icon, displayString, null, additionalProposalInfo, JSONRelevanceConstants.R_OBJECT_KEY); contentAssistRequest.addProposal(proposal); } @Override public String getErrorMessage() { return null; } @Override public void sessionEnded() { // default is to do nothing } }