| /******************************************************************************* |
| * Copyright (c) 2006 Oracle Corporation. |
| * 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: |
| * Cameron Bateman/Oracle - initial API and implementation |
| * |
| ********************************************************************************/ |
| package org.eclipse.jst.jsf.context.symbol.internal.impl; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.ECollections; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.impl.ENotificationImpl; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jst.jsf.common.internal.types.TypeConstants; |
| import org.eclipse.jst.jsf.context.symbol.IInstanceSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IJavaTypeDescriptor2; |
| import org.eclipse.jst.jsf.context.symbol.IMapTypeDescriptor; |
| import org.eclipse.jst.jsf.context.symbol.IObjectSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IPropertySymbol; |
| import org.eclipse.jst.jsf.context.symbol.ITypeDescriptor; |
| import org.eclipse.jst.jsf.context.symbol.SymbolFactory; |
| import org.eclipse.jst.jsf.context.symbol.SymbolPackage; |
| |
| /** |
| * <!-- begin-user-doc --> |
| * An implementation of the model object '<em><b>IMap Type Descriptor</b></em>'. |
| * <!-- end-user-doc --> |
| * <p> |
| * The following features are implemented: |
| * <ul> |
| * <li>{@link org.eclipse.jst.jsf.context.symbol.internal.impl.IMapTypeDescriptorImpl#getMapSource <em>Map Source</em>}</li> |
| * <li>{@link org.eclipse.jst.jsf.context.symbol.internal.impl.IMapTypeDescriptorImpl#isImmutable <em>Immutable</em>}</li> |
| * </ul> |
| * </p> |
| * |
| * @generated |
| */ |
| public class IMapTypeDescriptorImpl extends ITypeDescriptorImpl implements IMapTypeDescriptor { |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| @SuppressWarnings("hiding") |
| public static final String copyright = "Copyright 2006 Oracle"; //$NON-NLS-1$ |
| |
| /** |
| * The default value of the '{@link #getMapSource() <em>Map Source</em>}' attribute. |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @see #getMapSource() |
| * @generated |
| * @ordered |
| */ |
| protected static final Map MAP_SOURCE_EDEFAULT = null; |
| |
| /** |
| * The cached value of the '{@link #getMapSource() <em>Map Source</em>}' attribute. |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @see #getMapSource() |
| * @generated |
| * @ordered |
| */ |
| protected Map mapSource = MAP_SOURCE_EDEFAULT; |
| |
| /** |
| * The default value of the '{@link #isImmutable() <em>Immutable</em>}' attribute. |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @see #isImmutable() |
| * @generated |
| * @ordered |
| */ |
| protected static final boolean IMMUTABLE_EDEFAULT = true; |
| |
| private static final Object MAP_TYPE_DESCRIPTOR_PROP_KEY = new Object(); |
| |
| /** |
| * The cached value of the '{@link #isImmutable() <em>Immutable</em>}' attribute. |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @see #isImmutable() |
| * @generated |
| * @ordered |
| */ |
| protected boolean immutable = IMMUTABLE_EDEFAULT; |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| protected IMapTypeDescriptorImpl() { |
| super(); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @return the static eClass |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| protected EClass eStaticClass() { |
| return SymbolPackage.Literals.IMAP_TYPE_DESCRIPTOR; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @return the map source |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public Map getMapSource() { |
| return mapSource; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @param newMapSource |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public void setMapSource(Map newMapSource) { |
| Map oldMapSource = mapSource; |
| mapSource = newMapSource; |
| if (eNotificationRequired()) |
| eNotify(new ENotificationImpl(this, Notification.SET, SymbolPackage.IMAP_TYPE_DESCRIPTOR__MAP_SOURCE, oldMapSource, mapSource)); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @return true if this map is immutable as defined in the java.util.Map |
| * interface. |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public boolean isImmutable() { |
| return immutable; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @param newImmutable |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public void setImmutable(boolean newImmutable) { |
| boolean oldImmutable = immutable; |
| immutable = newImmutable; |
| if (eNotificationRequired()) |
| eNotify(new ENotificationImpl(this, Notification.SET, SymbolPackage.IMAP_TYPE_DESCRIPTOR__IMMUTABLE, oldImmutable, immutable)); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getTypeSignature() |
| * @generated NOT |
| */ |
| public String getTypeSignature() |
| { |
| // if the delegate has been set, use it |
| if (eIsSet(SymbolPackage.IMAP_TYPE_DESCRIPTOR__TYPE_SIGNATURE_DELEGATE)) |
| { |
| return getTypeSignatureDelegate(); |
| } |
| |
| // otherwise use Map |
| return TypeConstants.TYPE_MAP; |
| } |
| |
| public EList getInterfaceTypeSignatures() { |
| return ECollections.EMPTY_ELIST; |
| } |
| |
| public EList getSuperTypeSignatures() { |
| return ECollections.EMPTY_ELIST; |
| } |
| |
| /** |
| * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getProperties() |
| */ |
| public EList getProperties() |
| { |
| final BasicEList list = new BasicEList(); |
| final Map source = getMapSource(); |
| if (source instanceof IMapSourceInfo) |
| { |
| if (!((IMapSourceInfo) source).hasChanged(MAP_TYPE_DESCRIPTOR_PROP_KEY)) |
| { |
| EList cachedList = (EList) ((IMapSourceInfo)source).getCachedValue(MAP_TYPE_DESCRIPTOR_PROP_KEY); |
| |
| if (cachedList != null) |
| { |
| return cachedList; |
| } |
| } |
| } |
| final Map segmentMap = processSegments(source); |
| list.addAll(segmentMap.values()); |
| |
| if (source instanceof IMapSourceInfo) |
| { |
| ((IMapSourceInfo)source).putCachedValue(MAP_TYPE_DESCRIPTOR_PROP_KEY, list); |
| } |
| return list; |
| } |
| |
| public EList getMethods() |
| { |
| // TODO: should this return the methods on a Map? |
| return ECollections.EMPTY_ELIST; |
| } |
| |
| /** |
| * @generated NOT |
| */ |
| public IObjectSymbol getArrayElement() |
| { |
| return null; |
| } |
| |
| /** |
| * @generated NOT |
| */ |
| public boolean isArray() |
| { |
| // a map is never an array |
| return false; |
| } |
| |
| private Map processSegments(final Map source) |
| { |
| final Map segmentMap = new HashMap(); |
| final Set<Map.Entry<String, Object>> entrySet = source.entrySet(); |
| for (final Map.Entry<String, Object> entry : entrySet) |
| { |
| |
| final String key = entry.getKey(); |
| final String segments[] = fastTokenSplit(key); |
| if (segments.length == 0) |
| { |
| continue; |
| } |
| IPropertySymbol property = (IPropertySymbol) segmentMap |
| .get(segments[0]); |
| |
| if (property == null) |
| { |
| final Object propValue = entry.getValue(); |
| property = SymbolFactory.eINSTANCE.createIPropertySymbol(); |
| property.setName(segments[0]); |
| ITypeDescriptor typeDesc = null; |
| |
| // TODO: need wrapper object to rationalize |
| if (propValue != null) |
| { |
| if (propValue instanceof IType) |
| { |
| typeDesc = SymbolFactory.eINSTANCE |
| .createIJavaTypeDescriptor2(); |
| ((IJavaTypeDescriptor2) typeDesc) |
| .setType((IType) propValue); |
| } else if (propValue instanceof IInstanceSymbol) |
| { |
| typeDesc = ((IInstanceSymbol) propValue) |
| .getTypeDescriptor(); |
| } else if (propValue instanceof IPropertySymbol) |
| { |
| typeDesc = ((IPropertySymbol) propValue) |
| .getTypeDescriptor(); |
| } else |
| { |
| final String className = propValue.getClass().getName(); |
| final String typeSignature = Signature |
| .createTypeSignature(className, true); |
| typeDesc = SymbolFactory.eINSTANCE |
| .createIMapTypeDescriptor(); |
| ((IMapTypeDescriptor) typeDesc) |
| .setMapSource(new HashMap()); |
| ((IMapTypeDescriptor) typeDesc) |
| .setTypeSignatureDelegate(typeSignature); |
| // inherit this descriptor's mutability |
| ((IMapTypeDescriptor) typeDesc) |
| .setImmutable(isImmutable()); |
| property.setIntermediate(true); // set the property as |
| // intermediate until we |
| // find out different |
| } |
| |
| property.setTypeDescriptor(typeDesc); |
| property.setReadable(true); |
| // is only writable if map is not immutable |
| property.setWritable(!isImmutable()); |
| } |
| |
| segmentMap.put(segments[0], property); |
| } |
| |
| final ITypeDescriptor typeDesc = property.getTypeDescriptor(); |
| |
| if (typeDesc instanceof IMapTypeDescriptor) |
| { |
| if (segments.length == 1) |
| { |
| // TODO: not always allowed |
| // ((IMapTypeDescriptor)typeDesc).getMapSource().put(null, |
| // source.get(key)); |
| // property is more than simply intermediate |
| property.setIntermediate(false); |
| } else |
| { |
| ((IMapTypeDescriptor) typeDesc).getMapSource().put( |
| key.substring(key.indexOf('.') + 1), |
| entry.getValue()); |
| } |
| } |
| } |
| |
| return segmentMap; |
| } |
| |
| /** |
| * Based on measurements, this beats Pattern.split by 15-30% even with |
| * a pre-compiled pattern. |
| * |
| * @param splitValue |
| * @return the array of strings split by the '.' token |
| */ |
| private static String[] fastTokenSplit(final String splitValue) |
| { |
| if (splitValue == null || splitValue.length() == 0) |
| { |
| return new String[0]; |
| } |
| if (splitValue.indexOf('.') > -1) |
| { |
| return tokenizerSplit(splitValue); |
| } |
| return new String[] {splitValue}; |
| } |
| |
| private static String[] tokenizerSplit(final String splitValue) |
| { |
| StringTokenizer stringTokenizer = new StringTokenizer(splitValue, "."); //$NON-NLS-1$ |
| // initialize to a large size, since we're just going to truncate |
| // it once at the end and want to reduce the chance of resize during |
| // the loop. |
| final List<String> splitValues = new ArrayList<String>(32); |
| |
| while (stringTokenizer.hasMoreTokens()) |
| { |
| splitValues.add(stringTokenizer.nextToken()); |
| } |
| |
| return splitValues.toArray(new String[0]); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @param featureID |
| * @param resolve |
| * @param coreType |
| * @return the object for the feature id |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public Object eGet(int featureID, boolean resolve, boolean coreType) { |
| switch (featureID) { |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__MAP_SOURCE: |
| return getMapSource(); |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__IMMUTABLE: |
| return isImmutable() ? Boolean.TRUE : Boolean.FALSE; |
| } |
| return super.eGet(featureID, resolve, coreType); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @param featureID |
| * @param newValue |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public void eSet(int featureID, Object newValue) { |
| switch (featureID) { |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__MAP_SOURCE: |
| setMapSource((Map)newValue); |
| return; |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__IMMUTABLE: |
| setImmutable(((Boolean)newValue).booleanValue()); |
| return; |
| } |
| super.eSet(featureID, newValue); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @param featureID |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public void eUnset(int featureID) { |
| switch (featureID) { |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__MAP_SOURCE: |
| setMapSource(MAP_SOURCE_EDEFAULT); |
| return; |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__IMMUTABLE: |
| setImmutable(IMMUTABLE_EDEFAULT); |
| return; |
| } |
| super.eUnset(featureID); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @param featureID |
| * @return true if is set |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public boolean eIsSet(int featureID) { |
| switch (featureID) { |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__MAP_SOURCE: |
| return MAP_SOURCE_EDEFAULT == null ? mapSource != null : !MAP_SOURCE_EDEFAULT.equals(mapSource); |
| case SymbolPackage.IMAP_TYPE_DESCRIPTOR__IMMUTABLE: |
| return immutable != IMMUTABLE_EDEFAULT; |
| } |
| return super.eIsSet(featureID); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * @return the string representation |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public String toString() { |
| if (eIsProxy()) return super.toString(); |
| |
| StringBuffer result = new StringBuffer(super.toString()); |
| result.append(" (mapSource: "); //$NON-NLS-1$ |
| result.append(mapSource); |
| result.append(", immutable: "); //$NON-NLS-1$ |
| result.append(immutable); |
| result.append(')'); |
| return result.toString(); |
| } |
| |
| } //IMapTypeDescriptorImpl |