Java DataFlavor for MacOS clipbord - java

Does the MacOS clipboard use some super-secret DataFlavor that Java apps can only read from, not write to?
I've written an app (Java 15) that copies files to the clipboard, and I can retrieve these files from within my app. But when I go to Finder, it says Clipboard contents: unknown. Here's the code:
public static class FileTransferable implements Transferable {
private final List listOfFiles;
public FileTransferable(List listOfFiles) {
this.listOfFiles = listOfFiles;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{DataFlavor.javaFileListFlavor};
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return DataFlavor.javaFileListFlavor.equals(flavor);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return listOfFiles;
}
}
public void copyItems() {
ArrayList<File> files = new ArrayList<>();
files.add(new File("/Users/fred/Public/IMG0010.HEIC"));
files.add(new File("/Users/fred/Public/IMG0011.HEIC"));
files.add(new File("/Users/fred/Public/IMG0012.HEIC"));
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
clip.setContents(new FileTransferable(files), null);
}
Is there any way around this?

Related

JFileChooser with custom FileSystemView implementation

I extended FileSystemView and overwrote every method in this class. The model looks like this:
public class RemoteSystemFilesView extends FileSystemView {
private IDirectoryService directoryService;
public RemoteSystemFilesView(IDirectoryService aDirectoryService){
this.directoryService = aDirectoryService;
}
....
}
The directoryService object returns directories from the remote UNIX server. Then, I create JFileChooser.
JFileChooser fc = new JFileChooser(new RemoteSystemFilesView(new DirectoryService()));
int returnVal = fc.showOpenDialog(this);
The dialog shows remote dirs and files correctly, but then I doubleClick on one of the displayed folders, I expect to navigate into that folder, but instead folder path appears in the field "File name" and that's it. I can't go to any other directory except root (/). Should I implement something else also in JFileChooser, not just in FileSystemView?
The problem might be that your FileSystemView is actually returning plain java.io.File objects.
Instead try to return a VirtualFile wrapper object that extends java.io.File and returns true for the public boolean exists() and wraps returns VirtualFile instead of java.io.File for all the necessary methods.
This is an example of a VirtualFileSystem that I developed. It uses java.nio.Path because my code is mainly based in them. I hope it gives you a good starting point for understanding how to modify your code.
private static class VirtualFileSystemView extends FileSystemView {
final Path base;
final Set<Path> choices;
private VirtualFileSystemView(final Path base,
final Set<Path> choices) {
this.base = base;
this.choices = choices;
}
#Override
protected File createFileSystemRoot(File f) {
return new VirtualFile(f);
}
#Override
public boolean isComputerNode(File dir) {
return false;
}
#Override
public boolean isFloppyDrive(File dir) {
return false;
}
#Override
public boolean isDrive(File dir) {
return false;
}
#Override
public Icon getSystemIcon(File f) {
return null;
}
#Override
public String getSystemTypeDescription(File f) {
return f.toPath().toString();
}
#Override
public String getSystemDisplayName(File f) {
return f.getName();
}
#Override
public File getParentDirectory(final File dir) {
return new VirtualFile(dir.getParentFile());
}
#Override
public File[] getFiles(final File dir, boolean useFileHiding) {
final List<File> files = new ArrayList<>(choices.size());
choices.stream()
.filter((path) -> (path.getParent().equals(dir.toPath()))).
forEach((path) -> {
files.add(new VirtualFile(path.toFile()));
});
return files.toArray(new File[files.size()]);
}
#Override
public File createFileObject(final String path) {
return new VirtualFile(path);
}
#Override
public File createFileObject(final File dir, final String filename) {
Path fileObject;
if (dir != null) {
fileObject = Paths.get(dir.toPath().toString(), filename);
} else {
fileObject = Paths.get(filename);
}
return new VirtualFile(fileObject.toFile());
}
#Override
public File getDefaultDirectory() {
return new VirtualFile(base.toFile());
}
#Override
public File getHomeDirectory() {
return new VirtualFile(base.toFile());
}
#Override
public File[] getRoots() {
final List<File> files = new ArrayList<>(choices.size());
files.add(new VirtualFile(base.toFile()));
return files.toArray(new File[files.size()]);
}
#Override
public boolean isFileSystemRoot(final File dir) {
boolean isRoot = dir.toPath().getParent() == null;
return isRoot;
}
#Override
public boolean isHiddenFile(final File f) {
return false;
}
#Override
public boolean isFileSystem(final File f) {
return !isFileSystemRoot(f);
}
#Override
public File getChild(final File parent, final String fileName) {
return new VirtualFile(parent, fileName);
}
#Override
public boolean isParent(final File folder, final File file) {
return file.toPath().getParent().equals(folder.toPath());
}
#Override
public Boolean isTraversable(final File f) {
boolean isTraversable = false;
for (final Path path : choices) {
if (path.startsWith(f.toPath())) {
isTraversable = true;
break;
}
}
return isTraversable;
}
#Override
public boolean isRoot(final File f) {
boolean isRoot = false;
for (final Path path : choices) {
if (path.getParent().equals(f.toPath())) {
isRoot = true;
}
}
return isRoot;
}
#Override
public File createNewFolder(final File containingDir) throws IOException {
return new VirtualFile(containingDir);
}
private class VirtualFile extends File {
private static final long serialVersionUID = -1752685357864733168L;
private VirtualFile(final File file) {
super(file.toString());
}
private VirtualFile(String pathname) {
super(pathname);
}
private VirtualFile(String parent, String child) {
super(parent, child);
}
private VirtualFile(File parent, String child) {
super(parent, child);
}
#Override
public boolean exists() {
return true;
}
#Override
public boolean isDirectory() {
return VirtualFileSystemView.this.isTraversable(this);
}
#Override
public File getCanonicalFile() throws IOException {
return new VirtualFile(super.getCanonicalFile());
}
#Override
public File getAbsoluteFile() {
return new VirtualFile(super.getAbsoluteFile());
}
#Override
public File getParentFile() {
File parent = super.getParentFile();
if (parent != null) {
parent = new VirtualFile(super.getParentFile());
}
return parent;
}
}
}

Error during in-memory compilation and loading of classes with annotation processing

I am compiling and loading classes in memory with JavaCompiler.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaCompiler.CompilationTask compile = compiler.getTask(null, inMemoryFileManager, diagnostics, null, annotationClasses, compilationUnits);
if (!compile.call()) {
diagnostics.getDiagnostics().forEach(System.err::println);
}
I am using a similar Filemanager and SimpleJavaObject as in this example. I modified it slightly to be able to handle multiple files. If you need my source code please let me know.
The code above runs fine as long as annotationClasses is null.
When I try to add classes to annotation processing however I get the following error:
Class names, '<classNames>', are only accepted if annotation processing is explicitly requested
For classNames I tried Main, Main.java, package.Main and package.Main.java. They all result in the same error.
I know this is a very specific scenario. Did anybody try to do something similar and can help me?
Edit:
/* Container for a Java compilation unit (ie Java source) in memory. */
private static class CompilationUnit extends SimpleJavaFileObject {
private final String source;
public CompilationUnit(String className, String source) {
super(URI.create("file:///" + className.replaceAll("\\.", "/") + ".java"), Kind.SOURCE);
this.source = source;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
#Override
public OutputStream openOutputStream() {
throw new IllegalStateException();
}
#Override
public InputStream openInputStream() {
return new ByteArrayInputStream(source.getBytes());
}
}
/* Container for Java byte code in memory. */
private static class ByteJavaFileObject extends SimpleJavaFileObject {
private ByteArrayOutputStream byteArrayOutputStream;
public ByteJavaFileObject(String className) {
super(URI.create("byte:///" + className.replaceAll("\\.", "/") + ".class"), Kind.CLASS);
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return null;
}
#Override
public OutputStream openOutputStream() {
byteArrayOutputStream = new ByteArrayOutputStream();
return byteArrayOutputStream;
}
#Override
public InputStream openInputStream() {
return null;
}
public byte[] getByteCode() {
return byteArrayOutputStream.toByteArray();
}
}
private static class InMemoryFileManager extends ForwardingJavaFileManager {
private final InMemoryClassLoader inMemoryClassLoader;
public InMemoryFileManager(JavaCompiler compiler, Map<String, ByteJavaFileObject> fileObjects) {
super(compiler.getStandardFileManager(null, null, null));
inMemoryClassLoader = new InMemoryClassLoader(fileObjects);
}
#Override
public JavaFileObject getJavaFileForOutput(Location notUsed, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
return inMemoryClassLoader.getFileObject(className);
}
#Override
public ClassLoader getClassLoader(Location location) {
return inMemoryClassLoader;
}
public InMemoryClassLoader getClassLoader() {
return inMemoryClassLoader;
}
}
private static class InMemoryClassLoader extends ClassLoader {
private final Map<String, ByteJavaFileObject> fileObjects;
public InMemoryClassLoader(Map<String, ByteJavaFileObject> fileObjects) {
this.fileObjects = fileObjects;
}
#Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
ByteJavaFileObject fileObject = fileObjects.get(className);
return defineClass(className, fileObject.getByteCode(), 0, fileObject.getByteCode().length);
}
JavaFileObject getFileObject(String className) {
return fileObjects.get(className);
}
}

Java Zip File System Provider: ReadOnly on remote drive [Windows]

I have a problem with Zip File System Provider: If the zip file is on a remote drive (mapped or not seems to be irrelevant), the virtual file system is readonly, although the file itself is not. I wrote a minimal sample code:
public static void main(String[] args) throws IOException {
File workingDir = new File(args[0]);
File source = new File(workingDir, "in.zip");
File target = new File(workingDir, "out.zip");
Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
try (FileSystem zipfs = FileSystems.newFileSystem(target.toPath(), null)) {
Path pathInZipfile = zipfs.getPath("test.xml");
System.out.println("zipfile writable: " + target.canWrite());
System.out.println("zipFS writable: " + !zipfs.isReadOnly());
Files.delete(pathInZipfile);
System.out.println("File successfully deleted");
} catch (IOException e) {
e.printStackTrace();
}
}
If workingDir is a local directory, everything works fine. However, if it is a (mapped) remote drive, i get:
zipfile writable: true
zipFS writable: false
Exception in thread "main" java.nio.file.ReadOnlyFileSystemException
at com.sun.nio.zipfs.ZipFileSystem.checkWritable(ZipFileSystem.java:155)
at com.sun.nio.zipfs.ZipFileSystem.deleteFile(ZipFileSystem.java:1335)
at com.sun.nio.zipfs.ZipPath.delete(ZipPath.java:655)
at com.sun.nio.zipfs.ZipFileSystemProvider.delete(ZipFileSystemProvider.java:206)
at java.nio.file.Files.delete(Unknown Source)
at zipfs.ZipFS.main(ZipFS.java:23)
Am I doing something wrong? Is it impossible? Is there a workaround?
I ran into the same thing and I looked at the JDK code.
Findings
In ZipFileSystem.java there are three relevant lines:
zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);
if (!Files.isWritable(zfpath))
this.readOnly = true;
zfPath is a Path object. Something in the Windows FileSystem provider blocks write access to a zip archive path. Doesn't seem like there is much to be done about that.
Workaround
What I used as a workaround was to:
Create the zip archive in a temp folder
Populate the zip archive
Copy the temp file to the original mapped drive location
As long as the mapped drive is writable in contexts outside of the zip FileSystem, this method works.
We run in exactly the same problem, and have deviced somewhat barbarian solution.
FileSystems.newFileSystem(Path) accepts Path interface, so it's possible to satisfy it wrapping real Path instance.
Here is the solution that implements required FileSysteProvider.checkAccess():
private static FileSystem createZip(Path zipPath)
throws Exception
{
var fileSystem = zipPath.getFileSystem();
var provider = fileSystem.provider();
return FileSystems.newFileSystem(
new Path()
{
private Path path(Path path)
{
return this == path ? zipPath : path;
}
#Override
public FileSystem getFileSystem()
{
return new FileSystem()
{
public Set<String> supportedFileAttributeViews()
{
return fileSystem.supportedFileAttributeViews();
}
#Override
public FileSystemProvider provider()
{
return new FileSystemProvider()
{
#Override
public void setAttribute(
Path path,
String attribute,
Object value,
LinkOption... options)
throws IOException
{
provider.setAttribute(path(path), attribute, value, options);
}
#Override
public Map<String, Object> readAttributes(
Path path,
String attributes,
LinkOption... options)
throws IOException
{
return provider.
readAttributes(path(path), attributes, options);
}
#Override
public <A extends BasicFileAttributes> A readAttributes(
Path path,
Class<A> type,
LinkOption... options)
throws IOException
{
return provider.readAttributes(path(path), type, options);
}
#Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
throws IOException
{
return provider.newFileSystem(uri, env);
}
#Override
public DirectoryStream<Path> newDirectoryStream(
Path dir,
Filter<? super Path> filter)
throws IOException
{
return provider.newDirectoryStream(dir, filter);
}
#Override
public SeekableByteChannel newByteChannel(
Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return provider.newByteChannel(path(path), options, attrs);
}
#Override
public void move(
Path source,
Path target,
CopyOption... options)
throws IOException
{
provider.move(path(source), path(target), options);
}
#Override
public boolean isSameFile(Path path, Path path2)
throws IOException
{
return provider.isSameFile(path(path), path(path2));
}
#Override
public boolean isHidden(Path path)
throws IOException
{
return provider.isHidden(path(path));
}
#Override
public String getScheme()
{
return provider.getScheme();
}
#Override
public Path getPath(URI uri)
{
return provider.getPath(uri);
}
#Override
public FileSystem getFileSystem(URI uri)
{
return provider.getFileSystem(uri);
}
#Override
public FileStore getFileStore(Path path)
throws IOException
{
return provider.getFileStore(path(path));
}
#Override
public <V extends FileAttributeView> V getFileAttributeView(
Path path,
Class<V> type,
LinkOption... options)
{
return provider.
getFileAttributeView(path(path), type, options);
}
#Override
public void delete(Path path)
throws IOException
{
provider.delete(path(path));
}
#Override
public void createDirectory(Path dir, FileAttribute<?>... attrs)
throws IOException
{
provider.createDirectory(path(dir), attrs);
}
#Override
public void copy(
Path source,
Path target,
CopyOption... options)
throws IOException
{
provider.copy(path(source), path(target), options);
}
#Override
public void checkAccess(Path path, AccessMode... modes)
throws IOException
{
if ((modes != null) &&
(modes.length == 1) &&
(modes[0] == AccessMode.WRITE))
{
return;
}
provider.checkAccess(path(path), modes);
}
};
}
#Override
public WatchService newWatchService()
throws IOException
{
return fileSystem.newWatchService();
}
#Override
public boolean isReadOnly()
{
return false;
}
#Override
public boolean isOpen()
{
return fileSystem.isOpen();
}
#Override
public UserPrincipalLookupService getUserPrincipalLookupService()
{
return fileSystem.getUserPrincipalLookupService();
}
#Override
public String getSeparator()
{
return fileSystem.getSeparator();
}
#Override
public Iterable<Path> getRootDirectories()
{
return fileSystem.getRootDirectories();
}
#Override
public PathMatcher getPathMatcher(String syntaxAndPattern)
{
return fileSystem.getPathMatcher(syntaxAndPattern);
}
#Override
public Path getPath(String first, String... more)
{
return fileSystem.getPath(first, more);
}
#Override
public Iterable<FileStore> getFileStores()
{
return fileSystem.getFileStores();
}
#Override
public void close() throws IOException
{
fileSystem.close();
}
};
}
#Override
public boolean isAbsolute()
{
return zipPath.isAbsolute();
}
#Override
public Path getRoot()
{
return zipPath.getRoot();
}
#Override
public Path getFileName()
{
return zipPath.getFileName();
}
#Override
public Path getParent()
{
return zipPath.getParent();
}
#Override
public int getNameCount()
{
return zipPath.getNameCount();
}
#Override
public Path getName(int index)
{
return zipPath.getName(index);
}
#Override
public Path subpath(int beginIndex, int endIndex)
{
return zipPath.subpath(beginIndex, endIndex);
}
#Override
public boolean startsWith(Path other)
{
return zipPath.startsWith(other);
}
#Override
public boolean endsWith(Path other)
{
return zipPath.endsWith(other);
}
#Override
public Path normalize()
{
return zipPath.normalize();
}
#Override
public Path resolve(Path other)
{
return zipPath.relativize(other);
}
#Override
public Path relativize(Path other)
{
return zipPath.relativize(other);
}
#Override
public URI toUri()
{
return zipPath.toUri();
}
#Override
public Path toAbsolutePath()
{
return zipPath.toAbsolutePath();
}
#Override
public Path toRealPath(LinkOption... options)
throws IOException
{
return zipPath.toRealPath(options);
}
#Override
public WatchKey register(
WatchService watcher,
Kind<?>[] events,
Modifier... modifiers)
throws IOException
{
return zipPath.register(watcher, events, modifiers);
}
#Override
public int compareTo(Path other)
{
return zipPath.compareTo(other);
}
},
Map.of("create", "true"));
}
This is more like a hack but we think it solves the bug in original ZipFileSystem
Try setting the private field readOnly in ZipFileSystem to false with reflection.

Filtering and selecting files from a directory

I have an application right now that selects files from a directory. what I want to do is be able to have a feature where you can put in a file extension such as .gif, .txt etc.. and after a button is clicked the application will run through the directory and find and select all files of that type. The only code I have to show is of my current application which has none of this. Was hoping for a point in the right direction or some advice.
private List<File> getMatchingFiles(File parent, final String extension) {
File[] files = parent.listFiles(new FileFilter() {
public boolean accept(File dir) {
String name = dir.getName();
if(name.endsWith(extension)) {
return true;
}
}
});
List<File> retval = Arrays.asList( files );
return retval;
}
For use in a JFileChooser, all the answers so far offered are sub-optimal. Instead implement JFileChooser.setFileFilter(javax.swing.filechooser.FileFilter) for the best user experience.
It might end up looking something like this1:
Image obtained using code courtesy of A Sample FileChooser Program.
What you are looking for is probably the java.io.File#list(filter).
edit: If you want the entire file system be searched, then you need to recursively scan every direcotry:
public static void filter (String dirname, List<File> result) {
try {
for (String f : new File(dirname).list()) {
String filename = dirname + f;
File theFile = new File(filename);
if (theFile.isDirectory()) {
filter(filename + "/", result);
} else if (new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".gif");
}
}.accept(theFile)) {
result.add(theFile);
}
}
} catch (Exception e) {
// may raise null-pointer when access denied
}
}
public static void main(String[] args) {
List<File> result = new ArrayList<File>();
filter("F:/", result);
System.out.println(result.size());
}
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return (!name.endsWith(".gif")
}
};

Not the same object after a deserialization in a data transfer

I really need some help down here...
I'm working on drag and drop event in a Jtree.
I've created a TransferHandler to manage the drag and drop.
Source : KineticsTransferHandler.java
package tree;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.TreePath;
import datastructures.base.Acquisition;
import datastructures.base.Kinetics;
import datastructures.base.Location;
public class KineticsTransferHandler extends TransferHandler{
private static final long serialVersionUID = -5653477841078614666L;
final public static DataFlavor ACQUISITION_NODE = new DataFlavor(Acquisition.class, "Acquisition Node");
static DataFlavor flavors[] = { ACQUISITION_NODE };
#Override
public int getSourceActions(JComponent c) {
return MOVE;
}
#Override
protected Transferable createTransferable(JComponent c) {
JTree tree = (JTree) c;
TreePath path = tree.getSelectionPath();
System.out.println(tree.getSelectionPath().toString());
if (path != null) {
Object o = path.getLastPathComponent();
if(o instanceof Acquisition) {
return new AcquisitionTransferable((Acquisition)o);
}
}
return null;
}
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
if(action != NONE) {
JTree tree = (JTree) source;
StudyTreeModel model = (StudyTreeModel)tree.getModel();
model.printStudy();
tree.updateUI();
}
}
#Override
public boolean canImport(TransferHandler.TransferSupport support) {
boolean canImport = false;
if (support.isDrop()) {
Acquisition source = null;
if (support.isDataFlavorSupported(ACQUISITION_NODE)) {
try {
source = (Acquisition) support.getTransferable().getTransferData(ACQUISITION_NODE);
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if(source != null) {
JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation();
Object dest = dropLocation.getPath().getLastPathComponent();
canImport = sameLocation(source, dest);
}
}
}
return canImport;
}
/*Verifies that the source and the dest are in the same Location*/
private boolean sameLocation(Acquisition source, Object dest) {
/*...
A method to check if the source has the same Location than the dest.
...*/
}
#Override
public boolean importData(TransferHandler.TransferSupport support) {
boolean importData = false;
if (canImport(support)) {
Acquisition source = null;
if (support.isDataFlavorSupported(ACQUISITION_NODE)) {
try {
source = (Acquisition) support.getTransferable().getTransferData(ACQUISITION_NODE);
((StudyTree)support.getComponent()).gettr
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation();
Object dest = dropLocation.getPath().getLastPathComponent();
int childIndex = dropLocation.getChildIndex();
if (sameLocation(source, dest)) {// dest and source get the same Location
/*...
Management of the drop according to the dest.
...*/
}
}
return importData;
}
public class AcquisitionTransferable implements Transferable {
Acquisition acquisition;
public AcquisitionTransferable(Acquisition s) {
acquisition = s;
}
#Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException {
if (!isDataFlavorSupported(flavor))
throw new UnsupportedFlavorException(flavor);
return acquisition;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return ACQUISITION_NODE.equals(flavor);
}
}
}
It uses an Transferable for the data transfert that I've called AcquisitionTransferable (as you can see in the end).
My problem(s) came(s) from this part
Source : KineticsTransferHandler.canImport(TransferHandler.TransferSupport)
source = (Acquisition) support.getTransferable().getTransferData(ACQUISITION_NODE);
The structure that I've, in the end, in source(the one above) is like a copy of the real one. When I'm debugging, I can see that the source's ID is not the same as in the real one.
But in support(parameter of KineticsTransferHandler.canImport(TransferHandler.TransferSupport)), I've my Jtree which contains the structure, wich is the good one.
So, what I'm thinking is, there is a problem in the access of the structure in getTransferData, it may be a problem with the serialization. When I access my structure, getTransferData deserializes the structure and this is why I get like a clone of it.
Do you have any idea of how I should fix it?
You need to define your DataFlavor with a javaJVMLocalObjectMimeType, to signify that the transferred data resides in the local JVM. In your case, the DataFlavor definition should look like:
final public static DataFlavor ACQUISITION_NODE =
new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType+"; class=\"" +Acquisition.class.getCanonicalName()+"\""
,"Acquisition Node");
Check the other two Java object MIME types here also.

Categories