/******************************************************************************* * Copyright (c) 2009, 2015 IBM Corporation 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: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.index; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.core.*; import org.eclipse.dltk.core.SourceRange; import org.eclipse.dltk.core.index2.IElementResolver; import org.eclipse.dltk.internal.core.*; import org.eclipse.php.core.compiler.IPHPModifiers; import org.eclipse.php.core.compiler.PHPFlags; import org.eclipse.php.internal.core.Constants; import org.eclipse.php.internal.core.Logger; import org.eclipse.php.internal.core.PHPCoreConstants; import org.eclipse.php.internal.core.model.IncludeField; public class PHPElementResolver implements IElementResolver { private static final char SPLIT_CHAR = ';'; private static final char SEPARATOR_CHAR = ','; private static final char RETURN_TYPE_CHAR = ':'; private static final String[] EMPTY = new String[0]; public IModelElement resolve(int elementType, int flags, int offset, int length, int nameOffset, int nameLength, String elementName, String metadata, String doc, String qualifier, String parent, ISourceModule sourceModule) { int occurrenceCount = 1; String metadataToDecode = null; if (metadata != null) { String[] split = StringUtils.split(metadata, SPLIT_CHAR); if (split.length >= 1) { try { occurrenceCount = Integer.parseInt(split[0]); } catch (NumberFormatException e) { // should never happen Logger.logException(e); } } if (split.length >= 2) { metadataToDecode = split[1]; } } ModelElement parentElement = (ModelElement) sourceModule; if (PHPCoreConstants.FILE_PARENT.equals(parent)) { parent = null; } if (PHPCoreConstants.GLOBAL_NAMESPACE.equals(qualifier)) { qualifier = null; } if (qualifier != null) { // namespace: parentElement = new IndexType(parentElement, qualifier, Modifiers.AccNameSpace, 0, 0, 0, 0, null, doc, occurrenceCount); } if (parent != null) { parentElement = new SourceType(parentElement, parent); } String[] superClassNames = null; switch (elementType) { case IModelElement.PACKAGE_DECLARATION: if (metadataToDecode != null) { superClassNames = StringUtils.split(metadataToDecode, SEPARATOR_CHAR); } // a namespace cannot have a nested namespace otherwise // occurrenceCount will be applied to both! assert qualifier == null; return new IndexType(parentElement, elementName, flags, offset, length, nameOffset, nameLength, superClassNames, doc, occurrenceCount); case IModelElement.TYPE: if (metadataToDecode != null) { superClassNames = StringUtils.split(metadataToDecode, SEPARATOR_CHAR); } return new IndexType(parentElement, elementName, flags, offset, length, nameOffset, nameLength, superClassNames, doc, 1); case IModelElement.METHOD: String[] parameters = EMPTY; String returnType = null; if (metadataToDecode != null && metadataToDecode.length() > 1) { if (metadataToDecode.charAt(0) == RETURN_TYPE_CHAR) { returnType = null; metadataToDecode = metadataToDecode.substring(1); } else if (metadataToDecode.charAt(metadataToDecode.length() - 1) == RETURN_TYPE_CHAR) { returnType = metadataToDecode.substring(0, metadataToDecode.length() - 1); metadataToDecode = null; } else { String[] decoded = StringUtils.split(metadataToDecode, RETURN_TYPE_CHAR); if (decoded.length == 2) { metadataToDecode = decoded[1]; returnType = decoded[0]; } } if (metadataToDecode != null) { parameters = StringUtils.split(metadataToDecode, SEPARATOR_CHAR); } } return new IndexMethod(parentElement, elementName, returnType, flags, offset, length, nameOffset, nameLength, parameters, doc, 1); case IModelElement.FIELD: return new IndexField(parentElement, elementName, flags, offset, length, nameOffset, nameLength, doc, 1); case IModelElement.IMPORT_DECLARATION: // XXX: replace with import declaration element return new IncludeField(parentElement, metadataToDecode); default: Logger.log(Logger.WARNING, PHPElementResolver.class.getName() + ": Unsupported element type (" + elementType + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } return null; } /** * See * {@link PHPIndexingVisitor#encodeDocInfo(org.eclipse.dltk.ast.declarations.Declaration)} * for the encoding routine * * @param doc * String representation of encoded PHPDoc info * @return map of encoded information, or <code>null</code> in case PHPDoc * info is <code>null</code> */ protected static Map<String, String> decodeDocInfo(String doc) { if (doc == null) { return null; } Map<String, String> info = new HashMap<String, String>(); StringTokenizer tok = new StringTokenizer(doc, ";"); //$NON-NLS-1$ while (tok.hasMoreTokens()) { String key = tok.nextToken(); String value = null; int i = key.indexOf(':'); if (i != -1) { value = key.substring(i + 1); key = key.substring(0, i); } info.put(key, value); } return info; } private static class IndexField extends SourceField implements IPHPDocAwareElement { private int flags; private ISourceRange sourceRange; private ISourceRange nameRange; private String doc; public IndexField(ModelElement parent, String name, int flags, int offset, int length, int nameOffset, int nameLength, String doc, int occurrenceCount) { super(parent, name); this.flags = flags; this.sourceRange = new SourceRange(offset, length); this.nameRange = new SourceRange(nameOffset, nameLength); this.doc = doc; this.occurrenceCount = occurrenceCount; } public int getFlags() throws ModelException { return flags; } public ISourceRange getNameRange() throws ModelException { return nameRange; } public ISourceRange getSourceRange() throws ModelException { return sourceRange; } public boolean isDeprecated() { return PHPFlags.isDeprecated(flags); } public String[] getReturnTypes() { return null; } @Override public INamespace getNamespace() throws ModelException { return null; } @Override public String getType() throws ModelException { Map<String, String> info = decodeDocInfo(doc); if (info != null) { String types = info.get("v"); //$NON-NLS-1$ if (types != null) { types = types.replace(Constants.DOT, Constants.TYPE_SEPARATOR_CHAR); return types; } } return null; } } private static class IndexMethod extends SourceMethod implements IPHPDocAwareElement { private int flags; private ISourceRange sourceRange; private ISourceRange nameRange; private String returnType; private IParameter[] parameters; private String doc; public IndexMethod(ModelElement parent, String name, String returnType, int flags, int offset, int length, int nameOffset, int nameLength, String[] parameterNames, String doc, int occurrenceCount) { super(parent, name); this.returnType = returnType; this.flags = flags; this.sourceRange = new SourceRange(offset, length); this.nameRange = new SourceRange(nameOffset, nameLength); // MethodParameterInfo this.parameters = new IParameter[0]; if (parameterNames != null) { this.parameters = new IParameter[parameterNames.length]; for (int i = 0; i < parameterNames.length; i++) { String[] values = parameterNames[i].split("\\" //$NON-NLS-1$ + PHPIndexingVisitor.PARAMETER_SEPERATOR); if (values.length == 1) { this.parameters[i] = new MethodParameterInfo(values[0]); } else { String type = values[0]; if (PHPIndexingVisitor.NULL_VALUE.equals(type)) { type = null; } if (type != null) { type = type.replace(Constants.DOT, Constants.TYPE_SEPARATOR_CHAR); } String param = values[1]; String defaultValue = values[2]; if (PHPIndexingVisitor.NULL_VALUE.equals(defaultValue)) { defaultValue = null; } if (defaultValue != null) { defaultValue = defaultValue.replace("&p", PHPIndexingVisitor.PARAMETER_SEPERATOR) //$NON-NLS-1$ .replace("&a", "&"); //$NON-NLS-1$ //$NON-NLS-2$ } int modifiers = 0; if (values.length == 4) { try { modifiers = Integer.parseInt(values[3]); } catch (NumberFormatException e) { // should never happen Logger.logException(e); } } this.parameters[i] = new MethodParameterInfo(param, type, defaultValue, modifiers); } } } this.doc = doc; this.occurrenceCount = occurrenceCount; } public int getFlags() throws ModelException { return flags; } public ISourceRange getNameRange() throws ModelException { return super.getNameRange(); } public ISourceRange getSourceRange() throws ModelException { return sourceRange; } public IParameter[] getParameters() throws ModelException { return parameters; } @Override public String[] getParameterNames() throws ModelException { return SourceMethodUtils.getParameterNames(parameters); } public boolean isConstructor() throws ModelException { return (flags & IPHPModifiers.Constructor) != 0; } public boolean isDeprecated() { return PHPFlags.isDeprecated(flags); } public String[] getReturnTypes() { if (returnType != null) { return new String[] { returnType }; } Map<String, String> info = decodeDocInfo(doc); if (info != null) { String types = info.get("r"); //$NON-NLS-1$ if (types != null) { String[] returnTypes = types.split(","); //$NON-NLS-1$ for (int i = 0; i < returnTypes.length; i++) { returnTypes[i] = returnTypes[i].replaceAll("~", ","); //$NON-NLS-1$ //$NON-NLS-2$ } return returnTypes; } } return null; } @Override public INamespace getNamespace() throws ModelException { return null; } } private static class IndexType extends SourceType implements IPHPDocAwareElement { private int flags; private ISourceRange sourceRange; private ISourceRange nameRange; private String[] superClassNames; private String doc; public IndexType(ModelElement parent, String name, int flags, int offset, int length, int nameOffset, int nameLength, String[] superClassNames, String doc, int occurrenceCount) { super(parent, name); this.flags = flags; this.sourceRange = new SourceRange(offset, length); this.nameRange = new SourceRange(nameOffset, nameLength); this.superClassNames = superClassNames; this.doc = doc; this.occurrenceCount = occurrenceCount; } public int getFlags() throws ModelException { return flags; } public ISourceRange getNameRange() throws ModelException { return super.getNameRange(); } public ISourceRange getSourceRange() throws ModelException { return sourceRange; } public String[] getSuperClasses() throws ModelException { return superClassNames; } public boolean isDeprecated() { return PHPFlags.isDeprecated(flags); } public String[] getReturnTypes() { return null; } @Override public INamespace getNamespace() throws ModelException { return null; } } }