// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.shared.grok;
import com.google.collide.dto.CodeBlock;
import com.google.collide.dto.CodeGraph;
import com.google.collide.json.shared.JsonArray;
import com.google.collide.json.shared.JsonStringMap;
import com.google.collide.shared.util.StringUtils;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
/**
* Shared utilities for working with grok-related data structures.
*/
@GwtCompatible
public class GrokUtils {
private static final String QNAME_DELIMITER = ".";
/**
* A pair of (container, missing child name)
*/
public static class MissingChildCodeBlock {
public final CodeBlock container;
public final String childName;
private MissingChildCodeBlock(CodeBlock container, String name) {
this.container = container;
this.childName = name;
}
}
public static final Function<MissingChildCodeBlock, CodeBlock> NO_MISSING_CHILD =
new Function<MissingChildCodeBlock, CodeBlock>() {
@Override
public CodeBlock apply(MissingChildCodeBlock input) {
return null;
}
};
/**
* Finds code block by its qualified name relative to the {@code root} code
* block.
*
* @param root code block to search from
* @param qname dot-separated qualified name
* @return code block accessible by {@code qname} from {@code root} or {@code
* null}
*/
public static CodeBlock findCodeBlockByQname(CodeBlock root, String qname) {
return getOrCreateCodeBlock(root, StringUtils.split(qname, QNAME_DELIMITER), NO_MISSING_CHILD);
}
/**
* Finds code block by its qualified name relative to the {@code root} code
* block and creates missing blocks if necessary using a factory function. If
* factory function returns {@code null} at some point, this function also
* returns {@code null}, otherwise it inserts code blocks created by a factory
* function into the tree (like mkdir -p does for directories).
*
* @param root code block to search from
* @param qname dot-separated qualified name
* @param createCodeBlock factory function
* @return code block accessible by {@code qname} from {@code root} or {@code
* null} if the path was missing in the original tree and factory
* function refused to create some part of it
*/
public static CodeBlock getOrCreateCodeBlock(CodeBlock root, JsonArray<String> qname,
Function<MissingChildCodeBlock, CodeBlock> createCodeBlock) {
for (int nameIdx = 0, end = qname.size(); nameIdx < end; nameIdx++) {
CodeBlock newRoot = null;
for (int i = 0; i < root.getChildren().size(); i++) {
if (root.getChildren().get(i).getName().equals(qname.get(nameIdx))) {
newRoot = root.getChildren().get(i);
break;
}
}
if (newRoot == null) {
newRoot = createCodeBlock.apply(new MissingChildCodeBlock(root, qname.get(nameIdx)));
if (newRoot == null) {
return null;
}
root.getChildren().add(newRoot);
}
root = newRoot;
}
return root;
}
/**
* Search in graph blocks map for block with specific file path.
*
* @return {@code null} if filePath or graph is {@code null}, or there is
* no code block with specified path
*/
public static CodeBlock findFileCodeBlock(CodeGraph graph, String filePath) {
if (filePath == null) {
return null;
}
if (graph == null) {
return null;
}
JsonStringMap<CodeBlock> blockMap = graph.getCodeBlockMap();
if (blockMap == null) {
return null;
}
JsonArray<String> keys = blockMap.getKeys();
final int l = keys.size();
for (int i = 0; i < l; i++) {
String key = keys.get(i);
CodeBlock codeBlock = blockMap.get(key);
if (CodeBlock.Type.VALUE_FILE == codeBlock.getBlockType()) {
if (filePath.equals(codeBlock.getName())) {
return codeBlock;
}
}
}
return null;
}
}