package cloudsync.model; import java.io.File; import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import cloudsync.exceptions.FileIOException; import org.apache.commons.csv.CSVRecord; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import cloudsync.helper.Handler; public class Item { public final static Integer METADATA_VERSION = 1; public final static String SEPARATOR = File.separator; public final static String ATTRIBUTE_POSIX = "posix"; public final static String ATTRIBUTE_DOS = "dos"; public final static String ATTRIBUTE_OWNER = "owner"; public final static String ATTRIBUTE_ACL = "acl"; private final static String METADATA_SEPARATOR = ":"; private final static String ATTRIBUTE_SEPARATOR = "|"; private Item parent; protected String name; protected String remoteIdentifier; protected ItemType type; private Long filesize; private Long creationtime; private Long modifytime; private Long accesstime; private String[] attributes; private String checksum; private boolean needsMetadataUpgrade; protected Map<String, Item> children; protected Item() { } public static Item getDummyRoot() { Item item = new Item(); item.name = ""; item.remoteIdentifier = ""; item.type = ItemType.FOLDER; item.children = new HashMap<>(); return item; } public static Item fromCSV(final CSVRecord values) { final String name = FilenameUtils.getName(values.get(0)); final String remoteIndentifier = values.get(1); String[] metadata = new String[values.size() - 2]; for (int i = 2; i < values.size(); i++) { metadata[i - 2] = values.get(i); } return initItem(new Item(), name, remoteIndentifier, metadata); } public String[] toCSVArray() { return ArrayUtils.addAll(new String[] { getPath(), remoteIdentifier }, getDataArray()); } public static RemoteItem fromMetadata(final String remoteIdentifier, final boolean isFolder, final String name, final String metadata, Long remoteFilesize, FileTime remoteCreationtime) { RemoteItem item = new RemoteItem(remoteFilesize, remoteCreationtime); if (!StringUtils.isEmpty(metadata)) { item = (RemoteItem) initItem(item, name, remoteIdentifier, metadata.split(METADATA_SEPARATOR)); } else { item.name = name; item.remoteIdentifier = remoteIdentifier; item.type = isFolder ? ItemType.FOLDER : ItemType.FILE; if (item.type.equals(ItemType.FOLDER)) { item.children = new HashMap<>(); } } return item; } public String getMetadata(Handler handler) throws FileIOException { if (needsMetadataUpgrade) { if (checksum == null) { // force a checksum update handler.getLocalProcessedBinary(this); } } return StringUtils.join(getDataArray(), METADATA_SEPARATOR); } public static Item fromLocalData(String name, ItemType type, Long filesize, FileTime creationtime, FileTime modifytime, FileTime accesstime, Map<String, String[]> map) { Item item = new Item(); item.name = name; item.type = type; item.filesize = filesize; item.creationtime = creationtime.to(TimeUnit.SECONDS); item.modifytime = modifytime.to(TimeUnit.SECONDS); item.accesstime = accesstime.to(TimeUnit.SECONDS); item.attributes = convertToAttributes(map); if (item.type.equals(ItemType.FOLDER)) { item.children = new HashMap<>(); } return item; } private static Item initItem(final Item item, final String name, final String remoteIdentifier, final String[] metadata) { int metadataVersion = 0; if (metadata.length != 9 || metadata[8].contains(ATTRIBUTE_SEPARATOR)) { metadataVersion = Integer.parseInt(metadata[0]); } ItemType type; Long filesize; Long creationtime; Long modifytime; Long accesstime; String checksum; String[] attributes; switch ( metadataVersion ) { case 0: type = ItemType.fromString(metadata[0]); filesize = StringUtils.isEmpty(metadata[1]) ? null : Long.parseLong(metadata[1]); creationtime = StringUtils.isEmpty(metadata[2]) ? null : Long.parseLong(metadata[2]); modifytime = StringUtils.isEmpty(metadata[3]) ? null : Long.parseLong(metadata[3]); accesstime = StringUtils.isEmpty(metadata[4]) ? null : Long.parseLong(metadata[4]); final String group = StringUtils.isEmpty(metadata[5]) ? null : metadata[5]; final String user = StringUtils.isEmpty(metadata[6]) ? null : metadata[6]; final Integer permissions = StringUtils.isEmpty(metadata[7]) ? null : Integer.parseInt(metadata[7]); Map<String, String[]> map = new HashMap<>(); if (permissions == null) map.put(ATTRIBUTE_POSIX, new String[] { group, user }); else map.put(ATTRIBUTE_POSIX, new String[] { group, user, permissions.toString() }); attributes = convertToAttributes(map); checksum = metadata[8]; break; default: type = ItemType.fromString(metadata[1]); filesize = StringUtils.isEmpty(metadata[2]) ? null : Long.parseLong(metadata[2]); creationtime = StringUtils.isEmpty(metadata[3]) ? null : Long.parseLong(metadata[3]); modifytime = StringUtils.isEmpty(metadata[4]) ? null : Long.parseLong(metadata[4]); accesstime = StringUtils.isEmpty(metadata[5]) ? null : Long.parseLong(metadata[5]); checksum = metadata[6]; attributes = ArrayUtils.subarray(metadata, 7, metadata.length); break; } item.name = name; item.remoteIdentifier = remoteIdentifier; item.type = type; item.filesize = filesize; item.creationtime = creationtime; item.modifytime = modifytime; item.accesstime = accesstime; item.attributes = attributes; if (ItemType.FOLDER.equals(item.type)) { item.children = new HashMap<>(); } item.checksum = checksum; item.needsMetadataUpgrade = metadataVersion != METADATA_VERSION; return item; } private String[] getDataArray() { return ArrayUtils.addAll( new String[] { METADATA_VERSION.toString(), type.toString(), filesize != null ? filesize.toString() : null, creationtime != null ? creationtime.toString() : null, modifytime != null ? modifytime.toString() : null, accesstime != null ? accesstime.toString() : null, checksum }, attributes); } private static Map<String, String[]> convertToMap(String[] attributes) { Map<String, String[]> map = new HashMap<>(); for (String row : attributes) { String[] values = StringUtils.splitPreserveAllTokens(row, ATTRIBUTE_SEPARATOR); map.put(values[0], ArrayUtils.subarray(values, 1, values.length)); } return map; } private static String[] convertToAttributes(Map<String, String[]> map) { List<String> list = new ArrayList<>(); for (String type : map.keySet()) { list.add(type + ATTRIBUTE_SEPARATOR + StringUtils.join(map.get(type), ATTRIBUTE_SEPARATOR)); } String[] arr = new String[list.size()]; arr = list.toArray(arr); return arr; } public void setParent(final Item parent) { this.parent = parent; } public Item getParent() { return parent; } public void setRemoteIdentifier(final String remoteIdentifier) { this.remoteIdentifier = remoteIdentifier; } public void addChild(final Item child) { children.put(child.name, child); } public void removeChild(final Item child) { children.remove(child.name); } public Item getChildByName(final String name) { return children.get(name); } public Map<String, Item> getChildren() { return new HashMap<>(children); } public String getTypeName() { return type.getName(); } public ItemType getType() { return type; } public boolean isTypeChanged(final Item item) { return !type.equals(item.type); } public boolean isFiledataChanged(final Item item) { if (type.equals(ItemType.FILE) || type.equals(ItemType.LINK)) { if (isChanged(filesize, item.filesize)) { return true; } if (isChanged(creationtime, item.creationtime)) { return true; } if (isChanged(modifytime, item.modifytime)) { return true; } /* * if (isChanged(accesstime, item.accesstime)) { return true; } */ } return false; } public boolean isMetadataChanged(final Item item) { if (isChanged(filesize, item.filesize)) { return true; } if (isChanged(creationtime, item.creationtime)) { return true; } if (isChanged(modifytime, item.modifytime)) { return true; } /* * if (isChanged(accesstime, item.accesstime)) { return true; } */ if ((attributes == null) != (item.attributes == null) || (attributes != null && !Arrays.equals(attributes, item.attributes))) { return true; } if (needsMetadataUpgrade) { return true; } return false; } public boolean isMetadataFormatChanged() { return needsMetadataUpgrade; } private boolean isChanged(final Object o1, final Object o2) { return (o1 == null) != (o2 == null) || (o1 != null && !o1.equals(o2)); } public void update(final Item item) { type = item.type; filesize = item.filesize; modifytime = item.modifytime; creationtime = item.creationtime; attributes = item.attributes; } public String getPath() { String path = ""; if (parent != null) { path += parent.getPath(); } if (!path.isEmpty()) { path += Item.SEPARATOR; } path += name; return path; } public String getName() { return name; } public void setName(final String name) { this.name = name; } public void setChecksum(final String checksum) { this.checksum = checksum; } public String getChecksum() { return this.checksum; } public String getRemoteIdentifier() { return remoteIdentifier; } public boolean isType(final ItemType type) { return this.type.equals(type); } public Map<String, String[]> getAttributes() { return convertToMap(attributes); } public Long getFilesize() { return filesize; } public FileTime getCreationTime() { return FileTime.from(creationtime, TimeUnit.SECONDS); } public FileTime getModifyTime() { return FileTime.from(modifytime, TimeUnit.SECONDS); } public FileTime getAccessTime() { return FileTime.from(accesstime, TimeUnit.SECONDS); } public String getInfo() { StringBuilder info = new StringBuilder(); info.append( getTypeName() ); info.append( " '" ); info.append( getPath() ); info.append( "'" ); return info.toString(); } }