/******************************************************************************* * Copyright (c) 2002, 2008 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICElementDelta; import org.eclipse.cdt.core.model.ICModel; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IOpenable; import org.eclipse.cdt.core.model.ISourceRoot; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.LanguageManager; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent; import org.eclipse.core.runtime.preferences.IScopeContext; /** * ContentType processor */ public class ContentTypeProcessor extends CModelOperation { CModelManager fManager; CElementDelta fCurrentDelta; ContentTypeChangeEvent[] fEvents; public ContentTypeProcessor(ContentTypeChangeEvent[] events) { super(CModelManager.getDefault().getCModel()); this.fEvents = events; fManager = CModelManager.getDefault(); ICElement root = fManager.getCModel(); fCurrentDelta = new CElementDelta(root); } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.model.CModelOperation#isReadOnly() */ @Override public boolean isReadOnly() { return true; } @Override protected void executeOperation() throws CModelException { for (int i = 0; i < fEvents.length; ++i) { IContentType contentType = fEvents[i].getContentType(); IScopeContext context = fEvents[i].getContext(); ICProject[] cprojects = getAffectedProjects(fEvents[i]); for (int k = 0; k < cprojects.length; ++k) { processContentType(cprojects[k], contentType, context); } } if (fCurrentDelta.getAffectedChildren().length > 0) { addDelta(fCurrentDelta); } } static public void processContentTypeChanges(ContentTypeChangeEvent[] events) { try { CModelOperation op = new ContentTypeProcessor(events); op.runOperation(null); } catch (CModelException e) { // } } private boolean isRegisteredContentTypeId(String id) { // bailout early if (id == null || id.length() == 0) { return false; } return LanguageManager.getInstance().isContributedContentType(id); } protected void processContentType(ICElement celement, IContentType contentType, IScopeContext context) { /* * We only know that an association was added/removed from the contentType. * we need to go to all the members that are in the cache(known ICElement) and recheck there * state to see if there were affected. That also include children that were non-celement * the new assiociation may have turn them to new ICElement. * algo: * if ICContainer check if we have a CElementinfo(children) * if yes, * look at the members(IResource) and get there contentType * if the type is the same as the event * check if element was already a celement * if yes, * check if the element as the same contentType * if yes, * do nothing * else * if the content ID is registered ID * fired changed event * else * fire remove event * check for the CElement * get the new oldID and the newID * if not equal * check if the newID is registered * if yes * fire changed event * else * fire remove event */ if (celement instanceof IOpenable) { int type = celement.getElementType(); // if the type is not a TranslationUnit switch (type) { case ICElement.C_PROJECT: { CElementInfo info = (CElementInfo)fManager.peekAtInfo(celement); if (info != null) { ICElement[] celements = info.getChildren(); for (int i = 0; i < celements.length; ++i) { processContentType(celements[i], contentType, context); } } break; } case ICElement.C_CCONTAINER: { CElementInfo info = (CElementInfo)fManager.peekAtInfo(celement); if (info != null) { try { ICElement[] celements = info.getChildren(); IResource resource = celement.getResource(); IResource[] members = null; if (resource instanceof IContainer) { members = ((IContainer)resource).members(); } if (members != null) { for (int i = 0; i < members.length; ++i) { if (members[i] instanceof IFile) { IFile file = (IFile) members[i]; String name = file.getName(); IContentType cType = CCorePlugin.getContentType(file.getProject(), name); if (cType != null && cType.equals(contentType)) { boolean found = false; for (int j = 0; j < celements.length; ++j) { if (celements[j].getElementName().equals(name)) { if (celements[j].getElementType() == ICElement.C_UNIT) { ITranslationUnit unit = (ITranslationUnit)celements[j]; if (!cType.getId().equals(unit.getContentTypeId())) { if (isRegisteredContentTypeId(cType.getId())) { elementChanged(celements[j]); } } } found = true; break; } } if (! found) { ICElement newElement = CoreModel.getDefault().create(file); if (newElement != null) { elementAdded(newElement, celement); } } } } } } for (int i = 0; i < celements.length; ++i) { processContentType(celements[i], contentType, context); } } catch (CoreException e) { // } } } break; case ICElement.C_UNIT: { String oldId = ((ITranslationUnit)celement).getContentTypeId(); if (contentType.getId().equals(oldId)) { try { IContentType cType = CCorePlugin.getContentType(celement.getCProject().getProject(), celement.getElementName()); String newId = (cType != null) ? cType.getId() : ""; //$NON-NLS-1$ if (isRegisteredContentTypeId(newId)) { if (!oldId.equals(newId)) { elementChanged(celement); } } else { elementRemoved(celement, celement.getParent()); } } catch (CoreException e) { // } } break; } } } } /** * Add the resource delta to the right CElementDelta tree. * @param parent * @param delta */ private void nonCResourcesChanged(ICElement parent) { if (parent instanceof Openable && ((Openable)parent).isOpen()) { try { CElementInfo info = ((Openable)parent).getElementInfo(); switch (parent.getElementType()) { case ICElement.C_MODEL: ((CModelInfo)info).setNonCResources(null); break; case ICElement.C_PROJECT: ((CProjectInfo)info).setNonCResources(null); break; case ICElement.C_CCONTAINER: ((CContainerInfo)info).setNonCResources(null); if (parent instanceof ISourceRoot) { // if sourceRoot == Project we nee to update the nonCResource of the project also. if (parent.getResource() instanceof IProject) { ICElement cproject = parent.getCProject(); CProjectInfo pinfo = (CProjectInfo)fManager.peekAtInfo(cproject); if (pinfo != null) { pinfo.setNonCResources(null); } } } break; } } catch (CModelException e) { // } } } private void elementAdded(ICElement celement, ICElement parent) throws CModelException { if (celement instanceof Openable) { addToParentInfo((Openable)celement); } fCurrentDelta.added(celement); nonCResourcesChanged(parent); } /** * Adds the given child handle to its parent's cache of children. */ private void addToParentInfo(Openable child) throws CModelException { Openable parent = (Openable) child.getParent(); if (parent != null && parent.isOpen()) { CElementInfo info = parent.getElementInfo(); // Check if the element exits if (!info.includesChild(child)) { info.addChild(child); } } } private void elementRemoved(ICElement celement, ICElement parent) throws CModelException { fCurrentDelta.removed(celement); nonCResourcesChanged(parent); removeFromParentInfo(celement); fManager.releaseCElement(celement); } private void elementChanged(ICElement element) throws CModelException { // For Binary/Archive We can not call close() to do the work // closing will remove the element from the {Binary,Archive}Container // We neef to clear the cache explicitely // if (element instanceof IBinary || element instanceof IArchive) { // closeBinary(element); // } else if (element instanceof Openable) { // close((Openable)element); // } // fCurrentDelta.changed(element, ICElementDelta.F_CONTENT); if (element instanceof IOpenable) { ((IOpenable)element).close(); } fCurrentDelta.changed(element, ICElementDelta.F_CONTENT |ICElementDelta.F_CONTENT_TYPE); } /** * Removes the given element from its parents cache of children. If the * element does not have a parent, or the parent is not currently open, * this has no effect. */ private void removeFromParentInfo(ICElement child) throws CModelException { // Remove the child from the parent list. ICElement parent = child.getParent(); if (parent != null && parent instanceof Parent && fManager.peekAtInfo(parent) != null) { ((Parent)parent).removeChild(child); } } private ICProject[] getAffectedProjects(ContentTypeChangeEvent event) { try { ICModel cmodel = CoreModel.getDefault().getCModel(); ICProject[] cprojects = cmodel.getCProjects(); IScopeContext context = event.getContext(); if (context != null) { if (ProjectScope.SCOPE.equals((context.getName()))) { IPath location = context.getLocation(); for (int i = 0; i < cprojects.length; i++) { if (cprojects[i].getProject().getLocation().isPrefixOf(location)) { return new ICProject[] { cprojects[i] }; } } return new ICProject[0]; } } // Assume a workspace resolver List<ICProject> list = new ArrayList<ICProject>(cprojects.length); for (int i = 0; i < cprojects.length; ++i) { list.add(cprojects[i]); } return list.toArray(new ICProject[list.size()]); } catch (CModelException e) { // } return new ICProject[0]; } }