Click here to Skip to main content
Click here to Skip to main content

Open Sourced Code Attributing in Code

, 11 Aug 2014 CDDL
Rate this:
Please Sign up or sign in to vote.
Custom attribute to give proper attribution to open sourced code.

Introduction

When working on large projects I regularly use snippets of code I find online and sometimes port classes from other languages (e.g. Java) to use in my VB.NET project.  Over time I've tried to figure out the best way to keep track of the different pieces of code I've used from all the online source along with their license, where I found the code, the original author, etc. so that I can give proper credit to the original author.

Then the idea of using a custom attribute came to me.  By using a custom attribute I can mark individual functions, modules, classes, and even assemblies (if I had to) write in the code.  This would also give me the ability to use a small utility that through reflection could compile a list of all the different author's code that may be in my project along with the proper license.

Using the code

The first thing needed before creating the attribute is a list of licenses. For this I used the list of licenses on Code Project (http://www.codeproject.com/info/Licenses.aspx) and TLDR Legal's Most Popular licenses list (https://tldrlegal.com/).

You'll notice that I have added a Description attribute to each of the enum members that includes the full name of the license.  The Licenses class we will shortly will use this attribute to get the full name of a license based on the enum member.  To use the Description attribute you must import the System.ComponentModel namespace.

Imports System.ComponentModel

Public Enum LicenseType
    <Description("The Apache License, Version 2.0")> Apache_2_0
    <Description("The BSD 2-Clause License (FreeBSD/Simplified)")> BSD_2_Clause
    <Description("The BSD 3-Clause License (BSD New/BSD Modified)")> BSD_3_Clause
    <Description("The Creative Commons Attribution 3.0 Unported License")> CCA_3_0_Unported
    <Description("The Creative Commons Attribution-NoDerivatives 3.0 Unported License")> CCA_ND_3_0
    <Description("The Creative Commons Attribution-ShareAlike 2.5 License")> CCA_SA_2_5
    <Description("The Creative Commons Attribution-ShareAlike 3.0 Unported License")> CCA_SA_3_0_Unported
    <Description("The Creative Commons Attribution 4.0 International")> CCA_4_International
    <Description("The Common Development and Distribution License 1.0")> CDDL_1_0
    <Description("The Common Public License Version 1.0")> CPL_1_0
    <Description("The Code Project Open License 1.02")> CPOL_1_02
    <Description("The Eclipse Public License 1.0")> EPL_1_0
    <Description("The GNU General Public License v2")> GPLv2
    <Description("The GNU General Public License v3")> GPLv3
    <Description("The GNU Lesser General Public License v2.1")> LGPLv2_1
    <Description("Teh GNU Lesser General Public License v3")> LGPLv3
    <Description("The MIT License")> MIT
    <Description("The Mozilla Public License 1.1")> MPL_1_1
    <Description("The Mozilla Public License 2.0")> MPL_2_0
    <Description("Microsoft Reciprocal License")> Ms_RL
    <Description("The Microsoft Public License")> Ms_PL
    <Description("The zlib/libpng License")> zlib_libpng
End Enum

The next thing needed is the Licenses class itself.  This class has three properties:

  • FullName - This is the full name of the license that will be taken from the description attribute
  • Abbreviation - An abbreviation of the license name (In this project I just used the Enum.ToString method but you can use whatever you like)
  • LicenseSummaryUrl - This is a link to a summary of the license for quick reference
  • FullLicenseUrl - This is a link to the full text of the license.  I tried to use the official link when I could find it.
Private _fullName As String
Private _abbreviation As String
Private _licenseSummaryUrl As String
Private _fullLicenseUrl As String 

Public ReadOnly Property FullName As String
    Get
        Return Me._fullName
    End Get
End Property
Public ReadOnly Property Abbreviation As String
    Get
        Return Me._abbreviation
    End Get
End Property
Public ReadOnly Property LicenseSummaryUrl As String
    Get
        Return Me._licenseSummaryUrl
    End Get
End Property
Public ReadOnly Property FullLicenseUrl As String
    Get
        Return Me._fullLicenseUrl
    End Get
End Property

Public Sub New(fullName As String, abbreviation As String, licenseSummaryUrl As String, fullLicenseUrl As String)
    Me._fullName = fullName
    Me._abbreviation = abbreviation
    Me._licenseSummaryUrl = licenseSummaryUrl
    Me._fullLicenseUrl = fullLicenseUrl
End Sub

To have the classes for each version of the license act like an Enum (so I could reference a license like Licenses.Apache20 and then access the additional license information) I created a public, shared, readonly variable for each of the license types.  To keep the text short I have only included a few of definitions as an example; please see the source code for all of them.

Public Shared ReadOnly CPOL102 As Licenses = New Licenses(LicenseType.CPOL_1_02.GetDescription, LicenseType.CPOL_1_02.ToString, "http://www.codeproject.com/info/cpol10.aspx", "http://www.codeproject.com/info/cpol10.aspx")
    Public Shared ReadOnly EPL10 As Licenses = New Licenses(LicenseType.EPL_1_0.GetDescription, LicenseType.EPL_1_0.ToString, "https://tldrlegal.com/license/eclipse-public-license-1.0-(epl-1.0)#summary", "https://www.eclipse.org/legal/epl-v10.html")
    Public Shared ReadOnly GPLv2 As Licenses = New Licenses(LicenseType.GPLv2.GetDescription, LicenseType.GPLv2.ToString, "https://tldrlegal.com/license/gnu-general-public-license-v2#summary", "http://www.gnu.org/licenses/gpl-2.0.html")
    Public Shared ReadOnly GPLv3 As Licenses = New Licenses(LicenseType.GPLv3.GetDescription, LicenseType.GPLv3.ToString, "https://tldrlegal.com/license/gnu-general-public-license-v3-(gpl-3)#summary", "http://www.gnu.org/licenses/gpl-3.0.html")

You'll notice that a method called GetDescription is called to pass a string to the FullName parameter of the Licenses constructor.  This is where the Description attribute is used; GetDescription is an extension method to get the Description atttribute text.

Imports System.ComponentModel
Imports System.Reflection
Imports System.Runtime.CompilerServices

Public Module Extensions
    <Extension>
    Public Function GetDescription(currentEnum As [Enum]) As String
        Dim description As String = String.Empty
        Dim da As DescriptionAttribute
        Dim fi As FieldInfo = currentEnum.GetType.GetField(currentEnum.ToString)

        da = DirectCast(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
        If da IsNot Nothing Then
            description = da.Description
        Else
            description = currentEnum.ToString
        End If

        Return description
    End Function
End Module

By having this extension method a Description attribute can be added to any Enum and the text easily retrived by calling GetDescription on any of the Enum members.  If there is no Description attribute on the Enum, the Enum.ToString text is returned.  You can read more about extension methods http://msdn.microsoft.com/en-us/library/bb384936.aspx

I created a Dictionary object which maps a LicenseType to a Licenses class.  This dictionary is used by GetLicense to return the proper Licenses class.

Private Shared ReadOnly lics As New Dictionary(Of LicenseType, Licenses) From {{LicenseType.Apache_2_0, Licenses.Apache20}, {LicenseType.BSD_2_Clause, Licenses.BSD2}, {LicenseType.BSD_3_Clause, Licenses.BSD3}, {LicenseType.CCA_3_0_Unported, Licenses.CCA3}, {LicenseType.CCA_ND_3_0, Licenses.CCAND30}, {LicenseType.CCA_SA_2_5, Licenses.CCASA25}, {LicenseType.CCA_SA_3_0_Unported, Licenses.CCASA30}, {LicenseType.CCA_4_International, Licenses.CCA4International}, {LicenseType.CDDL_1_0, Licenses.CDDL10}, {LicenseType.CPL_1_0, Licenses.CPL10}, {LicenseType.CPOL_1_02, Licenses.CPOL102}, {LicenseType.EPL_1_0, Licenses.EPL10}, {LicenseType.GPLv2, Licenses.GPLv2}, {LicenseType.GPLv3, Licenses.GPLv3}, {LicenseType.LGPLv2_1, Licenses.LGPL21}, {LicenseType.LGPLv3, Licenses.LGPLv3}, {LicenseType.MIT, Licenses.MIT}, {LicenseType.MPL_1_1, Licenses.MPL11}, {LicenseType.MPL_2_0, Licenses.MPL20}, {LicenseType.Ms_RL, Licenses.MsPL}, {LicenseType.zlib_libpng, Licenses.zlib_libpng}}

Public Shared Function GetLicense(license As LicenseType) As Licenses
    Return lics.Item(license)
End Function

Now that the licenses class is created we can work on creating the custom attribute.  I wanted the attribute to be able to be applied to anything (class, method, etc.) since the code snippets can be any of these.  Also the attribute should not be able to be applied multiple times to the same object and it should not be inherited.  So I created a class called OpenSourcedAttribute which inherits System.Attribute and applied an AttributeUsage attribute to it.

<AttributeUsage(AttributeTargets.All, AllowMultiple:=False, Inherited:=False)>
Public Class OpenSourcedAttribute
    Inherits Attribute

End Class

I felt the most important pieces of information to record were the original author's name, the original project name that the  code came from, the date I accessed the code, the original code's URL, and the obviously the code's license.  The properties are all public read/write to allow for name parameters (instead of just positional parameters) when the attribute is applied.

Private _author As String
Private _originalProjectName As String
Private _dateAttributed As String
Private _codeUrl As String
Private _codeLicense As Licenses 

Public Property Author As String
    Get
        Return Me._author
    End Get
    Set(value As String)
        Me._author = value
    End Set
End Property
Public Property DateAttributed As String
    Get
        Return Me._dateAttributed
    End Get
    Set(value As String)
        Me._dateAttributed = value
    End Set
End Property
Public Property OriginalProjectName As String
    Get
        Return Me._originalProjectName
    End Get
    Set(value As String)
        Me._originalProjectName = value
    End Set
End Property
Public Property CodeUrl As String
    Get
        Return Me._codeUrl
    End Get
    Set(value As String)
        Me._codeUrl = value
    End Set
End Property
Public Property CodeLicense As Licenses
    Get
        Return Me._codeLicense
    End Get
    Set(value As Licenses)
        Me._codeLicense = value
    End Set
End Property

The constructor only has one required parameter and that is the license.  The other parameters can be included but using one of the overloaded constructors

Public Sub New(license As LicenseType)
    Me.New(license, String.Empty, String.Empty, String.Empty)
End Sub
Public Sub New(license As LicenseType, author As String)
    Me.New(license, author, String.Empty, String.Empty)
End Sub
Public Sub New(license As LicenseType, author As String, originalProjectName As String)
    Me.New(license, author, originalProjectName, String.Empty)
End Sub
Public Sub New(license As LicenseType, author As String, originalProjectName As String, dateAttributed As String)
    Me.New(license, author, originalProjectName, dateAttributed, String.Empty)
End Sub
Public Sub New(license As LicenseType, Optional author As String = "", Optional originalProjectName As String = "", Optional dateAttributed As String = "", Optional codeUrl As String = "")
    Me._codeLicense = Licenses.GetLicense(license)
    Me._author = author
    Me._originalProjectName = originalProjectName
    Me._codeUrl = codeUrl
End Sub

Now with all that in place we can actually use the attribute.  Add a console applicate to the solution.  In the default module (Module1) apply the new attribute:

<OpenSourced(LicenseType.Apache_2_0, "Dominick", "http://www.google.com", "8/8/2014", "Open Sourced License Attribute")>
Module Module1 

End Module

To get the information from the attribute we first must get a reference to the attribute.  This is done by first defining a new OpenSourcedAttribute and using the static GetCustomAttribute method of the System.Attribute class

Dim osAttrib As OpenSourcedAttribute
osAttrib = CType(Attribute.GetCustomAttribute(GetType(Module1), GetType(OpenSourcedAttribute)), OpenSourcedAttribute)

Now the you can get all the information from the attribute by using the osAttrib object:

With osAttrib
    Dim author As String = .Author
    Dim dateAttributed As String = .DateAttributed
    Dim projectName As String = .OriginalProjectName
    Dim codeUrl As String = .CodeUrl
    Dim codeLink As Uri = .CodeLink
End With

Dim lic As Licenses = osAttrib.CodeLicense
With lic
    Dim fullName As String = .FullName
    Dim abbrev As String = .Abbreviation
    Dim summaryUrl As String = .LicenseSummaryUrl
    Dim summaryLink As Uri = .LicenseSummaryLink
    Dim fullUrl As String = .FullLicenseUrl
    Dim fullLink As Uri = .FullLicenseLink
    Dim i As Integer = 0
End With

With this custom attribute I can now easily record open source snippets of code (or classes) that I use in my projects and the code is directly attributed.  By using these tags in all my assemblies I can even get the license information of a method in assembly A from assembly B; for me it is also much neater.

I hope others find and get as must use out of this custom attribute as I do.

Points of Interest

To read more about creating and using custom attributes please visit http://msdn.microsoft.com/en-us/library/5x6cd29c(v=vs.110).aspx

History

1.0 - Initial post
1.1 - Minor formatting corrections

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

Share

About the Author

Dominick Marciano
CEO Sci-Med Codimg
United States United States
No Biography provided
Follow on   LinkedIn

Comments and Discussions

 
QuestionCould you check... PinmemberNelek12-Sep-14 2:13 
AnswerRe: Could you check... PinprofessionalDisIsHoody12-Sep-14 7:32 
GeneralGreat solution to important problem PinmemberSeth Morris22-Aug-14 10:48 
GeneralRe: Great solution to important problem PinprofessionalDisIsHoody12-Sep-14 7:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141220.1 | Last Updated 12 Aug 2014
Article Copyright 2014 by Dominick Marciano
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid