001: /*
002: * ExtractOp.java
003: *
004: * Created on October 26, 2001, 1:53 PM
005: */
006:
007: package com.sun.portal.desktop.deployment;
008:
009: import java.util.StringTokenizer;
010: import java.util.Map;
011: import java.util.HashMap;
012: import java.util.Set;
013: import java.util.Iterator;
014:
015: import java.net.URLEncoder;
016: import java.net.URLDecoder;
017:
018: /**
019: * This class encapsulates extraction operations from .par files. Not all of the
020: * members make sense in all situations, and we define explicit subclasses which
021: * assure proper definitions for various types of operations.
022: *
023: * Naming information is optional, as that information may be provided from the XML
024: * file in the .par. This would be the "normal" type of operation for replication.
025: * It can be overridden, which is appropriate for constructing operations following
026: * web downloads, and so on.
027: *
028: * This class is also defines a human-readable string specification of an ExtractOp,
029: * so that the operation may be readily understood in .par file manifests, and specified
030: * reasonably on command lines.
031: *
032: * Programatic construction:
033: *
034: * There is an "add" call which allows another operation to be added to the current
035: * one. The way this actually works is that there are subclasses for constructing
036: * each of the operation types, and if you want to do both you simply "add()" one
037: * to the other, eg:
038: *
039: * op1 = new ExOProvider(...);
040: * op2 = new ExOChannel(...);
041: *
042: * op1.add(op2);
043: *
044: * now, op1 does both, with the restriction that they have to target the same DP
045: * document (DSAME node).
046: *
047: * @author yabob
048: * @version
049: */
050: public class ExtractOp {
051:
052: // types of operations are used to compose a bit mask.
053:
054: public static final int TYPE_PROVIDER = 1;
055: public static final int TYPE_CHANNEL = 2;
056: public static final int TYPE_OTHER = 3;
057: public static final int ALLTYPES = TYPE_CHANNEL | TYPE_PROVIDER;
058:
059: // dpnode specifies the DSAME node whose display profile we are targetting.
060: // Required for all current operations.
061:
062: protected ExtractOp(int types, String dpnode) {
063: m_Types = types;
064: m_DPNode = dpnode;
065: }
066:
067: public ExtractOp(ExtractOp op) {
068: m_Types = 0;
069: m_DPNode = op.getDPNode();
070: mergeOp(op);
071: }
072:
073: public int getTypes() {
074: return m_Types;
075: }
076:
077: public String getDPNode() {
078: return m_DPNode;
079: }
080:
081: // Provider name only applies to TYPE_PROVIDER. If non-null,
082: // it overrides the provider name setting drawn from the .par
083: // file (useful for web download as opposed to replication)
084:
085: public String getProviderName() {
086: return m_ProviderName;
087: }
088:
089: // Channel name only applies to TYPE_CHANNEL. If non-null,
090: // it overrides the channel name setting drawn from the .par
091: // file (useful for web download as opposed to replication)
092:
093: public String getChannelName() {
094: return m_ChannelName;
095: }
096:
097: // ChannelContainer only applies to TYPE_CHANNEL, and specifies
098: // the target container whose "channels" bag is to receive
099: // the new channel definition. If null, channel is added to
100: // the "root" level.
101:
102: public String getChannelContainer() {
103: return m_ChannelContainer;
104: }
105:
106: // AvailContainer only applies to TYPE_CHANNEL, and is optional.
107: // If non-null, it provides the target container whose "avail"
108: // list is to receive a reference to the newly installed channel.
109:
110: public String getAvailContainer() {
111: return m_AvailContainer;
112: }
113:
114: // MakeSelected only applies to TYPE_CHANNEL, and is relevent
115: // only if AvailContainer is non-null. If true, it specifies
116: // that the AvailContainer's "selected" list is also to receive
117: // a reference to the newly installed channel.
118:
119: public boolean getMakeSelected() {
120: return m_MakeSelected;
121: }
122:
123: // Entry Name indicates which .par file entry is used for this operation.
124: // If null, the default entry will be used.
125:
126: public String getEntryName() {
127: return m_EntryName;
128: }
129:
130: public void setEntryName(String name) {
131: m_EntryName = name;
132: }
133:
134: public void setDPNode(String dpnode) {
135: m_DPNode = dpnode;
136: }
137:
138: public static void describeTypes(StringBuffer bf, int types) {
139:
140: String pfx = "";
141:
142: if ((types & TYPE_PROVIDER) != 0) {
143: bf.append(ARG_PROVIDER);
144: pfx = ARG_DELIM;
145: }
146:
147: if ((types & TYPE_CHANNEL) != 0) {
148: bf.append(pfx);
149: bf.append(ARG_CHANNEL);
150: }
151: }
152:
153: // Add another operation to this one. Checks sanity of combined operations,
154: // which should be targetted for the same dpnode, and be disjoint.
155:
156: public void add(ExtractOp op) throws ParFileException {
157:
158: if (!m_DPNode.equals(op.m_DPNode)) {
159: throw new ParFileException("errorXOPSanity");
160: }
161:
162: if ((m_Types & op.m_Types) != 0) {
163: throw new ParFileException("errorXOPSanity");
164: }
165:
166: mergeOp(op);
167: }
168:
169: // "add" without the error check, so we can use internally without worrying about exceptions.
170:
171: protected void mergeOp(ExtractOp op) {
172:
173: m_Types |= op.m_Types;
174: if (m_ProviderName == null)
175: m_ProviderName = op.m_ProviderName;
176: if (m_ChannelName == null)
177: m_ChannelName = op.m_ChannelName;
178: if (m_ChannelContainer == null)
179: m_ChannelContainer = op.m_ChannelContainer;
180: if (m_AvailContainer == null)
181: m_AvailContainer = op.m_AvailContainer;
182: if (m_DPMappingStr == null)
183: m_DPMappingStr = op.m_DPMappingStr;
184: m_MakeSelected = m_MakeSelected || op.m_MakeSelected;
185: }
186:
187: public String toArg() {
188:
189: StringBuffer buf = new StringBuffer();
190:
191: buf.append(ARG_DPNODE);
192: buf.append(ARG_EQ);
193: argEscape(buf, m_DPNode);
194:
195: if ((m_Types & TYPE_PROVIDER) != 0) {
196: buf.append(ARG_DELIM);
197: buf.append(ARG_PROVIDER);
198: if (m_ProviderName != null) {
199: buf.append(ARG_EQ);
200: argEscape(buf, m_ProviderName);
201: }
202: }
203:
204: if ((m_Types & TYPE_CHANNEL) != 0) {
205: buf.append(ARG_DELIM);
206: buf.append(ARG_CHANNEL);
207: if (m_ChannelName != null) {
208: buf.append(ARG_EQ);
209: argEscape(buf, m_ChannelName);
210: }
211: }
212:
213: if (m_EntryName != null) {
214: buf.append(ARG_DELIM);
215: buf.append(ARG_ENTRY);
216: buf.append(ARG_EQ);
217: argEscape(buf, m_EntryName);
218: }
219:
220: if (m_ChannelContainer != null) {
221: buf.append(ARG_DELIM);
222: buf.append(ARG_CONTAINER);
223: buf.append(ARG_EQ);
224: argEscape(buf, m_ChannelContainer);
225: if (m_AvailContainer != null) {
226: buf.append(ARG_DELIM);
227: buf.append(ARG_AVAIL);
228: buf.append(ARG_EQ);
229: argEscape(buf, m_AvailContainer);
230: if (m_MakeSelected) {
231: buf.append(ARG_DELIM);
232: buf.append(ARG_SELECTED);
233: }
234: }
235: }
236:
237: if (m_DPMappingStr != null) {
238: buf.append(ARG_DELIM);
239: buf.append(m_DPMappingStr);
240: }
241:
242: return buf.toString();
243: }
244:
245: public static ExtractOp makeOpWithDPMapping(String dpnode,
246: Map dpMappings) throws ParFileException {
247: ExtractOp op = new ExtractOp(0, dpnode);
248: op.m_DPMappingStr = dpMappingsToStr(dpMappings);
249:
250: return op;
251: }
252:
253: public static ExtractOp makeOpFromArgument(String arg)
254: throws ParFileException {
255: return makeOpFromArgument(null, arg);
256: }
257:
258: public static ExtractOp makeOpFromArgument(String dpnode, String arg)
259: throws ParFileException {
260:
261: StringTokenizer tok = new StringTokenizer(arg, ARG_DELIM);
262: ExtractOp op = dpnode == null ? null : new ExtractOp(0, dpnode);
263: Map dpMappings = new HashMap();
264:
265: while (tok.hasMoreTokens()) {
266:
267: String key = tok.nextToken();
268: String val = null;
269: int vidx = key.indexOf(ARG_EQ);
270: if (vidx > 0) {
271: val = argUnEscape(key.substring(vidx + 1));
272: key = key.substring(0, vidx);
273: }
274:
275: /*if (op == null) {
276: if (key.equalsIgnoreCase(ARG_DPNODE)) {
277: if (val == null) {
278: throw new ParFileException("errorXOPSyntax");
279: }
280: op = new ExtractOp(0,val);
281: continue;
282: }
283: throw new ParFileException("errorXOPSyntax");
284: } */
285: if (key.equalsIgnoreCase(ARG_DPNODE)) {
286: if (op == null) {
287: if (val == null) {
288: throw new ParFileException("errorXOPSyntax");
289: }
290: op = new ExtractOp(0, val);
291: }
292: continue;
293: }
294: if (key.equalsIgnoreCase(ARG_CHANNEL)) {
295: op.m_Types |= TYPE_CHANNEL;
296: op.m_ChannelName = val;
297: continue;
298: }
299: if (key.equalsIgnoreCase(ARG_PROVIDER)) {
300: op.m_Types |= TYPE_PROVIDER;
301: op.m_ProviderName = val;
302: continue;
303: }
304: if (key.equalsIgnoreCase(ARG_ENTRY)) {
305: op.m_EntryName = val;
306: continue;
307: }
308: if (key.equalsIgnoreCase(ARG_CONTAINER)) {
309: op.m_ChannelContainer = val;
310: continue;
311: }
312: if (key.equalsIgnoreCase(ARG_AVAIL)) {
313: op.m_AvailContainer = val;
314: continue;
315: }
316: if (key.equalsIgnoreCase(ARG_SELECTED)) {
317: op.m_MakeSelected = val == null ? true : Boolean
318: .valueOf(val).booleanValue();
319: continue;
320: }
321: if (key.startsWith(ARG_DP)) {
322: key = key.substring(ARG_DP.length(), key.length());
323: dpMappings.put(key, val);
324: continue;
325: }
326:
327: throw new ParFileException("errorXOPSyntax");
328: }
329:
330: if (op == null) {
331: throw new ParFileException("errorXOPSyntax");
332: }
333:
334: if (!dpMappings.isEmpty()) {
335: op.m_DPMappings = dpMappings;
336: op.m_DPMappingStr = dpMappingsToStr(dpMappings);
337: }
338:
339: return op;
340: }
341:
342: public Map getDPMappings() throws ParFileException {
343: if (m_DPMappingStr == null) {
344: return null;
345: }
346:
347: StringTokenizer tok = new StringTokenizer(m_DPMappingStr,
348: ARG_DELIM);
349: Map dpMappings = new HashMap();
350:
351: while (tok.hasMoreTokens()) {
352:
353: String key = tok.nextToken();
354: String val = null;
355: int vidx = key.indexOf(ARG_EQ);
356: if (vidx > 0) {
357: val = argUnEscape(key.substring(vidx + 1));
358: key = argUnEscape(key.substring(0, vidx));
359: key = key.substring(ARG_DP.length(), key.length());
360: dpMappings.put(key, val);
361: }
362: }
363: return dpMappings;
364: }
365:
366: // convert dpmappings from a map to a string
367: private static String dpMappingsToStr(Map dpMappings) {
368: StringBuffer buf = new StringBuffer(256);
369:
370: if (dpMappings != null && !dpMappings.isEmpty()) {
371: Set keys = dpMappings.keySet();
372: for (Iterator i = keys.iterator(); i.hasNext();) {
373: String key = (String) i.next();
374: String val = (String) dpMappings.get(key);
375: argEscape(buf, ARG_DP + key);
376: buf.append(ARG_EQ);
377: argEscape(buf, val);
378: buf.append(ARG_DELIM);
379: }
380: buf.deleteCharAt(buf.length() - 1);
381: }
382: return buf.toString();
383: }
384:
385: // We use URL encoding because it's widely understood, and serves the purpose
386: // of avoiding delimiter characters in the string, so that we may take arguments
387: // apart with a simple Stringtokenizer.
388:
389: private static void argEscape(StringBuffer buf, String val) {
390: buf.append(URLEncoder.encode(val));
391: }
392:
393: private static String argUnEscape(String val)
394: throws ParFileException {
395: try {
396: return URLDecoder.decode(val);
397: } catch (Exception ex) {
398: throw new ParFileException("errorXOPSyntax", ex);
399: }
400: }
401:
402: private static final String ARG_EQ = "=";
403: private static final String ARG_PROVIDER = "provider";
404: private static final String ARG_CHANNEL = "channel";
405: private static final String ARG_ENTRY = "entry";
406: private static final String ARG_CONTAINER = "container";
407: private static final String ARG_AVAIL = "avail";
408: private static final String ARG_SELECTED = "selected";
409: private static final String ARG_DP = "dp-";
410: public static final String ARG_DELIM = ",";
411: public static final String ARG_DPNODE = "dpnode";
412:
413: private int m_Types;
414: private String m_DPNode;
415:
416: protected String m_ProviderName = null;
417: protected String m_ChannelName = null;
418: protected String m_ChannelContainer = null;
419: protected String m_AvailContainer = null;
420: protected boolean m_MakeSelected = false;
421: protected String m_EntryName = null;
422: protected String m_DPMappingStr = null;
423: protected Map m_DPMappings = null;
424: }
|