简体   繁体   中英

recursively call a method but log only second time?

I have a below method which does URL decoding on the value passed along with using charset.

  public String decodeValue(String value, Charset charset) {
    if (!Strings.isNullOrEmpty(value)) {
      try {
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        // log error
        return null;
      }
    }
    return value;
  }

Now if URLDecoder.decode line throws UnsupportedEncodingException first time then I want to run same value against below three lines:

value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
value = value.replaceAll("\\+", "%2B");
value = URLDecoder.decode(value, charset.name());

And if then again URLDecoder.decode line throws exception second time, then I will log the error but only second time and return null value otherwise return the value which is decoded.

What is the best and elegant way to do this?

The easiest way is to make a private version of your function signature which includes an extra flag.

  private String decodeValue(String value, Charset charset, boolean isFirstTime) {
    if (!Strings.isNullOrEmpty(value)) {
      try {
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        if (isFirstTime) {
            value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
            value = value.replaceAll("\\+", "%2B");
            return decodeValue(value, charset.name(), false);
        } else {
            // log error
            return null;
        }
      }
    }
    return value;
  }

Then, just pass true the first time and false in the recursive call. Inside the function, only execute the next three lines if true is passed.

The public version can pass true .

  public String decodeValue(String value, Charset charset) {
       decodeValue(value, charset, true);
  }

While it isn't recursive, you could make use of a while loop and a flag.

public String decode(String value, Charset charset) {
    boolean first = true;
    while(!Strings.isNullOrEmpty(value)) {
        try {
            return value = URLDecoder.decode(value, charset);
        } catch (UnsupportedEncodingException e) {
            if(first == false) {
                // Log error.
                return null;
            }
            value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25").replaceAll("\\+", "%2B");
        }
        first = false;
    }
    return value;
}

Here you go :

 public String decode(String value, Charset charset) {
    if (!Strings.isNullOrEmpty(value)) {
      try {
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        try {
        value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
        value = value.replaceAll("\\+", "%2B");
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        // log error
        return null;
      }
      }
    }
    return value;
  }

Hope this solves your problem.

Adding a flag is one option. Yes, it is the easier one; but many people argue that having flags is simply spoken: bad practice.

You simply try to minimize those things.

In other words: if you have a method that should behave differently for the first and subsequent calls; consider creating two methods there. Of course, you avoid code duplication as far as possible, but unless it becomes to costly, you should at least consider avoiding such flag arguments, too.

Here is version without extra flags, duplicated code, recursion and loops:

public String decodeValue(final String value, Charset charset) throws UnsupportedEncodingException {
    String result;
    if (!Strings.isNullOrEmpty(value)) {
        UnsupportedEncodingException[] lastException = new UnsupportedEncodingException[1];
        result = Stream.<Function<String, String>>of(
                Function.identity(),
                s -> {
                    s = s.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
                    s = s.replaceAll("\\+", "%2B");
                    return s;
                })
                .map(f -> f.apply(value))
                .map(appliedValue -> {
                    try {
                        return URLDecoder.decode(appliedValue, charset.name());
                    } catch (UnsupportedEncodingException e) {
                        lastException[0] = e;
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .findFirst()
                .orElseThrow(() -> lastException[0]);
    }
    return result;
}

Here I just take stream with 2 functions: identity and function making string correction. Then I apply these functions to initial string and try to decode. If first attempt is successful then code will not apply correction function and just return correct result. If decoder throws exception after value correction then "findFirst" will not find any value. Then we throw last caught exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM