/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * 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: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package com.googlecode.goclipse.tooling.oracle; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import static melnorme.utilbox.core.CoreUtil.areEqual; import com.google.gson.JsonObject; import com.googlecode.goclipse.tooling.env.GoEnvironment; import melnorme.lang.tooling.common.ops.IOperationMonitor; import melnorme.lang.tooling.toolchain.ops.AbstractToolOperation; import melnorme.lang.tooling.toolchain.ops.OperationSoftFailure; import melnorme.lang.tooling.toolchain.ops.SourceLocation; import melnorme.lang.tooling.toolchain.ops.ToolOutputParseHelper; import melnorme.lang.utils.gson.GsonHelper; import melnorme.lang.utils.gson.JsonParserX; import melnorme.utilbox.concurrency.OperationCancellation; import melnorme.utilbox.core.CommonException; import melnorme.utilbox.misc.Location; import melnorme.utilbox.misc.StringUtil; import melnorme.utilbox.process.ExternalProcessHelper.ExternalProcessResult; public class GuruFindDefinitionOperation extends GuruDescribeOperation implements AbstractToolOperation<SourceLocation> { protected final GoOperationContext goOperationContext; public GuruFindDefinitionOperation(GoOperationContext goOperationContext, String goOraclePath) { super(goOraclePath); this.goOperationContext = assertNotNull(goOperationContext); } @Override public SourceLocation executeToolOperation(IOperationMonitor om) throws CommonException, OperationCancellation, OperationSoftFailure { Location inputLoc = goOperationContext.getFileLocation(); int byteOffset = goOperationContext.getByteOffsetFromEncoding(goOperationContext.opContext.getOffset()); GoEnvironment goEnv = goOperationContext.goEnv; ProcessBuilder pb = createProcessBuilder(goEnv, inputLoc, byteOffset); ExternalProcessResult result = goOperationContext.getToolOpService().runProcess(pb, null, om); if(result.exitValue != 0) { throw new OperationSoftFailure("`guru` did not complete successfully."); } return parseToolResult(result); } public SourceLocation parseToolResult(ExternalProcessResult result) throws CommonException { if(result.exitValue != 0) { throw new CommonException("Program exited with non-zero status: " + result.exitValue, null); } String resultMessage = result.getStdOutBytes().toString(); return new GuruFindDefinitionResultParser().parseJsonResult(resultMessage); } public static class GuruFindDefinitionResultParser { protected final GsonHelper helper = new GsonHelper(); protected SourceLocation parseJsonResult(String output) throws CommonException { try { return assertNotNull(doParseJsonResult(output)); } catch(OperationSoftFailure sf) { throw new CommonException(sf.getMessage()); } } protected SourceLocation doParseJsonResult(String output) throws CommonException, OperationSoftFailure { JsonObject describe = new JsonParserX().parseObject(output, true); String desc = helper.getString(describe, "desc"); String detail = helper.getString(describe, "detail"); if(areEqual(desc, "source file")) { return null; } if(areEqual(desc, "identifier")) { JsonObject value = helper.getObject(describe, "value"); String sourceLocStr = getString(value, "objpos", "Definition not available."); return ToolOutputParseHelper.parsePathLineColumn(sourceLocStr, ":"); } if(areEqual(detail, "type")) { final String DEFINITION_OF = "definition of "; if(desc != null && desc.startsWith(DEFINITION_OF)) { desc = StringUtil.segmentAfterMatch(desc, DEFINITION_OF); throw new CommonException("Already at a definition: " + desc); } JsonObject value = helper.getObject(describe, "type"); String sourceLocStr = getString(value, "namepos", "Definition not available."); return ToolOutputParseHelper.parsePathLineColumn(sourceLocStr, ":"); } throw new CommonException( "Selected position does not refer to a definition. Rather, it's a:\n" + desc); } protected String getString(JsonObject value, String key, String resultErrorMessage) throws OperationSoftFailure { String pathStr = getStringOrNull(value, key); if(pathStr == null) { throw new OperationSoftFailure(resultErrorMessage); } return pathStr; } protected String getStringOrNull(JsonObject element, String key) { try { return helper.getOptionalString(element, key); } catch(CommonException e) { return null; } } } }