Implemented aspect to deal with long/bad data in Facebook-Entries
[facebook-errors] / src / main / java / de / juplo / facebook / aspects / SanitizeAspect.java
1 package de.juplo.facebook.aspects;
2
3
4 import org.aspectj.lang.ProceedingJoinPoint;
5 import org.aspectj.lang.annotation.Around;
6 import org.aspectj.lang.annotation.Aspect;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9
10
11
12 /**
13  * @author Kai Moritz
14  */
15 @Aspect
16 public class SanitizeAspect
17 {
18   private static final Logger log =
19       LoggerFactory.getLogger(SanitizeAspect.class);
20
21
22   /**
23    * This method sanitizes the given string in all means:
24    * <ul>
25    * <li>It removes leading and trailing whitspace.</li>
26    * <li>It removes characters, that are not allowed in the XML-output</li>
27    * <li>It checks the allowed length of the string</li>
28    * </ul>
29    *
30    * This method ensures that the output String has only
31    * valid XML unicode characters as specified by the
32    * XML 1.0 standard. For reference, please see
33    * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
34    * standard</a>. This method will return an empty
35    * String if the input is null or empty.
36    *
37    * @param jp The join-point captured by AspectJ.
38    * @param in The String whose non-valid characters we want to remove.
39    * @param sanitize The annotation, the field was marked with.
40    * @see <a href="http://blog.mark-mclaren.info/2007/02/invalid-xml-characters-when-valid-utf8_5873.html">Invalid XML Characters: when valid UTF8 does not mean valid XML</a>
41    * @see <a href="http://up-download.de/up/docs/werkstatt/de/intrexx-werkstatt-ungueltige-zeichen-in-eingabefeldern-abfangen.pdf">Ungültige Zeichen in Eingabefeldern abfangen</a>
42    */
43   @Around("set(String *) && args(in) && @annotation(sanitize)")
44   public void sanitize(
45       ProceedingJoinPoint jp,
46       String in,
47       Sanitize sanitize
48       )
49       throws Throwable
50   {
51     if (in == null)
52     {
53       jp.proceed(new Object[] { null });
54       return;
55     }
56
57     in = in.trim();
58     if ("".equals(in))
59     {
60       jp.proceed(new Object[] { null });
61       return;
62     }
63
64     StringBuilder out = new StringBuilder(); // Used to hold the output.
65     char current; // Used to reference the current character.
66
67     for (int i = 0; i < in.length(); i++)
68     {
69       current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
70       if ((current == 0x9) ||
71           (current == 0xA) ||
72           (current == 0xD) ||
73           ((current >= 0x20) && (current <= 0xD7FF)) ||
74           ((current >= 0xE000) && (current <= 0xFFFD)) ||
75           ((current >= 0x10000) && (current <= 0x10FFFF)))
76         out.append(current);
77     }
78     if (out.length() > sanitize.length())
79     {
80       log.error(
81           "Maximum length for attribute {} exceeded: should={}, was={}",
82           jp.getSignature().getName(),
83           sanitize.length(),
84           out.length()
85           );
86       if (sanitize.fail())
87         throw new RuntimeException("String is longer than " + sanitize.length());
88       else
89         out.setLength(sanitize.length());
90     }
91     jp.proceed(new Object[] { out.toString() });
92   }
93 }