/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.model.filesystems.impl;
import java.io.*;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.internal.ui.*;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IContributorResourceAdapter;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.model.filesystems.FilePathHelper;
import org.jboss.tools.common.model.impl.*;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.*;
public class FileSystemsImpl extends OrderedObjectImpl implements IResourceChangeListener {
private static final long serialVersionUID = 1527918897367197051L;
boolean needsUpdateOverlapping = false;
protected Set<String> overlapped = null;
Ov overlapper = null;
FileSystemsRenameListener fileSystemsRenameListener = new FileSystemsRenameListener(this);
Libs libs = new Libs(this);
public FileSystemsImpl() {
IWorkspace workspace = ModelPlugin.getWorkspace();
if (workspace != null) workspace.addResourceChangeListener(this);
}
public Libs getLibs() {
return libs;
}
protected void onSetEntity(String entity) {
super.onSetEntity(entity);
libs.init();
}
public String getPresentationString() {
IProject p = EclipseResourceUtil.getProject(this);
String app = getAttributeValue("application name"); //$NON-NLS-1$
if(p != null && !app.equals(p.getName())) {
app = app.length() > 0 ? p.getName() + " (" + app + ")" : p.getName(); //$NON-NLS-1$ //$NON-NLS-2$
}
return (app != null && app.length() > 0) ? app : super.getPresentationString();
}
public boolean addChild_0(XModelObject o) {
boolean b = super.addChild_0(o);
if(b && o instanceof FileSystemImpl && o.isActive()) {
((FileSystemImpl)o).getResource();
}
if(b) updateOverlappedLater();
return b;
}
public void removeChild_0(XModelObject o) {
super.removeChild_0(o);
updateOverlappedLater();
}
private static int OV_SLEEPING = 0;
private static int OV_STOPPED = 1;
private static int OV_RUNNING = 2;
class Ov implements XJob.XRunnable {
int status = OV_SLEEPING;
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
return;
}
if(status == OV_STOPPED) return;
status = OV_RUNNING;
try {
updateOverlappedInternal();
} finally {
overlapper = null;
}
}
public String getId() {
return "Model:update overlapped: " + XModelConstants.getWorkspace(FileSystemsImpl.this.getModel()); //$NON-NLS-1$
}
}
private void updateOverlappedLater() {
if(isActive() && overlapper == null) {
if(EclipseResourceUtil.isProjectFragment(getModel())) return;
needsUpdateOverlapping = true;
XJob.addRunnable(overlapper = new Ov());
// (overlapper = new Thread(new Ov())).start();
}
}
public boolean updateOverlapped() {
if(overlapper != null && overlapper.status == OV_RUNNING) {
return false;
} else if(overlapper != null) {
overlapper.status = OV_STOPPED;
overlapper = null;
}
updateOverlappedInternal();
return true;
}
private //synchronized
void updateOverlappedInternal() {
if(!needsUpdateOverlapping) return;
needsUpdateOverlapping = false;
if(EclipseResourceUtil.isProjectFragment(getModel())) return;
Set<String> overlappedWorkingCopy = new HashSet<String>();
synchronized(this) {
if(overlapped != null) {
overlappedWorkingCopy.addAll(overlapped);
}
}
Set<String> _overlapped = new HashSet<String>();
Map<String,String> overlappedSystems = new HashMap<String,String>();
XModelObject[] cs = getChildren();
String[] paths = new String[cs.length];
List<Integer> folders = new ArrayList<Integer>();
for (int i = 0; i < cs.length; i++) {
String path = XModelObjectUtil.getExpandedValue(cs[i], XModelObjectConstants.ATTR_NAME_LOCATION, null);
try {
File f = new File(path);
path = f.getCanonicalPath().replace('\\', '/');
path = FilePathHelper.toPathPath(path);
if (path.charAt(path.length()-1) != '/') path += '/';
paths[i] = path;
if(cs[i].getModelEntity().getName().equals(XModelObjectConstants.ENT_FILE_SYSTEM_FOLDER)) {
folders.add(i);
}
} catch (IOException e) {
paths[i] = null;
}
}
for (int i = 0; i < paths.length; i++) {
if(paths[i] == null) continue;
for (int j: folders) {
if(i == j || paths[j] == null) continue;
if(!paths[i].startsWith(paths[j])) continue;
if(paths[i].equals(paths[j]) && j < i) continue;
String overlap = cs[j].getPathPart() + paths[i].substring(paths[j].length()-1, paths[i].length()-1);
_overlapped.add(overlap);
overlappedSystems.put(overlap, cs[i].getPath());
}
}
List<XModelObject> fire = new ArrayList<XModelObject>();
Iterator<String> it = overlappedWorkingCopy.iterator();
while(it.hasNext()) {
String path = it.next();
if(_overlapped.contains(path)) {
XModelObject c = getChildByPath(path);
if(c == null || XModelObjectConstants.TRUE.equals(c.get("overlapped"))) { //$NON-NLS-1$
_overlapped.remove(path);
}
} else {
it.remove();
XModelObject c = getChildByPath(path);
if(c == null) continue;
c.set("overlapped", ""); //$NON-NLS-1$ //$NON-NLS-2$
c.set("overlappedSystem", ""); //$NON-NLS-1$ //$NON-NLS-2$
c = c.getParent();
if(c != null) fire.add(c);
}
}
it = _overlapped.iterator();
while(it.hasNext()) {
String path = it.next();
overlappedWorkingCopy.add(path);
XModelObject c = getChildByPath(path);
if(c == null) continue;
c.set("overlapped", XModelObjectConstants.TRUE); //$NON-NLS-1$
c.set("overlappedSystem", "" + overlappedSystems.get(path)); //$NON-NLS-1$ //$NON-NLS-2$
c = c.getParent();
if(c != null) fire.add(c);
}
XModelObject[] os = (XModelObject[])fire.toArray(new XModelObject[0]);
for (int i = 0; i < os.length; i++)
((XModelImpl)getModel()).fireStructureChanged(os[i]);
synchronized(this) {
overlapped = (overlappedWorkingCopy.size() == 0) ? null : overlappedWorkingCopy;
}
}
public synchronized boolean isOverlapped(XModelObject o) {
return (overlapped != null && overlapped.contains(o.getPath()));
}
public String get(String name) {
if("APPLICATION_NAME".equals(name) || "application-name".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
String s = super.get(name);
if(s == null || s.length() == 0) {
s = ""; //$NON-NLS-1$
// project name ?
}
return (s == null) ? "" : s; //$NON-NLS-1$
} else {
return super.get(name);
}
}
public void forceUpdate() {
if(currentUpdate != null) {
currentUpdate.run();
}
}
public void resourceChanged(IResourceChangeEvent event) {
if(event.getType() == IResourceChangeEvent.PRE_DELETE
&& event.getResource() instanceof IProject && event.getResource().equals(EclipseResourceUtil.getProject(this))) {
ModelPlugin.getWorkspace().removeResourceChangeListener(this);
ModelPlugin.getDefault().getSaveParticipant().removeModel(getModel());
libs.destroy();
//TODO implement safe destroy.
// destroy();
return;
}
if(!isActive() || event == null || event.getDelta() == null) return;
if(!checkDelta(event.getDelta())) return;
List<IResourceDelta> fileDeltas = findFileDeltas(event.getDelta(), null);
if(fileDeltas != null) {
boolean onlyMarkers = true;
for (IResourceDelta d: fileDeltas) {
IResource r = d.getResource();
if(r != null && r.isDerived(IResource.CHECK_ANCESTORS)) {
continue;
}
if(d.getKind() != IResourceDelta.CHANGED) {
onlyMarkers = false;
break;
}
int flags = d.getFlags();
if(flags != IResourceDelta.MARKERS) {
onlyMarkers = false;
break;
}
}
if(onlyMarkers) {
return;
}
}
IFile f = fileDeltas == null || fileDeltas.size() == 0 ? null : (IFile)fileDeltas.get(0).getResource();
if(f != null && f.getName().equals(".classpath")) { //$NON-NLS-1$
new FileSystemsLoader().updateClassPath(this);
return;
}
fileSystemsRenameListener.checkFileSystemRename(event);
requireUpdate();
}
List<IResourceDelta> findFileDeltas(IResourceDelta delta, List<IResourceDelta> fileDeltas) {
IResource r = delta.getResource();
if(r instanceof IFile) {
if(fileDeltas == null) fileDeltas = new ArrayList<IResourceDelta>();
fileDeltas.add(delta);
}
IResourceDelta[] ds = delta.getAffectedChildren();
for (IResourceDelta d: ds) {
fileDeltas = findFileDeltas(d, fileDeltas);
}
return fileDeltas;
}
private void onUpdateClassPath() {
}
UpdateRunnable currentUpdate = null;
void requireUpdate() {
if(lock == 0 && currentUpdate == null) {
// synchronized (this) {
currentUpdate = new UpdateRunnable();
// }
XJob.addRunnableWithPriority(currentUpdate);
} else {
// synchronized (this) {
if(currentUpdate != null) currentUpdate.request++;
// }
}
}
boolean checkDelta(IResourceDelta delta) {
IResource resource = delta.getResource();
if(resource == null) return false;
if(resource instanceof IWorkspaceRoot) {
IResourceDelta[] d = delta.getAffectedChildren();
return (d.length > 0 && checkDelta(d[0]));
} else {
IProject p = resource.getProject();
IProject cp = EclipseResourceUtil.getProject(this);
if(cp != null && cp != p && p != null) {
return false;
}
}
return true;
}
int lock = 0;
public void lockUpdate() {
lock++;
}
public void unlockUpdate() {
lock--;
}
private boolean isUpdating = false;
boolean saveRequested = false;
public boolean requestSave() {
if(isUpdating) saveRequested = true;
return isUpdating;
}
class UpdateRunnable implements XJob.XRunnable {
String id = "Update File Systems - " + XModelConstants.getWorkspace(getModel()); //$NON-NLS-1$
int request = 0;
int usage = 0;
public String getId() {
return id;
}
public void run() {
if(this != currentUpdate) return;
if(!isActive()) {
ModelPlugin.getWorkspace().removeResourceChangeListener(FileSystemsImpl.this);
} else {
doUpdate();
if(request != 0) {
request = 0;
doUpdate();
}
}
if(this == currentUpdate) currentUpdate = null;
}
}
private void doUpdate() {
if(lock > 0) return;
isUpdating = true;
try {
boolean b = isOpenProject();
if(b) {
XModelObjectLoaderUtil.getObjectLoader(FileSystemsImpl.this).update(FileSystemsImpl.this);
}
} catch (XModelException e) {
ModelPlugin.getPluginLog().logError(e);
}
if(saveRequested) {
try {
XModelObjectLoaderUtil.getObjectLoader(FileSystemsImpl.this).save(FileSystemsImpl.this);
} finally {
saveRequested = false;
isUpdating = false;
}
}
isUpdating = false;
}
private boolean isOpenProject() {
IProject p = EclipseResourceUtil.getProject(this);
return p != null && p.isAccessible() && p.isOpen();
}
private IContributorResourceAdapter contributorResourceAdapter = null;
public Object getAdapter(Class adapter) {
if(IResource.class == adapter || IProject.class == adapter) return EclipseResourceUtil.getProject(this);
if(IContributorResourceAdapter.class == adapter) {
if(contributorResourceAdapter == null) {
contributorResourceAdapter = new ExtendedJavaElementAdapterFactory();
}
return contributorResourceAdapter;
} else if(IWorkbenchAdapter.class == adapter) {
return new ExtendedJavaWorkbenchAdapter();
} else if(IContributorResourceAdapter.class == adapter) {
return new IContributorResourceAdapterImpl();
}
return super.getAdapter(adapter);
}
private IAdaptable validateAdaptable(IAdaptable adaptable) {
IAdaptable a = null;
if(adaptable instanceof XModelObject) {
IProject p = EclipseResourceUtil.getProject(FileSystemsImpl.this);
if(p != null) a = EclipseResourceUtil.getJavaProject(p);
}
return a == null ? adaptable : a;
}
class ExtendedJavaElementAdapterFactory extends JavaElementAdapterFactory {
public IResource getAdaptedResource(IAdaptable adaptable) {
return super.getAdaptedResource(validateAdaptable(adaptable));
}
// public ResourceMapping getAdaptedResourceMapping(IAdaptable adaptable) {
// return super.getAdaptedResourceMapping(validateAdaptable(adaptable));
// }
}
class ExtendedJavaWorkbenchAdapter extends JavaWorkbenchAdapter {
public Object[] getChildren(Object element) {
return super.getChildren(getJavaElement(element));
}
public ImageDescriptor getImageDescriptor(Object element) {
return super.getImageDescriptor(getJavaElement(element));
}
public String getLabel(Object element) {
return super.getLabel(getJavaElement(element));
}
public Object getParent(Object element) {
return super.getLabel(getJavaElement(element));
}
private IJavaElement getJavaElement(Object element) {
if (element instanceof IJavaElement)
return (IJavaElement)element;
IProject p = EclipseResourceUtil.getProject(FileSystemsImpl.this);
return EclipseResourceUtil.getJavaProject(p);
}
}
class IContributorResourceAdapterImpl implements IContributorResourceAdapter {
public IResource getAdaptedResource(IAdaptable adaptable) {
return EclipseResourceUtil.getProject(FileSystemsImpl.this);
}
}
}