View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software 
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License.
16   */
17  
18  package org.apache.jdo.impl.enhancer.meta.prop;
19  
20  import java.lang.reflect.Modifier;
21  
22  import java.util.Iterator;
23  import java.util.Enumeration;
24  import java.util.Map;
25  import java.util.List;
26  import java.util.Collection;
27  import java.util.HashSet;
28  import java.util.HashMap;
29  import java.util.ArrayList;
30  import java.util.Properties;
31  import java.util.StringTokenizer;
32  
33  import java.text.MessageFormat;
34  
35  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError;
36  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException;
37  
38  
39  
40  /***
41   * This class parses properties containing meta data information
42   * about classes. The syntax of the properties is the following:
43   * <ul>
44   *   <li> the keys in the properties file are fully qualified classnames or
45   *        fully qualified fieldnames </li>
46   *   <li> a fields is separated by a classname with a hash mark ('#')
47   *        (e.g. "test.Test#test1") </li>
48   *   <li> all classnames are given in a natural form (e.g.
49   *        "java.lang.Integer", "java.lang.Integer[][]", "int",
50   *        "test.Test$Test1") </li>
51   *   <li> property keys are classnames and fieldnames
52   *        (e.g. "test.Test=...", "test.Test#field1=...") <br> </li>
53   *   <li> Classnames can have the following attributes:
54   *     <ul>
55   *       <li> jdo:{persistent|transactional} </li>
56   *       <li> super: &#60;classname&#62; </li>
57   *       <li> oid: &#60;classname&#62; </li>
58   *       <li> access: {public|protected|package|private} </li>
59   *     </ul> </li>
60   *   <li> Fieldnames can have the following attributes:
61   *     <ul>
62   *       <li> type:&#60;type&#62; </li>
63   *       <li> access: {public|protected|package|private} </li>
64   *       <li> jdo:{persistent|transactional|transient} </li>
65   *       <li> annotation:{key|dfg|mediated} </li>
66   *     </ul> </li>
67   *   <li> the names of the attributes can be ommitted: you can say <br>
68   *        test.Test1#field1=jdo:persistent,type:java.lang.String,key,... <br>
69   *        or <br>
70   *        test.Test1#field1=persistent,java.lang.String,key,... <br>
71   *        or <br>
72   *        test.Test1#field1=jdo:persistent,java.lang.String,key,... <br> </li>
73   *   <li> in order to find fields of a class, a line for the class has to be
74   *        specified in the properties: To find the field
75   *        <code>test.Test1#field</code>, the keys <code>test.Test1</code> and
76   *        <code>test.Test1#Field</code> have to be present. </li>
77   * </ul>
78   * This class is not thread safe.
79   */
80  final class MetaDataProperties
81  {
82      /***
83       *  The delimiter of a property key between the class- and fieldname.
84       */
85      static final char FIELD_DELIMITER = '#';
86  
87      /***
88       *  A string of delimiter characters between attributes.
89       */
90      static final String PROPERTY_DELIMITERS = " \t,;";
91  
92      /***
93       *  A delimiter character between attribute name and attribute value
94       */
95      static final char PROPERTY_ASSIGNER = ':';
96  
97      // attribute names for classes and fields
98      static final String PROPERTY_ACCESS_MODIFIER = "access";
99      static final String PROPERTY_JDO_MODIFIER    = "jdo";
100     static final String PROPERTY_SUPER_CLASSNAME = "super";
101     static final String PROPERTY_OID_CLASSNAME   = "oid";
102     static final String PROPERTY_TYPE            = "type";
103     static final String PROPERTY_ANNOTATION_TYPE = "annotation";
104 
105     // values of the access attribute of classes and fields.
106     static final String ACCESS_PRIVATE       = "private";
107     static final String ACCESS_PACKAGE_LOCAL = "package";
108     static final String ACCESS_PROTECTED     = "protected";
109     static final String ACCESS_PUBLIC        = "public";
110 
111     // values of the jdo attribute of classes and fields.
112     static final String JDO_TRANSIENT     = "transient";
113     static final String JDO_PERSISTENT    = "persistent";
114     static final String JDO_TRANSACTIONAL = "transactional";
115 
116     // values of the annotation type attribute of fields.
117     static final String ANNOTATION_TYPE_KEY      = "key";
118     static final String ANNOTATION_TYPE_DFG      = "dfg";
119     static final String ANNOTATION_TYPE_MEDIATED = "mediated";
120 
121     /***
122      *  The properties to parse.
123      */
124     private Properties properties;
125 
126     /***
127      *  A map of already read class properties. The keys are the
128      *  classnames, the values are the appropriate
129      *  <code>JDOClass</code>-object.
130      */
131     private final Map cachedJDOClasses = new HashMap();
132 
133     /***
134      *  A constant for the cache indicating that a given classname
135      *  if not specified in the properties.
136      */
137     static private final JDOClass NULL = new JDOClass(null);
138 
139     /***
140      *  A temporary vector (this is the reason why the implementation is not
141      *  thread safe).
142      */
143     private final List tmpTokens = new ArrayList();
144 
145     /***
146      *  Creates a new object with the given properties.
147      *
148      *  @param  props  The properties.
149      */
150     public MetaDataProperties(Properties props)
151     {
152         this.properties = props;
153     }
154 
155     /***
156      *  Get the information about the class with the given name.
157      *
158      *  @param  classname  The classname.
159      *  @return  The information about the class or <code>null</code> if no
160      *           information is given.
161      *  @throws  EnhancerMetaDataUserException  If something went wrong parsing
162      *                                     the properties.
163      */
164     public final JDOClass getJDOClass(String classname)
165         throws EnhancerMetaDataUserException
166     {
167         classname = NameHelper.toCanonicalClassName(classname);
168         JDOClass clazz = (JDOClass)cachedJDOClasses.get(classname);
169         if (clazz == NULL) { //already searched but not found
170             return null;
171         }
172         if (clazz != null) {
173             return clazz;
174         }
175 
176         //load it from the properties file
177         String s = properties.getProperty(classname);
178         if (s == null) { //class not defined
179             cachedJDOClasses.put(classname, NULL);
180             return null;
181         }
182 
183         //the class could be found in the properties
184         clazz = parseJDOClass(classname, s);  //parse the class attributes
185         parseJDOFields(clazz);  //parse all fields
186         validateDependencies(clazz);  //check dependencies
187         cachedJDOClasses.put(clazz.getName(), clazz);
188 
189         return clazz;
190     }
191 
192     /***
193      *  Gets the information about the specified field.
194      *
195      *  @param  classname  The name of the class.
196      *  @param  fieldname  The name of the field of the class.
197      *  @return  The information about the field or <code>null</code> if
198      *           no information could be found.
199      *  @throws  EnhancerMetaDataUserException  If something went wrong parsing
200      *                                     the properties.
201      */
202     public final JDOField getJDOField(String fieldname,
203                                       String classname)
204         throws EnhancerMetaDataUserException
205     {
206         JDOClass clazz = getJDOClass(classname);
207         return (clazz != null ? clazz.getField(fieldname) : null);
208     }
209 
210     /***
211      *  Gets all classnames in the properties.
212      *
213      *  @return  All classnames in the properties.
214      */
215     public final String[] getKnownClassNames()
216     {
217         Collection classnames = new HashSet();
218         for (Enumeration names = properties.propertyNames();
219              names.hasMoreElements();) {
220             String name = (String)names.nextElement();
221             if (name.indexOf(FIELD_DELIMITER) < 0) {
222                 classnames.add(NameHelper.fromCanonicalClassName(name));
223             }
224         }
225 
226         return (String[])classnames.toArray(new String[classnames.size()]);
227     }
228 
229     /***
230      *  Parses the attributes-string of a class and puts them into a
231      *  <code>JDOClass</code>-object.
232      *
233      *  @param  classname  The name of the class.
234      *  @param  attributes  The attribute-string as specified in the properties.
235      *  @return  The create <code>JDOClass</code>-object.
236      *  @throws  EnhancerMetaDataUserException  If something went wrong parsing
237      *                                     the attributes.
238      */
239     private final JDOClass parseJDOClass(String classname,
240                                          String attributes)
241         throws EnhancerMetaDataUserException
242     {
243         List props = parseProperties(attributes);
244 
245         // check each property
246         for (int i = 0; i < props.size(); i++) {
247             final Property prop = (Property)props.get(i);
248             validateClassProperty(prop, classname);
249         }
250 
251         // check dependencies of all properties
252         checkForDuplicateProperties(props, classname);
253 
254         // properties are OK - assign them to the JDOClass object
255         JDOClass clazz = new JDOClass(classname);
256         for (int i = 0; i < props.size(); i++) {
257             Property prop = (Property)props.get(i);
258             if (prop.name.equals(PROPERTY_ACCESS_MODIFIER)) {
259                 clazz.setModifiers(getModifiers(prop.value));
260             } else if (prop.name.equals(PROPERTY_JDO_MODIFIER)) {
261                 clazz.setPersistent(prop.value.equals(JDO_PERSISTENT));
262             } else if (prop.name.equals(PROPERTY_SUPER_CLASSNAME)) {
263                 clazz.setSuperClassName(prop.value);
264             } else if (prop.name.equals(PROPERTY_OID_CLASSNAME)) {
265                 clazz.setOidClassName(prop.value);
266             }
267         }
268 
269         return clazz;
270     }
271 
272     /***
273      *  Checks if the given attribute-property of a class is valid.
274      *
275      *  @param  prop       The attribute-property.
276      *  @param  classname  The classname.
277      *  @throws  EnhancerMetaDataUserException  If the validation failed.
278      */
279     static private void validateClassProperty(Property prop,
280                                               String classname)
281         throws EnhancerMetaDataUserException
282     {
283         String value = prop.value;
284         if (prop.name == null) {
285             // try to guess the property name
286             if (value.equals(ACCESS_PUBLIC)
287                 || value.equals(ACCESS_PROTECTED)
288                 || value.equals(ACCESS_PACKAGE_LOCAL)
289                 || value.equals(ACCESS_PRIVATE)) {
290                 // assume access modifier
291                 prop.name = PROPERTY_ACCESS_MODIFIER;
292             } else if (value.equals(JDO_PERSISTENT)
293                        || value.equals(JDO_TRANSIENT)) {
294                 // assume persistence modifier
295                 prop.name = PROPERTY_JDO_MODIFIER;
296             }
297             //@olsen: not unique anymore, could also be oid class name
298             // else {
299             //     //assume the the given value is the superclassname
300             //     prop.name = PROPERTY_SUPER_CLASSNAME;
301             // }
302         } else {
303             // do we have a valid property name?
304             String name = prop.name;
305             checkPropertyName(prop.name,
306                               new String[]{
307                                   PROPERTY_ACCESS_MODIFIER,
308                                   PROPERTY_JDO_MODIFIER,
309                                   PROPERTY_SUPER_CLASSNAME,
310                                   PROPERTY_OID_CLASSNAME
311                               },
312                               classname);
313 
314             // do we have a valid property value?
315             checkPropertyValue(prop,
316                                new String[]{
317                                    ACCESS_PUBLIC,
318                                    ACCESS_PROTECTED,
319                                    ACCESS_PACKAGE_LOCAL,
320                                    ACCESS_PRIVATE
321                                },
322                                PROPERTY_ACCESS_MODIFIER,
323                                classname);
324             checkPropertyValue(prop,
325                                new String[]{
326                                    JDO_TRANSIENT,
327                                    JDO_PERSISTENT
328                                },
329                                PROPERTY_JDO_MODIFIER,
330                                classname);
331         }
332     }
333 
334     /***
335      *  Parses all fields of a given class.
336      *
337      *  @param  clazz  the representation of the class
338      *  @throws EnhancerMetaDataUserException on parse errors
339      */
340     private final void parseJDOFields(JDOClass clazz)
341         throws EnhancerMetaDataUserException
342     {
343         //search for fields of the class
344         for (Enumeration names = properties.propertyNames();
345              names.hasMoreElements();) {
346             String name = (String)names.nextElement();
347             if (name.startsWith(clazz.getName() + FIELD_DELIMITER)) {
348                 //field found
349                 String fieldname
350                     = name.substring(name.indexOf(FIELD_DELIMITER) + 1,
351                                      name.length());
352                 validateFieldName(fieldname, clazz.getName());
353                 clazz.addField(parseJDOField(properties.getProperty(name),
354                                              fieldname, clazz));
355             }
356         }
357         clazz.sortFields();
358     }
359 
360     /***
361      *  Parses the attribute-string of a field.
362      *
363      *  @param  attributes  The attribute-string.
364      *  @param  fieldname   The fieldname.
365      *  @param  clazz       The class to field belongs to.
366      *  @throws  EnhancerMetaDataUserException  on parse errors
367      */
368     private final JDOField parseJDOField(String attributes,
369                                          String fieldname,
370                                          JDOClass clazz)
371         throws EnhancerMetaDataUserException
372     {
373         List props = parseProperties(attributes);
374 
375         //check each property
376         for (int i = 0; i < props.size(); i++) {
377             Property prop = (Property)props.get(i);
378             validateFieldProperty(prop, fieldname, clazz.getName());
379         }
380 
381         //check dependencies of all properties
382         checkForDuplicateProperties(props,
383                                     clazz.getName() + FIELD_DELIMITER
384                                     + fieldname);
385 
386         //properties are OK - assign them to the JDOField object
387         JDOField field = new JDOField(fieldname);
388         for (int i = 0; i < props.size(); i++) {
389             Property prop = (Property)props.get(i);
390             if (prop.name.equals(PROPERTY_ACCESS_MODIFIER)) {
391                 field.setModifiers(getModifiers(prop.value));
392             } else if (prop.name.equals(PROPERTY_JDO_MODIFIER)) {
393                 field.setJdoModifier(prop.value);
394             } else if (prop.name.equals(PROPERTY_TYPE)) {
395                 field.setType(prop.value);
396             } else if (prop.name.equals(PROPERTY_ANNOTATION_TYPE)) {
397                 field.setAnnotationType(prop.value);
398             }
399         }
400 
401         return field;
402     }
403 
404     /***
405      *  Checks if the given attribute-property if valid for a field.
406      *
407      *  @param  prop       The attribute-property.
408      *  @param  fieldname  The fieldname.
409      *  @param  classname  The classname.
410      *  @throws  EnhancerMetaDataUserException  If the check fails.
411      */
412     private final void validateFieldProperty(Property prop,
413                                              String fieldname,
414                                              String classname)
415         throws EnhancerMetaDataUserException
416     {
417         //try to guess the property name
418         String value = prop.value;
419         if (prop.name == null) {
420             if (value.equals(ACCESS_PUBLIC)  ||
421                 value.equals(ACCESS_PROTECTED)  ||
422                 value.equals(ACCESS_PACKAGE_LOCAL)  ||
423                 value.equals(ACCESS_PRIVATE)) {
424                 // access modifier
425                 prop.name = PROPERTY_ACCESS_MODIFIER;
426             } else if (value.equals(JDO_PERSISTENT)  ||
427                      value.equals(JDO_TRANSIENT)  ||
428                      value.equals(JDO_TRANSACTIONAL)) {
429                 // persistence modifier
430                 prop.name = PROPERTY_JDO_MODIFIER;
431             } else if (value.equals(ANNOTATION_TYPE_KEY)  ||
432                      value.equals(ANNOTATION_TYPE_DFG)  ||
433                      value.equals(ANNOTATION_TYPE_MEDIATED)) {
434                 // annotation type
435                 prop.name = PROPERTY_ANNOTATION_TYPE;
436             } else {
437                 //assume the the given value is the type
438                 prop.name = PROPERTY_TYPE;
439             }
440         } else {
441             String entry = classname + FIELD_DELIMITER + fieldname;
442 
443             //do we have a valid property name?
444             checkPropertyName(prop.name,
445                               new String[]{
446                                   PROPERTY_ACCESS_MODIFIER,
447                                   PROPERTY_JDO_MODIFIER,
448                                   PROPERTY_TYPE,
449                                   PROPERTY_ANNOTATION_TYPE
450                               },
451                               entry);
452 
453             //do we have a valid property value
454             checkPropertyValue(prop,
455                                new String[]{
456                                    ACCESS_PUBLIC,
457                                    ACCESS_PROTECTED,
458                                    ACCESS_PACKAGE_LOCAL,
459                                    ACCESS_PRIVATE
460                                },
461                                PROPERTY_ACCESS_MODIFIER,
462                                entry);
463             checkPropertyValue(prop,
464                                new String[]{
465                                    JDO_PERSISTENT,
466                                    JDO_TRANSIENT,
467                                    JDO_TRANSACTIONAL
468                                },
469                                PROPERTY_JDO_MODIFIER,
470                                entry);
471             checkPropertyValue(prop,
472                                new String[]{
473                                    ANNOTATION_TYPE_KEY,
474                                    ANNOTATION_TYPE_DFG,
475                                    ANNOTATION_TYPE_MEDIATED
476                                },
477                                PROPERTY_ANNOTATION_TYPE,
478                                entry);
479         }
480     }
481 
482     /***
483      *  Validates dependencies between a class and its fields and between.
484      *
485      *  @param clazz the class
486      *  @throws  EnhancerMetaDataUserException  if the validation fails
487      */
488     private final void validateDependencies(JDOClass clazz)
489         throws EnhancerMetaDataUserException
490     {
491         final List fields = clazz.getFields();
492         for (Iterator i = fields.iterator(); i.hasNext();) {
493             JDOField field = (JDOField)i.next();
494 
495             // check the jdo field modifier
496             if (field.isPersistent() && clazz.isTransient()) {
497                 // non-persistent classes cannot have persistent fields
498                 final String msg
499                     = getMsg(Msg.ERR_TRANSIENT_CLASS_WITH_PERSISTENT_FIELD,
500                              new String[]{
501                                  clazz.getName(),
502                                  field.getName() });
503                 throw new EnhancerMetaDataUserException(msg);
504             }
505             if (field.isTransactional() && clazz.isTransient()) {
506                 // non-persistent classes cannot have transactional fields
507                 final String msg
508                     = getMsg(Msg.ERR_TRANSIENT_CLASS_WITH_TRANSACTIONAL_FIELD,
509                              new String[]{
510                                  clazz.getName(),
511                                  field.getName() });
512                 throw new EnhancerMetaDataUserException(msg);
513             }
514             if (!field.isKnownTransient() && !field.isManaged()) {
515                 // unspecified persistence modifier
516                 final String msg
517                     = getMsg(Msg.ERR_UNSPECIFIED_FIELD_PERSISTENCE_MODIFIER,
518                              new String[]{
519                                  clazz.getName(),
520                                  field.getName() });
521                 throw new EnhancerMetaDataUserException(msg);
522             }
523 
524             // check annotation type
525             if (!field.isAnnotated() && field.isManaged()) {
526                 // unspecified annotation type
527                 final String msg
528                     = getMsg(Msg.ERR_UNSPECIFIED_FIELD_ANNOTATION_TYPE,
529                              new String[]{
530                                  clazz.getName(),
531                                  field.getName() });
532                 throw new EnhancerMetaDataUserException(msg);
533             }
534             if (field.isAnnotated() && !field.isManaged()) {
535                 // non managed field with annotation type
536                 final String msg
537                     = getMsg(Msg.ERR_NON_MANAGED_ANNOTATED_FIELD,
538                              new String[]{
539                                  clazz.getName(),
540                                  field.getName() });
541                 throw new EnhancerMetaDataUserException(msg);
542             }
543             if (field.isAnnotated() && clazz.isTransient()) {
544                 // a non-persistent class cannot have an annotated field
545                 final String msg
546                     = getMsg(Msg.ERR_TRANSIENT_CLASS_WITH_ANNOTATED_FIELD,
547                              new String[]{
548                                  clazz.getName(),
549                                  field.getName() });
550                 throw new EnhancerMetaDataUserException(msg);
551             }
552         }
553     }
554 
555     /***
556      *  Checks if a given fieldname is a valid Java identifier.
557      *
558      *  @param  fieldname  The fieldname.
559      *  @param  classname  The corresponding classname.
560      *  @throws  EnhancerMetaDataUserException  If the check fails.
561      */
562     static private void validateFieldName(String fieldname,
563                                           String classname)
564         throws EnhancerMetaDataUserException
565     {
566         if (fieldname.length() == 0) {
567             final String msg
568                 = getMsg(Msg.ERR_EMPTY_FIELDNAME,
569                          new String[]{ classname });
570             throw new EnhancerMetaDataUserException(msg);
571         }
572 
573         if (!Character.isJavaIdentifierStart(fieldname.charAt(0))) {
574             final String msg
575                 = getMsg(Msg.ERR_INVALID_FIELDNAME,
576                          new String[]{ classname, fieldname });
577             throw new EnhancerMetaDataUserException(msg);
578         }
579 
580         for (int i = fieldname.length() - 1; i >= 0; i--) {
581             final char c = fieldname.charAt(i);
582             if (!Character.isJavaIdentifierPart(c)) {
583                 final String msg
584                     = getMsg(Msg.ERR_INVALID_FIELDNAME,
585                              new String[]{ classname, fieldname });
586                 throw new EnhancerMetaDataUserException(msg);
587             }
588         }
589     }
590 
591     /***
592      *  Checks if an attribute-property was entered twice for a class or field.
593      *
594      *  @param  props  The properties.
595      *  @param  entry  The class- or fieldname.
596      *  @throws  EnhancerMetaDataUserException  If the check fails.
597      */
598     static private void checkForDuplicateProperties(List props,
599                                                     String entry)
600         throws EnhancerMetaDataUserException
601     {
602         for (int i = 0; i < props.size(); i++) {
603             for (int j = i + 1; j < props.size(); j++) {
604                 Property p1 = (Property)props.get(i);
605                 Property p2 = (Property)props.get(j);
606                 if (p1.name.equals(p2.name) && !p1.value.equals(p2.value)) {
607                     final String msg
608                         = getMsg(Msg.ERR_DUPLICATE_PROPERTY_NAME,
609                                  new String[]{
610                                      entry,
611                                      p1.name,
612                                      p1.value,
613                                      p2.value });
614                     throw new EnhancerMetaDataUserException(msg);
615                 }
616             }
617         }
618     }
619 
620     /***
621      *  Checks if an attribute name is recognized by the parser.
622      *
623      *  @param  name        The name of the attribute.
624      *  @param  validnames  A list of valid names(the attribute name has to
625      *                      be in this list).
626      *  @param  entry       The class- or fieldname.
627      *  @throws  EnhancerMetaDataUserException  If the check fails.
628      */
629     static private void checkPropertyName(String name,
630                                           String[] validnames,
631                                           String entry)
632         throws EnhancerMetaDataUserException
633     {
634         for (int i = 0; i < validnames.length; i++) {
635             if (name.equals(validnames[i])) {
636                 return;
637             }
638         }
639 
640         final String msg
641             = getMsg(Msg.ERR_INVALID_PROPERTY_NAME,
642                      new String[]{ entry, name });
643         throw new EnhancerMetaDataUserException(msg);
644     }
645 
646     /***
647      *  Checks if the given value of an attribute-property is recognized by
648      *  by the parser if that value belongs to a given attribute name.
649      *
650      *  @param  prop         The attribute-property(with name and value).
651      *  @param  validvalues  A list of valid values.
652      *  @param  name         The name of the attribute-property to check.
653      *  @param  entry        The class- or fieldname.
654      *  @throws  EnhancerMetaDataUserException  If the check fails.
655      */
656     static private void checkPropertyValue(Property  prop,
657                                            String[] validvalues,
658                                                  String    name,
659                                                  String    entry)
660         throws EnhancerMetaDataUserException
661     {
662         if ( !prop.name.equals(name)) {
663             return;
664         }
665 
666         for (int i = 0; i < validvalues.length; i++) {
667             if (prop.value.equals(validvalues[i])) {
668                 return;
669             }
670         }
671 
672         final String msg
673             = getMsg(Msg.ERR_INVALID_PROPERTY_VALUE,
674                      new String[]{ entry, name, prop.value });
675         throw new EnhancerMetaDataUserException(msg);
676     }
677 
678     /***
679      *  Formats an error message with the given parameters.
680      *
681      *  @param  msg     The message with format strings.
682      *  @param  params  The params to format the message with.
683      *  @return  The formatted error message.
684      */
685     static final String getMsg(String   msg,
686                                String[] params)
687     {
688         return MessageFormat.format(msg, params);
689     }
690 
691     /***
692      *  Parses the attribute-string of a class- or fieldname.
693      *
694      *  @param  attributes  The attribute-string.
695      *  @return  A list of <code>Propert<</code>-objects for the attributes.
696      *  @exception  EnhancerMetaDataUserException  If the parsing fails.
697      */
698     final List parseProperties(String attributes)
699         throws EnhancerMetaDataUserException
700     {
701         tmpTokens.clear();
702         for (StringTokenizer t
703                  = new StringTokenizer(attributes, PROPERTY_DELIMITERS);
704              t.hasMoreTokens();) {
705             tmpTokens.add(parseProperty(t.nextToken()));
706         }
707 
708         return tmpTokens;
709     }
710 
711     /***
712      *  Parses the given attribute and splits it into name and value.
713      *
714      *  @param  attribute  The attribute-string.
715      *  @return  The <code>Propert</code>-object.
716      *  @exception  EnhancerMetaDataUserException  If the parsing fails.
717      */
718     private final Property parseProperty(String attribute)
719         throws EnhancerMetaDataUserException
720     {
721         Property prop = new Property();
722         int idx = attribute.indexOf(PROPERTY_ASSIGNER);
723         if (idx < 0) {
724             prop.value = attribute;
725         } else {
726             prop.name = attribute.substring(0, idx);
727             prop.value = attribute.substring(idx + 1, attribute.length());
728             if (prop.name.length() == 0 || prop.value.length() == 0) {
729                 final String msg
730                     = getMsg(Msg.ERR_EMPTY_PROPERTY_NAME_OR_VALUE,
731                              new String[]{ attribute });
732                 throw new EnhancerMetaDataUserException(msg);
733             }
734         }
735 
736         return prop;
737     }
738 
739     /***
740      * Returns the modifier value for a Java modifier name.
741      */
742     static private int getModifiers(String modifier)
743     {
744         if (modifier.equals(ACCESS_PUBLIC)) {
745             return Modifier.PUBLIC;
746         }
747         if (modifier.equals(ACCESS_PRIVATE)) {
748             return Modifier.PRIVATE;
749         }
750         if (modifier.equals(ACCESS_PROTECTED)) {
751             return Modifier.PROTECTED;
752         }
753         return 0;
754     }
755 
756     /***
757      *  A simple test to run from the command line.
758      *
759      *  @param  argv  The command line arguments.
760      */
761     public static void main(String[] argv)
762     {
763         if (argv.length != 1) {
764             System.err.println("Error: no property filename specified");
765             return;
766         }
767         final Properties p = new Properties();
768         try {
769             java.io.InputStream in
770                 = new java.io.FileInputStream(new java.io.File(argv[0]));
771             p.load(in);
772             in.close();
773             System.out.println("PROPERTIES: " + p);
774             System.out.println("############");
775             final MetaDataProperties props = new MetaDataProperties(p);
776             String[] classnames = props.getKnownClassNames();
777             for (int i = 0; i < classnames.length; i++) {
778                 String classname = classnames[i];
779                 System.out.println(classname + ": "
780                                    + props.getJDOClass(classname));
781             }
782         } catch(Throwable ex) {
783             ex.printStackTrace(System.err);
784         }
785     }
786 
787     /***
788      *  The holder-class for the name and the value of a property.
789      */
790     static private final class Property
791     {
792         /***
793          *  The name of the property.
794          */
795         String name = null;
796 
797         /***
798          *  The value of the property.
799          */
800         String value = null;
801 
802         /***
803          *  Creates a string-representation of this object.
804          *
805          *  @return  The string-representation of this object.
806          */
807         public final String toString()
808         {
809             return '<' + name + ':' + value + '>';
810         }
811     }
812 
813     //^olsen: -> Bundle.properties
814 
815     /***
816      *  Holds all unformatted error messages.
817      */
818     static private interface Msg
819     {
820         // the unformatted error messages
821         static final String PREFIX = "Error Parsing meta data properties: ";
822 
823         static final String ERR_EMPTY_FIELDNAME =
824         PREFIX + "The class ''{0}'' may not have an empty fieldname.";
825 
826         static final String ERR_INVALID_FIELDNAME =
827         PREFIX + "The field name ''{1}'' of class ''{0}'' is not valid.";
828 
829         static final String ERR_EMPTY_PROPERTY_NAME_OR_VALUE  =
830         PREFIX + "The property name and value may not be empty if a ''" + 
831         PROPERTY_ASSIGNER + "'' is specified: ''{0}''.";
832 
833         static final String ERR_INVALID_PROPERTY_NAME =
834         PREFIX + "Invalid property name for entry ''{0}'': ''{1}''.";
835 
836         static final String ERR_INVALID_PROPERTY_VALUE =
837         PREFIX + "Invalid value for property ''{1}'' of entry ''{0}'': ''{2}''.";
838 
839         static final String ERR_DUPLICATE_PROPERTY_NAME =
840         PREFIX + "The property ''{1}'' for the entry ''{0}'' entered twice with values: ''{2}'' and ''{3}''.";
841 
842         static final String ERR_UNSPECIFIED_FIELD_PERSISTENCE_MODIFIER =
843         PREFIX + "No persistence modifier specified for field: ''{0}.{1}''.";
844 
845         static final String ERR_TRANSIENT_CLASS_WITH_PERSISTENT_FIELD =
846         PREFIX + "A non-persistent class cannot have a persistent field(class ''{0}'' with field ''{1})''.";
847 
848         static final String ERR_TRANSIENT_CLASS_WITH_TRANSACTIONAL_FIELD =
849         PREFIX + "A non-persistent class cannot have a transactional field(class ''{0}'' with field ''{1})''.";
850 
851         static final String ERR_UNSPECIFIED_FIELD_ANNOTATION_TYPE =
852         PREFIX + "No annotation type specified for field: ''{0}.{1}''.";
853 
854         static final String ERR_TRANSIENT_CLASS_WITH_ANNOTATED_FIELD =
855         PREFIX + "A non-persistent class cannot have an annotated field(''{1}'' of class ''{0}'') can''t have a fetch group.";
856 
857         static final String ERR_NON_MANAGED_ANNOTATED_FIELD =
858         PREFIX + "A non-managed field(''{1}'' of class ''{0}'') can''t be a annotated.";
859     }
860 }