@Override
  public void revertChanges(ISemanticFileStore semanticFileStore, IProgressMonitor monitor)
      throws CoreException {

    MultiStatus status =
        new MultiStatus(
            TestPlugin.PLUGIN_ID,
            IStatus.OK,
            NLS.bind("Revert Change Result for {0}", semanticFileStore.getPath().toString()),
            null);
    dropCache(
        semanticFileStore,
        monitor,
        new IDropCacheVisitor() {

          public boolean shouldDrop(ISemanticFileStore store) {
            // TODO only files
            return !store.isLocalOnly();
          }
        },
        status);

    if (!status.isOK()) {
      throw new CoreException(status);
    }

    // this can only happen on files, no recursion
    this.setReadOnly(semanticFileStore, true, monitor);

    fillCache(semanticFileStore, monitor, status);

    if (!status.isOK()) {
      throw new CoreException(status);
    }
  }
 @Override
 public void addFileFromRemoteByURI(
     ISemanticFileStore parentStore, String name, URI uri, IProgressMonitor monitor)
     throws CoreException {
   super.addFileFromRemoteByURI(parentStore, name, uri, monitor);
   this.addDependentFiles(
       (ISemanticFileStore) parentStore.getChild(name), uri.toString(), monitor);
 }
  @Override
  public void synchronizeContentWithRemote(
      ISemanticFileStore semanticFileStore,
      SyncDirection direction,
      IProgressMonitor monitor,
      MultiStatus status) {
    super.synchronizeContentWithRemote(semanticFileStore, direction, monitor, status);

    if (semanticFileStore.getType() == ISemanticFileStore.FILE
        && !semanticFileStore.isLocalOnly()) {
      try {
        String uriString = this.getURIStringInternal(semanticFileStore);
        this.addDependentFiles(semanticFileStore, uriString, monitor);
      } catch (CoreException e) {
        status.add(e.getStatus());
      }
    }
  }
  @Override
  public ISemanticSpiResourceInfo fetchResourceInfo(
      ISemanticFileStore semanticFileStore, int options, IProgressMonitor monitor)
      throws CoreException {
    String uriString;
    if (SemanticSpiResourceInfo.isOptionRequested(
        ISemanticFileSystem.RESOURCE_INFO_URI_STRING, options)) {
      uriString = this.getURIStringInternal(semanticFileStore);
    } else {
      uriString = null;
    }

    boolean existsRemotely = false;
    if (SemanticSpiResourceInfo.isOptionRequested(
        ISemanticFileSystem.RESOURCE_INFO_EXISTS_REMOTELY, options)) {

      String remoteURI = getURIString(semanticFileStore);

      if (remoteURI == null) {
        throw new SemanticResourceException(
            SemanticResourceStatusCode.REMOTE_URI_NOT_FOUND,
            semanticFileStore.getPath(),
            NLS.bind("Remote URI is not set for file {0}", semanticFileStore.getPath().toString()));
      }

      try {
        InputStream is = RESTUtil.openInputStream(remoteURI, null);
        existsRemotely = is != null;
        Util.safeClose(is);
      } catch (IOException e) {
        // $JL-EXC$ ignore and simply return false here
      }
    }

    boolean isReadOnly = isReadOnlyInternal(semanticFileStore);
    return new SemanticSpiResourceInfo(
        options,
        false,
        false,
        isReadOnly,
        existsRemotely,
        uriString,
        this.getContentTypeInternal(semanticFileStore));
  }
  @Override
  public void addResource(
      ISemanticFileStore childStore,
      String name,
      ResourceType resourceType,
      IProgressMonitor monitor)
      throws CoreException {
    switch (resourceType) {
      case FOLDER_TYPE:
        // create internal data
        childStore.addChildFolder(name);

        break;

      default:
        throw new SemanticResourceException(
            SemanticResourceStatusCode.METHOD_NOT_SUPPORTED, childStore.getPath(), "Not supported");
    }
  }
  /**
   * Adds the dependent files
   *
   * @param store the store
   * @param uri the URI
   * @param monitor may be null
   * @throws CoreException on failure
   */
  public void addDependentFiles(ISemanticFileStore store, String uri, IProgressMonitor monitor)
      throws CoreException {
    InputStream is = store.openInputStream(EFS.NONE, monitor);
    List<String> uris;

    try {
      StringBuffer buf = RESTUtil.readStreamIntoStringBuffer(is, "UTF-8"); // $NON-NLS-1$

      uris = this.findRelativeURLs(buf);

    } catch (IOException e) {
      // $JL-EXC$ ignore
      throw new CoreException(
          new Status(
              IStatus.ERROR, SemanticResourcesPluginExamplesCore.PLUGIN_ID, e.getMessage(), e));
    } finally {
      Util.safeClose(is);
    }

    String rootURI = uri.substring(0, uri.lastIndexOf("/")); // $NON-NLS-1$

    for (String uriString : uris) {
      if (uriString.contains(":")) { // $NON-NLS-1$
        // this is an absolute URI; ignore
        continue;
      }

      if (uriString.contains("?")) { // $NON-NLS-1$
        // this is an invalid file name; ignore
        continue;
      }

      if (uriString.startsWith("/")) { // $NON-NLS-1$
        // this is an URI that is not relative to the document; ignore
        continue;
      }

      String[] parts = uriString.split("/"); // $NON-NLS-1$

      addDependentFiles((ISemanticFileStore) store.getParent(), rootURI, parts, 0, monitor);
    }
  }
  private void addChildrenHierarchy(
      ISemanticFileStore childStore,
      String rootURI,
      String[] parts,
      int index,
      IProgressMonitor monitor)
      throws CoreException {

    IFileStore child = childStore.getChild(parts[index]);

    if (parts.length > index + 1) {
      if (!child.fetchInfo().exists()) {
        this.addResource(childStore, parts[index], ResourceType.FOLDER_TYPE, monitor);
      }

      child = childStore.getChild(parts[index]);

      addChildrenHierarchy(
          (ISemanticFileStore) child,
          rootURI + "/" + parts[index],
          parts,
          index + 1,
          monitor); //$NON-NLS-1$
    } else {
      // create a file
      if (!child.fetchInfo().exists()) {
        super.addFileFromRemoteByURI(
            childStore,
            parts[index],
            URI.create(rootURI + "/" + parts[index]),
            monitor); //$NON-NLS-1$

        this.addDependentFiles(
            (ISemanticFileStore) childStore.getChild(parts[index]),
            rootURI + "/" + parts[index],
            monitor); //$NON-NLS-1$
      }
    }
  }
  @Override
  public void synchronizeContentWithRemote(
      ISemanticFileStore semanticFileStore,
      SyncDirection direction,
      IProgressMonitor monitor,
      MultiStatus status) {

    if (semanticFileStore.getType() == ISemanticFileStore.FILE) {
      if (semanticFileStore.isLocalOnly()) {
        return;
      }

      URI uri;
      try {
        uri = new URI(getURIString(semanticFileStore));
      } catch (URISyntaxException e) {
        // $JL-EXC$ ignore
        status.add(
            new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage()));
        return;
      } catch (CoreException e) {
        status.add(e.getStatus());
        return;
      }

      boolean syncIn = false;
      boolean syncOut = false;
      if (direction == SyncDirection.BOTH) {

        long remoteTimestamp;
        try {
          remoteTimestamp = uri.toURL().openConnection().getLastModified();
        } catch (MalformedURLException e) {
          // $JL-EXC$ ignore
          status.add(
              new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage()));
          return;
        } catch (IOException e) {
          // $JL-EXC$ ignore
          status.add(
              new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage()));
          return;
        }
        long localTimestamp;
        try {
          localTimestamp = getResourceTimestamp(semanticFileStore, monitor);
        } catch (CoreException e) {
          status.add(e.getStatus());
          return;
        }
        syncIn = remoteTimestamp > localTimestamp;
        syncOut = localTimestamp > remoteTimestamp;
      }

      if (direction == SyncDirection.INCOMING || syncIn) {
        this.dropCache(semanticFileStore, monitor, this.deleteAllVisitor, status);
        this.fillCache(semanticFileStore, monitor, status);
      }
      if (direction == SyncDirection.OUTGOING || syncOut) {
        try {
          File file = new File(uri);
          Util.transferStreams(
              getCacheService().getContent(semanticFileStore.getPath()),
              new FileOutputStream(file),
              monitor);
          file.setLastModified(getResourceTimestamp(semanticFileStore, monitor));
        } catch (CoreException e) {
          status.add(e.getStatus());
        } catch (IOException e) {
          // $JL-EXC$ ignore
          status.add(
              new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage()));
          return;
        }
      }

    } else {
      IFileStore[] childStores;
      try {
        childStores = semanticFileStore.childStores(EFS.NONE, monitor);
      } catch (CoreException e) {
        status.add(e.getStatus());
        return;
      }
      for (IFileStore store : childStores) {
        if (store instanceof ISemanticFileStore) {
          synchronizeContentWithRemote((ISemanticFileStore) store, direction, monitor, status);
        }
      }
    }
  }