001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.controls.runtime.generator.apt;
020:
021: import com.sun.mirror.apt.Filer;
022:
023: import java.io.*;
024: import java.util.*;
025:
026: /**
027: * The controls client manifest (aka "client manifest") surfaces the set of
028: * control types used by a client, and make the assembly process more
029: * efficient. The control client annotation processor generates a client
030: * manifest documenting the set of used control types. This manifest is a
031: * java.util.Properties file that specifies:
032: *
033: * - classname of the control client
034: * - classnames of each control type used by that control client (the set
035: * identified by @Control and @ControlReference usages) and the
036: * corresponding default implementation binding
037: *
038: * Example client manifest:
039: *
040: * FooImpl.controls.properties
041: * ---------------------------
042: * .client.name=org.acme.controls.FooImpl
043: * org.acme.controls.CustomerDbBean=org.apache.beehive.controls.scl.DatabaseControlImpl
044: * org.acme.controls.DailyTimerBean=org.apache.beehive.controls.scl.TimerControlImpl
045: *
046: * The manifest is a generated artifact and is not user-editable. Ideally, the apt
047: * environment optimizes the writing of the manifest such that it's only written
048: * to disk when changes occur (allowing external build tools to use the timestamp of
049: * the manifest to determine whether assembly on a client needs to occur).
050: */
051: public class ControlClientManifest {
052: public final static String CLIENT_NAME_PROP = ".client.name";
053: public final static String BEEHIVE_VERSION_PROP = ".beehive.version";
054: public final static String FILE_EXTENSION = ".controls.properties";
055:
056: /**
057: * Loads a ControlClientManifest from an existing manifest file.
058: * @param f the manifest file
059: * @throws FileNotFoundException
060: * @throws IOException
061: */
062: public ControlClientManifest(File f) throws FileNotFoundException,
063: IOException {
064: if (!f.exists())
065: throw new FileNotFoundException("Control manifest file="
066: + f + " not found");
067:
068: FileInputStream fis = new FileInputStream(f);
069: _properties.load(fis);
070:
071: String client = _properties.getProperty(CLIENT_NAME_PROP);
072: if (client == null || client.equals(""))
073: throw new IOException(
074: "Control client manifest missing client name");
075: }
076:
077: /**
078: * Creates a new ControlClientManifest
079: * @param client the fully qualified classname of the control client
080: */
081: public ControlClientManifest(String client) {
082: if (client == null || client.equals(""))
083: throw new RuntimeException("Missing or empty client name");
084:
085: _properties.setProperty(CLIENT_NAME_PROP, client);
086: }
087:
088: /**
089: * @return the name of the control client in this manifest
090: */
091: public String getControlClient() {
092: return _properties.getProperty(CLIENT_NAME_PROP);
093: }
094:
095: /**
096: * Adds a new control type to the manifest
097: * @param intf fully qualified name of the control type
098: * @param impl fully qualified name of the default implementation for the control type
099: */
100: public void addControlType(String intf, String impl) {
101: _properties.setProperty(intf, impl);
102: }
103:
104: /**
105: * @return a list of all control types listed in the manifest
106: */
107: public List<String> getControlTypes() {
108: ArrayList<String> l = new ArrayList<String>();
109:
110: Set keys = _properties.keySet();
111: for (Object k : keys) {
112: String propname = (String) k;
113: if (propname.equals(CLIENT_NAME_PROP))
114: continue;
115:
116: l.add(propname);
117: }
118:
119: return l;
120: }
121:
122: /**
123: * @param controlType
124: * @return the default implementation for the control type listed in the manifest
125: */
126: public String getDefaultImpl(String controlType) {
127: return (String) _properties.get(controlType);
128: }
129:
130: /**
131: * Emits the manifest via an apt Filer implementation
132: * @param f an apt Filer
133: * @param pkg the package structure to place the manifest in
134: * @param mf the name of the manifest
135: * @throws IOException
136: */
137: public void emit(Filer f, String pkg, File mf, String csn)
138: throws IOException {
139: PrintWriter pw = f.createTextFile(Filer.Location.CLASS_TREE,
140: pkg, mf, csn);
141:
142: pw
143: .println("# Apache Beehive Controls client manifest (auto-generated, do not edit!)");
144: Set props = _properties.keySet();
145: for (Object p : props) {
146: String name = (String) p;
147: String value = _properties.getProperty(name);
148:
149: /* convert the name and value to a format excpected by the Properties.load() method */
150: name = escapeJava(name);
151: if (value != null)
152: value = escapeJava(value);
153:
154: pw.println(name + "=" + value);
155: }
156:
157: pw.flush();
158: pw.close();
159: }
160:
161: private static String escapeJava(String str) {
162: if (str == null)
163: return null;
164:
165: try {
166: StringWriter writer = new StringWriter(str.length() * 2);
167: escapeJavaStyleString(writer, str);
168: return writer.toString();
169: } catch (IOException ioe) {
170: /* this should never ever happen while writing to a StringWriter */
171: ioe.printStackTrace();
172: return null;
173: }
174: }
175:
176: private static void escapeJavaStyleString(Writer out, String str)
177: throws IOException {
178:
179: assert str != null : "Received a null string";
180: assert out != null : "The writer must not be null";
181:
182: int sz = str.length();
183: for (int i = 0; i < sz; i++) {
184: char ch = str.charAt(i);
185:
186: /* convert to unicode */
187: if (ch > 0xfff)
188: out.write("\\u" + hex(ch));
189: else if (ch > 0xff)
190: out.write("\\u0" + hex(ch));
191: else if (ch > 0x7f)
192: out.write("\\u00" + hex(ch));
193: else if (ch < 32) {
194: switch (ch) {
195: case '\b':
196: out.write('\\');
197: out.write('b');
198: break;
199: case '\n':
200: out.write('\\');
201: out.write('n');
202: break;
203: case '\t':
204: out.write('\\');
205: out.write('t');
206: break;
207: case '\f':
208: out.write('\\');
209: out.write('f');
210: break;
211: case '\r':
212: out.write('\\');
213: out.write('r');
214: break;
215: default:
216: if (ch > 0xf)
217: out.write("\\u00" + hex(ch));
218: else
219: out.write("\\u000" + hex(ch));
220: break;
221: }
222: } else {
223: switch (ch) {
224: case '\'':
225: out.write('\'');
226: break;
227: case '"':
228: out.write('\\');
229: out.write('"');
230: break;
231: case '\\':
232: out.write('\\');
233: out.write('\\');
234: break;
235: default:
236: out.write(ch);
237: break;
238: }
239: }
240: }
241: }
242:
243: private static String hex(char ch) {
244: return Integer.toHexString(ch).toUpperCase();
245: }
246:
247: private Properties _properties = new Properties();
248: }
|