1 /****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19
20 package org.apache.james.jdkim.canon;
21
22 import java.io.FilterOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25
26 /**
27 * Implements Relaxed canonicalization for the body as defined in RFC4871 -
28 * 3.4.4. The "relaxed" Body Canonicalization Algorithm
29 */
30 public class RelaxedBodyCanonicalizer extends FilterOutputStream {
31
32 private boolean pendingSpaces;
33
34 public RelaxedBodyCanonicalizer(OutputStream out) {
35 super(new SimpleBodyCanonicalizer(out));
36 pendingSpaces = false;
37 }
38
39 public void write(byte[] buffer, int off, int len) throws IOException {
40 int start = off;
41 int end = len + off;
42 for (int k = off; k < end; k++) {
43 if (pendingSpaces) {
44 if (buffer[k] != ' ' && buffer[k] != '\t') {
45 if (buffer[k] != '\r')
46 out.write(' ');
47 pendingSpaces = false;
48 len = len - k + start;
49 start = k;
50 }
51 } else {
52 if (buffer[k] == ' ' || buffer[k] == '\t') {
53 if (k + 1 < end && buffer[k] == ' ' && buffer[k + 1] != ' '
54 && buffer[k + 1] != '\t' && buffer[k + 1] != '\r') {
55 // optimization: we skip single spaces
56 // make sure we optimize only when we are on a space.
57 } else {
58 // compute everything from start to end;
59 out.write(buffer, start, k - start);
60 pendingSpaces = true;
61 }
62 }
63 }
64 }
65 if (!pendingSpaces) {
66 out.write(buffer, start, len);
67 }
68 }
69
70 public void write(int b) throws IOException {
71 if (pendingSpaces) {
72 if (b != ' ' && b != '\t') {
73 if (b != '\r')
74 out.write(' ');
75 pendingSpaces = false;
76 out.write(b);
77 }
78 } else {
79 if (b == ' ' || b == '\t') {
80 pendingSpaces = true;
81 } else {
82 out.write(b);
83 }
84 }
85 }
86
87 public void close() throws IOException {
88 complete();
89 super.close();
90 }
91
92 /**
93 * Called internally to make sure we output the buffered whitespace if any.
94 */
95 private void complete() throws IOException {
96 if (pendingSpaces)
97 out.write(' ');
98 }
99
100 }