001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.plugins.transfer; 019 020import java.lang.reflect.Field; 021import java.util.List; 022 023import org.simpleframework.xml.Element; 024import org.syncany.util.ReflectionUtil; 025 026import com.google.common.collect.ImmutableList; 027import com.google.common.collect.Lists; 028import com.google.common.collect.Ordering; 029import com.google.common.primitives.Ints; 030 031/** 032 * Helper class to read the options of a {@link TransferSettings} using the 033 * {@link Setup} and {@link Element} annotations. 034 * 035 * @author Christian Roth (christian.roth@port17.de) 036 */ 037public class TransferPluginOptions { 038 private static final int MAX_NESTED_LEVELS = 3; 039 040 /** 041 * Get an ordered list of {@link TransferPluginOption}s, given class a {@link TransferSettings} class. 042 * 043 * <p>This method uses the {@link Setup} and {@link Element} annotation, and their attributes 044 * to sort the options. If no annotation is given or no order attribute is provided, the 045 * option will be listed last. 046 */ 047 public static List<TransferPluginOption> getOrderedOptions(Class<? extends TransferSettings> transferSettingsClass) { 048 return getOrderedOptions(transferSettingsClass, 0); 049 } 050 051 private static List<TransferPluginOption> getOrderedOptions(Class<? extends TransferSettings> transferSettingsClass, int level) { 052 List<Field> fields = getOrderedFields(transferSettingsClass); 053 ImmutableList.Builder<TransferPluginOption> options = ImmutableList.builder(); 054 055 for (Field field : fields) { 056 TransferPluginOption option = getOptionFromField(field, transferSettingsClass, level); 057 options.add(option); 058 } 059 060 return options.build(); 061 } 062 063 private static TransferPluginOption getOptionFromField(Field field, Class<? extends TransferSettings> transferSettingsClass, int level) { 064 Element elementAnnotation = field.getAnnotation(Element.class); 065 Setup setupAnnotation = field.getAnnotation(Setup.class); 066 067 boolean hasName = !elementAnnotation.name().equalsIgnoreCase(""); 068 boolean hasDescription = setupAnnotation != null && !setupAnnotation.description().equals(""); 069 boolean hasCallback = setupAnnotation != null && !setupAnnotation.callback().isInterface(); 070 boolean hasConverter = setupAnnotation != null && !setupAnnotation.converter().isInterface(); 071 boolean hasFileType = setupAnnotation != null && setupAnnotation.fileType() != null; 072 073 String name = (hasName) ? elementAnnotation.name() : field.getName(); 074 String description = (hasDescription) ? setupAnnotation.description() : field.getName(); 075 FileType fileType = (hasFileType) ? setupAnnotation.fileType() : null; 076 boolean required = elementAnnotation.required(); 077 boolean sensitive = setupAnnotation != null && setupAnnotation.sensitive(); 078 boolean singular = setupAnnotation != null && setupAnnotation.singular(); 079 boolean visible = setupAnnotation != null && setupAnnotation.visible(); 080 boolean encrypted = field.getAnnotation(Encrypted.class) != null; 081 Class<? extends TransferPluginOptionCallback> callback = (hasCallback) ? setupAnnotation.callback() : null; 082 Class<? extends TransferPluginOptionConverter> converter = (hasConverter) ? setupAnnotation.converter() : null; 083 084 boolean isNestedOption = TransferSettings.class.isAssignableFrom(field.getType()); 085 086 if (isNestedOption) { 087 return createNestedOption(field, level, name, description, fileType, encrypted, sensitive, singular, visible, required, callback, converter); 088 } 089 else { 090 return createNormalOption(field, transferSettingsClass, name, description, fileType, encrypted, sensitive, singular, visible, required, callback, converter); 091 } 092 } 093 094 @SuppressWarnings("unchecked") 095 private static TransferPluginOption createNestedOption(Field field, int level, String name, String description, FileType fileType, 096 boolean encrypted, boolean sensitive, boolean singular, boolean visible, boolean required, 097 Class<? extends TransferPluginOptionCallback> callback, Class<? extends TransferPluginOptionConverter> converter) { 098 099 if (++level > MAX_NESTED_LEVELS) { 100 throw new RuntimeException("Plugin uses too many nested transfer settings (max allowed value: " + MAX_NESTED_LEVELS + ")"); 101 } 102 103 Class<? extends TransferSettings> fieldClass = (Class<? extends TransferSettings>) field.getType(); 104 return new NestedTransferPluginOption(field, name, description, fieldClass, fileType, encrypted, sensitive, singular, visible, required, callback, converter, 105 getOrderedOptions(fieldClass)); 106 } 107 108 private static TransferPluginOption createNormalOption(Field field, Class<? extends TransferSettings> transferSettingsClass, String name, 109 String description, FileType fileType, boolean encrypted, boolean sensitive, boolean singular, boolean visible, boolean required, 110 Class<? extends TransferPluginOptionCallback> callback, Class<? extends TransferPluginOptionConverter> converter) { 111 112 return new TransferPluginOption(field, name, description, field.getType(), fileType, encrypted, sensitive, singular, visible, required, callback, converter); 113 } 114 115 private static List<Field> getOrderedFields(Class<? extends TransferSettings> transferSettingsClass) { 116 Ordering<Field> byOrderAnnotation = new Ordering<Field>() { 117 @Override 118 public int compare(Field leftField, Field rightField) { 119 int leftOrderValue = (leftField.getAnnotation(Setup.class) != null) ? leftField.getAnnotation(Setup.class).order() : -1; 120 int rightOrderValue = (rightField.getAnnotation(Setup.class) != null) ? rightField.getAnnotation(Setup.class).order() : -1; 121 122 return Ints.compare(leftOrderValue, rightOrderValue); 123 } 124 }; 125 126 List<Field> fields = Lists.newArrayList(ReflectionUtil.getAllFieldsWithAnnotation(transferSettingsClass, Element.class)); 127 return ImmutableList.copyOf(byOrderAnnotation.nullsLast().sortedCopy(fields)); 128 } 129}