/*******************************************************************************
* 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.File;
import java.io.IOException;
import java.net.URI;
import java.text.MessageFormat;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.eclipse.core.internal.resources.ResourceException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.widgets.Display;
import org.jboss.tools.common.meta.action.XActionInvoker;
import org.jboss.tools.common.meta.impl.XMetaDataConstants;
import org.jboss.tools.common.model.ServiceDialog;
import org.jboss.tools.common.model.XModelException;
import org.jboss.tools.common.model.XModelObjectConstants;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.filesystems.BodySource;
import org.jboss.tools.common.model.filesystems.FileAuxiliary;
import org.jboss.tools.common.model.filesystems.FilePathHelper;
import org.jboss.tools.common.model.filesystems.XFileObject;
import org.jboss.tools.common.model.impl.RegularObjectImpl;
import org.jboss.tools.common.model.impl.XModelImpl;
import org.jboss.tools.common.model.impl.XModelObjectImpl;
import org.jboss.tools.common.model.loaders.AuxiliaryLoader;
import org.jboss.tools.common.model.loaders.EntityRecognizer;
import org.jboss.tools.common.model.loaders.EntityRecognizerContext;
import org.jboss.tools.common.model.loaders.Reloadable;
import org.jboss.tools.common.model.loaders.XObjectLoader;
import org.jboss.tools.common.model.loaders.impl.ModelEntityRecognizer;
import org.jboss.tools.common.model.loaders.impl.PropertiesLoader;
import org.jboss.tools.common.model.markers.ResourceMarkers;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.Paths;
import org.jboss.tools.common.model.util.XModelObjectLoaderUtil;
import org.jboss.tools.common.util.FileUtil;
public class FolderImpl extends RegularObjectImpl implements FolderLoader {
private static final long serialVersionUID = 8082046187736790127L;
protected boolean loaded = false;
protected IContainer resource = null;
protected String pathForResource = null;
LinkedResources linked = new LinkedResources();
// protected Map<String, File> linked = new HashMap<String, File>();
// protected Map<String, IResource> linkedResources = new HashMap<String, IResource>();
public static File toFile(IResource resource) {
File f = null;
IPath path = resource.getLocation();
if(path == null || true) {
URI uri = resource.getLocationURI();
if(uri != null) {
String scheme = uri.getScheme();
if("file".equals(scheme)) try { //$NON-NLS-1$
f = new File(uri);
} catch (IllegalArgumentException e) {
ModelPlugin.getDefault().logError(e); //TODO remove and ignore after testing
}
if(f != null && !f.exists()) {
f = null;
}
}
} else {
f = path.toFile();
}
return f;
}
public FolderImpl() {}
public int getFileType() {
return FOLDER;
}
protected Comparator<XModelObject> createComparator() {
return new FileObjectComparator();
}
protected FileSystemImpl getFileSystem() {
FolderImpl folder = (FolderImpl)getParent();
return (folder == null) ? null : folder.getFileSystem();
}
public boolean isChildEditable(XModelObject o) {
if(!isObjectEditable()) return false;
File f = getChildIOFile(o);
if(f == null) return true;
return (!f.isFile() || f.canWrite());
}
protected File getFile() {
String path = getAbsolutePath();
File f = (path == null) ? null :
new File(Paths.expand(path, getModel().getProperties()));
if(f != null) {
try {
f = f.getCanonicalFile();
} catch (IOException e) {
//ignore
}
}
return f;
}
public BodySource getBodySource(String filename) {
File f = getFile();
return (f == null) ? null : getBodySource(getChildIOFile(filename));
}
BodySource getBodySource(File f) {
IFile ef = getChildFile(f.getName());
if(ef != null) return new EclipseFileBodySource(ef, f);
return new FileBodySource(f);
}
public boolean isAttributeEditable(String name) {
return false;
}
protected String getAbsolutePath() {
FolderImpl parent = (FolderImpl)getParent();
String p = (parent == null) ? null : parent.getAbsolutePath();
if(parent != null && parent.linked.containsFile(getPathPart())) {
return parent.linked.getFileByFileName(getPathPart()).getAbsolutePath();
}
return (p == null) ? null : p + XModelObjectConstants.SEPARATOR + name();
}
public IProject getProject() {
return (getParent() == null) ? null : ((FolderImpl)getParent()).getProject();
}
/**
* May return null.
*
* @return
*/
private File[] getFiles() {
File f = getFile();
if(f == null) return null;
if (!f.isDirectory()) return new File[0];
return f.listFiles(); //May return null if another thread removes directory right after the check.
}
private void fillMap(Map<String, File> m) {
File[] fs = getFiles();
if(fs != null) {
for (File f: fs) {
String p = FilePathHelper.toPathPath(f.getName());
m.put(p, f);
}
}
}
public void set(String name, String value) {
if(XModelObjectConstants.XML_ATTR_NAME.equals(name) && isActive()) {
if(value != null && !value.equals(get(name))) copy();
}
super.set(name, value);
}
protected void loadChildren() {
if(loaded || !isActive()) return;
File[] fs = getFiles();
IResource[] rs = getResources();
if(fs == null || rs == null) return;
loaded = true;
((XModelImpl)getModel()).addLoader();
try {
loadChildren(fs, rs);
} finally {
((XModelImpl)getModel()).removeLoader();
}
}
private void loadChildren(File[] fs, IResource[] rs) {
FileSystemImpl fileSystem = getFileSystem();
if(fileSystem == null) return;
FileSystemPeer peer = fileSystem.getPeer();
for (int i = 0; i < fs.length; i++) {
_loadChild(peer, fs[i]);
}
for (int i = 0; i < rs.length; i++) {
if(!rs[i].isAccessible()) continue;
if(!rs[i].isLinked()) continue;
File f = toFile(rs[i]);
if(f == null) {
continue;
}
linked.registerResource(rs[i]);
_loadChild(peer, f);
}
bindAuxiliary();
fire = true;
}
private void bindAuxiliary() {
XModelObject[] cs = getChildren();
for (int i = 0; i < cs.length; i++) {
if(cs[i].getFileType() == XModelObject.FILE) {
XObjectLoader loader = XModelObjectLoaderUtil.getObjectLoader(cs[i]);
if(loader instanceof AuxiliaryLoader) {
((AuxiliaryLoader)loader).bind(cs[i]);
}
}
}
}
private void _loadChild(FileSystemPeer peer, File f) {
if(f.isDirectory()) {
Properties p = new Properties();
p.setProperty(XModelObjectConstants.ATTR_NAME, f.getName());
XModelObject c = getModel().createModelObject("FileFolder", p); //$NON-NLS-1$
String pp = FilePathHelper.toPathPath(f.getName());
if(linked.containsFile(pp)) {
c.setObject("file", linked.getFileByFileName(pp)); //$NON-NLS-1$
}
addChild(c);
} else {
createFileObject(f, true);
}
peer.register(f);
}
private Properties getEntityProperties(File f) {
Properties p = new Properties();
parseFileName(p, f.getName());
String ext = p.getProperty(XModelObjectConstants.ATTR_NAME_EXTENSION);
String body = null;
EntityRecognizer recognizer = getModel().getEntityRecognizer();
EntityRecognizerContext context = new EntityRecognizerContext(f.getName(), ext, body);
String entity = recognizer.getEntityName(context);
if("FileAny".equals(entity)) { //$NON-NLS-1$
boolean isText = XModelObjectLoaderUtil.isTextFile(f, 100);
if(f.length() > 100000 || !isText) entity = XModelObjectConstants.ENT_FILE_ANY_LONG;
else if(isText) entity = "FileTXT"; //$NON-NLS-1$
} else if(isRecognizerNeedingBody(entity, recognizer, context)) {
body = getBodySource(f).get();
entity = recognizer.getEntityName(new EntityRecognizerContext(f.getName(), ext, body));
}
if(entity == null || getModel().getMetaData().getEntity(entity) == null) entity = "FileAny"; //$NON-NLS-1$
p.setProperty(XMetaDataConstants.ENTITY, entity);
if(body != null) p.setProperty(XModelObjectConstants.ATTR_NAME_BODY, body);
return p;
}
private boolean isRecognizerNeedingBody(String entityForNullBody, EntityRecognizer recognizer, EntityRecognizerContext context) {
if(recognizer instanceof ModelEntityRecognizer) {
return ((ModelEntityRecognizer)recognizer).isBodyRequired(context, entityForNullBody);
}
return true;
}
private XModelObject createFileObject(File f, boolean add) {
return createFileObject(f, getEntityProperties(f), add);
}
private XModelObject createFileObject(File f, Properties p) {
return createFileObject(f, p, true);
}
private XModelObject createFileObject(File f, Properties p, boolean add) {
BodySource bs = getBodySource(f);
String body = p.getProperty(XModelObjectConstants.ATTR_NAME_BODY);
String entity = p.getProperty(XMetaDataConstants.ENTITY);
XModelObject c = getModel().createModelObject(entity, p);
if(c == null) {
ModelPlugin.getPluginLog().logInfo("Cannot create file for entity " + entity); //$NON-NLS-1$
return null;
}
if(isLateloadFile2(c)) {
((FileAnyImpl)c).setBodySource(bs);
} else {
XObjectLoader loader = XModelObjectLoaderUtil.getObjectLoader(c);
if(loader != null) {
if(body == null) body = bs.get();
XModelObjectLoaderUtil.setTempBody(c, body);
if(XModelObjectConstants.ENTITY_FILE_PROPERTIES.equals(entity) && bs instanceof EclipseFileBodySource) {
String encoding = FileUtil.getEncoding(((EclipseFileBodySource)bs).ef);
if(encoding != null) c.set("_encoding_", encoding); //$NON-NLS-1$
}
loader.load(c);
} else if(c.getModelEntity().getAttribute(XModelObjectConstants.ATTR_NAME__FILE) != null) {
c.set(XModelObjectConstants.ATTR_NAME__FILE, f.getAbsolutePath());
}
}
if(linked.filesByFileName.containsValue(f)) {
c.setObject("file", f); //$NON-NLS-1$
}
if(add) {
addChild(c);
} else {
((XModelObjectImpl)c).setParent_0(this);
}
return c;
}
public XModelObject createValidChildCopy(XModelObject child) {
File pf = getFile();
String s = FileAnyImpl.toFileName(child);
File f = new File(pf, s);
if(f.exists()) {
return createFileObject(f, false);
}
return null;
}
int updateLock = 0;
Set<String> unsynchronized = null;
public boolean update() {
if(!loaded) return true;
if(updateLock > 0) return true;
updateLock++;
Map<String,File> mf = new HashMap<String,File>();
linked.clearFiles();
FileSystemImpl fileSystem = getFileSystem();
if(fileSystem == null) return false;
FileSystemsImpl fsi = (FileSystemsImpl)fileSystem.getParent();
try {
if(resource != null && resource.exists() && !resource.isSynchronized(IResource.DEPTH_ONE)) try {
fsi.lockUpdate();
resource.refreshLocal(IResource.DEPTH_ZERO, null);
if(resource.exists()) {
IResource[] rs = resource.members();
for (int i = 0; i < rs.length; i++) {
if(!rs[i].isSynchronized(IResource.DEPTH_ZERO)) {
if(unsynchronized == null) unsynchronized = new HashSet<String>();
String pp = FilePathHelper.toPathPath(rs[i].getName());
unsynchronized.add(pp);
}
}
if(resource.exists()) {
resource.refreshLocal(IResource.DEPTH_ONE, null);
}
}
} catch (ResourceException re) {
File f = toFile(resource);
if(f != null && f.exists()) {
ModelPlugin.getPluginLog().logError("Exception caught in FolderImpl.update()", re); //$NON-NLS-1$
} else {
//ignore we cannot prevent this when project is removed externally
}
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError("Exception caught in FolderImpl.update()", e); //$NON-NLS-1$
} finally {
fsi.unlockUpdate();
}
try {
if(resource != null && resource.exists()) {
IResource[] rs = resource.members();
for (int i = 0; i < rs.length; i++) {
if(rs[i].isLinked()) {
File f = toFile(rs[i]);
if(f != null) {
String p = FilePathHelper.toPathPath(f.getName());
mf.put(p, f);
linked.registerResource(rs[i]);
}
}
}
}
} catch (CoreException ex) {
ModelPlugin.getPluginLog().logError("Exception caught in FolderImpl.update()"); //$NON-NLS-1$
}
fillMap(mf);
Map<String,XModelObject> mc = children.getObjectsMap();
updateAuxiliary(mc, mf);
Map<String,XModelObject> toRemove = new HashMap<String,XModelObject>();
Iterator<String> io = mc.keySet().iterator();
while(io.hasNext()) {
String nm = io.next();
if(mf.containsKey(nm)) continue;
XModelObject o = (XModelObject)mc.get(nm);
File of = getChildIOFile(o);
if(o.getFileType() == XModelObject.FOLDER) {
if(!fileSystem.getPeer().containsDir(of)) continue;
} else {
if(!fileSystem.getPeer().contains(of)) continue;
}
toRemove.put(nm, o);
io.remove();
}
Iterator<String> it = mf.keySet().iterator();
while(it.hasNext()) {
String nm = it.next();
File f = (File)mf.get(nm);
XModelObject o = (XModelObject)mc.get(nm);
if(o != null) {
updateLoaded(o, f);
mc.remove(nm);
} else {
updateNew(nm, f, toRemove);
}
}
it = toRemove.keySet().iterator();
while(it.hasNext()) {
String nm = (String)it.next();
updateRemove((XModelObject)toRemove.get(nm));
}
bindAuxiliary();
} catch (NoClassDefFoundError error) {
//Most probably Eclipse is shutting down.
return true;
} catch (XModelException t) {
ModelPlugin.getPluginLog().logError("Exception caught in FolderImpl.update()"); //$NON-NLS-1$
} finally {
updateLock--;
unsynchronized = null;
synchronized (this) {
this.notifyAll();
}
}
return true;
}
/**
* Returns true if thread had to wait.
* @return
*/
public long waitForUpdate() {
if(updateLock == 0) {
return 0;
}
long t = System.currentTimeMillis();
long dt = 0;
while(updateLock > 0 && dt < 10000) {
synchronized(this) {
try {
this.wait(100);
} catch (InterruptedException e) {
return dt;
}
}
dt = System.currentTimeMillis() - t;
}
return dt;
}
protected File getChildIOFile(XModelObject o) {
String s = FileAnyImpl.toFileName(o);
File f = (File)o.getObject("file"); //for links //$NON-NLS-1$
if(f == null && linked.containsFile(o.getPathPart())) {
f = linked.getFileByFileName(o.getPathPart());
}
if(f == null) {
f = new File(getFile(), s);
}
return f;
}
protected File getChildIOFile(String filename) {
File f = null;
String p = FilePathHelper.toPathPath(filename);
if(linked.containsFile(p)) {
f = linked.getFileByFileName(p);
}
if(f == null) {
f = new File(getFile(), filename);
}
return f;
}
private void updateAuxiliary(Map<String,XModelObject> mc, Map<String,File> mf) throws XModelException {
Iterator<String> it = mf.keySet().iterator();
while(it.hasNext()) {
String nm = (String)it.next();
File f = mf.get(nm);
XModelObject o = mc.get(nm);
if(o == null || !o.getModelEntity().getName().equals(FileAuxiliary.AUX_FILE_ENTITY)) continue;
it.remove();
FileAnyAuxiliaryImpl aux = (FileAnyAuxiliaryImpl)o;
mc.remove(nm);
if(aux.isObsolete()) {
FileAuxiliary h = aux.getAuxiliaryHelper();
XModelObject main = aux.getMainObject();
if(main != null && main.isActive() && h != null) {
String n = h.getAuxiliaryName(main);
String p = n + "." + aux.getAttributeValue(XModelObjectConstants.ATTR_NAME_EXTENSION); //$NON-NLS-1$
XModelObject other = getChildByPath(p);
if(other != null && other != aux) other.removeFromParent();
aux.fileRenamed(n, aux.getAttributeValue(XModelObjectConstants.ATTR_NAME_EXTENSION));
aux.updateBodySource();
if(!isOverlapped()) {
File r = new File(f.getParentFile(), FileAnyImpl.toFileName(aux));
if(!r.equals(f) && f.exists()) {
if(!r.exists()) {
FileUtil.copyFile(f, r);
f.delete();
FileSystemPeer peer = getFileSystem().getPeer();
peer.unregister(f);
peer.register(r);
IFile ef = getChildFile(f.getName());
if(ef != null && !ef.isSynchronized(0)) {
try {
ef.refreshLocal(0, null);
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError("Exception caught in FolderImpl.update()"); //$NON-NLS-1$
}
}
ef = getChildFile(r.getName());
if(ef != null && !ef.isSynchronized(0)) {
try {
ef.refreshLocal(0, null);
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError("Exception caught in FolderImpl.update()"); //$NON-NLS-1$
}
}
} else {
f.delete();
getFileSystem().getPeer().unregister(f);
IFile ef = getChildFile(f.getName());
if(ef != null && !ef.isSynchronized(0)) {
try {
ef.refreshLocal(0, null);
} catch (CoreException e) {
//ignore
}
}
}
}
}
} else {
if(!isOverlapped()) {
updateRemove(aux);
f.delete();
getFileSystem().getPeer().unregister(f);
}
}
} else {
FileSystemPeer peer = getFileSystem().getPeer();
if(!registerFileInPeer(peer, f)) continue;
XModelObject main = aux.getMainObject();
if(main == null) continue;
File fmain = (File)mf.get(main.getPathPart());
if(fmain == null) continue;
peer.unregister(fmain);
setForceLoadProperty(main, true);
updateLoaded(main, fmain);
setForceLoadProperty(main, false);
mc.remove(main.getPathPart());
}
}
}
//returns true if registration was really done
private synchronized boolean registerFileInPeer(FileSystemPeer peer, File f) {
if(f.isFile() && peer.contains(f) && !peer.isUpdated(f)) return false;
peer.register(f);
return true;
}
private void setForceLoadProperty(XModelObject f, boolean b) {
f.set("forceLoad", b ? XModelObjectConstants.TRUE : ""); //$NON-NLS-1$ //$NON-NLS-2$
}
protected void updateLoaded(XModelObject o, File f) throws XModelException {
FileSystemImpl fs = getFileSystem();
if(fs == null) return;
FileSystemPeer peer = fs.getPeer();
if(o instanceof FolderImpl) {
if(!o.getAttributeValue(XModelObjectConstants.ATTR_NAME).equals(f.getName())) {
o.setAttributeValue(XModelObjectConstants.ATTR_NAME, f.getName());
((FolderImpl)o).getResource();
}
((FolderImpl)o).update();
} else {
if(!registerFileInPeer(peer, f) && !isEncodingChanged(o)) {
if(!f.getName().equals(FileAnyImpl.toFileName(o))) {
String n = f.getName();
int i = n.lastIndexOf("."); //$NON-NLS-1$
String nm = (i >= 0) ? n.substring(0, i) : n;
String ext = (i >= 0) ? n.substring(i + 1) : ""; //$NON-NLS-1$
((FileAnyImpl)o).fileRenamed(nm, ext);
}
return;
}
String p = FilePathHelper.toPathPath(f.getName());
int i = (!o.isModified() || unsynchronized == null || !unsynchronized.contains(p)) ? 0 : question(f);
if(i == 0) {
reload(o, f);
} else if(i == -100) {
final XModelObject o1 = o;
final File f1 = f;
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if(question(f1) == 0) {
try {
reload(o1, f1);
} catch (XModelException e) {
ModelPlugin.getPluginLog().logError(e);
}
}
}
});
}
}
}
private boolean isEncodingChanged(XModelObject o) {
if(o != null && o.getModelEntity().getName().equals(XModelObjectConstants.ENTITY_FILE_PROPERTIES)) {
String encoding = o.getAttributeValue(XModelObjectConstants.ATTR_NAME_ENCODING);
if(encoding == null) {
return false;
}
String newEncoding = PropertiesLoader.getEncoding(o);
if(newEncoding == null) {
newEncoding = "8859_1"; //$NON-NLS-1$
}
if(!encoding.equals(newEncoding)) {
return true;
}
}
return false;
}
private int question(File f) {
if(Display.getCurrent() == null) return -100;
return getModel().getService().showDialog("Update",
MessageFormat
.format(
"File {0} is externally modified.\nDo you want to reload it?",
f.getAbsolutePath()), new String[]{"Yes", "No"}, null,
ServiceDialog.QUESTION);
}
public void updateChildFile(XModelObject o, File f) throws XModelException {
FileSystemImpl fs = getFileSystem();
if(fs == null) return;
FileSystemPeer peer = fs.getPeer();
if(f.isFile() && peer.contains(f) && !peer.isUpdated(f)) return;
int i = (!o.isModified()) ? 0 : question(f);
if(i < 0) return;
if(!registerFileInPeer(peer, f)) return;
if(i == 0) {
reload(o, f);
}
}
public boolean isChangedEntity(XModelObject o, File f) {
Properties p = getEntityProperties(f);
return (!o.getModelEntity().getName().equals(p.getProperty(XMetaDataConstants.ENTITY)));
}
private void reload(XModelObject o, File f) throws XModelException {
Properties p = getEntityProperties(f);
if(!o.getModelEntity().getName().equals(p.getProperty(XMetaDataConstants.ENTITY))) {
o.removeFromParent();
createFileObject(f, p);
return;
}
BodySource bs = getBodySource(f);
if(isLateloadFile(o)) {
FileAnyImpl impl = (FileAnyImpl)o;
if(impl.getBodySource() != null) {
impl.setBodySource(bs);
fireObjectChanged(null);
} else {
impl.edit(bs.get());
impl.setModified(false);
XModelObjectLoaderUtil.updateModifiedOnSave(impl);
}
} else {
XObjectLoader loader = XModelObjectLoaderUtil.getObjectLoader(o);
if(loader != null) {
XModelObjectLoaderUtil.setTempBody(o, bs.get());
if(XModelObjectConstants.ENTITY_FILE_PROPERTIES.equals(o.getModelEntity().getName()) && bs instanceof EclipseFileBodySource) {
String encoding = FileUtil.getEncoding(((EclipseFileBodySource)bs).ef);
if(encoding != null) o.setAttributeValue("encoding", encoding); //$NON-NLS-1$
}
loader.update(o);
} else if(o instanceof Reloadable) {
((Reloadable)o).reload();
} else if(XModelObjectConstants.ENT_FILE_ANY_LONG.equals(o.getModelEntity().getName())) {
o.setModified(false);
}
}
}
protected boolean updateNew(String pathpart, File f, Map<String,XModelObject> toRemove) throws XModelException {
FileSystemImpl fs = getFileSystem();
if(fs == null) return false;
FileSystemPeer peer = fs.getPeer();
if(peer.contains(f) && !peer.isUpdated(f)) return false;
XModelObject c = null;
if(f.isDirectory()) {
Properties p = new Properties();
p.setProperty(XModelObjectConstants.ATTR_NAME, f.getName());
c = getModel().createModelObject("FileFolder", p); //$NON-NLS-1$
String pp = FilePathHelper.toPathPath(f.getName());
if(linked.containsFile(pp)) {
c.setObject("file", linked.getFileByFileName(pp)); //$NON-NLS-1$
}
} else {
Properties ep = getEntityProperties(f);
XModelObject old = findOldObject(ep.getProperty(XMetaDataConstants.ENTITY), toRemove);
if(old != null) {
String ofn = FileAnyImpl.toFileName(old);
if(!f.getName().equals(ofn)) {
File of = new File(f.getParent(), ofn);
peer.unregister(of);
String nm = ep.getProperty(XModelObjectConstants.ATTR_NAME);
String ext = ep.getProperty(XModelObjectConstants.ATTR_NAME_EXTENSION);
((FileAnyImpl)old).fileRenamed(nm, ext);
}
updateLoaded(old, f);
} else {
createFileObject(f, ep);
}
}
peer.register(f);
return (c != null && addChild(c));
}
private XModelObject findOldObject(String entity, Map<String,XModelObject> toRemove) {
if(entity == null || toRemove.size() == 0) return null;
Iterator<String> it = toRemove.keySet().iterator();
while(it.hasNext()) {
String nm = it.next().toString();
XModelObject o = (XModelObject)toRemove.get(nm);
if(o.getModelEntity().getName().equals(entity)) {
toRemove.remove(nm);
return o;
}
}
return null;
}
protected void updateRemove(XModelObject o) throws XModelException {
boolean d = (o instanceof FolderImpl);
FileSystemImpl fs = getFileSystem();
if(fs == null) return;
final FileSystemPeer peer = fs.getPeer();
final File rf = getChildIOFile(o);
boolean c = (d && peer.containsDir(rf)) || ((!d) && peer.contains(rf));
if(!c) return;
int i = (!o.isModified()) ? 1 : question(o);
if(i != 0) {
o.removeFromParent();
} else if(i == -100) {
if(rf.exists()) return;
final XModelObject o1 = o;
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if(rf.exists()) return;
if(question(o1) == 0) {
o1.removeFromParent();
} else {
try {
saveChild(o1, peer, rf);
XActionInvoker.invoke("Open", o1, null); //$NON-NLS-1$
} catch (XModelException e) {
ModelPlugin.getDefault().logError(e);
}
}
}
});
} else {
saveChild(o, peer, rf);
XActionInvoker.invoke("Open", o, null); //$NON-NLS-1$
}
if(d) peer.unregisterDir(rf); else peer.unregister(rf);
}
public void removeChildFile(XModelObject o) {
FileSystemImpl fs = getFileSystem();
if(fs == null) return;
boolean d = (o instanceof FolderImpl);
FileSystemPeer peer = fs.getPeer();
File rf = getChildIOFile(o);
boolean c = (d && peer.containsDir(rf)) || ((!d) && peer.contains(rf));
if(!c) return;
if(o.getModel().getModelBuffer().source() == o && rf.exists() && o.getModelEntity().getAttribute(XModelObjectConstants.ATTR_NAME__FILE) != null) {
File temp = null;
try {
temp = File.createTempFile(ModelPlugin.TEMP_FILE_PREFIX, rf.getName());
} catch (IOException e) {
ModelPlugin.getPluginLog().logError(e);
}
if(temp != null) {
FileUtil.copyFile(rf, temp);
temp.deleteOnExit();
o.set(XModelObjectConstants.ATTR_NAME__FILE, temp.getAbsolutePath());
}
}
IResource r = (d) ? (IResource) getChildContainer(o.getAttributeValue(XModelObjectConstants.ATTR_NAME))
: (IResource) getChildFile(FileAnyImpl.toFileName(o));
o.removeFromParent();
if(r.exists()) {
try {
r.delete(true, null);
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError(e);
}
} else {
rf.delete();
}
if(d) peer.unregisterDir(rf); else peer.unregister(rf);
XModelObjectLoaderUtil.updateModifiedFlag(this);
}
private static int question(XModelObject o) {
if(Display.getCurrent() == null) {
return -100;
}
return o.getModel().getService().showDialog("Update",
MessageFormat
.format(
"File {0} is removed from the disk.\n Do you want to save your changes?",
o.getModelEntity().getRenderer().getTitle(o)), new String[]{"Yes", "No"}, null,
ServiceDialog.QUESTION);
}
private boolean fire = false;
protected void fireStructureChanged(int kind, Object info) {
if(fire) super.fireStructureChanged(kind, info);
}
public boolean hasChildren() {
boolean q = super.hasChildren();
if (q || loaded) return q;
if(getParent() instanceof FolderImpl) {
FolderImpl p = (FolderImpl)getParent();
if(p.linked.containsFile(getPathPart())) return true;
}
File[] fs = getFiles();
q = (fs != null && fs.length > 0);
if(!q) loaded = fire = true;
return q;
}
public boolean save() {
File f = getFile();
if(f == null) return true;
if(f.exists() && !isModified()) return true;
if(isOverlapped()) return true;
boolean b = true;
if(!f.exists()) {
IContainer c = getResource();
if(getFileType() == FOLDER && c instanceof IFolder) {
IFolder ef = (IFolder)c;
try {
ef.create(true, ef.getParent().isLocal(0), null);
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError(e);
}
}
if(!f.exists()) f.mkdirs();
}
Map<String,File> t = new HashMap<String,File>();
fillMap(t);
FileSystemPeer peer = getFileSystem().getPeer();
peer.register(f);
XModelObject[] cs = getChildren();
for (int i = 0; i < cs.length; i++) {
if(cs[i].getModelEntity().getName().equals(FileAuxiliary.AUX_FILE_ENTITY)) continue;
if(cs[i] instanceof FolderLoader) {
b &= ((FolderLoader)cs[i]).save();
} else {
File d = linked.getFileByFileName(cs[i].getPathPart());
if(d == null) {
d = new File(f, FileAnyImpl.toFileName(cs[i]));
}
try {
b &= saveChild(cs[i], peer, d);
} catch (XModelException ee) {
//TODO maybe it should be rethrown
ModelPlugin.getPluginLog().logError(ee);
}
}
t.remove(cs[i].getPathPart());
}
cs = getChildren(FileAuxiliary.AUX_FILE_ENTITY);
for (int i = 0; i < cs.length; i++) {
File cf = getChildIOFile(cs[i]);
if(!cf.exists()) continue;
peer.register(cf);
FileAnyImpl impl = (FileAnyImpl)cs[i];
if(impl.getBodySource() == null) impl.setBodySource(getBodySource(cf));
t.remove(cs[i].getPathPart());
}
Iterator<File> it = t.values().iterator();
while(it.hasNext()) {
File df = it.next();
boolean d = df.isDirectory();
boolean r = (d && peer.containsDir(df)) || ((!d) && peer.contains(df));
if(!r) continue;
XModelObjectLoaderUtil.remove(df);
if(d) peer.unregisterDir(df); else peer.unregister(df);
}
if(b) setModified(false);
return b;
}
public boolean saveChild(XModelObject c) throws XModelException {
if(c == null || c.getParent() != this) return false;
if(!c.isModified()) return true;
File folder = getFile();
if(folder==null || !folder.exists()) return save();
updateLock++;
boolean b = false;
try {
File d = linked.getFileByFileName(c.getPathPart());
if(d == null) {
d = new File(folder, FileAnyImpl.toFileName(c));
}
saveChild(c, getFileSystem().getPeer(), d);
if(b) XModelObjectLoaderUtil.updateModifiedOnSave(c);
} finally {
updateLock--;
}
update(); // on action
ResourceMarkers.refreshProblemMarkersAsync(c);
return b;
}
private boolean saveChild(XModelObject c, FileSystemPeer peer, File cf) throws XModelException {
boolean b = true;
if(!cf.exists()) c.setModified(true);
if(!c.isModified()) return true;
XObjectLoader loader = XModelObjectLoaderUtil.getObjectLoader(c);
if(loader != null) b &= loader.save(c);
BodySource bs = getBodySource(cf);
boolean h = (loader == null) || bs.write(c);
if(loader == null) saveFileWithoutLoader(cf, c);
if(h && c.isModified() || isChangedEntity(c, cf)) reload(c, cf);
b &= h;
peer.register(cf);
if(isLateloadFile(c)) {
FileAnyImpl impl = (FileAnyImpl)c;
if(impl.getBodySource() == null) impl.setBodySource(bs);
}
return b;
}
/**
* Used only by ObjectMultiPageEditor to prevent
* reload after saveX method. This is a precaution
* for the case when file update job may fail.
* @param child
*/
public void updateRegistration(XModelObject child) {
if(child == null || child.getParent() != this) return;
File folder = getFile();
if(folder == null || !folder.exists() || getFileSystem() == null) return;
FileSystemPeer peer = getFileSystem().getPeer();
File cf = new File(folder, FileAnyImpl.toFileName(child));
if(!cf.exists()) child.setModified(true);
if(child.isModified()) return;
peer.register(cf);
}
private boolean saveFileWithoutLoader(File f, XModelObject o) {
if(!o.isModified()) return true;
if(o.getModelEntity().getAttribute(XModelObjectConstants.ATTR_NAME__FILE) == null) return true;
String sfn = o.get(XModelObjectConstants.ATTR_NAME__FILE);
if(sfn.length() == 0) return true;
if(f.getAbsolutePath().equalsIgnoreCase(sfn)) {
o.setModified(false);
return true;
}
File sf = new File(sfn);
if(sf.isFile()) {
f.getParentFile().mkdirs();
FileUtil.copyFile(sf, f);
}
o.set(XModelObjectConstants.ATTR_NAME__FILE, f.getAbsolutePath());
o.setModified(false);
return true;
}
public boolean changeChildTimeStamp(XModelObject c) throws XModelException {
if(c == null || c.getParent() != this) return false;
File cf = new File(getFile(), FileAnyImpl.toFileName(c));
if(!cf.exists()) return saveChild(c);
long s = cf.lastModified();
if(c.isModified()) {
saveChild(c);
if(s != cf.lastModified()) return true;
}
cf.setLastModified(System.currentTimeMillis());
getFileSystem().getPeer().register(cf);
return true;
}
public void discardChildFile(XModelObject c) throws XModelException {
if(c == null || !c.isActive() || !c.isModified() || c.getParent() != this) return;
c.setModified(false);
XModelObjectLoaderUtil.updateModifiedOnSave(c);
File folder = getFile();
if(!folder.exists()) return;
File cf = new File(folder, FileAnyImpl.toFileName(c));
String path = c.getPath();
setForceLoadProperty(c, true);
reload(c, cf);
setForceLoadProperty(c, false);
c = getModel().getByPath(path);
if(c != null) c.getChildren();
}
public static void parseFileName(Properties p, String fn) {
int i = fn.lastIndexOf('.');
String n = (i < 0) ? fn : fn.substring(0, i);
String e = (i < 0) ? "" : fn.substring(i + 1); //$NON-NLS-1$
p.setProperty(XModelObjectConstants.ATTR_NAME, n);
p.setProperty(XModelObjectConstants.ATTR_NAME_EXTENSION, e);
}
public String getPathPart() {
String s = get(XModelObjectConstants.XML_ATTR_NAME);
return FilePathHelper.toPathPath(s);
}
public XModelObject getChildByPathPart(String pathpart) {
if(linked.filesByLinkName.containsKey(pathpart)) {
File f = linked.getFileByResourceName(pathpart);
pathpart = f.getName();
}
pathpart = FilePathHelper.toPathPath(pathpart);
return super.getChildByPathPart(pathpart);
}
static boolean isLateloadFile(XModelObject o) {
return (o.getModelEntity().getAttribute("_lateload") != null && //$NON-NLS-1$
o.getModelEntity().getAttribute(XModelObjectConstants.ATTR_NAME_BODY) != null);
}
static boolean isLateloadFile2(XModelObject o) {
return (o.getModelEntity().getAttribute("_lateload") != null); //$NON-NLS-1$
}
protected void copy_children(XModelObject copy, boolean transform) {
super.copy_children(copy, transform);
if(copy instanceof FolderImpl) {
FolderImpl f = (FolderImpl)copy;
f.loaded = true;
f.fire = true;
}
}
private static int editability = -1;
private void initEditability() {
if(editability > -1) return;
editability = (null != getModelEntity().getActionList().getItem("DeleteActions").getItem("Delete")) //$NON-NLS-1$ //$NON-NLS-2$
? 1 : 0;
}
public boolean isObjectEditable() {
initEditability();
return (editability == 1 && !XModelObjectConstants.TRUE.equals(get(XModelObjectConstants.ATTR_NAME_OVERLAPPED)) && isActive());
}
public String getMainIconName() {
if(XModelObjectConstants.TRUE.equals(get(XModelObjectConstants.ATTR_NAME_OVERLAPPED)) && isActive()) {
String oin = get(XModelObjectConstants.ATTR_NAME_OVERLAPPED_SYSTEM);
XModelObject o = (oin == null || oin.length() == 0) ? null : getModel().getByPath(oin);
if(o == null) {
o = this;
while(o != null && o.getFileType() != XFileObject.SYSTEM) o = o.getParent();
}
if(o != null && o != this) return o.getMainIconName();
}
return super.getMainIconName();
}
public IContainer getChildContainer(String name) {
if(linked.containsFile(name)) {
IResource r = linked.getResourceByFileName(name);
return r instanceof IContainer ? (IContainer)r : null;
}
IContainer c = getResource();
return (c == null) ? null : c.getFolder(new Path(XModelObjectConstants.SEPARATOR + name));
}
public IFile getChildFile(String name) {
if(linked.containsFile(name)) {
IResource r = linked.getResourceByFileName(name);
return r instanceof IFile ? (IFile)r : null;
}
IContainer c = getResource();
return (c == null) ? null : c.getFile(new Path(XModelObjectConstants.SEPARATOR + name));
}
public IContainer getResource() {
if(!needUpdateResource()) return resource;
resource = ((FolderImpl)getParent()).getChildContainer(getAttributeValue(XModelObjectConstants.ATTR_NAME));
pathForResource = getPath();
return resource;
}
protected boolean needUpdateResource() {
if(resource != null && !resource.getName().equals(getAttributeValue(XModelObjectConstants.ATTR_NAME))) return true;
if(!isActive()) return false;
if(pathForResource == null || resource == null) return true;
String path = getPath();
return (path != null && !path.equals(pathForResource));
}
public IResource[] getResources() {
try {
if(!isActive()) return null;
IContainer c = getResource();
return (c != null && c.isAccessible()) ? c.members() : new IResource[0];
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError(e);
return new IResource[0];
}
}
public boolean isOverlapped() {
XModelObject p = this;
while(p != null && !XModelObjectConstants.TRUE.equals(get(XModelObjectConstants.ATTR_NAME_OVERLAPPED))) p = p.getParent();
return p != null;
}
public Object getAdapter(Class adapter) {
if(IResource.class == adapter) return getResource();
return super.getAdapter(adapter);
}
public boolean isRemoved() {
return resource != null && !resource.exists();
}
}
class FileBodySource implements BodySource {
private File f = null;
public FileBodySource(File f) {
this.f = f;
}
public String get() {
String encoding = ResourcesPlugin.getEncoding();
return FileUtil.readFileWithEncodingCheck(f, encoding);
}
public boolean write(Object object) {
if(!(object instanceof XModelObject)) return false;
XModelObject o = (XModelObject)object;
boolean b = XModelObjectLoaderUtil.saveBody(f, o, ResourcesPlugin.getEncoding());
XModelObject p = o.getParent();
while(p != null && p.getFileType() < XFileObject.FOLDER) p = p.getParent();
if(p instanceof FolderImpl) {
((FolderImpl)p).getFileSystem().getPeer().register(f);
}
return b;
}
}
class LinkedResources {
protected Map<String, File> filesByLinkName = new HashMap<String, File>();
protected Map<String, File> filesByFileName = new HashMap<String, File>();
protected Map<String, IResource> resourcesByLinkName = new HashMap<String, IResource>();
protected Map<String, IResource> resourcesByFileName = new HashMap<String, IResource>();
public boolean containsFile(String name) {
if(filesByFileName.containsKey(name)) return true;
return false;
}
public File getFileByResourceName(String name) {
return filesByLinkName.get(name);
}
public File getFileByFileName(String name) {
return filesByFileName.get(name);
}
public IResource getResourceByResourceName(String name) {
return resourcesByLinkName.get(name);
}
public IResource getResourceByFileName(String name) {
return resourcesByFileName.get(name);
}
public void registerResource(IResource r) {
File f = FolderImpl.toFile(r);
String pp = FilePathHelper.toPathPath(f.getName());
filesByFileName.put(pp, f);
filesByLinkName.put(r.getName(), f);
resourcesByFileName.put(pp, r);
resourcesByLinkName.put(r.getName(), r);
}
public void clearFiles() {
filesByFileName.clear();
filesByLinkName.clear();
}
}
class EclipseFileBodySource implements BodySource {
IFile ef = null;
File f;
public EclipseFileBodySource(IFile ef, File f) {
this.ef = ef;
this.f = f;
}
public String get() {
String encoding = null;
if (ef != null && ef.exists()) {
encoding = FileUtil.getEncoding(ef);
}
if (encoding == null) {
encoding = ResourcesPlugin.getEncoding();
}
try {
boolean isUTF8BOM = ModelPlugin.isUTF8BOM(encoding, ef);
if (!isUTF8BOM) {
return FileUtil.readFileWithEncodingCheck(f, encoding);
} else {
return ModelPlugin.getContent(ef.getContents(), encoding, true);
}
} catch (CoreException e) {
IStatus status = new Status(IStatus.ERROR, ModelPlugin.PLUGIN_ID, IStatus.OK,e.getLocalizedMessage(), e);
ModelPlugin.getDefault().getLog().log(status);
return null;
}
}
public boolean write(Object object) {
if(!(object instanceof XModelObject)) return false;
XModelObject o = (XModelObject)object;
try {
String encoding = null;
if(ef != null && ef.exists()) {
encoding = FileUtil.getEncoding(ef);
}
if(encoding == null) {
encoding = ResourcesPlugin.getEncoding();
}
XModelObjectLoaderUtil.saveBody(f, o, encoding);
/*
String r = XModelObjectLoaderUtil.getTempBody(o);
ByteArrayInputStream is = new ByteArrayInputStream(r.getBytes());
if(ef.exists()) ef.setContents(is, true, false, null);
else ef.create(is, true, null);
*/
o.setModified(false);
XModelObject p = o.getParent();
while(p != null && p.getFileType() < XFileObject.FOLDER) p = p.getParent();
if(p instanceof FolderImpl) {
((FolderImpl)p).getFileSystem().getPeer().register(f);
}
ef.refreshLocal(IFile.DEPTH_INFINITE, null);
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError(e);
}
return true;
}
}