1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.io;
22
23import proguard.classfile.*;
24
25import java.io.*;
26
27/**
28 * This DataEntryReader writes the manifest data entries that it reads to a
29 * given DataEntryWriter, updating their contents based on the renamed classes
30 * in the given ClassPool.
31 *
32 * @author Eric Lafortune
33 */
34public class ManifestRewriter extends DataEntryRewriter
35{
36    /**
37     * Creates a new ManifestRewriter.
38     */
39    public ManifestRewriter(ClassPool       classPool,
40                            DataEntryWriter dataEntryWriter)
41    {
42        super(classPool, dataEntryWriter);
43    }
44
45
46    // Implementations for DataEntryRewriter.
47
48    protected void copyData(Reader reader,
49                            Writer writer)
50    throws IOException
51    {
52        super.copyData(new SplitLineReader(reader),
53                       new SplitLineWriter(writer));
54    }
55
56
57    /**
58     * This Reader reads manifest files, joining any split lines.
59     */
60    private static class SplitLineReader extends FilterReader
61    {
62        private char[] buffer      = new char[2];
63        private int    bufferIndex = 0;
64        private int    bufferSize  = 0;
65
66
67        public SplitLineReader(Reader reader)
68        {
69            super(reader);
70        }
71
72
73        // Implementations for Reader.
74
75        public int read() throws IOException
76        {
77            while (true)
78            {
79                if (bufferIndex < bufferSize)
80                {
81                    return buffer[bufferIndex++];
82                }
83
84                // Read the first character.
85                int c1 = super.read();
86                if (c1 != '\n' && c1 != '\r')
87                {
88                    return c1;
89                }
90
91                bufferIndex = 0;
92                bufferSize  = 0;
93                buffer[bufferSize++] = '\n';
94
95                // Read the second character.
96                int c2 = super.read();
97                if (c2 == ' ')
98                {
99                    bufferSize = 0;
100                    continue;
101                }
102
103                if (c1 != '\r' || c2 != '\n')
104                {
105                    buffer[bufferSize++] = (char)c2;
106                    continue;
107                }
108
109                // Read the third character.
110                int c3 = super.read();
111                if (c3 == ' ')
112                {
113                    bufferSize = 0;
114                    continue;
115                }
116
117                buffer[bufferSize++] = (char)c3;
118            }
119        }
120
121
122        public int read(char[] cbuf, int off, int len) throws IOException
123        {
124            // Delegate to reading a single character at a time.
125            int count = 0;
126            while (count < len)
127            {
128                int c = read();
129                if (c == -1)
130                {
131                    break;
132                }
133
134                cbuf[off + count++] = (char)c;
135            }
136
137            return count;
138        }
139
140
141        public long skip(long n) throws IOException
142        {
143            // Delegate to reading a single character at a time.
144            int count = 0;
145            while (count < n)
146            {
147                int c = read();
148                if (c == -1)
149                {
150                    break;
151                }
152
153                count++;
154            }
155
156            return count;
157        }
158    }
159
160
161    /**
162     * This Writer writes manifest files, splitting any long lines.
163     */
164    private static class SplitLineWriter extends FilterWriter
165    {
166        private int counter = 0;
167
168
169        public SplitLineWriter(Writer writer)
170        {
171            super(writer);
172        }
173
174
175        // Implementations for Reader.
176
177        public void write(int c) throws IOException
178        {
179            // TODO: We should actually count the Utf-8 bytes, not the characters.
180            if (c == '\n')
181            {
182                // Reset the character count.
183                counter = 0;
184            }
185            else if (counter == 70)
186            {
187                // Insert are newline and space.
188                super.write('\n');
189                super.write(' ');
190
191                counter = 2;
192            }
193            else
194            {
195                counter++;
196            }
197
198            super.write(c);
199        }
200
201
202        public void write(char[] cbuf, int off, int len) throws IOException
203        {
204            for (int count = 0; count < len; count++)
205            {
206                write(cbuf[off + count]);
207            }
208        }
209
210
211        public void write(String str, int off, int len) throws IOException
212        {
213            write(str.toCharArray(), off, len);
214        }
215    }
216}