001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.felix.framework.util.manifestparser;
020    
021    import org.apache.felix.framework.util.MapToDictionary;
022    import org.apache.felix.framework.util.VersionRange;
023    import org.apache.felix.moduleloader.ICapability;
024    import org.apache.felix.moduleloader.IRequirement;
025    import org.osgi.framework.*;
026    
027    public class Requirement implements IRequirement
028    {
029        private final String m_namespace;
030        private final R4Directive[] m_directives;
031        private final R4Attribute[] m_attributes;
032        private final boolean m_isOptional;
033    
034        private final String m_targetName;
035        private final VersionRange m_targetVersionRange;
036        private volatile Filter m_filter;
037    
038        public Requirement(String namespace, String filterStr) throws InvalidSyntaxException
039        {
040            m_namespace = namespace;
041            m_filter = FrameworkUtil.createFilter(filterStr);
042            m_directives = null;
043            m_attributes = null;
044            m_isOptional = false;
045            m_targetName = null;
046            m_targetVersionRange = null;
047        }
048    
049        public Requirement(String namespace, R4Directive[] directives, R4Attribute[] attributes)
050        {
051            m_namespace = namespace;
052            m_directives = directives;
053            m_attributes = attributes;
054            m_filter = null;
055    
056            // Find all import directives: resolution.
057            boolean optional = false;
058            for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
059            {
060                if (m_directives[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
061                {
062                    optional = m_directives[i].getValue().equals(Constants.RESOLUTION_OPTIONAL);
063                }
064            }
065            m_isOptional = optional;
066    
067            String targetName = null;
068            VersionRange targetVersionRange = VersionRange.infiniteRange;
069            for (int i = 0; i < m_attributes.length; i++)
070            {
071                if (m_namespace.equals(ICapability.MODULE_NAMESPACE))
072                {
073                    if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
074                    {
075                        targetName = (String) m_attributes[i].getValue();
076                    }
077                    else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
078                    {
079                        targetVersionRange = (VersionRange) m_attributes[i].getValue();
080                    }
081                }
082                else if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE))
083                {
084                    if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY))
085                    {
086                        targetName = (String) m_attributes[i].getValue();
087                    }
088                    else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY))
089                    {
090                        targetVersionRange = (VersionRange) m_attributes[i].getValue();
091                    }
092                }
093                else if (m_namespace.equals(ICapability.HOST_NAMESPACE))
094                {
095                    if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
096                    {
097                        targetName = (String) m_attributes[i].getValue();
098                    }
099                    else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
100                    {
101                        targetVersionRange = (VersionRange) m_attributes[i].getValue();
102                    }
103                }
104            }
105            m_targetName = targetName;
106            m_targetVersionRange = targetVersionRange;
107        }
108    
109        public String getNamespace()
110        {
111            return m_namespace;
112        }
113    
114        public Filter getFilter()
115        {
116            if (m_filter == null)
117            {
118                m_filter = convertToFilter();
119            }
120            return m_filter;
121        }
122    
123    // TODO: RB - We need to verify that the resolver code does not
124    // touch these implementation-specific methods.
125    
126        public String getTargetName()
127        {
128            return m_targetName;
129        }
130    
131        public VersionRange getTargetVersionRange()
132        {
133            return m_targetVersionRange;
134        }
135    
136        public R4Directive[] getDirectives()
137        {
138            // TODO: RB - We should return copies of the arrays probably.
139            return m_directives;
140        }
141    
142        public R4Attribute[] getAttributes()
143        {
144            // TODO: RB - We should return copies of the arrays probably.
145            return m_attributes;
146        }
147    
148        public boolean isMultiple()
149        {
150            return false;
151        }
152    
153        public boolean isOptional()
154        {
155            return m_isOptional;
156        }
157    
158        public String getComment()
159        {
160            return "Comment for " + toString();
161        }
162    
163        public boolean isSatisfied(ICapability capability)
164        {
165            // If the requirement was constructed with a filter, then
166            // we must use that filter for evaluation.
167            if ((m_attributes == null) && (m_filter != null))
168            {
169                return m_namespace.equals(capability.getNamespace()) &&
170                    getFilter().match(new MapToDictionary(capability.getProperties()));
171            }
172            // Otherwise, if the requirement was constructed with attributes, then
173            // perform the evaluation manually instead of using the filter for
174            // performance reasons.
175            else if (m_attributes != null)
176            {
177                return capability.getNamespace().equals(getNamespace()) &&
178                    doAttributesMatch((Capability) capability);
179            }
180    
181            return false;
182        }
183    
184        private boolean doAttributesMatch(Capability ec)
185        {
186            // Grab the capability's attributes.
187            R4Attribute[] capAttrs = ec.getAttributes();
188    
189            // Cycle through all attributes of this import package
190            // and make sure its values match the attribute values
191            // of the specified export package.
192            for (int reqAttrIdx = 0; reqAttrIdx < m_attributes.length; reqAttrIdx++)
193            {
194                // Get current attribute from this import package.
195                R4Attribute reqAttr = m_attributes[reqAttrIdx];
196    
197                // Check if the export package has the same attribute.
198                boolean found = false;
199                for (int capAttrIdx = 0;
200                    (!found) && (capAttrIdx < capAttrs.length);
201                    capAttrIdx++)
202                {
203                    // Get current attribute for the export package.
204                    R4Attribute capAttr = capAttrs[capAttrIdx];
205                    // Check if the attribute names are equal.
206                    if (reqAttr.getName().equals(capAttr.getName()))
207                    {
208                        // We only recognize version types. If the value of the
209                        // attribute is a version/version range, then we use the
210                        // "in range" comparison, otherwise we simply use equals().
211                        if (capAttr.getValue() instanceof Version)
212                        {
213                            if (!((VersionRange) reqAttr.getValue()).isInRange((Version) capAttr.getValue()))
214                            {
215                                return false;
216                            }
217                        }
218                        else if (capAttr.getValue() instanceof Object[])
219                        {
220                            Object[] values = (Object[]) capAttr.getValue();
221                            boolean matched = false;
222                            for (int valIdx = 0; !matched && (valIdx < values.length); valIdx++)
223                            {
224                                if (reqAttr.getValue().equals(values[valIdx]))
225                                {
226                                    matched = true;
227                                }
228                            }
229                            if (!matched)
230                            {
231                                return false;
232                            }
233                        }
234                        else if (!reqAttr.getValue().equals(capAttr.getValue()))
235                        {
236                            return false;
237                        }
238                        found = true;
239                    }
240                }
241                // If the attribute was not found, then return false.
242                if (!found)
243                {
244                    return false;
245                }
246            }
247    
248            // Now, cycle through all attributes of the export package and verify that
249            // all mandatory attributes are present in this import package.
250            for (int capAttrIdx = 0; capAttrIdx < capAttrs.length; capAttrIdx++)
251            {
252                // Get current attribute for this package.
253                R4Attribute capAttr = capAttrs[capAttrIdx];
254    
255                // If the export attribute is mandatory, then make sure
256                // this import package has the attribute.
257                if (capAttr.isMandatory())
258                {
259                    boolean found = false;
260                    for (int reqAttrIdx = 0;
261                        (!found) && (reqAttrIdx < m_attributes.length);
262                        reqAttrIdx++)
263                    {
264                        // Get current attribute from specified package.
265                        R4Attribute reqAttr = m_attributes[reqAttrIdx];
266    
267                        // Check if the attribute names are equal
268                        // and set found flag.
269                        if (capAttr.getName().equals(reqAttr.getName()))
270                        {
271                            found = true;
272                        }
273                    }
274                    // If not found, then return false.
275                    if (!found)
276                    {
277                        return false;
278                    }
279                }
280            }
281    
282            return true;
283        }
284    
285        private Filter convertToFilter()
286        {
287            StringBuffer sb = new StringBuffer();
288            if ((m_attributes != null) && (m_attributes.length > 1))
289            {
290                sb.append("(&");
291            }
292            for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++)
293            {
294                // If this is a package import, then convert wild-carded
295                // dynamically imported package names to an OR comparison.
296                if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE) &&
297                    m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY) &&
298                    m_attributes[i].getValue().toString().endsWith(".*"))
299                {
300                    int idx = m_attributes[i].getValue().toString().indexOf(".*");
301                    sb.append("(|(package=");
302                    sb.append(m_attributes[i].getValue().toString().substring(0, idx));
303                    sb.append(")(package=");
304                    sb.append(m_attributes[i].getValue().toString());
305                    sb.append("))");
306                }
307                else if (m_attributes[i].getValue() instanceof VersionRange)
308                {
309                    VersionRange vr = (VersionRange) m_attributes[i].getValue();
310                    if (vr.isLowInclusive())
311                    {
312                        sb.append("(");
313                        sb.append(m_attributes[i].getName());
314                        sb.append(">=");
315                        sb.append(vr.getLow().toString());
316                        sb.append(")");
317                    }
318                    else
319                    {
320                        sb.append("(!(");
321                        sb.append(m_attributes[i].getName());
322                        sb.append("<=");
323                        sb.append(vr.getLow().toString());
324                        sb.append("))");
325                    }
326    
327                    if (vr.getHigh() != null)
328                    {
329                        if (vr.isHighInclusive())
330                        {
331                            sb.append("(");
332                            sb.append(m_attributes[i].getName());
333                            sb.append("<=");
334                            sb.append(vr.getHigh().toString());
335                            sb.append(")");
336                        }
337                        else
338                        {
339                            sb.append("(!(");
340                            sb.append(m_attributes[i].getName());
341                            sb.append(">=");
342                            sb.append(vr.getHigh().toString());
343                            sb.append("))");
344                        }
345                    }
346                }
347                else
348                {
349                    sb.append("(");
350                    sb.append(m_attributes[i].getName());
351                    sb.append("=");
352                    sb.append(m_attributes[i].getValue().toString());
353                    sb.append(")");
354                }
355            }
356    
357            if ((m_attributes != null) && (m_attributes.length > 1))
358            {
359                sb.append(")");
360            }
361    
362            try
363            {
364                return FrameworkUtil.createFilter(sb.toString());
365            }
366            catch (InvalidSyntaxException ex)
367            {
368                // This should never happen, so we can safely ignore.
369            }
370    
371            return null;
372        }
373    
374        public String toString()
375        {
376            return getNamespace() + "; " + getFilter().toString();
377        }
378    }