001: package org.wings.resource;
002:
003: import org.apache.commons.logging.Log;
004: import org.apache.commons.logging.LogFactory;
005: import org.wings.externalizer.ExternalizeManager;
006: import org.wings.util.SStringBuilder;
007:
008: import java.io.BufferedInputStream;
009: import java.io.IOException;
010: import java.io.InputStream;
011:
012: /**
013: * filters an input stream of a css file for occurences of "url([classPath])".
014: * These image classpaths are then externalized and replaced in the resulting
015: * InputStream.
016: * This gives us the possibility to load css files and their included images
017: * via the classpath, so no external files are needed.
018: * If the image is not found at the classpath provided, the occurence is not
019: * altered.
020: * @author ole
021: * @version $$
022: *
023: */
024: public class CssUrlFilterInputStream extends BufferedInputStream {
025: private final transient static Log log = LogFactory
026: .getLog(CssUrlFilterInputStream.class);
027:
028: private final static byte STATE_NONE = 0;
029: private final static byte STATE_U = 1;
030: private final static byte STATE_UR = 2;
031: private final static byte STATE_URL = 3;
032: private final static byte STATE_URL_DONE = 4;
033: private final static byte STATE_SUBST = 5;
034: private byte state = STATE_NONE;
035:
036: private byte[] urlBuffer;
037: private int bufferPos;
038:
039: /**
040: * The ExternalizeManager to use for Image externalization.
041: * Externalized Resources cannot count on a session, but we need an
042: * ExternalizeManager for image externalization.
043: */
044: private ExternalizeManager extManager;
045:
046: /**
047: * Creates a new Instance.
048: * @param in the InputStream to filter
049: * @param extManager The ExternalizeManager to use for Image externalization.
050: */
051: public CssUrlFilterInputStream(InputStream in,
052: ExternalizeManager extManager) {
053: super (in);
054: this .extManager = extManager;
055: }
056:
057: /* (non-Javadoc)
058: * @see java.io.InputStream#read()
059: */
060: public int read() throws IOException {
061: int result = 0;
062: if (state == STATE_SUBST) {
063: result = readFromUrlBuffer();
064: } else {
065: result = super .read();
066: if (result != -1) {
067: analyzeState(result);
068: if (state == STATE_URL_DONE) {
069: substitute();
070: }
071: }
072: }
073: return result;
074: }
075:
076: /**
077: * substitutes the ocurrences of "url([classPath])" with the externalized
078: * images.
079: * @throws IOException
080: */
081: private void substitute() throws IOException {
082: SStringBuilder classPathBuffer = new SStringBuilder();
083: int tempBuffer = super .read();
084: while (tempBuffer != -1 && tempBuffer != ')') {
085: classPathBuffer.append((char) tempBuffer);
086: super .mark(2);
087: tempBuffer = super .read();
088: }
089: super .reset();
090: String classPath = strip(classPathBuffer.toString(), ' ');
091: classPath = strip(classPath, '\'');
092: classPath = strip(classPath, '"');
093: String extension = null;
094: int dotIndex = classPath.lastIndexOf('.');
095: if (dotIndex > -1) {
096: extension = classPath.substring(dotIndex + 1).toLowerCase();
097: if ("jpg".equals(extension))
098: extension = "jpeg";
099:
100: }
101: bufferPos = 0;
102: urlBuffer = externalizeImage(classPath, "image/" + extension)
103: .getBytes();
104: if (urlBuffer.length == 0) {
105: // not externalized, use old String
106: urlBuffer = classPathBuffer.toString().getBytes();
107: }
108: state = STATE_SUBST;
109: }
110:
111: /** externalizes an Image found.
112: * @param classPath the classPath of the Image
113: * @param mimeType the mimeType of the Image
114: * @return the url of the externalized Image
115: */
116: private String externalizeImage(String classPath, String mimeType) {
117: ClassPathResource res = new ClassPathResource(classPath,
118: mimeType);
119: try {
120: if (res.getResourceStream() == null) {
121: // no resource found at classPath, return old string
122: log.debug("Could not find resource at classpath: "
123: + classPath);
124: return "";
125: }
126: } catch (ResourceNotFoundException e) {
127: log.debug("Could not find resource: " + e.getMessage());
128: return "";
129: }
130: SStringBuilder imageUrl = new SStringBuilder("'");
131: imageUrl.append(extManager.externalize(res,
132: ExternalizeManager.GLOBAL));
133: imageUrl.append("'");
134: return imageUrl.toString();
135: }
136:
137: /** reads from the filename buffer. is called when an Image classPath is
138: * replaces by it's url.
139: * @return the character at the current position
140: */
141: private int readFromUrlBuffer() {
142: int result = urlBuffer[bufferPos];
143: bufferPos++;
144: if (bufferPos == urlBuffer.length) {
145: state = STATE_NONE;
146: }
147: return result;
148: }
149:
150: /* (non-Javadoc)
151: * @see java.io.InputStream#read(byte[], int, int)
152: */
153: public int read(byte[] b, int off, int len) throws IOException {
154: int i = 0;
155: for (i = off; i < (off + len); i++) {
156: byte tempByte = (byte) read();
157: if (tempByte == -1) {
158: break;
159: }
160: b[i] = tempByte;
161: }
162: return i - off;
163: }
164:
165: /**
166: * Strips a String of occurences of character. works recursively.
167: * @param buffer the String
168: * @param character the Character to be stripped
169: * @return the stripped string
170: */
171: private String strip(String buffer, char character) {
172: if (buffer.charAt(0) == character) {
173: return strip(buffer.substring(1, buffer.length()),
174: character);
175: }
176: if (buffer.charAt(buffer.length() - 1) == character) {
177: return strip(buffer.substring(0, buffer.length() - 1),
178: character);
179: }
180: return buffer;
181: }
182:
183: /* (non-Javadoc)
184: * @see java.io.InputStream#read(byte[])
185: */
186: public int read(byte[] b) throws IOException {
187: return read(b, 0, b.length);
188: }
189:
190: /**
191: * analyzes the Parse State and sets the state variable accordingly.
192: *
193: * @param character the character to analyze.
194: */
195: private void analyzeState(int character) {
196: switch (character) {
197: case 'u':
198: if (state == STATE_NONE) {
199: state = STATE_U;
200: } else {
201: state = STATE_NONE;
202: }
203: break;
204: case 'r':
205: if (state == STATE_U) {
206: state = STATE_UR;
207: } else {
208: state = STATE_NONE;
209: }
210: break;
211: case 'l':
212: if (state == STATE_UR) {
213: state = STATE_URL;
214: } else {
215: state = STATE_NONE;
216: }
217: break;
218: case '(':
219: if (state == STATE_URL) {
220: state = STATE_URL_DONE;
221: } else {
222: state = STATE_NONE;
223: }
224: break;
225: default:
226: state = STATE_NONE;
227: break;
228: }
229: }
230: }
|