/** Sets the access flag of a member */ @Override protected void setAccessFlag(final Member m) { final int mods = m.getModifiers(); final Class c = m.getDeclaringClass(); final int cmods = c.getModifiers(); final String pkg = this.importationManager.getCurrentPackage(); final String mp = getPackageName(c); final boolean samePkg = pkg.equals(mp); if (Modifier.isPublic(cmods) || samePkg) { if (Modifier.isPublic(mods)) { ((AccessibleObject) m).setAccessible(true); } else if (Modifier.isProtected(mods)) { if (c.isAssignableFrom(this.declaringClass.getSuperclass()) || samePkg) { ((AccessibleObject) m).setAccessible(true); } } else if (!Modifier.isPrivate(mods)) { if (samePkg) { ((AccessibleObject) m).setAccessible(true); } } else { if (this.declaringClass == c || isInnerClass(this.declaringClass, c)) { ((AccessibleObject) m).setAccessible(true); } } } }
/** * Appends the fields and values defined by the given object of the given Class. * * @param lhs the left hand object * @param rhs the right hand object * @param clazz the class to append details of * @param builder the builder to append to * @param useTransients whether to test transient fields * @param excludeFields array of field names to exclude from testing */ private static void reflectionAppend( Object lhs, Object rhs, Class<?> clazz, EqualsBuilder builder, boolean useTransients, String[] excludeFields) { if (isRegistered(lhs, rhs)) { return; } try { register(lhs, rhs); Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (int i = 0; i < fields.length && builder.isEquals; i++) { Field f = fields[i]; if (!Arrays.contains(excludeFields, f.getName()) && (f.getName().indexOf('$') == -1) && (useTransients || !Modifier.isTransient(f.getModifiers())) && (!Modifier.isStatic(f.getModifiers()))) { try { builder.append(f.get(lhs), f.get(rhs)); } catch (IllegalAccessException e) { // this can't happen. Would get a Security exception instead // throw a runtime exception in case the impossible happens. throw new InternalError("Unexpected IllegalAccessException"); } } } } finally { unregister(lhs, rhs); } }
private static void reflectionAppend( Object obj, Object obj1, Class class1, CompareToBuilder comparetobuilder, boolean flag, String as[]) { Field afield[] = class1.getDeclaredFields(); AccessibleObject.setAccessible(afield, true); int i = 0; do { if (i >= afield.length || comparetobuilder.comparison != 0) { return; } Field field = afield[i]; if (!ArrayUtils.contains(as, field.getName()) && field.getName().indexOf('$') == -1 && (flag || !Modifier.isTransient(field.getModifiers())) && !Modifier.isStatic(field.getModifiers())) { try { comparetobuilder.append(field.get(obj), field.get(obj1)); } catch (IllegalAccessException illegalaccessexception) { throw new InternalError("Unexpected IllegalAccessException"); } } i++; } while (true); }
/** * Method called to check if we can use the passed method or constructor (wrt access restriction * -- public methods can be called, others usually not); and if not, if there is a work-around for * the problem. */ public static void checkAndFixAccess(Member member) { // We know all members are also accessible objects... AccessibleObject ao = (AccessibleObject) member; /* 14-Jan-2009, tatu: It seems safe and potentially beneficial to * always to make it accessible (latter because it will force * skipping checks we have no use for...), so let's always call it. */ // if (!ao.isAccessible()) { try { ao.setAccessible(true); } catch (SecurityException se) { /* 17-Apr-2009, tatu: Related to [JACKSON-101]: this can fail on * platforms like EJB and Google App Engine); so let's * only fail if we really needed it... */ if (!ao.isAccessible()) { Class<?> declClass = member.getDeclaringClass(); throw new IllegalArgumentException( "Can not access " + member + " (from class " + declClass.getName() + "; failed to set access: " + se.getMessage()); } } // } }
private Method[] getMemberMethods() { if (memberMethods == null) { Method[] mm = type.getDeclaredMethods(); AccessibleObject.setAccessible(mm, true); memberMethods = mm; } return memberMethods; }
public String toString(Object object) { if (object == null) { return "null"; } if (visited.contains(object)) { return "..."; } visited.add(object); Class clazz = object.getClass(); if (clazz == String.class) { return (String) object; } if (clazz.isArray()) { String r = clazz.getComponentType() + "[]{"; for (int i = 0; i < Array.getLength(object); i++) { if (i > 0) { r += ","; } Object val = Array.get(object, i); if (clazz.getComponentType().isPrimitive()) { r += val; } else { r += toString(val); } } return r + "}"; } String r = clazz.getName(); do { r += "["; Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (Field field : fields) { if (!Modifier.isStatic(field.getModifiers())) { if (!r.endsWith("[")) { r += ","; } r += field.getName() + "="; try { Class t = field.getType(); Object val = field.get(object); if (t.isPrimitive()) { r += val; } else { r += toString(val); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } r += "]"; clazz = clazz.getSuperclass(); } while (clazz != null); return r; }
/** * This method uses reflection to build a valid hash code. * * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This * means that it will throw a security exception if run under a security manager, if the * permissions are not set up correctly. It is also not as efficient as testing explicitly. * * <p>Transient members will not be used, as they are likely derived fields, and not part of the * value of the <code>Object</code>. * * <p>Static fields will not be tested. Superclass fields will be included. * * @param obj the object to create a <code>hashCode</code> for * @return the generated hash code, or zero if the given object is <code>null</code> */ public static int reflectionHashCode(Object obj) { if (obj == null) return 0; Class<?> targetClass = obj.getClass(); if (isArrayOfPrimitives(obj)) { // || ObjectUtils.isPrimitiveOrWrapper(targetClass)) { return ObjectUtils.nullSafeHashCode(obj); } if (targetClass.isArray()) { return reflectionHashCode((Object[]) obj); } if (obj instanceof Collection) { return reflectionHashCode((Collection<?>) obj); } if (obj instanceof Map) { return reflectionHashCode((Map<?, ?>) obj); } // determine whether the object's class declares hashCode() or has a // superClass other than java.lang.Object that declares hashCode() Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass(); Method hashCodeMethod = ReflectionUtils.findMethod(clazz, "hashCode", new Class[0]); if (hashCodeMethod != null) { return obj.hashCode(); } // could not find a hashCode other than the one declared by // java.lang.Object int hash = INITIAL_HASH; try { while (targetClass != null) { Field[] fields = targetClass.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; int modifiers = field.getModifiers(); if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) { hash = MULTIPLIER * hash + reflectionHashCode(field.get(obj)); } } targetClass = targetClass.getSuperclass(); } } catch (IllegalAccessException exception) { // ///CLOVER:OFF ReflectionUtils.handleReflectionException(exception); // ///CLOVER:ON } return hash; }
/** * Suppress access check against a reflection object. SecurityException is silently ignored. * Checks first if the object is already accessible. */ public static void forceAccess(AccessibleObject accObject) { if (accObject.isAccessible() == true) { return; } try { accObject.setAccessible(true); } catch (SecurityException sex) { // ignore } }
/** * Changes the accessibility of a reflection object and returns the previous value. This method * can be used to set the accessibility of {@link Method} and {@link Field} instances. * * @param ao accessible object to set the accessibility on * @param accessible the accessibility statues to set on the object * @return the previous value of the accessibility flag */ public static boolean exchangeAccessible(final AccessibleObject ao, final boolean accessible) { // // At least synchronize on the object so that users of this // particular method won't step on their toes. // synchronized (ao) { final boolean oldAccessible = ao.isAccessible(); ao.setAccessible(accessible); return oldAccessible; } }
/** * XXX Default access superclass workaround * * <p>When a public class has a default access superclass with public members, these members are * accessible. Calling them from compiled code works fine. Unfortunately, on some JVMs, using * reflection to invoke these members seems to (wrongly) prevent access even when the modifier is * public. Calling setAccessible(true) solves the problem but will only work from sufficiently * privileged code. Better workarounds would be gratefully accepted. * * @param o the AccessibleObject to set as accessible */ static void setAccessibleWorkaround(AccessibleObject o) { if (o == null || o.isAccessible()) { return; } Member m = (Member) o; if (Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) { try { o.setAccessible(true); } catch (SecurityException e) { // NOPMD // ignore in favor of subsequent IllegalAccessException } } }
/** * Return information about the scripting variables to be created. * * @param data the input data * @return VariableInfo array of variable information */ @SuppressWarnings("unchecked") public VariableInfo[] getVariableInfo(TagData data) { // loop through and expose all attributes List<VariableInfo> vars = new ArrayList<VariableInfo>(); try { String clazz = data.getAttributeString("className"); if (clazz == null) { clazz = Constants.class.getName(); } Class c = Class.forName(clazz); // if no var specified, get all if (data.getAttributeString("var") == null) { Field[] fields = c.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (Field field : fields) { String type = field.getType().getName(); vars.add( new VariableInfo( field.getName(), ((field.getType().isArray()) ? type.substring(2, type.length() - 1) + "[]" : type), true, VariableInfo.AT_END)); } } else { String var = data.getAttributeString("var"); String type = c.getField(var).getType().getName(); vars.add( new VariableInfo( c.getField(var).getName(), ((c.getField(var).getType().isArray()) ? type.substring(2, type.length() - 1) + "[]" : type), true, VariableInfo.AT_END)); } } catch (Exception cnf) { log.error(cnf.getMessage()); cnf.printStackTrace(); } return vars.toArray(new VariableInfo[] {}); }
/** * Compares the declared primitives of two instances of a class * * @param <T> type of the instances * @param cls type to compare the declared fields for * @param instance1 first instance to test * @param instance2 second instance to test * @return a list of differing primitives * @throws IllegalAccessException * @throws IllegalArgumentException */ public static <T> ArrayList<Field> compareDeclaredPrimitives( Class<? extends T> cls, T instance1, T instance2) throws IllegalArgumentException, IllegalAccessException { ArrayList<Field> differingFields = new ArrayList<Field>(); Field[] fields = cls.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (Field field : fields) { if (field.getType().isPrimitive() && !isPrimitiveFieldContentTheSame(instance1, instance2, field)) { differingFields.add(field); } } return differingFields; }
public static void makeAccessible(final AccessibleObject object) { if (!object.isAccessible()) { if (System.getSecurityManager() == null) { object.setAccessible(true); } else { AccessController.doPrivileged( new PrivilegedAction<Object>() { @Override public Object run() { object.setAccessible(true); return null; } }); } } }
/** * XXX Default access superclass workaround. * * <p>When a {@code public} class has a default access superclass with {@code public} members, * these members are accessible. Calling them from compiled code works fine. Unfortunately, on * some JVMs, using reflection to invoke these members seems to (wrongly) prevent access even when * the modifier is {@code public}. Calling {@code setAccessible(true)} solves the problem but will * only work from sufficiently privileged code. Better workarounds would be gratefully accepted. * * @param o the AccessibleObject to set as accessible * @return a boolean indicating whether the accessibility of the object was set to true. */ static boolean setAccessibleWorkaround(final AccessibleObject o) { if (o == null || o.isAccessible()) { return false; } final Member m = (Member) o; if (!o.isAccessible() && Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) { try { o.setAccessible(true); return true; } catch (final SecurityException e) { // NOPMD // ignore in favor of subsequent IllegalAccessException } } return false; }
private static <T extends Annotation> AnnotationMetadata<T> randomAnnotation( Class<T> annotationClass, Map<String, Object> properties) { Map<String, Object> allProps = new LinkedHashMap<String, Object>(); Map<String, Object> defaults = hasAnnotationType ? getAnnotationDefaults(annotationClass) : new HashMap<String, Object>(); Method[] mm = annotationClass.getDeclaredMethods(); AccessibleObject.setAccessible(mm, true); for (Method method : mm) { String name = method.getName(); if (properties.containsKey(name)) allProps.put(name, properties.get(name)); else { Object o = defaults.get(name); if (o == null) o = getTypeDefaults(method.getReturnType()); allProps.put(name, o); } } return new AnnotationMetadata<T>(annotationClass, allProps); }
/** * Main method that does processing and exposes Constants in specified scope * * @return int * @throws JspException if processing fails */ @Override public int doStartTag() throws JspException { // Using reflection, get the available field names in the class Class c = null; int toScope = PageContext.PAGE_SCOPE; if (scope != null) { toScope = getScope(scope); } try { c = Class.forName(clazz); } catch (ClassNotFoundException cnf) { log.error("ClassNotFound - maybe a typo?"); throw new JspException(cnf.getMessage()); } try { // if var is null, expose all variables if (var == null) { Field[] fields = c.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (Field field : fields) { pageContext.setAttribute(field.getName(), field.get(this), toScope); } } else { try { Object value = c.getField(var).get(this); pageContext.setAttribute(c.getField(var).getName(), value, toScope); } catch (NoSuchFieldException nsf) { log.error(nsf.getMessage()); throw new JspException(nsf); } } } catch (IllegalAccessException iae) { log.error("Illegal Access Exception - maybe a classloader issue?"); throw new JspException(iae); } // Continue processing this page return (SKIP_BODY); }
/** * Construct a variable * * @param aType the type * @param aName the name * @param aValue the value */ public Variable(Class<?> aType, String aName, Object aValue) { type = aType; name = aName; value = aValue; fields = new ArrayList<Field>(); // find all fields if we have a class type except we don't expand strings and null values if (!type.isPrimitive() && !type.isArray() && !type.equals(String.class) && value != null) { // get fields from the class and all superclasses for (Class<?> c = value.getClass(); c != null; c = c.getSuperclass()) { Field[] fs = c.getDeclaredFields(); AccessibleObject.setAccessible(fs, true); // get all nonstatic fields for (Field f : fs) if ((f.getModifiers() & Modifier.STATIC) == 0) fields.add(f); } } }
protected void dumpObject(CodeWriter w, Object obj, Map<Object, Object> cache) { w.write(" fields {"); w.allowBreak(2); w.begin(0); try { Field[] declaredFields = obj.getClass().getDeclaredFields(); java.lang.reflect.AccessibleObject.setAccessible(declaredFields, true); for (int i = 0; i < declaredFields.length; i++) { if (Modifier.isStatic(declaredFields[i].getModifiers())) continue; w.begin(4); w.write(declaredFields[i].getName() + ": "); w.allowBreak(0); try { Object o = declaredFields[i].get(obj); if (o != null) { Class<?> rtType = o.getClass(); w.write("<" + rtType.toString() + ">:"); w.allowBreak(0); w.write(o.toString()); w.allowBreak(4); if (!Object.class.equals(rtType) && !dontDump(rtType) && !rtType.isArray() && !(cache.containsKey(o) && cache.get(o) == o)) { cache.put(o, o); dumpObject(w, o, cache); } } else { w.write("null"); } } catch (IllegalAccessException exn) { w.write("##[" + exn.getMessage() + "]"); } w.end(); w.allowBreak(0); } } catch (SecurityException exn) { } finally { w.end(); w.allowBreak(0); w.write("}"); } }
protected void dumpObject(Object obj, Set<Object> cache) { if (obj == null) { w.write("null"); return; } w.write(StringUtil.getShortNameComponent(obj.getClass().getName())); // w.allowBreak(0, " "); // w.write(obj.toString()); if (cache.contains(obj)) { return; } cache.add(obj); w.allowBreak(1, " "); w.begin(0); try { Field[] fields = obj.getClass().getDeclaredFields(); java.lang.reflect.AccessibleObject.setAccessible(fields, true); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; if ((field.getModifiers() & modifiersMask) != 0) continue; w.write("("); w.write(field.getName()); w.allowBreak(1, " "); try { Object o = field.get(obj); dumpObject(o, cache); } catch (IllegalAccessException exn) { w.write("##[" + exn.getMessage() + "]"); } w.write(")"); w.newline(0); } } catch (SecurityException exn) { } w.end(); }
private Constructor<?>[] getAccessibleConstructors(boolean includePrivate) { // The JVM currently doesn't allow changing access on java.lang.Class // constructors, so don't try if (includePrivate && cl != ScriptRuntime.ClassClass) { try { Constructor<?>[] cons = cl.getDeclaredConstructors(); AccessibleObject.setAccessible(cons, true); return cons; } catch (SecurityException e) { // Fall through to !includePrivate case Context.reportWarning( "Could not access constructor " + " of class " + cl.getName() + " due to lack of privileges."); } } return cl.getConstructors(); }
private void navigateClassFields(Object obj, Class<?> clazz, Visitor visitor) { Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (Field f : fields) { FieldAttributes fieldAttributes = new FieldAttributes(clazz, f); if (exclusionStrategy.shouldSkipField(fieldAttributes) || exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) { continue; // skip } TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.type); Type declaredTypeOfField = fieldTypeInfo.getActualType(); boolean visitedWithCustomHandler = visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj); if (!visitedWithCustomHandler) { if (fieldTypeInfo.isArray()) { visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj); } else { visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj); } } } }
protected void scanOptions(final Object object) { final Class<?> class1 = object.getClass(); final List<Field> fields = new LinkedList<Field>(); final List<Method> privateMethods = new LinkedList<Method>(); final List<Method> otherPackageNonPrivateMethods = new LinkedList<Method>(); final List<Method> currentPackageNonPrivateMethods = new LinkedList<Method>(); Class<?> parentClass = class1; while (parentClass != null && !parentClass.equals(Object.class)) { // We cannot override fields in child classes, so we simple collect // all fields we found fields.addAll(Arrays.asList(parentClass.getDeclaredFields())); // for methods, we need to respect overridden methods when // inspecting the parent classes for (final Method method : parentClass.getDeclaredMethods()) { if (isPrivate(method)) { privateMethods.add(method); } else if (isPublicOrProtected(method)) { if (!containsMethod(otherPackageNonPrivateMethods, method) && !containsMethod(currentPackageNonPrivateMethods, method)) { currentPackageNonPrivateMethods.add(method); } } else if (isPackagePrivate(method)) { // if (!containsMethod(publicOrProtectedMethods, method)) { // method not overloaded if (isPackagePrivate(method)) { if (!containsMethod(currentPackageNonPrivateMethods, method)) { currentPackageNonPrivateMethods.add(method); } } } } final Package pack = parentClass.getPackage(); parentClass = parentClass.getSuperclass(); if ((pack == null && parentClass.getPackage() != null) || (pack != null && !pack.equals(parentClass.getPackage()))) { otherPackageNonPrivateMethods.addAll(currentPackageNonPrivateMethods); currentPackageNonPrivateMethods.clear(); } } // inspect elements final Set<AccessibleObject> elements = new LinkedHashSet<AccessibleObject>(); elements.addAll(fields); elements.addAll(privateMethods); elements.addAll(otherPackageNonPrivateMethods); elements.addAll(currentPackageNonPrivateMethods); for (final AccessibleObject element : elements) { if (element instanceof Field && element.getAnnotation(CmdOptionDelegate.class) != null) { debug("Found delegate object at: {0}", element); try { final boolean origAccessibleFlag = element.isAccessible(); if (!origAccessibleFlag) { element.setAccessible(true); } final Object delegate = ((Field) element).get(object); if (!origAccessibleFlag) { // do not leave doors open element.setAccessible(origAccessibleFlag); } if (delegate != null) { scanOptions(delegate); } } catch (final IllegalArgumentException e) { debug("Could not scan delegate object at: {0}", element); } catch (final IllegalAccessException e) { debug("Could not scan delegate object at: {0}", element); } continue; } final CmdOption anno = element.getAnnotation(CmdOption.class); if (anno == null) { continue; } if (element instanceof Field && Modifier.isFinal(((Field) element).getModifiers())) { debug("Detected option on final field: {0}", element); // continue; } final String[] names = anno.names(); final CmdOptionHandler handler = findHandler(element, anno.args().length, anno.handler()); if (handler == null) { final PreparedI18n msg = i18n.preparetr( "No suitable handler found for option(s): {0} ({1} argument(s))", FList.mkString(anno.names(), ","), anno.args().length); throw new CmdlineParserException(msg.notr(), msg.tr()); } if (names == null || names.length == 0) { // No names means this is the ONLY parameter if (parameter != null) { final PreparedI18n msg = i18n.preparetr( "More than one parameter definition found. First definition: {0} Second definition: {1}", parameter.getElement(), element); throw new CmdlineParserException(msg.notr(), msg.tr()); } // TODO: should we ignore the help parameter? final OptionHandle paramHandle = new OptionHandle( new String[] {}, anno.description(), handler, object, element, anno.args(), anno.minCount(), anno.maxCount(), false /* * cannot * be * a * help * option */, anno.hidden(), anno.requires(), anno.conflictsWith()); if (paramHandle.getArgsCount() <= 0) { final PreparedI18n msg = i18n.preparetr("Parameter definition must support at least on argument."); throw new CmdlineParserException(msg.notr(), msg.tr()); } parameter = paramHandle; } else { final OptionHandle option = new OptionHandle( names, anno.description(), handler, object, element, anno.args(), anno.minCount(), anno.maxCount(), anno.isHelp(), anno.hidden(), anno.requires(), anno.conflictsWith()); for (final String name : names) { if (quickCommandMap.containsKey(name) || quickOptionMap.containsKey(name)) { final PreparedI18n msg = i18n.preparetr( "Duplicate command/option name \"{0}\" found in: {1}", name, element); throw new CmdlineParserException(msg.notr(), msg.tr()); } quickOptionMap.put(name, option); } options.add(option); } } }
@Override public Object run() { accessible.setAccessible(true); return null; }
public Void run() { member.setAccessible(true); return null; }
public void parse( final boolean dryrun, final boolean detectHelpAndSkipValidation, String... cmdline) { if (log.isDebugEnabled()) { log.debug( "About to start parsing. dryrun: " + dryrun + ", detectHelpAndSkipValidation: " + detectHelpAndSkipValidation + ", state: " + debugState(" ")); } if (defaultCommandName != null && !quickCommandMap.containsKey(defaultCommandName)) { final PreparedI18n msg = i18n.preparetr("Default command \"{0}\" is not a known command.", defaultCommandName); throw new CmdlineParserException(msg.notr(), msg.tr()); } // Avoid null access cmdline = cmdline == null ? new String[] {} : cmdline; if (argsFromFilePrefix.isDefined()) { cmdline = FList.flatMap( cmdline, new F1<String, List<String>>() { @Override public List<String> apply(final String arg) { if (arg.startsWith(argsFromFilePrefix.get())) { debug("Expanding {0} into argument list", arg); final File file = new File(arg.substring(1)); if (file.exists() && file.isFile()) { try { final BufferedReader reader = new BufferedReader(new FileReader(file)); final List<String> args = new LinkedList<String>(); String line; while ((line = reader.readLine()) != null) { // if (line.trim().length() > 0) { args.add(line); // } } reader.close(); return args; } catch (final FileNotFoundException e) { final PreparedI18n msg = i18n.preparetr("File referenced via {0} does not exist.", arg); throw new CmdlineParserException(msg.notr(), e, msg.tr()); } catch (final IOException e) { final PreparedI18n msg = i18n.preparetr("File referenced via {0} could not be read.", arg); throw new CmdlineParserException(msg.notr(), e, msg.tr()); } } else { final PreparedI18n msg = i18n.preparetr("File referenced via {0} does not exist.", arg); throw new CmdlineParserException(msg.notr(), msg.tr()); } } else { return Arrays.asList(arg); } } }) .toArray(new String[0]); } if (!dryrun) { debug("Parsing..."); // Check without applying anything parse(true, detectHelpAndSkipValidation, cmdline); } if (dryrun) { validateOptions(); } // Should be set to false, if an stopOption was found and parsing of // options is no longer allowed boolean parseOptions = true; final String stopOption = "--"; // optionCount counts the occurrence for each option handle in the // cmdline final Map<OptionHandle, Integer> optionCount = new LinkedHashMap<OptionHandle, Integer>(); for (final OptionHandle option : options) { optionCount.put(option, 0); } if (parameter != null) { optionCount.put(parameter, 0); } boolean helpDetected = false; // Actually iterate over the command line elements for (int index = 0; index < cmdline.length; ++index) { final String param = cmdline[index]; if (parseOptions && stopOption.equals(param)) { parseOptions = false; } else if (debugAllowed && param.equals("--CMDOPTION_DEBUG")) { if (!debugMode) { debugMode = true; debug("Enabled debug mode\n" + debugState("")); } } else if (parseOptions && quickOptionMap.containsKey(param)) { // Found an option final OptionHandle optionHandle = quickOptionMap.get(param); optionCount.put(optionHandle, optionCount.get(optionHandle) + 1); if (optionHandle.isHelp()) { debug("Detected a help request through: " + param); helpDetected = true; } if (cmdline.length <= index + optionHandle.getArgsCount()) { final PreparedI18n msg = i18n.preparetr( "Missing arguments(s): {0}. Option \"{1}\" requires {2} arguments, but you gave {3}.", FList.mkString( Arrays.asList(optionHandle.getArgs()) .subList(cmdline.length - index - 1, optionHandle.getArgsCount()), ", "), param, optionHandle.getArgsCount(), cmdline.length - index - 1); throw new CmdlineParserException(msg.notr(), msg.tr()); } // slurp next cmdline arguments into option arguments final String[] optionArgs = Arrays.copyOfRange(cmdline, index + 1, index + 1 + optionHandle.getArgsCount()); index += optionHandle.getArgsCount(); final AccessibleObject element = optionHandle.getElement(); final CmdOptionHandler handler = optionHandle.getCmdOptionHandler(); if (!dryrun) { try { final boolean origAccessibleFlag = element.isAccessible(); if (!origAccessibleFlag) { element.setAccessible(true); } handler.applyParams(optionHandle.getObject(), element, optionArgs, param); if (!origAccessibleFlag) { // do not leave doors open element.setAccessible(origAccessibleFlag); } } catch (final CmdOptionHandlerException e) { throw new CmdlineParserException(e.getMessage(), e, e.getLocalizedMessage()); } catch (final Exception e) { final PreparedI18n msg = i18n.preparetr( "Could not apply parameters {0} to field/method {1}", Arrays.toString(optionArgs), element); throw new CmdlineParserException(msg.notr(), e, msg.tr()); } } } else if (parseOptions && quickCommandMap.containsKey(param)) { // Found a command final CommandHandle commandHandle = quickCommandMap.get(param); if (!dryrun) { parsedCommandName = param; } // Delegate parsing of the rest of the cmdline to the command commandHandle .getCmdlineParser() .parse( dryrun, detectHelpAndSkipValidation, Arrays.copyOfRange(cmdline, index + 1, cmdline.length)); // Stop parsing break; } else if (parameter == null && defaultCommandName != null && quickCommandMap.containsKey(defaultCommandName)) { // Assume a default command inserted here debug( "Unsupported option '" + param + "' found, assuming default command: " + defaultCommandName); final CommandHandle commandHandle = quickCommandMap.get(defaultCommandName); if (!dryrun) { parsedCommandName = defaultCommandName; } // Delegate parsing of the rest of the cmdline to the command commandHandle .getCmdlineParser() .parse( dryrun, detectHelpAndSkipValidation, Arrays.copyOfRange(cmdline, index, cmdline.length)); // Stop parsing break; } else if (parameter != null) { // Found a parameter optionCount.put(parameter, optionCount.get(parameter) + 1); if (cmdline.length <= index + parameter.getArgsCount() - 1) { final int countOfGivenParams = cmdline.length - index; final PreparedI18n msg = i18n.preparetr( "Missing arguments: {0} Parameter requires {1} arguments, but you gave {2}.", Arrays.asList(parameter.getArgs()) .subList(countOfGivenParams, parameter.getArgsCount()), parameter.getArgsCount(), countOfGivenParams); throw new CmdlineParserException(msg.notr(), msg.tr()); } // slurp next cmdline arguments into option arguments final String[] optionArgs = Arrays.copyOfRange(cmdline, index, index + parameter.getArgsCount()); // -1, because index gets increased by one at end of for-loop index += parameter.getArgsCount() - 1; final AccessibleObject element = parameter.getElement(); final CmdOptionHandler handler = parameter.getCmdOptionHandler(); if (!dryrun) { try { debug("Apply main parameter from parameters: {0}", FList.mkString(optionArgs, ", ")); final boolean origAccessibleFlag = element.isAccessible(); if (!origAccessibleFlag) { element.setAccessible(true); } handler.applyParams(parameter.getObject(), element, optionArgs, param); if (!origAccessibleFlag) { // do not leave doors open element.setAccessible(origAccessibleFlag); } } catch (final CmdOptionHandlerException e) { throw new CmdlineParserException(e.getMessage(), e, e.getLocalizedMessage()); } catch (final Exception e) { final PreparedI18n msg = i18n.preparetr( "Could not apply parameters {0} to field/method {1}", Arrays.toString(optionArgs), element); throw new CmdlineParserException(msg.notr(), e, msg.tr()); } } } else { final PreparedI18n msg = i18n.preparetr("Unsupported option or parameter found: {0}", param); throw new CmdlineParserException(msg.notr(), msg.tr()); } } if (!detectHelpAndSkipValidation || !helpDetected) { // Validate optionCount matches allowed for (final Entry<OptionHandle, Integer> optionC : optionCount.entrySet()) { final OptionHandle option = optionC.getKey(); final Integer count = optionC.getValue(); if (count < option.getMinCount() || (option.getMaxCount() > 0 && count > option.getMaxCount())) { final PreparedI18n rangeMsg; if (option.getMaxCount() < 0) { rangeMsg = i18n.preparetr("at least {0}", option.getMinCount()); } else { if (option.getMinCount() == option.getMaxCount()) { rangeMsg = i18n.preparetr("exactly {0}", option.getMinCount()); } else { rangeMsg = i18n.preparetr("between {0} and {1}", option.getMinCount(), option.getMaxCount()); } } final String msg; final Object[] msgArgs; final Object[] msgArgsTr; if (option.getNames() == null || option.getNames().length == 0) { msg = I18n.marktr( "Main parameter \"{0}\" was given {1} times, but must be given {2} times"); msgArgs = new Object[] {FList.mkString(option.getArgs(), " "), count, rangeMsg.notr()}; msgArgsTr = new Object[] {FList.mkString(option.getArgs(), " "), count, rangeMsg.tr()}; } else { msg = I18n.marktr("Option \"{0}\" was given {1} times, but must be given {2} times"); msgArgs = new Object[] {option.getNames()[0], count, rangeMsg.notr()}; msgArgsTr = new Object[] {option.getNames()[0], count, rangeMsg.tr()}; } throw new CmdlineParserException( MessageFormat.format(msg, msgArgs), i18n.tr(msg, msgArgsTr)); } } // Validate required options because of 'required' attribute in // other options for (final Entry<OptionHandle, Integer> optionC : optionCount.entrySet()) { if (optionC.getValue() > 0) { final OptionHandle calledOption = optionC.getKey(); for (final String required : calledOption.getRequires()) { // check, of an option was called with that name, if // not, this is an error final OptionHandle reqOptionHandle = quickOptionMap.get(required); if (reqOptionHandle == null) { // required option does not exists, error // TODO: error } else { final Integer reqOptionCount = optionCount.get(reqOptionHandle); if (reqOptionCount == null || reqOptionCount.intValue() <= 0) { // required option was not called, this is an // error final PreparedI18n msg = i18n.preparetr( "When using option \"{0}\" also option \"{1}\" must be given.", calledOption.getNames()[0], required); throw new CmdlineParserException(msg.notr(), msg.tr()); } } } for (final String conflict : calledOption.getConflictsWith()) { // check, of an option was called with that name, if // not, this is an error final OptionHandle conflictOptionHandle = quickOptionMap.get(conflict); if (conflictOptionHandle == null) { // conflicting option does not exists, error // TODO: error } else { final Integer conflictOptionCount = optionCount.get(conflictOptionHandle); if (conflictOptionCount != null && conflictOptionCount.intValue() > 0) { // conflicting option was called, this is an // conflict final PreparedI18n msg = i18n.preparetr( "Options \"{0}\" and \"{1}\" cannot be used at the same time.", calledOption.getNames()[0], conflict); throw new CmdlineParserException(msg.notr(), msg.tr()); } } } } } } }